DPsim
Loading...
Searching...
No Matches
MNASolver.cpp
Go to the documentation of this file.
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 <algorithm>
10#include <dpsim/MNASolver.h>
12#include <functional>
13#include <memory>
14#include <stdexcept>
15#include <type_traits>
16
17using namespace DPsim;
18using namespace CPS;
19
20namespace DPsim {
21
22template <typename VarType>
24 CPS::Logger::Level logLevel)
25 : Solver(name, logLevel), mDomain(domain) {
26
27 // Raw source and solution vector logging
28 mLeftVectorLog = std::make_shared<DataLogger>(
29 name + "_LeftVector", logLevel == CPS::Logger::Level::trace);
30 mRightVectorLog = std::make_shared<DataLogger>(
31 name + "_RightVector", logLevel == CPS::Logger::Level::trace);
32}
33
34template <typename VarType>
36 mSystem = system;
37}
38
39template <typename VarType>
43
44template <typename VarType>
48 throw std::logic_error(
49 "MNA state-space extractor has not been initialized.");
50
52}
53
54template <typename VarType> void MnaSolver<VarType>::initialize() {
55 // TODO: check that every system matrix has the same dimensions
56 SPDLOG_LOGGER_INFO(mSLog, "---- Start initialization ----");
57 mLeftVectorLog->start();
58 mRightVectorLog->start();
59
60 // Register attribute for solution vector
62 // Best case we have some kind of sub-attributes for attribute vectors / tensor attributes...
64 SPDLOG_LOGGER_INFO(mSLog, "Computing network harmonics in parallel.");
65 for (Int freq = 0; freq < mSystem.mFrequencies.size(); ++freq) {
67 }
68 } else {
70 }
71
72 SPDLOG_LOGGER_INFO(mSLog, "-- Process topology");
73 for (auto comp : mSystem.mComponents)
74 SPDLOG_LOGGER_INFO(mSLog, "Added {:s} '{:s}' to simulation.", comp->type(),
75 comp->name());
76
77 // Otherwise LU decomposition will fail
78 if (mSystem.mComponents.size() == 0)
79 throw SolverException();
80
81 // We need to differentiate between power and signal components and
82 // ground nodes should be ignored.
85
87 throw CPS::SystemError("System-matrix recomputation is not supported with "
88 "frequency-parallel MNA.");
89 }
90
91 // Ensure all subcomponents (and their virtual nodes) are registered before
92 // collectVirtualNodes() sizes the system matrices. Recurses into nested
93 // sub-components so e.g. SST -> Load -> {R, L, C} is fully created.
94 CPS::MNAInterface::List allMNAComps;
95 allMNAComps.insert(allMNAComps.end(), mMNAComponents.begin(),
96 mMNAComponents.end());
97 allMNAComps.insert(allMNAComps.end(), mMNAIntfVariableComps.begin(),
99 allMNAComps.insert(allMNAComps.end(), mMNAIntfSwitches.begin(),
100 mMNAIntfSwitches.end());
101
102 // Some composites (e.g. AvVoltageSourceInverterDQ) eagerly create and
103 // register their subcomponents in their own constructor, before those
104 // subcomponents are connected (connect() only happens later, in
105 // initializeParentFromNodesAndTerminals()). Calling createSubComponents()
106 // on such a not-yet-connected subcomponent crashes, since it relies on
107 // its own terminals being wired up. Only subcomponents that are newly
108 // registered as a direct result of THIS createSubComponents() call (the
109 // lazy create+connect+register pattern, e.g. SST -> Load -> {R, L, C})
110 // are guaranteed to already be connected, so only recurse into those.
111 std::function<void(CPS::MNAInterface::Ptr)> createSubComponentsRec =
112 [&](CPS::MNAInterface::Ptr comp) {
113 auto pComp =
114 std::dynamic_pointer_cast<CPS::SimPowerComp<VarType>>(comp);
115 typename CPS::SimPowerComp<VarType>::List subCompsBefore =
116 pComp ? pComp->subComponents()
118
119 comp->createSubComponents();
120
121 if (pComp) {
122 for (auto subComp : pComp->subComponents()) {
123 bool isNew = std::find(subCompsBefore.begin(), subCompsBefore.end(),
124 subComp) == subCompsBefore.end();
125 if (!isNew)
126 continue;
127 if (auto subMna =
128 std::dynamic_pointer_cast<CPS::MNAInterface>(subComp))
129 createSubComponentsRec(subMna);
130 }
131 }
132 };
133 for (auto comp : allMNAComps)
134 createSubComponentsRec(comp);
135 // These steps complete the network information.
138
139 SPDLOG_LOGGER_INFO(mSLog, "-- Create empty MNA system matrices and vectors");
142
143 // Initialize components from powerflow solution and
144 // calculate MNA specific initialization values.
146
147 if (mSteadyStateInit) {
148 mIsInInitialization = true;
150 }
151 mIsInInitialization = false;
152
153 // Some components feature a different behaviour for simulation and initialization
154 for (auto comp : mSystem.mComponents) {
155 auto powerComp = std::dynamic_pointer_cast<CPS::TopologicalPowerComp>(comp);
156 if (powerComp)
157 powerComp->setBehaviour(TopologicalPowerComp::Behaviour::MNASimulation);
158
159 auto sigComp = std::dynamic_pointer_cast<CPS::SimSignalComp>(comp);
160 if (sigComp)
161 sigComp->setBehaviour(SimSignalComp::Behaviour::Simulation);
162 }
163
164 // Initialize system matrices and source vector.
166
169
170 SPDLOG_LOGGER_INFO(mSLog, "--- Initialization finished ---");
171 SPDLOG_LOGGER_INFO(mSLog, "--- Initial system matrices and vectors ---");
173
174 mSLog->flush();
175}
176
178 SPDLOG_LOGGER_INFO(mSLog, "-- Initialize components from power flow");
179
180 CPS::MNAInterface::List allMNAComps;
181 allMNAComps.insert(allMNAComps.end(), mMNAComponents.begin(),
182 mMNAComponents.end());
183 allMNAComps.insert(allMNAComps.end(), mMNAIntfVariableComps.begin(),
185
186 for (auto comp : allMNAComps) {
187 auto pComp = std::dynamic_pointer_cast<SimPowerComp<Real>>(comp);
188 if (!pComp)
189 continue;
190 pComp->checkForUnconnectedTerminals();
192 pComp->initializeFromNodesAndTerminals(mSystem.mSystemFrequency);
193 }
194
195 // Initialize signal components.
196 for (auto comp : mSimSignalComps)
197 comp->initialize(mSystem.mSystemOmega, mTimeStep);
198
199 // Initialize MNA specific parts of components.
200 for (auto comp : allMNAComps) {
201 comp->mnaInitialize(mSystem.mSystemOmega, mTimeStep, mLeftSideVector);
202 const Matrix &stamp = comp->getRightVector()->get();
203 if (stamp.size() != 0) {
204 mRightVectorStamps.push_back(&stamp);
205 }
206 }
207
208 for (auto comp : mMNAIntfSwitches)
209 comp->mnaInitialize(mSystem.mSystemOmega, mTimeStep, mLeftSideVector);
210
211 // Initialize nodes
212 for (UInt nodeIdx = 0; nodeIdx < mNodes.size(); ++nodeIdx)
213 mNodes[nodeIdx]->initialize();
214}
215
217 SPDLOG_LOGGER_INFO(mSLog, "-- Initialize components from power flow");
218
219 CPS::MNAInterface::List allMNAComps;
220 allMNAComps.insert(allMNAComps.end(), mMNAComponents.begin(),
221 mMNAComponents.end());
222 allMNAComps.insert(allMNAComps.end(), mMNAIntfVariableComps.begin(),
224
225 // Initialize power components with frequencies and from powerflow results
226 for (auto comp : allMNAComps) {
227 auto pComp = std::dynamic_pointer_cast<SimPowerComp<Complex>>(comp);
228 if (!pComp)
229 continue;
230 pComp->checkForUnconnectedTerminals();
232 pComp->initializeFromNodesAndTerminals(mSystem.mSystemFrequency);
233 }
234
235 // Initialize signal components.
236 for (auto comp : mSimSignalComps)
237 comp->initialize(mSystem.mSystemOmega, mTimeStep);
238
239 SPDLOG_LOGGER_INFO(mSLog, "-- Initialize MNA properties of components");
240 if (mFrequencyParallel) {
241 // Initialize MNA specific parts of components.
242 for (auto comp : mMNAComponents) {
243 // Initialize MNA specific parts of components.
244 comp->mnaInitializeHarm(mSystem.mSystemOmega, mTimeStep,
246 const Matrix &stamp = comp->getRightVector()->get();
247 if (stamp.size() != 0)
248 mRightVectorStamps.push_back(&stamp);
249 }
250 // Initialize nodes
251 for (UInt nodeIdx = 0; nodeIdx < mNodes.size(); ++nodeIdx) {
252 mNodes[nodeIdx]->mnaInitializeHarm(mLeftSideVectorHarm);
253 }
254 } else {
255 // Initialize MNA specific parts of components.
256 for (auto comp : allMNAComps) {
257 comp->mnaInitialize(mSystem.mSystemOmega, mTimeStep, mLeftSideVector);
258 const Matrix &stamp = comp->getRightVector()->get();
259 if (stamp.size() != 0) {
260 mRightVectorStamps.push_back(&stamp);
261 }
262 }
263
264 for (auto comp : mMNAIntfSwitches)
265 comp->mnaInitialize(mSystem.mSystemOmega, mTimeStep, mLeftSideVector);
266
267 // Initialize nodes
268 for (UInt nodeIdx = 0; nodeIdx < mNodes.size(); ++nodeIdx)
269 mNodes[nodeIdx]->initialize();
270 }
271}
272
273template <typename VarType> void MnaSolver<VarType>::initializeSystem() {
274 SPDLOG_LOGGER_INFO(mSLog,
275 "-- Initialize MNA system matrices and source vector");
276 mRightSideVector.setZero();
277
278 // just a sanity check in case we change the static
279 // initialization of the switch number in the future
280 if (mSwitches.size() > sizeof(std::size_t) * 8) {
281 throw SystemError("Too many Switches.");
282 }
283
288 else
290}
291
292template <typename VarType>
294 // iterate over all possible switch state combinations and frequencies
295 for (std::size_t sw = 0; sw < (1ULL << mSwitches.size()); ++sw) {
296 for (Int freq = 0; freq < mSystem.mFrequencies.size(); ++freq) {
297 switchedMatrixEmpty(sw, freq);
299 }
300 }
301
302 if (mSwitches.size() > 0)
304
305 // Initialize source vector
306 for (Int freq = 0; freq < mSystem.mFrequencies.size(); ++freq) {
307 for (auto comp : mMNAComponents)
308 comp->mnaApplyRightSideVectorStampHarm(mRightSideVectorHarm[freq], freq);
309 }
310}
311
312template <typename VarType>
314 // iterate over all possible switch state combinations
315 for (std::size_t i = 0; i < (1ULL << mSwitches.size()); i++) {
317 }
318
319 if (mSwitches.size() < 1) {
321 } else {
322 // Generate switching state dependent system matrices
323 for (std::size_t i = 0; i < (1ULL << mSwitches.size()); i++) {
325 }
327 }
328
329 // Initialize source vector for debugging
330 // CAUTION: this does not always deliver proper source vector initialization
331 // as not full pre-step is executed (not involving necessary electrical or signal
332 // subcomp updates before right vector calculation)
333 for (auto comp : mMNAComponents) {
334 comp->mnaApplyRightSideVectorStamp(mRightSideVector);
335 auto idObj = std::dynamic_pointer_cast<IdentifiedObject>(comp);
336 SPDLOG_LOGGER_DEBUG(mSLog, "Stamping {:s} {:s} into source vector",
337 idObj->type(), idObj->name());
338 if (mSLog->should_log(spdlog::level::trace))
340 }
341}
342
343template <typename VarType>
345
346 // Collect index pairs of varying matrix entries from components
347 for (auto varElem : mVariableComps)
348 for (auto varEntry : varElem->mVariableSystemMatrixEntries)
349 mListVariableSystemMatrixEntries.push_back(varEntry);
350 SPDLOG_LOGGER_INFO(mSLog, "List of index pairs of varying matrix entries: ");
351 for (auto indexPair : mListVariableSystemMatrixEntries)
352 SPDLOG_LOGGER_INFO(mSLog, "({}, {})", indexPair.first, indexPair.second);
353
355
356 // Initialize source vector for debugging
357 // CAUTION: this does not always deliver proper source vector initialization
358 // as not full pre-step is executed (not involving necessary electrical or signal
359 // subcomp updates before right vector calculation)
360 for (auto comp : mMNAComponents) {
361 comp->mnaApplyRightSideVectorStamp(mRightSideVector);
362 auto idObj = std::dynamic_pointer_cast<IdentifiedObject>(comp);
363 SPDLOG_LOGGER_DEBUG(mSLog, "Stamping {:s} {:s} into source vector",
364 idObj->type(), idObj->name());
365 if (mSLog->should_log(spdlog::level::trace))
367 }
368}
369
370template <typename VarType>
373 throw std::logic_error(
374 "MNA state-space extraction supports EMT and DP domains only.");
375 }
376
377 if (mFrequencyParallel) {
378 throw std::logic_error(
379 "MNA state-space extraction does not support frequency-parallel "
380 "MNA systems.");
381 }
382
383 CPS::MNAInterface::List stateSpaceComponents;
384 stateSpaceComponents.insert(stateSpaceComponents.end(),
385 mMNAComponents.begin(), mMNAComponents.end());
386 stateSpaceComponents.insert(stateSpaceComponents.end(),
387 mMNAIntfVariableComps.begin(),
389
390 const UInt mnaVectorSize = static_cast<UInt>((**mLeftSideVector).rows());
391
392 mStateSpaceExtractor = std::make_shared<MNAStateSpaceExtractor>();
393 mStateSpaceExtractor->initialize(stateSpaceComponents, mnaVectorSize,
394 mTimeStep);
395
396 SPDLOG_LOGGER_INFO(
397 mSLog,
398 "Initialized MNA state-space extractor with {:d} extraction states.",
399 mStateSpaceExtractor->getStateCount());
400}
401
402template <typename VarType>
404 for (auto varElem : mVariableComps) {
405 if (varElem->hasParameterChanged()) {
406 auto idObj = std::dynamic_pointer_cast<IdentifiedObject>(varElem);
407 SPDLOG_LOGGER_DEBUG(
408 mSLog, "Component ({:s} {:s}) value changed -> Update System Matrix",
409 idObj->type(), idObj->name());
410 return true;
411 }
412 }
413 return false;
414}
415
416template <typename VarType> void MnaSolver<VarType>::updateSwitchStatus() {
417 for (UInt i = 0; i < mSwitches.size(); ++i) {
418 mCurrentSwitchStatus.set(i, mSwitches[i]->mnaIsClosed());
419 }
420}
421
422template <typename VarType>
425
426 const auto recomputationComp = std::find_if(
427 mVariableComps.begin(), mVariableComps.end(), [](const auto &comp) {
428 const auto switchComp =
429 std::dynamic_pointer_cast<CPS::MNASwitchInterface>(comp);
430
431 return !switchComp || !switchComp->supportsPrecomputedSystemMatrices();
432 });
433
434 const Bool hasRecomputationComp = recomputationComp != mVariableComps.end();
435
437 case Mode::Auto:
438 mSystemMatrixRecomputationEnabled = hasRecomputationComp;
439
440 if (hasRecomputationComp) {
441 const auto component =
442 std::dynamic_pointer_cast<CPS::IdentifiedObject>(*recomputationComp);
443
444 SPDLOG_LOGGER_INFO(
445 mSLog,
446 "System-matrix recomputation enabled automatically for {:s} '{:s}'.",
447 component->type(), component->name());
448 } else {
449 SPDLOG_LOGGER_INFO(mSLog,
450 "System-matrix recomputation disabled automatically.");
451 }
452 break;
453
454 case Mode::Enabled:
456 SPDLOG_LOGGER_INFO(mSLog, "System-matrix recomputation enabled.");
457 break;
458
459 case Mode::Disabled:
461
462 if (hasRecomputationComp) {
463 const auto component =
464 std::dynamic_pointer_cast<CPS::IdentifiedObject>(*recomputationComp);
465
466 SPDLOG_LOGGER_WARN(
467 mSLog,
468 "System-matrix recomputation disabled, but {:s} '{:s}' may require "
469 "it.",
470 component->type(), component->name());
471 } else {
472 SPDLOG_LOGGER_INFO(mSLog, "System-matrix recomputation disabled.");
473 }
474 break;
475 }
476}
477
478template <typename VarType> void MnaSolver<VarType>::identifyTopologyObjects() {
479 for (auto baseNode : mSystem.mNodes) {
480 // Add nodes to the list and ignore ground nodes.
481 if (!baseNode->isGround()) {
482 auto node = std::dynamic_pointer_cast<CPS::SimNode<VarType>>(baseNode);
483 mNodes.push_back(node);
484 SPDLOG_LOGGER_INFO(mSLog, "Added node {:s}", node->name());
485 }
486 }
487
488 for (auto comp : mSystem.mComponents) {
489
490 auto genComp = std::dynamic_pointer_cast<CPS::MNASyncGenInterface>(comp);
491 if (genComp) {
492 mSyncGen.push_back(genComp);
493 }
494
495 auto swComp = std::dynamic_pointer_cast<CPS::MNASwitchInterface>(comp);
496 if (swComp) {
497 mSwitches.push_back(swComp);
498 auto mnaComp = std::dynamic_pointer_cast<CPS::MNAInterface>(swComp);
499 if (mnaComp)
500 mMNAIntfSwitches.push_back(mnaComp);
501 }
502
503 auto varComp =
504 std::dynamic_pointer_cast<CPS::MNAVariableCompInterface>(comp);
505 if (varComp) {
506 mVariableComps.push_back(varComp);
507 auto mnaComp = std::dynamic_pointer_cast<CPS::MNAInterface>(varComp);
508 if (mnaComp)
509 mMNAIntfVariableComps.push_back(mnaComp);
510 }
511
512 if (!(swComp || varComp)) {
513 auto mnaComp = std::dynamic_pointer_cast<CPS::MNAInterface>(comp);
514 if (mnaComp)
515 mMNAComponents.push_back(mnaComp);
516
517 auto sigComp = std::dynamic_pointer_cast<CPS::SimSignalComp>(comp);
518 if (sigComp)
519 mSimSignalComps.push_back(sigComp);
520 }
521 }
522}
523
524template <typename VarType> void MnaSolver<VarType>::assignMatrixNodeIndices() {
525 UInt matrixNodeIndexIdx = 0;
526 for (UInt idx = 0; idx < mNodes.size(); ++idx) {
527 mNodes[idx]->setMatrixNodeIndex(0, matrixNodeIndexIdx);
528 SPDLOG_LOGGER_INFO(mSLog, "Assigned index {} to phase A of node {}",
529 matrixNodeIndexIdx, idx);
530 ++matrixNodeIndexIdx;
531 if (mNodes[idx]->phaseType() == CPS::PhaseType::ABC) {
532 mNodes[idx]->setMatrixNodeIndex(1, matrixNodeIndexIdx);
533 SPDLOG_LOGGER_INFO(mSLog, "Assigned index {} to phase B of node {}",
534 matrixNodeIndexIdx, idx);
535 ++matrixNodeIndexIdx;
536 mNodes[idx]->setMatrixNodeIndex(2, matrixNodeIndexIdx);
537 SPDLOG_LOGGER_INFO(mSLog, "Assigned index {} to phase C of node {}",
538 matrixNodeIndexIdx, idx);
539 ++matrixNodeIndexIdx;
540 }
541 // This should be true when the final network node is reached, not considering virtual nodes
542 if (idx == mNumNetNodes - 1)
543 mNumNetMatrixNodeIndices = matrixNodeIndexIdx;
544 }
545 // Total number of network nodes including virtual nodes is matrixNodeIndexIdx + 1, which is why the variable is incremented after assignment
546 mNumMatrixNodeIndices = matrixNodeIndexIdx;
550 static_cast<UInt>(mSystem.mFrequencies.size() - 1) *
553 static_cast<UInt>(mSystem.mFrequencies.size()) * mNumMatrixNodeIndices;
554
555 SPDLOG_LOGGER_INFO(mSLog, "Assigned simulation nodes to topology nodes:");
556 SPDLOG_LOGGER_INFO(mSLog, "Number of network simulation nodes: {:d}",
558 SPDLOG_LOGGER_INFO(mSLog, "Number of simulation nodes: {:d}",
560 SPDLOG_LOGGER_INFO(mSLog, "Number of harmonic simulation nodes: {:d}",
562}
563
565 mRightSideVector = Matrix::Zero(mNumMatrixNodeIndices, 1);
566 **mLeftSideVector = Matrix::Zero(mNumMatrixNodeIndices, 1);
567}
568
570 if (mFrequencyParallel) {
571 for (Int freq = 0; freq < mSystem.mFrequencies.size(); ++freq) {
572 mRightSideVectorHarm.push_back(
573 Matrix::Zero(2 * (mNumMatrixNodeIndices), 1));
575 Matrix::Zero(2 * (mNumMatrixNodeIndices), 1)));
576 }
577 } else {
578 mRightSideVector = Matrix::Zero(
580 **mLeftSideVector = Matrix::Zero(
582 }
583}
584
585template <typename VarType> void MnaSolver<VarType>::collectVirtualNodes() {
586 // We have not added virtual nodes yet so the list has only network nodes
587 mNumNetNodes = (UInt)mNodes.size();
588 // virtual nodes are placed after network nodes
589 UInt virtualNode = mNumNetNodes - 1;
590
591 for (auto comp : mMNAComponents) {
592 auto pComp = std::dynamic_pointer_cast<SimPowerComp<VarType>>(comp);
593 if (!pComp)
594 continue;
595
596 // Check if component requires virtual node and if so get a reference
597 if (pComp->hasVirtualNodes()) {
598 for (UInt node = 0; node < pComp->virtualNodesNumber(); ++node) {
599 mNodes.push_back(pComp->virtualNode(node));
600 SPDLOG_LOGGER_INFO(mSLog, "Collected virtual node {} of {}",
601 virtualNode, node, pComp->name());
602 }
603 }
604
605 // Repeat the same steps for virtual nodes of sub components
606 // TODO: recursive behavior
607 if (pComp->hasSubComponents()) {
608 for (auto pSubComp : pComp->subComponents()) {
609 for (UInt node = 0; node < pSubComp->virtualNodesNumber(); ++node) {
610 auto vnode = pSubComp->virtualNode(node);
611 // Skip if already registered (e.g. parent reused its VN via
612 // setVirtualNodeAt).
613 bool alreadyRegistered = false;
614 for (auto registeredNode : mNodes) {
615 if (registeredNode == vnode) {
616 alreadyRegistered = true;
617 break;
618 }
619 }
620 if (alreadyRegistered)
621 continue;
622 mNodes.push_back(vnode);
623 SPDLOG_LOGGER_INFO(mSLog, "Collected virtual node {} of {}", node,
624 pSubComp->name());
625 }
626 }
627 }
628 }
629
630 // collect virtual nodes of variable components
631 for (auto comp : mVariableComps) {
632 auto pComp = std::dynamic_pointer_cast<SimPowerComp<VarType>>(comp);
633 if (!pComp)
634 continue;
635
636 // Check if component requires virtual node and if so get a reference
637 if (pComp->hasVirtualNodes()) {
638 for (UInt node = 0; node < pComp->virtualNodesNumber(); ++node) {
639 mNodes.push_back(pComp->virtualNode(node));
640 SPDLOG_LOGGER_INFO(mSLog,
641 "Collected virtual node {} of Varible Comp {}", node,
642 pComp->name());
643 }
644 }
645 }
646
647 // Update node number to create matrices and vectors
648 mNumNodes = (UInt)mNodes.size();
650 SPDLOG_LOGGER_INFO(mSLog, "Created virtual nodes:");
651 SPDLOG_LOGGER_INFO(mSLog, "Number of network nodes: {:d}", mNumNetNodes);
652 SPDLOG_LOGGER_INFO(mSLog, "Number of network and virtual nodes: {:d}",
653 mNumNodes);
654}
655
656template <typename VarType>
658 SPDLOG_LOGGER_INFO(mSLog, "--- Run steady-state initialization ---");
659
660 DataLogger initLeftVectorLog(mName + "_InitLeftVector",
661 mLogLevel != CPS::Logger::Level::off);
662 initLeftVectorLog.start();
663 DataLogger initRightVectorLog(mName + "_InitRightVector",
664 mLogLevel != CPS::Logger::Level::off);
665 initRightVectorLog.start();
666
667 TopologicalPowerComp::Behaviour initBehaviourPowerComps =
669 SimSignalComp::Behaviour initBehaviourSignalComps =
671
672 // TODO: enable use of timestep distinct from simulation timestep
673 Real initTimeStep = mTimeStep;
674
675 Int timeStepCount = 0;
676 Real time = 0;
677 Real maxDiff = 1.0;
678 Real max = 1.0;
679 Matrix diff = Matrix::Zero(2 * mNumNodes, 1);
680 Matrix prevLeftSideVector = Matrix::Zero(2 * mNumNodes, 1);
681
682 SPDLOG_LOGGER_INFO(mSLog,
683 "Time step is {:f}s for steady-state initialization",
684 initTimeStep);
685
686 for (auto comp : mSystem.mComponents) {
687 auto powerComp = std::dynamic_pointer_cast<CPS::TopologicalPowerComp>(comp);
688 if (powerComp)
689 powerComp->setBehaviour(initBehaviourPowerComps);
690
691 auto sigComp = std::dynamic_pointer_cast<CPS::SimSignalComp>(comp);
692 if (sigComp)
693 sigComp->setBehaviour(initBehaviourSignalComps);
694 }
695
698
699 // Use sequential scheduler
701 CPS::Task::List tasks;
702 Scheduler::Edges inEdges, outEdges;
703
704 for (auto node : mNodes) {
705 for (auto task : node->mnaTasks())
706 tasks.push_back(task);
707 }
708 for (auto comp : mMNAComponents) {
709 for (auto task : comp->mnaTasks()) {
710 tasks.push_back(task);
711 }
712 }
713 // TODO signal components should be moved out of MNA solver
714 for (auto comp : mSimSignalComps) {
715 for (auto task : comp->getTasks()) {
716 tasks.push_back(task);
717 }
718 }
719 tasks.push_back(createSolveTask());
720
721 sched.resolveDeps(tasks, inEdges, outEdges);
722 sched.createSchedule(tasks, inEdges, outEdges);
723
724 while (time < mSteadStIniTimeLimit) {
725 // Reset source vector
726 mRightSideVector.setZero();
727
728 sched.step(time, timeStepCount);
729
730 if (mDomain == CPS::Domain::EMT) {
731 initLeftVectorLog.logEMTNodeValues(time, leftSideVector());
732 initRightVectorLog.logEMTNodeValues(time, rightSideVector());
733 } else {
734 initLeftVectorLog.logPhasorNodeValues(time, leftSideVector());
735 initRightVectorLog.logPhasorNodeValues(time, rightSideVector());
736 }
737
738 // Calculate new simulation time
739 time = time + initTimeStep;
740 ++timeStepCount;
741
742 // Calculate difference
743 diff = prevLeftSideVector - **mLeftSideVector;
744 prevLeftSideVector = **mLeftSideVector;
745 maxDiff = diff.lpNorm<Eigen::Infinity>();
746 max = (**mLeftSideVector).lpNorm<Eigen::Infinity>();
747 // If difference is smaller than some epsilon, break
748 if ((maxDiff / max) < mSteadStIniAccLimit)
749 break;
750 }
751
752 SPDLOG_LOGGER_INFO(mSLog, "Max difference: {:f} or {:f}% at time {:f}",
753 maxDiff, maxDiff / max, time);
754
755 // Reset system for actual simulation
756 mRightSideVector.setZero();
757
758 SPDLOG_LOGGER_INFO(mSLog, "--- Finished steady-state initialization ---");
759}
760
761template <typename VarType> Task::List MnaSolver<VarType>::getTasks() {
762 Task::List l;
763
764 for (auto comp : mMNAComponents) {
765 for (auto task : comp->mnaTasks()) {
766 l.push_back(task);
767 }
768 }
769 for (auto comp : mMNAIntfSwitches) {
770 for (auto task : comp->mnaTasks()) {
771 l.push_back(task);
772 }
773 }
774 for (auto node : mNodes) {
775 for (auto task : node->mnaTasks())
776 l.push_back(task);
777 }
778 // TODO signal components should be moved out of MNA solver
779 for (auto comp : mSimSignalComps) {
780 for (auto task : comp->getTasks()) {
781 l.push_back(task);
782 }
783 }
784 if (mFrequencyParallel) {
785 for (UInt i = 0; i < mSystem.mFrequencies.size(); ++i)
786 l.push_back(createSolveTaskHarm(i));
788 for (auto comp : this->mMNAIntfVariableComps) {
789 for (auto task : comp->mnaTasks())
790 l.push_back(task);
791 }
792 l.push_back(createSolveTaskRecomp());
794 l.push_back(createStateSpaceExtractionTask());
795 }
796 } else {
797 l.push_back(createSolveTask());
799 l.push_back(createStateSpaceExtractionTask());
800 }
801 l.push_back(createLogTask());
802 }
803 return l;
804}
805
806template <typename VarType>
807void MnaSolver<VarType>::log(Real time, Int timeStepCount) {
808 if (mLogLevel == Logger::Level::off)
809 return;
810
811 if (mDomain == CPS::Domain::EMT) {
812 mLeftVectorLog->logEMTNodeValues(time, leftSideVector());
813 mRightVectorLog->logEMTNodeValues(time, rightSideVector());
814 } else {
815 mLeftVectorLog->logPhasorNodeValues(time, leftSideVector());
816 mRightVectorLog->logPhasorNodeValues(time, rightSideVector());
817 }
818}
819
820} // namespace DPsim
821
822template class DPsim::MnaSolver<Real>;
823template class DPsim::MnaSolver<Complex>;
spdlog::level::level_enum Level
Definition Logger.h:33
static String matrixToString(const Matrix &mat)
Definition Logger.cpp:31
std::vector< Ptr > List
std::shared_ptr< MNAInterface > Ptr
std::vector< Ptr > List
std::vector< Ptr > List
Definition Task.h:28
void logEMTNodeValues(Real time, const Matrix &data)
void logPhasorNodeValues(Real time, const Matrix &data, Int freqNum=1)
virtual void start() override
Solver class using Modified Nodal Analysis (MNA).
Definition MNASolver.h:39
std::bitset< SWITCH_NUM > mCurrentSwitchStatus
Current status of all switches encoded as bitset.
Definition MNASolver.h:79
virtual void setSystem(const CPS::SystemTopology &system) override
Definition MNASolver.cpp:35
CPS::Domain mDomain
Simulation domain, which can be dynamic phasor (DP) or EMT.
Definition MNASolver.h:43
void resolveSystemMatrixRecomputationMode()
Resolve the requested system-matrix recomputation mode.
Matrix & rightSideVector()
Definition MNASolver.h:231
void identifyTopologyObjects()
Identify Nodes and SimPowerComps and SimSignalComps.
std::vector< Matrix > mRightSideVectorHarm
Source vector of known quantities.
Definition MNASolver.h:90
void steadyStateInitialization()
Matrix mRightSideVector
Source vector of known quantities.
Definition MNASolver.h:84
Bool mStateSpaceExtraction
Enables extraction of the MNA-coupled discrete-time state matrix.
Definition MNASolver.h:126
CPS::SystemTopology mSystem
System topology.
Definition MNASolver.h:64
virtual std::shared_ptr< CPS::Task > createStateSpaceExtractionTask()=0
Create state-space extraction task for this solver implementation.
Matrix & leftSideVector()
Definition MNASolver.h:229
void initializeSystemWithVariableMatrix()
Initialization of system matrices and source vector.
virtual void initialize() override
Calls subroutines to set up everything that is required before simulation.
Definition MNASolver.cpp:54
virtual void logSystemMatrices()=0
Logging of system matrices and source vector.
virtual void initializeSystem()
Initialization of system matrices and source vector.
std::vector< CPS::Attribute< Matrix >::Ptr > mLeftSideVectorHarm
Solution vector of unknown quantities (parallel frequencies)
Definition MNASolver.h:213
Bool hasVariableComponentChanged()
Checks whether the status of variable MNA elements have changed.
CPS::MNAInterface::List mMNAIntfVariableComps
List of variable components if they must be accessed as MNAInterface objects.
Definition MNASolver.h:99
UInt mNumNetMatrixNodeIndices
Number of network nodes, considering individual phases.
Definition MNASolver.h:53
UInt mNumNetNodes
Number of network nodes, single line equivalent.
Definition MNASolver.h:47
virtual void switchedMatrixStamp(std::size_t index, std::vector< std::shared_ptr< CPS::MNAInterface > > &comp)=0
Applies a component stamp to the matrix with the given switch index.
MNAStateSpaceExtractor::Ptr mStateSpaceExtractor
Extractor for the MNA-coupled state-space model.
Definition MNASolver.h:129
virtual void log(Real time, Int timeStepCount) override
Logs left and right vector.
UInt mNumTotalMatrixNodeIndices
Total number of network and virtual nodes, considering individual phases and additional frequencies.
Definition MNASolver.h:59
UInt mNumVirtualMatrixNodeIndices
Number of virtual nodes, considering individual phases.
Definition MNASolver.h:55
CPS::MNASyncGenInterface::List mSyncGen
List of synchronous generators that need iterate to solve the differential equations.
Definition MNASolver.h:81
CPS::MNAInterface::List mMNAIntfSwitches
List of switches if they must be accessed as MNAInterface objects.
Definition MNASolver.h:75
std::shared_ptr< DataLogger > mRightVectorLog
Right side vector logger.
Definition MNASolver.h:115
virtual std::shared_ptr< CPS::Task > createSolveTaskHarm(UInt freqIdx)=0
Create a solve task for this solver implementation.
std::vector< const Matrix * > mRightVectorStamps
List of all right side vector contributions.
Definition MNASolver.h:86
void initializeComponents()
Initialization of individual components.
void updateSwitchStatus()
Collects the status of switches to select correct system matrix.
std::vector< std::pair< UInt, UInt > > mListVariableSystemMatrixEntries
List of index pairs of varying matrix entries.
Definition MNASolver.h:61
CPS::MNAVariableCompInterface::List mVariableComps
Definition MNASolver.h:97
virtual std::shared_ptr< CPS::Task > createSolveTaskRecomp()=0
Create a solve task for recomputation solver.
CPS::MNAInterface::List mMNAComponents
List of MNA components with static stamp into system matrix.
Definition MNASolver.h:70
virtual void stampVariableSystemMatrix()=0
Stamps components into the variable system matrix.
std::shared_ptr< DataLogger > mLeftVectorLog
Left side vector logger.
Definition MNASolver.h:113
UInt mNumMatrixNodeIndices
Number of network and virtual nodes, considering individual phases.
Definition MNASolver.h:51
void initializeSystemWithParallelFrequencies()
Initialization of system matrices and source vector.
void collectVirtualNodes()
UInt mNumHarmMatrixNodeIndices
Number of nodes, excluding the primary frequency.
Definition MNASolver.h:57
UInt mNumNodes
Number of network and virtual nodes, single line equivalent.
Definition MNASolver.h:45
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:23
void assignMatrixNodeIndices()
Assign simulation node index according to index in the vector.
UInt mNumVirtualNodes
Number of virtual nodes, single line equivalent.
Definition MNASolver.h:49
void initializeSystemWithPrecomputedMatrices()
Initialization of system matrices and source vector.
CPS::Attribute< Matrix >::Ptr mLeftSideVector
Solution vector of unknown quantities.
Definition MNASolver.h:210
CPS::SimSignalComp::List mSimSignalComps
List of signal type components that do not directly interact with the MNA solver.
Definition MNASolver.h:77
virtual void createEmptySystemMatrix()=0
Create system matrix.
virtual std::shared_ptr< CPS::Task > createLogTask()=0
Create a solve task for this solver implementation.
const MNAStateSpaceExtractor & getStateSpaceExtractor() const
Read-only access to the MNA state-space extractor.
Definition MNASolver.cpp:46
void doStateSpaceExtraction(Bool value=true)
Enable or disable MNA state-space extraction.
Definition MNASolver.cpp:40
CPS::MNASwitchInterface::List mSwitches
Definition MNASolver.h:73
void createEmptyVectors()
Create left and right side vector.
virtual void switchedMatrixEmpty(std::size_t index)=0
Sets all entries in the matrix with the given switch index to zero.
virtual std::shared_ptr< CPS::Task > createSolveTask()=0
Create a solve task for this solver implementation.
CPS::SimNode< VarType >::List mNodes
List of simulation nodes.
Definition MNASolver.h:66
void initializeStateSpaceExtractor()
Initialization of state-space extraction.
virtual CPS::Task::List getTasks() override
Get tasks for scheduler.
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.
String mName
Name for logging.
Definition Solver.h:49
Real mSteadStIniAccLimit
steady state initialization accuracy limit
Definition Solver.h:65
Bool mSystemMatrixRecomputationEnabled
Effective system-matrix recomputation setting used by the solver.
Definition Solver.h:77
Real mTimeStep
Time step for fixed step solvers.
Definition Solver.h:57
CPS::Logger::Log mSLog
Logger.
Definition Solver.h:55
CPS::Logger::Level mLogLevel
Logging level.
Definition Solver.h:51
Bool mIsInInitialization
Determines if solver is in initialization phase, which requires different behavior.
Definition Solver.h:69
Solver(String name, CPS::Logger::Level logLevel)
Definition Solver.h:83
Real mSteadStIniTimeLimit
steady state initialization time limit
Definition Solver.h:63
Bool mInitFromNodesAndTerminals
Definition Solver.h:72
Bool mFrequencyParallel
Activates parallelized computation of frequencies.
Definition Solver.h:59
Bool mSteadyStateInit
Activates steady state initialization.
Definition Solver.h:67
SystemMatrixRecomputationMode mSystemMatrixRecomputationMode
Requested system-matrix recomputation mode.
Definition Solver.h:74
SystemMatrixRecomputationMode
System-matrix recomputation mode for MNA solvers.
Definition Solver.h:38
CPS::Real Real
Definition Definitions.h:18
CPS::String String
Definition Definitions.h:20
CPS::Int Int
Definition Definitions.h:22
CPS::Matrix Matrix
Definition Definitions.h:24
CPS::Bool Bool
Definition Definitions.h:21
CPS::UInt UInt
Definition Definitions.h:23