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