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 if (mNodes[idx]->phaseType() == CPS::PhaseType::DC)
529 SPDLOG_LOGGER_INFO(mSLog, "Assigned index {} to DC node {}",
530 matrixNodeIndexIdx, idx);
531 else
532 SPDLOG_LOGGER_INFO(mSLog, "Assigned index {} to phase A of node {}",
533 matrixNodeIndexIdx, idx);
534 ++matrixNodeIndexIdx;
535 if (mNodes[idx]->phaseType() == CPS::PhaseType::ABC) {
536 mNodes[idx]->setMatrixNodeIndex(1, matrixNodeIndexIdx);
537 SPDLOG_LOGGER_INFO(mSLog, "Assigned index {} to phase B of node {}",
538 matrixNodeIndexIdx, idx);
539 ++matrixNodeIndexIdx;
540 mNodes[idx]->setMatrixNodeIndex(2, matrixNodeIndexIdx);
541 SPDLOG_LOGGER_INFO(mSLog, "Assigned index {} to phase C of node {}",
542 matrixNodeIndexIdx, idx);
543 ++matrixNodeIndexIdx;
544 }
545 // This should be true when the final network node is reached, not considering virtual nodes
546 if (idx == mNumNetNodes - 1)
547 mNumNetMatrixNodeIndices = matrixNodeIndexIdx;
548 }
549 // Total number of network nodes including virtual nodes is matrixNodeIndexIdx + 1, which is why the variable is incremented after assignment
550 mNumMatrixNodeIndices = matrixNodeIndexIdx;
554 static_cast<UInt>(mSystem.mFrequencies.size() - 1) *
557 static_cast<UInt>(mSystem.mFrequencies.size()) * mNumMatrixNodeIndices;
558
559 SPDLOG_LOGGER_INFO(mSLog, "Assigned simulation nodes to topology nodes:");
560 SPDLOG_LOGGER_INFO(mSLog, "Number of network simulation nodes: {:d}",
562 SPDLOG_LOGGER_INFO(mSLog, "Number of simulation nodes: {:d}",
564 SPDLOG_LOGGER_INFO(mSLog, "Number of harmonic simulation nodes: {:d}",
566}
567
569 mRightSideVector = Matrix::Zero(mNumMatrixNodeIndices, 1);
570 **mLeftSideVector = Matrix::Zero(mNumMatrixNodeIndices, 1);
571}
572
574 if (mFrequencyParallel) {
575 for (Int freq = 0; freq < mSystem.mFrequencies.size(); ++freq) {
576 mRightSideVectorHarm.push_back(
577 Matrix::Zero(2 * (mNumMatrixNodeIndices), 1));
579 Matrix::Zero(2 * (mNumMatrixNodeIndices), 1)));
580 }
581 } else {
582 mRightSideVector = Matrix::Zero(
584 **mLeftSideVector = Matrix::Zero(
586 }
587}
588
589template <typename VarType> void MnaSolver<VarType>::collectVirtualNodes() {
590 // We have not added virtual nodes yet so the list has only network nodes
591 mNumNetNodes = (UInt)mNodes.size();
592 // virtual nodes are placed after network nodes
593 UInt virtualNode = mNumNetNodes - 1;
594
595 for (auto comp : mMNAComponents) {
596 auto pComp = std::dynamic_pointer_cast<SimPowerComp<VarType>>(comp);
597 if (!pComp)
598 continue;
599
600 // Check if component requires virtual node and if so get a reference
601 if (pComp->hasVirtualNodes()) {
602 for (UInt node = 0; node < pComp->virtualNodesNumber(); ++node) {
603 mNodes.push_back(pComp->virtualNode(node));
604 SPDLOG_LOGGER_INFO(mSLog, "Collected virtual node {} of {}",
605 virtualNode, node, pComp->name());
606 }
607 }
608
609 // Repeat the same steps for virtual nodes of sub components
610 // TODO: recursive behavior
611 if (pComp->hasSubComponents()) {
612 for (auto pSubComp : pComp->subComponents()) {
613 for (UInt node = 0; node < pSubComp->virtualNodesNumber(); ++node) {
614 auto vnode = pSubComp->virtualNode(node);
615 // Skip if already registered (e.g. parent reused its VN via
616 // setVirtualNodeAt).
617 bool alreadyRegistered = false;
618 for (auto registeredNode : mNodes) {
619 if (registeredNode == vnode) {
620 alreadyRegistered = true;
621 break;
622 }
623 }
624 if (alreadyRegistered)
625 continue;
626 mNodes.push_back(vnode);
627 SPDLOG_LOGGER_INFO(mSLog, "Collected virtual node {} of {}", node,
628 pSubComp->name());
629 }
630 }
631 }
632 }
633
634 // collect virtual nodes of variable components
635 for (auto comp : mVariableComps) {
636 auto pComp = std::dynamic_pointer_cast<SimPowerComp<VarType>>(comp);
637 if (!pComp)
638 continue;
639
640 // Check if component requires virtual node and if so get a reference
641 if (pComp->hasVirtualNodes()) {
642 for (UInt node = 0; node < pComp->virtualNodesNumber(); ++node) {
643 mNodes.push_back(pComp->virtualNode(node));
644 SPDLOG_LOGGER_INFO(mSLog,
645 "Collected virtual node {} of Varible Comp {}", node,
646 pComp->name());
647 }
648 }
649 }
650
651 // Update node number to create matrices and vectors
652 mNumNodes = (UInt)mNodes.size();
654 SPDLOG_LOGGER_INFO(mSLog, "Created virtual nodes:");
655 SPDLOG_LOGGER_INFO(mSLog, "Number of network nodes: {:d}", mNumNetNodes);
656 SPDLOG_LOGGER_INFO(mSLog, "Number of network and virtual nodes: {:d}",
657 mNumNodes);
658}
659
660template <typename VarType>
662 SPDLOG_LOGGER_INFO(mSLog, "--- Run steady-state initialization ---");
663
664 DataLogger initLeftVectorLog(mName + "_InitLeftVector",
665 mLogLevel != CPS::Logger::Level::off);
666 initLeftVectorLog.start();
667 DataLogger initRightVectorLog(mName + "_InitRightVector",
668 mLogLevel != CPS::Logger::Level::off);
669 initRightVectorLog.start();
670
671 TopologicalPowerComp::Behaviour initBehaviourPowerComps =
673 SimSignalComp::Behaviour initBehaviourSignalComps =
675
676 // TODO: enable use of timestep distinct from simulation timestep
677 Real initTimeStep = mTimeStep;
678
679 Int timeStepCount = 0;
680 Real time = 0;
681 Real maxDiff = 1.0;
682 Real max = 1.0;
683 Matrix diff = Matrix::Zero(2 * mNumNodes, 1);
684 Matrix prevLeftSideVector = Matrix::Zero(2 * mNumNodes, 1);
685
686 SPDLOG_LOGGER_INFO(mSLog,
687 "Time step is {:f}s for steady-state initialization",
688 initTimeStep);
689
690 for (auto comp : mSystem.mComponents) {
691 auto powerComp = std::dynamic_pointer_cast<CPS::TopologicalPowerComp>(comp);
692 if (powerComp)
693 powerComp->setBehaviour(initBehaviourPowerComps);
694
695 auto sigComp = std::dynamic_pointer_cast<CPS::SimSignalComp>(comp);
696 if (sigComp)
697 sigComp->setBehaviour(initBehaviourSignalComps);
698 }
699
702
703 // Use sequential scheduler
705 CPS::Task::List tasks;
706 Scheduler::Edges inEdges, outEdges;
707
708 for (auto node : mNodes) {
709 for (auto task : node->mnaTasks())
710 tasks.push_back(task);
711 }
712 for (auto comp : mMNAComponents) {
713 for (auto task : comp->mnaTasks()) {
714 tasks.push_back(task);
715 }
716 }
717 // TODO signal components should be moved out of MNA solver
718 for (auto comp : mSimSignalComps) {
719 for (auto task : comp->getTasks()) {
720 tasks.push_back(task);
721 }
722 }
723 tasks.push_back(createSolveTask());
724
725 sched.resolveDeps(tasks, inEdges, outEdges);
726 sched.createSchedule(tasks, inEdges, outEdges);
727
728 while (time < mSteadStIniTimeLimit) {
729 // Reset source vector
730 mRightSideVector.setZero();
731
732 sched.step(time, timeStepCount);
733
734 if (mDomain == CPS::Domain::EMT) {
735 initLeftVectorLog.logEMTNodeValues(time, leftSideVector());
736 initRightVectorLog.logEMTNodeValues(time, rightSideVector());
737 } else {
738 initLeftVectorLog.logPhasorNodeValues(time, leftSideVector());
739 initRightVectorLog.logPhasorNodeValues(time, rightSideVector());
740 }
741
742 // Calculate new simulation time
743 time = time + initTimeStep;
744 ++timeStepCount;
745
746 // Calculate difference
747 diff = prevLeftSideVector - **mLeftSideVector;
748 prevLeftSideVector = **mLeftSideVector;
749 maxDiff = diff.lpNorm<Eigen::Infinity>();
750 max = (**mLeftSideVector).lpNorm<Eigen::Infinity>();
751 // If difference is smaller than some epsilon, break
752 if ((maxDiff / max) < mSteadStIniAccLimit)
753 break;
754 }
755
756 SPDLOG_LOGGER_INFO(mSLog, "Max difference: {:f} or {:f}% at time {:f}",
757 maxDiff, maxDiff / max, time);
758
759 // Reset system for actual simulation
760 mRightSideVector.setZero();
761
762 SPDLOG_LOGGER_INFO(mSLog, "--- Finished steady-state initialization ---");
763}
764
765template <typename VarType> Task::List MnaSolver<VarType>::getTasks() {
766 Task::List l;
767
768 for (auto comp : mMNAComponents) {
769 for (auto task : comp->mnaTasks()) {
770 l.push_back(task);
771 }
772 }
773 for (auto comp : mMNAIntfSwitches) {
774 for (auto task : comp->mnaTasks()) {
775 l.push_back(task);
776 }
777 }
778 for (auto node : mNodes) {
779 for (auto task : node->mnaTasks())
780 l.push_back(task);
781 }
782 // TODO signal components should be moved out of MNA solver
783 for (auto comp : mSimSignalComps) {
784 for (auto task : comp->getTasks()) {
785 l.push_back(task);
786 }
787 }
788 if (mFrequencyParallel) {
789 for (UInt i = 0; i < mSystem.mFrequencies.size(); ++i)
790 l.push_back(createSolveTaskHarm(i));
792 for (auto comp : this->mMNAIntfVariableComps) {
793 for (auto task : comp->mnaTasks())
794 l.push_back(task);
795 }
796 l.push_back(createSolveTaskRecomp());
798 l.push_back(createStateSpaceExtractionTask());
799 }
800 } else {
801 l.push_back(createSolveTask());
803 l.push_back(createStateSpaceExtractionTask());
804 }
805 l.push_back(createLogTask());
806 }
807 return l;
808}
809
810template <typename VarType>
811void MnaSolver<VarType>::log(Real time, Int timeStepCount) {
812 if (mLogLevel == Logger::Level::off)
813 return;
814
815 if (mDomain == CPS::Domain::EMT) {
816 mLeftVectorLog->logEMTNodeValues(time, leftSideVector());
817 mRightVectorLog->logEMTNodeValues(time, rightSideVector());
818 } else {
819 mLeftVectorLog->logPhasorNodeValues(time, leftSideVector());
820 mRightVectorLog->logPhasorNodeValues(time, rightSideVector());
821 }
822}
823
824} // namespace DPsim
825
826template class DPsim::MnaSolver<Real>;
827template 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