DPsim
MNASolver.cpp
1 /* Copyright 2017-2021 Institute for Automation of Complex Power Systems,
2  * EONERC, RWTH Aachen University
3  *
4  * This Source Code Form is subject to the terms of the Mozilla Public
5  * License, v. 2.0. If a copy of the MPL was not distributed with this
6  * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7  *********************************************************************************/
8 
9 #include <dpsim/MNASolver.h>
10 #include <dpsim/SequentialScheduler.h>
11 #include <memory>
12 
13 using namespace DPsim;
14 using namespace CPS;
15 
16 namespace DPsim {
17 
18 template <typename VarType>
19 MnaSolver<VarType>::MnaSolver(String name, CPS::Domain domain,
20  CPS::Logger::Level logLevel)
21  : Solver(name, logLevel), mDomain(domain) {
22 
23  // Raw source and solution vector logging
24  mLeftVectorLog = std::make_shared<DataLogger>(
25  name + "_LeftVector", logLevel == CPS::Logger::Level::trace);
26  mRightVectorLog = std::make_shared<DataLogger>(
27  name + "_RightVector", logLevel == CPS::Logger::Level::trace);
28 }
29 
30 template <typename VarType>
32  mSystem = system;
33 }
34 
35 template <typename VarType> void MnaSolver<VarType>::initialize() {
36  // TODO: check that every system matrix has the same dimensions
37  SPDLOG_LOGGER_INFO(mSLog, "---- Start initialization ----");
38  mLeftVectorLog->start();
39  mRightVectorLog->start();
40 
41  // Register attribute for solution vector
43  // Best case we have some kind of sub-attributes for attribute vectors / tensor attributes...
44  if (mFrequencyParallel) {
45  SPDLOG_LOGGER_INFO(mSLog, "Computing network harmonics in parallel.");
46  for (Int freq = 0; freq < mSystem.mFrequencies.size(); ++freq) {
47  mLeftSideVectorHarm.push_back(AttributeStatic<Matrix>::make());
48  }
49  } else {
50  mLeftSideVector = AttributeStatic<Matrix>::make();
51  }
52 
53  SPDLOG_LOGGER_INFO(mSLog, "-- Process topology");
54  for (auto comp : mSystem.mComponents)
55  SPDLOG_LOGGER_INFO(mSLog, "Added {:s} '{:s}' to simulation.", comp->type(),
56  comp->name());
57 
58  // Otherwise LU decomposition will fail
59  if (mSystem.mComponents.size() == 0)
60  throw SolverException();
61 
62  // We need to differentiate between power and signal components and
63  // ground nodes should be ignored.
64  identifyTopologyObjects();
65  // These steps complete the network information.
66  collectVirtualNodes();
67  assignMatrixNodeIndices();
68 
69  SPDLOG_LOGGER_INFO(mSLog, "-- Create empty MNA system matrices and vectors");
70  createEmptyVectors();
71  createEmptySystemMatrix();
72 
73  // Initialize components from powerflow solution and
74  // calculate MNA specific initialization values.
75  initializeComponents();
76 
77  if (mSteadyStateInit) {
78  mIsInInitialization = true;
79  steadyStateInitialization();
80  }
81  mIsInInitialization = false;
82 
83  // Some components feature a different behaviour for simulation and initialization
84  for (auto comp : mSystem.mComponents) {
85  auto powerComp = std::dynamic_pointer_cast<CPS::TopologicalPowerComp>(comp);
86  if (powerComp)
87  powerComp->setBehaviour(TopologicalPowerComp::Behaviour::MNASimulation);
88 
89  auto sigComp = std::dynamic_pointer_cast<CPS::SimSignalComp>(comp);
90  if (sigComp)
91  sigComp->setBehaviour(SimSignalComp::Behaviour::Simulation);
92  }
93 
94  // Initialize system matrices and source vector.
95  initializeSystem();
96 
97  SPDLOG_LOGGER_INFO(mSLog, "--- Initialization finished ---");
98  SPDLOG_LOGGER_INFO(mSLog, "--- Initial system matrices and vectors ---");
99  logSystemMatrices();
100 
101  mSLog->flush();
102 }
103 
104 template <> void MnaSolver<Real>::initializeComponents() {
105  SPDLOG_LOGGER_INFO(mSLog, "-- Initialize components from power flow");
106 
107  CPS::MNAInterface::List allMNAComps;
108  allMNAComps.insert(allMNAComps.end(), mMNAComponents.begin(),
109  mMNAComponents.end());
110  allMNAComps.insert(allMNAComps.end(), mMNAIntfVariableComps.begin(),
111  mMNAIntfVariableComps.end());
112 
113  for (auto comp : allMNAComps) {
114  auto pComp = std::dynamic_pointer_cast<SimPowerComp<Real>>(comp);
115  if (!pComp)
116  continue;
117  pComp->checkForUnconnectedTerminals();
118  if (mInitFromNodesAndTerminals)
119  pComp->initializeFromNodesAndTerminals(mSystem.mSystemFrequency);
120  }
121 
122  // Initialize signal components.
123  for (auto comp : mSimSignalComps)
124  comp->initialize(mSystem.mSystemOmega, mTimeStep);
125 
126  // Initialize MNA specific parts of components.
127  for (auto comp : allMNAComps) {
128  comp->mnaInitialize(mSystem.mSystemOmega, mTimeStep, mLeftSideVector);
129  const Matrix &stamp = comp->getRightVector()->get();
130  if (stamp.size() != 0) {
131  mRightVectorStamps.push_back(&stamp);
132  }
133  }
134 
135  for (auto comp : mMNAIntfSwitches)
136  comp->mnaInitialize(mSystem.mSystemOmega, mTimeStep, mLeftSideVector);
137 
138  // Initialize nodes
139  for (UInt nodeIdx = 0; nodeIdx < mNodes.size(); ++nodeIdx)
140  mNodes[nodeIdx]->initialize();
141 }
142 
143 template <> void MnaSolver<Complex>::initializeComponents() {
144  SPDLOG_LOGGER_INFO(mSLog, "-- Initialize components from power flow");
145 
146  CPS::MNAInterface::List allMNAComps;
147  allMNAComps.insert(allMNAComps.end(), mMNAComponents.begin(),
148  mMNAComponents.end());
149  allMNAComps.insert(allMNAComps.end(), mMNAIntfVariableComps.begin(),
150  mMNAIntfVariableComps.end());
151 
152  // Initialize power components with frequencies and from powerflow results
153  for (auto comp : allMNAComps) {
154  auto pComp = std::dynamic_pointer_cast<SimPowerComp<Complex>>(comp);
155  if (!pComp)
156  continue;
157  pComp->checkForUnconnectedTerminals();
158  if (mInitFromNodesAndTerminals)
159  pComp->initializeFromNodesAndTerminals(mSystem.mSystemFrequency);
160  }
161 
162  // Initialize signal components.
163  for (auto comp : mSimSignalComps)
164  comp->initialize(mSystem.mSystemOmega, mTimeStep);
165 
166  SPDLOG_LOGGER_INFO(mSLog, "-- Initialize MNA properties of components");
167  if (mFrequencyParallel) {
168  // Initialize MNA specific parts of components.
169  for (auto comp : mMNAComponents) {
170  // Initialize MNA specific parts of components.
171  comp->mnaInitializeHarm(mSystem.mSystemOmega, mTimeStep,
172  mLeftSideVectorHarm);
173  const Matrix &stamp = comp->getRightVector()->get();
174  if (stamp.size() != 0)
175  mRightVectorStamps.push_back(&stamp);
176  }
177  // Initialize nodes
178  for (UInt nodeIdx = 0; nodeIdx < mNodes.size(); ++nodeIdx) {
179  mNodes[nodeIdx]->mnaInitializeHarm(mLeftSideVectorHarm);
180  }
181  } else {
182  // Initialize MNA specific parts of components.
183  for (auto comp : allMNAComps) {
184  comp->mnaInitialize(mSystem.mSystemOmega, mTimeStep, mLeftSideVector);
185  const Matrix &stamp = comp->getRightVector()->get();
186  if (stamp.size() != 0) {
187  mRightVectorStamps.push_back(&stamp);
188  }
189  }
190 
191  for (auto comp : mMNAIntfSwitches)
192  comp->mnaInitialize(mSystem.mSystemOmega, mTimeStep, mLeftSideVector);
193 
194  // Initialize nodes
195  for (UInt nodeIdx = 0; nodeIdx < mNodes.size(); ++nodeIdx)
196  mNodes[nodeIdx]->initialize();
197  }
198 }
199 
200 template <typename VarType> void MnaSolver<VarType>::initializeSystem() {
201  SPDLOG_LOGGER_INFO(mSLog,
202  "-- Initialize MNA system matrices and source vector");
203  mRightSideVector.setZero();
204 
205  // just a sanity check in case we change the static
206  // initialization of the switch number in the future
207  if (mSwitches.size() > sizeof(std::size_t) * 8) {
208  throw SystemError("Too many Switches.");
209  }
210 
211  if (mFrequencyParallel)
212  initializeSystemWithParallelFrequencies();
213  else if (mSystemMatrixRecomputation)
214  initializeSystemWithVariableMatrix();
215  else
216  initializeSystemWithPrecomputedMatrices();
217 }
218 
219 template <typename VarType>
221  // iterate over all possible switch state combinations and frequencies
222  for (std::size_t sw = 0; sw < (1ULL << mSwitches.size()); ++sw) {
223  for (Int freq = 0; freq < mSystem.mFrequencies.size(); ++freq) {
224  switchedMatrixEmpty(sw, freq);
225  switchedMatrixStamp(sw, freq, mMNAComponents, mSwitches);
226  }
227  }
228 
229  if (mSwitches.size() > 0)
230  updateSwitchStatus();
231 
232  // Initialize source vector
233  for (Int freq = 0; freq < mSystem.mFrequencies.size(); ++freq) {
234  for (auto comp : mMNAComponents)
235  comp->mnaApplyRightSideVectorStampHarm(mRightSideVectorHarm[freq], freq);
236  }
237 }
238 
239 template <typename VarType>
241  // iterate over all possible switch state combinations
242  for (std::size_t i = 0; i < (1ULL << mSwitches.size()); i++) {
243  switchedMatrixEmpty(i);
244  }
245 
246  if (mSwitches.size() < 1) {
247  switchedMatrixStamp(0, mMNAComponents);
248  } else {
249  // Generate switching state dependent system matrices
250  for (std::size_t i = 0; i < (1ULL << mSwitches.size()); i++) {
251  switchedMatrixStamp(i, mMNAComponents);
252  }
253  updateSwitchStatus();
254  }
255 
256  // Initialize source vector for debugging
257  // CAUTION: this does not always deliver proper source vector initialization
258  // as not full pre-step is executed (not involving necessary electrical or signal
259  // subcomp updates before right vector calculation)
260  for (auto comp : mMNAComponents) {
261  comp->mnaApplyRightSideVectorStamp(mRightSideVector);
262  auto idObj = std::dynamic_pointer_cast<IdentifiedObject>(comp);
263  SPDLOG_LOGGER_DEBUG(mSLog, "Stamping {:s} {:s} into source vector",
264  idObj->type(), idObj->name());
265  if (mSLog->should_log(spdlog::level::trace))
266  mSLog->trace("\n{:s}", Logger::matrixToString(mRightSideVector));
267  }
268 }
269 
270 template <typename VarType>
272 
273  // Collect index pairs of varying matrix entries from components
274  for (auto varElem : mVariableComps)
275  for (auto varEntry : varElem->mVariableSystemMatrixEntries)
276  mListVariableSystemMatrixEntries.push_back(varEntry);
277  SPDLOG_LOGGER_INFO(mSLog, "List of index pairs of varying matrix entries: ");
278  for (auto indexPair : mListVariableSystemMatrixEntries)
279  SPDLOG_LOGGER_INFO(mSLog, "({}, {})", indexPair.first, indexPair.second);
280 
281  stampVariableSystemMatrix();
282 
283  // Initialize source vector for debugging
284  // CAUTION: this does not always deliver proper source vector initialization
285  // as not full pre-step is executed (not involving necessary electrical or signal
286  // subcomp updates before right vector calculation)
287  for (auto comp : mMNAComponents) {
288  comp->mnaApplyRightSideVectorStamp(mRightSideVector);
289  auto idObj = std::dynamic_pointer_cast<IdentifiedObject>(comp);
290  SPDLOG_LOGGER_DEBUG(mSLog, "Stamping {:s} {:s} into source vector",
291  idObj->type(), idObj->name());
292  if (mSLog->should_log(spdlog::level::trace))
293  mSLog->trace("\n{:s}", Logger::matrixToString(mRightSideVector));
294  }
295 }
296 
297 template <typename VarType>
299  for (auto varElem : mVariableComps) {
300  if (varElem->hasParameterChanged()) {
301  auto idObj = std::dynamic_pointer_cast<IdentifiedObject>(varElem);
302  SPDLOG_LOGGER_DEBUG(
303  mSLog, "Component ({:s} {:s}) value changed -> Update System Matrix",
304  idObj->type(), idObj->name());
305  return true;
306  }
307  }
308  return false;
309 }
310 
311 template <typename VarType> void MnaSolver<VarType>::updateSwitchStatus() {
312  for (UInt i = 0; i < mSwitches.size(); ++i) {
313  mCurrentSwitchStatus.set(i, mSwitches[i]->mnaIsClosed());
314  }
315 }
316 
317 template <typename VarType> void MnaSolver<VarType>::identifyTopologyObjects() {
318  for (auto baseNode : mSystem.mNodes) {
319  // Add nodes to the list and ignore ground nodes.
320  if (!baseNode->isGround()) {
321  auto node = std::dynamic_pointer_cast<CPS::SimNode<VarType>>(baseNode);
322  mNodes.push_back(node);
323  SPDLOG_LOGGER_INFO(mSLog, "Added node {:s}", node->name());
324  }
325  }
326 
327  for (auto comp : mSystem.mComponents) {
328 
329  auto genComp = std::dynamic_pointer_cast<CPS::MNASyncGenInterface>(comp);
330  if (genComp) {
331  mSyncGen.push_back(genComp);
332  }
333 
334  auto swComp = std::dynamic_pointer_cast<CPS::MNASwitchInterface>(comp);
335  if (swComp) {
336  mSwitches.push_back(swComp);
337  auto mnaComp = std::dynamic_pointer_cast<CPS::MNAInterface>(swComp);
338  if (mnaComp)
339  mMNAIntfSwitches.push_back(mnaComp);
340  }
341 
342  auto varComp =
343  std::dynamic_pointer_cast<CPS::MNAVariableCompInterface>(comp);
344  if (varComp) {
345  mVariableComps.push_back(varComp);
346  auto mnaComp = std::dynamic_pointer_cast<CPS::MNAInterface>(varComp);
347  if (mnaComp)
348  mMNAIntfVariableComps.push_back(mnaComp);
349  }
350 
351  if (!(swComp || varComp)) {
352  auto mnaComp = std::dynamic_pointer_cast<CPS::MNAInterface>(comp);
353  if (mnaComp)
354  mMNAComponents.push_back(mnaComp);
355 
356  auto sigComp = std::dynamic_pointer_cast<CPS::SimSignalComp>(comp);
357  if (sigComp)
358  mSimSignalComps.push_back(sigComp);
359  }
360  }
361 }
362 
363 template <typename VarType> void MnaSolver<VarType>::assignMatrixNodeIndices() {
364  UInt matrixNodeIndexIdx = 0;
365  for (UInt idx = 0; idx < mNodes.size(); ++idx) {
366  mNodes[idx]->setMatrixNodeIndex(0, matrixNodeIndexIdx);
367  SPDLOG_LOGGER_INFO(mSLog, "Assigned index {} to phase A of node {}",
368  matrixNodeIndexIdx, idx);
369  ++matrixNodeIndexIdx;
370  if (mNodes[idx]->phaseType() == CPS::PhaseType::ABC) {
371  mNodes[idx]->setMatrixNodeIndex(1, matrixNodeIndexIdx);
372  SPDLOG_LOGGER_INFO(mSLog, "Assigned index {} to phase B of node {}",
373  matrixNodeIndexIdx, idx);
374  ++matrixNodeIndexIdx;
375  mNodes[idx]->setMatrixNodeIndex(2, matrixNodeIndexIdx);
376  SPDLOG_LOGGER_INFO(mSLog, "Assigned index {} to phase C of node {}",
377  matrixNodeIndexIdx, idx);
378  ++matrixNodeIndexIdx;
379  }
380  // This should be true when the final network node is reached, not considering virtual nodes
381  if (idx == mNumNetNodes - 1)
382  mNumNetMatrixNodeIndices = matrixNodeIndexIdx;
383  }
384  // Total number of network nodes including virtual nodes is matrixNodeIndexIdx + 1, which is why the variable is incremented after assignment
385  mNumMatrixNodeIndices = matrixNodeIndexIdx;
386  mNumVirtualMatrixNodeIndices =
387  mNumMatrixNodeIndices - mNumNetMatrixNodeIndices;
388  mNumHarmMatrixNodeIndices =
389  static_cast<UInt>(mSystem.mFrequencies.size() - 1) *
390  mNumMatrixNodeIndices;
391  mNumTotalMatrixNodeIndices =
392  static_cast<UInt>(mSystem.mFrequencies.size()) * mNumMatrixNodeIndices;
393 
394  SPDLOG_LOGGER_INFO(mSLog, "Assigned simulation nodes to topology nodes:");
395  SPDLOG_LOGGER_INFO(mSLog, "Number of network simulation nodes: {:d}",
396  mNumNetMatrixNodeIndices);
397  SPDLOG_LOGGER_INFO(mSLog, "Number of simulation nodes: {:d}",
398  mNumMatrixNodeIndices);
399  SPDLOG_LOGGER_INFO(mSLog, "Number of harmonic simulation nodes: {:d}",
400  mNumHarmMatrixNodeIndices);
401 }
402 
403 template <> void MnaSolver<Real>::createEmptyVectors() {
404  mRightSideVector = Matrix::Zero(mNumMatrixNodeIndices, 1);
405  **mLeftSideVector = Matrix::Zero(mNumMatrixNodeIndices, 1);
406 }
407 
408 template <> void MnaSolver<Complex>::createEmptyVectors() {
409  if (mFrequencyParallel) {
410  for (Int freq = 0; freq < mSystem.mFrequencies.size(); ++freq) {
411  mRightSideVectorHarm.push_back(
412  Matrix::Zero(2 * (mNumMatrixNodeIndices), 1));
413  mLeftSideVectorHarm.push_back(AttributeStatic<Matrix>::make(
414  Matrix::Zero(2 * (mNumMatrixNodeIndices), 1)));
415  }
416  } else {
417  mRightSideVector = Matrix::Zero(
418  2 * (mNumMatrixNodeIndices + mNumHarmMatrixNodeIndices), 1);
419  **mLeftSideVector = Matrix::Zero(
420  2 * (mNumMatrixNodeIndices + mNumHarmMatrixNodeIndices), 1);
421  }
422 }
423 
424 template <typename VarType> void MnaSolver<VarType>::collectVirtualNodes() {
425  // We have not added virtual nodes yet so the list has only network nodes
426  mNumNetNodes = (UInt)mNodes.size();
427  // virtual nodes are placed after network nodes
428  UInt virtualNode = mNumNetNodes - 1;
429 
430  for (auto comp : mMNAComponents) {
431  auto pComp = std::dynamic_pointer_cast<SimPowerComp<VarType>>(comp);
432  if (!pComp)
433  continue;
434 
435  // Check if component requires virtual node and if so get a reference
436  if (pComp->hasVirtualNodes()) {
437  for (UInt node = 0; node < pComp->virtualNodesNumber(); ++node) {
438  mNodes.push_back(pComp->virtualNode(node));
439  SPDLOG_LOGGER_INFO(mSLog, "Collected virtual node {} of {}",
440  virtualNode, node, pComp->name());
441  }
442  }
443 
444  // Repeat the same steps for virtual nodes of sub components
445  // TODO: recursive behavior
446  if (pComp->hasSubComponents()) {
447  for (auto pSubComp : pComp->subComponents()) {
448  for (UInt node = 0; node < pSubComp->virtualNodesNumber(); ++node) {
449  mNodes.push_back(pSubComp->virtualNode(node));
450  SPDLOG_LOGGER_INFO(mSLog, "Collected virtual node {} of {}",
451  virtualNode, node, pComp->name());
452  }
453  }
454  }
455  }
456 
457  // collect virtual nodes of variable components
458  for (auto comp : mVariableComps) {
459  auto pComp = std::dynamic_pointer_cast<SimPowerComp<VarType>>(comp);
460  if (!pComp)
461  continue;
462 
463  // Check if component requires virtual node and if so get a reference
464  if (pComp->hasVirtualNodes()) {
465  for (UInt node = 0; node < pComp->virtualNodesNumber(); ++node) {
466  mNodes.push_back(pComp->virtualNode(node));
467  SPDLOG_LOGGER_INFO(mSLog,
468  "Collected virtual node {} of Varible Comp {}", node,
469  pComp->name());
470  }
471  }
472  }
473 
474  // Update node number to create matrices and vectors
475  mNumNodes = (UInt)mNodes.size();
476  mNumVirtualNodes = mNumNodes - mNumNetNodes;
477  SPDLOG_LOGGER_INFO(mSLog, "Created virtual nodes:");
478  SPDLOG_LOGGER_INFO(mSLog, "Number of network nodes: {:d}", mNumNetNodes);
479  SPDLOG_LOGGER_INFO(mSLog, "Number of network and virtual nodes: {:d}",
480  mNumNodes);
481 }
482 
483 template <typename VarType>
485  SPDLOG_LOGGER_INFO(mSLog, "--- Run steady-state initialization ---");
486 
487  DataLogger initLeftVectorLog(mName + "_InitLeftVector",
488  mLogLevel != CPS::Logger::Level::off);
489  initLeftVectorLog.start();
490  DataLogger initRightVectorLog(mName + "_InitRightVector",
491  mLogLevel != CPS::Logger::Level::off);
492  initRightVectorLog.start();
493 
494  TopologicalPowerComp::Behaviour initBehaviourPowerComps =
495  TopologicalPowerComp::Behaviour::Initialization;
496  SimSignalComp::Behaviour initBehaviourSignalComps =
497  SimSignalComp::Behaviour::Initialization;
498 
499  // TODO: enable use of timestep distinct from simulation timestep
500  Real initTimeStep = mTimeStep;
501 
502  Int timeStepCount = 0;
503  Real time = 0;
504  Real maxDiff = 1.0;
505  Real max = 1.0;
506  Matrix diff = Matrix::Zero(2 * mNumNodes, 1);
507  Matrix prevLeftSideVector = Matrix::Zero(2 * mNumNodes, 1);
508 
509  SPDLOG_LOGGER_INFO(mSLog,
510  "Time step is {:f}s for steady-state initialization",
511  initTimeStep);
512 
513  for (auto comp : mSystem.mComponents) {
514  auto powerComp = std::dynamic_pointer_cast<CPS::TopologicalPowerComp>(comp);
515  if (powerComp)
516  powerComp->setBehaviour(initBehaviourPowerComps);
517 
518  auto sigComp = std::dynamic_pointer_cast<CPS::SimSignalComp>(comp);
519  if (sigComp)
520  sigComp->setBehaviour(initBehaviourSignalComps);
521  }
522 
523  initializeSystem();
524  logSystemMatrices();
525 
526  // Use sequential scheduler
527  SequentialScheduler sched;
528  CPS::Task::List tasks;
529  Scheduler::Edges inEdges, outEdges;
530 
531  for (auto node : mNodes) {
532  for (auto task : node->mnaTasks())
533  tasks.push_back(task);
534  }
535  for (auto comp : mMNAComponents) {
536  for (auto task : comp->mnaTasks()) {
537  tasks.push_back(task);
538  }
539  }
540  // TODO signal components should be moved out of MNA solver
541  for (auto comp : mSimSignalComps) {
542  for (auto task : comp->getTasks()) {
543  tasks.push_back(task);
544  }
545  }
546  tasks.push_back(createSolveTask());
547 
548  sched.resolveDeps(tasks, inEdges, outEdges);
549  sched.createSchedule(tasks, inEdges, outEdges);
550 
551  while (time < mSteadStIniTimeLimit) {
552  // Reset source vector
553  mRightSideVector.setZero();
554 
555  sched.step(time, timeStepCount);
556 
557  if (mDomain == CPS::Domain::EMT) {
558  initLeftVectorLog.logEMTNodeValues(time, leftSideVector());
559  initRightVectorLog.logEMTNodeValues(time, rightSideVector());
560  } else {
561  initLeftVectorLog.logPhasorNodeValues(time, leftSideVector());
562  initRightVectorLog.logPhasorNodeValues(time, rightSideVector());
563  }
564 
565  // Calculate new simulation time
566  time = time + initTimeStep;
567  ++timeStepCount;
568 
569  // Calculate difference
570  diff = prevLeftSideVector - **mLeftSideVector;
571  prevLeftSideVector = **mLeftSideVector;
572  maxDiff = diff.lpNorm<Eigen::Infinity>();
573  max = (**mLeftSideVector).lpNorm<Eigen::Infinity>();
574  // If difference is smaller than some epsilon, break
575  if ((maxDiff / max) < mSteadStIniAccLimit)
576  break;
577  }
578 
579  SPDLOG_LOGGER_INFO(mSLog, "Max difference: {:f} or {:f}% at time {:f}",
580  maxDiff, maxDiff / max, time);
581 
582  // Reset system for actual simulation
583  mRightSideVector.setZero();
584 
585  SPDLOG_LOGGER_INFO(mSLog, "--- Finished steady-state initialization ---");
586 }
587 
588 template <typename VarType> Task::List MnaSolver<VarType>::getTasks() {
589  Task::List l;
590 
591  for (auto comp : mMNAComponents) {
592  for (auto task : comp->mnaTasks()) {
593  l.push_back(task);
594  }
595  }
596  for (auto comp : mMNAIntfSwitches) {
597  for (auto task : comp->mnaTasks()) {
598  l.push_back(task);
599  }
600  }
601  for (auto node : mNodes) {
602  for (auto task : node->mnaTasks())
603  l.push_back(task);
604  }
605  // TODO signal components should be moved out of MNA solver
606  for (auto comp : mSimSignalComps) {
607  for (auto task : comp->getTasks()) {
608  l.push_back(task);
609  }
610  }
611  if (mFrequencyParallel) {
612  for (UInt i = 0; i < mSystem.mFrequencies.size(); ++i)
613  l.push_back(createSolveTaskHarm(i));
614  } else if (mSystemMatrixRecomputation) {
615  for (auto comp : this->mMNAIntfVariableComps) {
616  for (auto task : comp->mnaTasks())
617  l.push_back(task);
618  }
619  l.push_back(createSolveTaskRecomp());
620  } else {
621  l.push_back(createSolveTask());
622  l.push_back(createLogTask());
623  }
624  return l;
625 }
626 
627 template <typename VarType>
628 void MnaSolver<VarType>::log(Real time, Int timeStepCount) {
629  if (mLogLevel == Logger::Level::off)
630  return;
631 
632  if (mDomain == CPS::Domain::EMT) {
633  mLeftVectorLog->logEMTNodeValues(time, leftSideVector());
634  mRightVectorLog->logEMTNodeValues(time, rightSideVector());
635  } else {
636  mLeftVectorLog->logPhasorNodeValues(time, leftSideVector());
637  mRightVectorLog->logPhasorNodeValues(time, rightSideVector());
638  }
639 }
640 
641 } // namespace DPsim
642 
643 template class DPsim::MnaSolver<Real>;
644 template class DPsim::MnaSolver<Complex>;
Solver class using Modified Nodal Analysis (MNA).
Definition: MNASolver.h:38
void identifyTopologyObjects()
Identify Nodes and SimPowerComps and SimSignalComps.
Definition: MNASolver.cpp:317
void initializeSystemWithVariableMatrix()
Initialization of system matrices and source vector.
Definition: MNASolver.cpp:271
virtual void initialize() override
Calls subroutines to set up everything that is required before simulation.
Definition: MNASolver.cpp:35
virtual void initializeSystem()
Initialization of system matrices and source vector.
Definition: MNASolver.cpp:200
Bool hasVariableComponentChanged()
Checks whether the status of variable MNA elements have changed.
Definition: MNASolver.cpp:298
virtual void log(Real time, Int timeStepCount) override
Logs left and right vector.
Definition: MNASolver.cpp:628
std::shared_ptr< DataLogger > mRightVectorLog
Right side vector logger.
Definition: MNASolver.h:114
void initializeComponents()
Initialization of individual components.
void updateSwitchStatus()
Collects the status of switches to select correct system matrix.
Definition: MNASolver.cpp:311
std::shared_ptr< DataLogger > mLeftVectorLog
Left side vector logger.
Definition: MNASolver.h:112
void initializeSystemWithParallelFrequencies()
Initialization of system matrices and source vector.
Definition: MNASolver.cpp:220
void collectVirtualNodes()
Definition: MNASolver.cpp:424
MnaSolver(String name, CPS::Domain domain=CPS::Domain::DP, CPS::Logger::Level logLevel=CPS::Logger::Level::info)
Constructor should not be called by users but by Simulation.
Definition: MNASolver.cpp:19
void assignMatrixNodeIndices()
Assign simulation node index according to index in the vector.
Definition: MNASolver.cpp:363
void initializeSystemWithPrecomputedMatrices()
Initialization of system matrices and source vector.
Definition: MNASolver.cpp:240
void createEmptyVectors()
Create left and right side vector.
virtual CPS::Task::List getTasks() override
Get tasks for scheduler.
Definition: MNASolver.cpp:588
void resolveDeps(CPS::Task::List &tasks, Edges &inEdges, Edges &outEdges)
Definition: Scheduler.cpp:78
std::unordered_map< CPS::Task::Ptr, std::deque< CPS::Task::Ptr > > Edges
Definition: Scheduler.h:31
void step(Real time, Int timeStepCount)
Performs a single simulation step.
void createSchedule(const CPS::Task::List &tasks, const Edges &inEdges, const Edges &outEdges)
Creates the schedule for the given dependency graph.
Base class for more specific solvers such as MNA, ODE or IDA.
Definition: Solver.h:30