DPsim
Loading...
Searching...
No Matches
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 <algorithm>
10#include <dpsim/MNASolver.h>
11#include <dpsim/SequentialScheduler.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>
23MnaSolver<VarType>::MnaSolver(String name, CPS::Domain domain,
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>
35void MnaSolver<VarType>::setSystem(const CPS::SystemTopology &system) {
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.
84 // Ensure all subcomponents (and their virtual nodes) are registered before
85 // collectVirtualNodes() sizes the system matrices. Recurses into nested
86 // sub-components so e.g. SST -> Load -> {R, L, C} is fully created.
87 CPS::MNAInterface::List allMNAComps;
88 allMNAComps.insert(allMNAComps.end(), mMNAComponents.begin(),
89 mMNAComponents.end());
90 allMNAComps.insert(allMNAComps.end(), mMNAIntfVariableComps.begin(),
92 allMNAComps.insert(allMNAComps.end(), mMNAIntfSwitches.begin(),
93 mMNAIntfSwitches.end());
94
95 // Some composites (e.g. AvVoltageSourceInverterDQ) eagerly create and
96 // register their subcomponents in their own constructor, before those
97 // subcomponents are connected (connect() only happens later, in
98 // initializeParentFromNodesAndTerminals()). Calling createSubComponents()
99 // on such a not-yet-connected subcomponent crashes, since it relies on
100 // its own terminals being wired up. Only subcomponents that are newly
101 // registered as a direct result of THIS createSubComponents() call (the
102 // lazy create+connect+register pattern, e.g. SST -> Load -> {R, L, C})
103 // are guaranteed to already be connected, so only recurse into those.
104 std::function<void(CPS::MNAInterface::Ptr)> createSubComponentsRec =
105 [&](CPS::MNAInterface::Ptr comp) {
106 auto pComp =
107 std::dynamic_pointer_cast<CPS::SimPowerComp<VarType>>(comp);
108 typename CPS::SimPowerComp<VarType>::List subCompsBefore =
109 pComp ? pComp->subComponents()
110 : typename CPS::SimPowerComp<VarType>::List();
111
112 comp->createSubComponents();
113
114 if (pComp) {
115 for (auto subComp : pComp->subComponents()) {
116 bool isNew = std::find(subCompsBefore.begin(), subCompsBefore.end(),
117 subComp) == subCompsBefore.end();
118 if (!isNew)
119 continue;
120 if (auto subMna =
121 std::dynamic_pointer_cast<CPS::MNAInterface>(subComp))
122 createSubComponentsRec(subMna);
123 }
124 }
125 };
126 for (auto comp : allMNAComps)
127 createSubComponentsRec(comp);
128 // These steps complete the network information.
131
132 SPDLOG_LOGGER_INFO(mSLog, "-- Create empty MNA system matrices and vectors");
135
136 // Initialize components from powerflow solution and
137 // calculate MNA specific initialization values.
139
140 if (mSteadyStateInit) {
141 mIsInInitialization = true;
142 steadyStateInitialization();
143 }
144 mIsInInitialization = false;
145
146 // Some components feature a different behaviour for simulation and initialization
147 for (auto comp : mSystem.mComponents) {
148 auto powerComp = std::dynamic_pointer_cast<CPS::TopologicalPowerComp>(comp);
149 if (powerComp)
150 powerComp->setBehaviour(TopologicalPowerComp::Behaviour::MNASimulation);
151
152 auto sigComp = std::dynamic_pointer_cast<CPS::SimSignalComp>(comp);
153 if (sigComp)
154 sigComp->setBehaviour(SimSignalComp::Behaviour::Simulation);
155 }
156
157 // Initialize system matrices and source vector.
159
162
163 SPDLOG_LOGGER_INFO(mSLog, "--- Initialization finished ---");
164 SPDLOG_LOGGER_INFO(mSLog, "--- Initial system matrices and vectors ---");
166
167 mSLog->flush();
168}
169
170template <> void MnaSolver<Real>::initializeComponents() {
171 SPDLOG_LOGGER_INFO(mSLog, "-- Initialize components from power flow");
172
173 CPS::MNAInterface::List allMNAComps;
174 allMNAComps.insert(allMNAComps.end(), mMNAComponents.begin(),
175 mMNAComponents.end());
176 allMNAComps.insert(allMNAComps.end(), mMNAIntfVariableComps.begin(),
177 mMNAIntfVariableComps.end());
178
179 for (auto comp : allMNAComps) {
180 auto pComp = std::dynamic_pointer_cast<SimPowerComp<Real>>(comp);
181 if (!pComp)
182 continue;
183 pComp->checkForUnconnectedTerminals();
184 if (mInitFromNodesAndTerminals)
185 pComp->initializeFromNodesAndTerminals(mSystem.mSystemFrequency);
186 }
187
188 // Initialize signal components.
189 for (auto comp : mSimSignalComps)
190 comp->initialize(mSystem.mSystemOmega, mTimeStep);
191
192 // Initialize MNA specific parts of components.
193 for (auto comp : allMNAComps) {
194 comp->mnaInitialize(mSystem.mSystemOmega, mTimeStep, mLeftSideVector);
195 const Matrix &stamp = comp->getRightVector()->get();
196 if (stamp.size() != 0) {
197 mRightVectorStamps.push_back(&stamp);
198 }
199 }
200
201 for (auto comp : mMNAIntfSwitches)
202 comp->mnaInitialize(mSystem.mSystemOmega, mTimeStep, mLeftSideVector);
203
204 // Initialize nodes
205 for (UInt nodeIdx = 0; nodeIdx < mNodes.size(); ++nodeIdx)
206 mNodes[nodeIdx]->initialize();
207}
208
210 SPDLOG_LOGGER_INFO(mSLog, "-- Initialize components from power flow");
211
212 CPS::MNAInterface::List allMNAComps;
213 allMNAComps.insert(allMNAComps.end(), mMNAComponents.begin(),
214 mMNAComponents.end());
215 allMNAComps.insert(allMNAComps.end(), mMNAIntfVariableComps.begin(),
216 mMNAIntfVariableComps.end());
217
218 // Initialize power components with frequencies and from powerflow results
219 for (auto comp : allMNAComps) {
220 auto pComp = std::dynamic_pointer_cast<SimPowerComp<Complex>>(comp);
221 if (!pComp)
222 continue;
223 pComp->checkForUnconnectedTerminals();
224 if (mInitFromNodesAndTerminals)
225 pComp->initializeFromNodesAndTerminals(mSystem.mSystemFrequency);
226 }
227
228 // Initialize signal components.
229 for (auto comp : mSimSignalComps)
230 comp->initialize(mSystem.mSystemOmega, mTimeStep);
231
232 SPDLOG_LOGGER_INFO(mSLog, "-- Initialize MNA properties of components");
233 if (mFrequencyParallel) {
234 // Initialize MNA specific parts of components.
235 for (auto comp : mMNAComponents) {
236 // Initialize MNA specific parts of components.
237 comp->mnaInitializeHarm(mSystem.mSystemOmega, mTimeStep,
238 mLeftSideVectorHarm);
239 const Matrix &stamp = comp->getRightVector()->get();
240 if (stamp.size() != 0)
241 mRightVectorStamps.push_back(&stamp);
242 }
243 // Initialize nodes
244 for (UInt nodeIdx = 0; nodeIdx < mNodes.size(); ++nodeIdx) {
245 mNodes[nodeIdx]->mnaInitializeHarm(mLeftSideVectorHarm);
246 }
247 } else {
248 // Initialize MNA specific parts of components.
249 for (auto comp : allMNAComps) {
250 comp->mnaInitialize(mSystem.mSystemOmega, mTimeStep, mLeftSideVector);
251 const Matrix &stamp = comp->getRightVector()->get();
252 if (stamp.size() != 0) {
253 mRightVectorStamps.push_back(&stamp);
254 }
255 }
256
257 for (auto comp : mMNAIntfSwitches)
258 comp->mnaInitialize(mSystem.mSystemOmega, mTimeStep, mLeftSideVector);
259
260 // Initialize nodes
261 for (UInt nodeIdx = 0; nodeIdx < mNodes.size(); ++nodeIdx)
262 mNodes[nodeIdx]->initialize();
263 }
264}
265
266template <typename VarType> void MnaSolver<VarType>::initializeSystem() {
267 SPDLOG_LOGGER_INFO(mSLog,
268 "-- Initialize MNA system matrices and source vector");
269 mRightSideVector.setZero();
270
271 // just a sanity check in case we change the static
272 // initialization of the switch number in the future
273 if (mSwitches.size() > sizeof(std::size_t) * 8) {
274 throw SystemError("Too many Switches.");
275 }
276
281 else
283}
284
285template <typename VarType>
287 // iterate over all possible switch state combinations and frequencies
288 for (std::size_t sw = 0; sw < (1ULL << mSwitches.size()); ++sw) {
289 for (Int freq = 0; freq < mSystem.mFrequencies.size(); ++freq) {
290 switchedMatrixEmpty(sw, freq);
292 }
293 }
294
295 if (mSwitches.size() > 0)
297
298 // Initialize source vector
299 for (Int freq = 0; freq < mSystem.mFrequencies.size(); ++freq) {
300 for (auto comp : mMNAComponents)
301 comp->mnaApplyRightSideVectorStampHarm(mRightSideVectorHarm[freq], freq);
302 }
303}
304
305template <typename VarType>
307 // iterate over all possible switch state combinations
308 for (std::size_t i = 0; i < (1ULL << mSwitches.size()); i++) {
310 }
311
312 if (mSwitches.size() < 1) {
314 } else {
315 // Generate switching state dependent system matrices
316 for (std::size_t i = 0; i < (1ULL << mSwitches.size()); i++) {
318 }
320 }
321
322 // Initialize source vector for debugging
323 // CAUTION: this does not always deliver proper source vector initialization
324 // as not full pre-step is executed (not involving necessary electrical or signal
325 // subcomp updates before right vector calculation)
326 for (auto comp : mMNAComponents) {
327 comp->mnaApplyRightSideVectorStamp(mRightSideVector);
328 auto idObj = std::dynamic_pointer_cast<IdentifiedObject>(comp);
329 SPDLOG_LOGGER_DEBUG(mSLog, "Stamping {:s} {:s} into source vector",
330 idObj->type(), idObj->name());
331 if (mSLog->should_log(spdlog::level::trace))
332 mSLog->trace("\n{:s}", Logger::matrixToString(mRightSideVector));
333 }
334}
335
336template <typename VarType>
338
339 // Collect index pairs of varying matrix entries from components
340 for (auto varElem : mVariableComps)
341 for (auto varEntry : varElem->mVariableSystemMatrixEntries)
342 mListVariableSystemMatrixEntries.push_back(varEntry);
343 SPDLOG_LOGGER_INFO(mSLog, "List of index pairs of varying matrix entries: ");
344 for (auto indexPair : mListVariableSystemMatrixEntries)
345 SPDLOG_LOGGER_INFO(mSLog, "({}, {})", indexPair.first, indexPair.second);
346
348
349 // Initialize source vector for debugging
350 // CAUTION: this does not always deliver proper source vector initialization
351 // as not full pre-step is executed (not involving necessary electrical or signal
352 // subcomp updates before right vector calculation)
353 for (auto comp : mMNAComponents) {
354 comp->mnaApplyRightSideVectorStamp(mRightSideVector);
355 auto idObj = std::dynamic_pointer_cast<IdentifiedObject>(comp);
356 SPDLOG_LOGGER_DEBUG(mSLog, "Stamping {:s} {:s} into source vector",
357 idObj->type(), idObj->name());
358 if (mSLog->should_log(spdlog::level::trace))
359 mSLog->trace("\n{:s}", Logger::matrixToString(mRightSideVector));
360 }
361}
362
363template <typename VarType>
365 if (mDomain != CPS::Domain::EMT) {
366 throw std::logic_error(
367 "MNA state-space extraction supports EMT domain only.");
368 }
369
370 if (!std::is_same<VarType, Real>::value) {
371 throw std::logic_error(
372 "MNA state-space extraction supports real-valued MNA systems only.");
373 }
374
375 if (mFrequencyParallel) {
376 throw std::logic_error(
377 "MNA state-space extraction does not support frequency-parallel "
378 "MNA systems.");
379 }
380
381 CPS::MNAInterface::List stateSpaceComponents;
382 stateSpaceComponents.insert(stateSpaceComponents.end(),
383 mMNAComponents.begin(), mMNAComponents.end());
384 stateSpaceComponents.insert(stateSpaceComponents.end(),
385 mMNAIntfVariableComps.begin(),
387
388 const UInt mnaVectorSize = static_cast<UInt>((**mLeftSideVector).rows());
389
390 mStateSpaceExtractor = std::make_shared<MNAStateSpaceExtractor>();
391 mStateSpaceExtractor->initialize(stateSpaceComponents, mnaVectorSize,
392 mTimeStep);
393
394 SPDLOG_LOGGER_INFO(
395 mSLog,
396 "Initialized MNA state-space extractor with {:d} extraction states.",
397 mStateSpaceExtractor->getStateCount());
398}
399
400template <typename VarType>
402 for (auto varElem : mVariableComps) {
403 if (varElem->hasParameterChanged()) {
404 auto idObj = std::dynamic_pointer_cast<IdentifiedObject>(varElem);
405 SPDLOG_LOGGER_DEBUG(
406 mSLog, "Component ({:s} {:s}) value changed -> Update System Matrix",
407 idObj->type(), idObj->name());
408 return true;
409 }
410 }
411 return false;
412}
413
414template <typename VarType> void MnaSolver<VarType>::updateSwitchStatus() {
415 for (UInt i = 0; i < mSwitches.size(); ++i) {
416 mCurrentSwitchStatus.set(i, mSwitches[i]->mnaIsClosed());
417 }
418}
419
420template <typename VarType> void MnaSolver<VarType>::identifyTopologyObjects() {
421 for (auto baseNode : mSystem.mNodes) {
422 // Add nodes to the list and ignore ground nodes.
423 if (!baseNode->isGround()) {
424 auto node = std::dynamic_pointer_cast<CPS::SimNode<VarType>>(baseNode);
425 mNodes.push_back(node);
426 SPDLOG_LOGGER_INFO(mSLog, "Added node {:s}", node->name());
427 }
428 }
429
430 for (auto comp : mSystem.mComponents) {
431
432 auto genComp = std::dynamic_pointer_cast<CPS::MNASyncGenInterface>(comp);
433 if (genComp) {
434 mSyncGen.push_back(genComp);
435 }
436
437 auto swComp = std::dynamic_pointer_cast<CPS::MNASwitchInterface>(comp);
438 if (swComp) {
439 mSwitches.push_back(swComp);
440 auto mnaComp = std::dynamic_pointer_cast<CPS::MNAInterface>(swComp);
441 if (mnaComp)
442 mMNAIntfSwitches.push_back(mnaComp);
443 }
444
445 auto varComp =
446 std::dynamic_pointer_cast<CPS::MNAVariableCompInterface>(comp);
447 if (varComp) {
448 mVariableComps.push_back(varComp);
449 auto mnaComp = std::dynamic_pointer_cast<CPS::MNAInterface>(varComp);
450 if (mnaComp)
451 mMNAIntfVariableComps.push_back(mnaComp);
452 }
453
454 if (!(swComp || varComp)) {
455 auto mnaComp = std::dynamic_pointer_cast<CPS::MNAInterface>(comp);
456 if (mnaComp)
457 mMNAComponents.push_back(mnaComp);
458
459 auto sigComp = std::dynamic_pointer_cast<CPS::SimSignalComp>(comp);
460 if (sigComp)
461 mSimSignalComps.push_back(sigComp);
462 }
463 }
464}
465
466template <typename VarType> void MnaSolver<VarType>::assignMatrixNodeIndices() {
467 UInt matrixNodeIndexIdx = 0;
468 for (UInt idx = 0; idx < mNodes.size(); ++idx) {
469 mNodes[idx]->setMatrixNodeIndex(0, matrixNodeIndexIdx);
470 SPDLOG_LOGGER_INFO(mSLog, "Assigned index {} to phase A of node {}",
471 matrixNodeIndexIdx, idx);
472 ++matrixNodeIndexIdx;
473 if (mNodes[idx]->phaseType() == CPS::PhaseType::ABC) {
474 mNodes[idx]->setMatrixNodeIndex(1, matrixNodeIndexIdx);
475 SPDLOG_LOGGER_INFO(mSLog, "Assigned index {} to phase B of node {}",
476 matrixNodeIndexIdx, idx);
477 ++matrixNodeIndexIdx;
478 mNodes[idx]->setMatrixNodeIndex(2, matrixNodeIndexIdx);
479 SPDLOG_LOGGER_INFO(mSLog, "Assigned index {} to phase C of node {}",
480 matrixNodeIndexIdx, idx);
481 ++matrixNodeIndexIdx;
482 }
483 // This should be true when the final network node is reached, not considering virtual nodes
484 if (idx == mNumNetNodes - 1)
485 mNumNetMatrixNodeIndices = matrixNodeIndexIdx;
486 }
487 // Total number of network nodes including virtual nodes is matrixNodeIndexIdx + 1, which is why the variable is incremented after assignment
488 mNumMatrixNodeIndices = matrixNodeIndexIdx;
492 static_cast<UInt>(mSystem.mFrequencies.size() - 1) *
495 static_cast<UInt>(mSystem.mFrequencies.size()) * mNumMatrixNodeIndices;
496
497 SPDLOG_LOGGER_INFO(mSLog, "Assigned simulation nodes to topology nodes:");
498 SPDLOG_LOGGER_INFO(mSLog, "Number of network simulation nodes: {:d}",
500 SPDLOG_LOGGER_INFO(mSLog, "Number of simulation nodes: {:d}",
502 SPDLOG_LOGGER_INFO(mSLog, "Number of harmonic simulation nodes: {:d}",
504}
505
506template <> void MnaSolver<Real>::createEmptyVectors() {
507 mRightSideVector = Matrix::Zero(mNumMatrixNodeIndices, 1);
508 **mLeftSideVector = Matrix::Zero(mNumMatrixNodeIndices, 1);
509}
510
512 if (mFrequencyParallel) {
513 for (Int freq = 0; freq < mSystem.mFrequencies.size(); ++freq) {
514 mRightSideVectorHarm.push_back(
515 Matrix::Zero(2 * (mNumMatrixNodeIndices), 1));
516 mLeftSideVectorHarm.push_back(AttributeStatic<Matrix>::make(
517 Matrix::Zero(2 * (mNumMatrixNodeIndices), 1)));
518 }
519 } else {
520 mRightSideVector = Matrix::Zero(
521 2 * (mNumMatrixNodeIndices + mNumHarmMatrixNodeIndices), 1);
522 **mLeftSideVector = Matrix::Zero(
523 2 * (mNumMatrixNodeIndices + mNumHarmMatrixNodeIndices), 1);
524 }
525}
526
527template <typename VarType> void MnaSolver<VarType>::collectVirtualNodes() {
528 // We have not added virtual nodes yet so the list has only network nodes
529 mNumNetNodes = (UInt)mNodes.size();
530 // virtual nodes are placed after network nodes
531 UInt virtualNode = mNumNetNodes - 1;
532
533 for (auto comp : mMNAComponents) {
534 auto pComp = std::dynamic_pointer_cast<SimPowerComp<VarType>>(comp);
535 if (!pComp)
536 continue;
537
538 // Check if component requires virtual node and if so get a reference
539 if (pComp->hasVirtualNodes()) {
540 for (UInt node = 0; node < pComp->virtualNodesNumber(); ++node) {
541 mNodes.push_back(pComp->virtualNode(node));
542 SPDLOG_LOGGER_INFO(mSLog, "Collected virtual node {} of {}",
543 virtualNode, node, pComp->name());
544 }
545 }
546
547 // Repeat the same steps for virtual nodes of sub components
548 // TODO: recursive behavior
549 if (pComp->hasSubComponents()) {
550 for (auto pSubComp : pComp->subComponents()) {
551 for (UInt node = 0; node < pSubComp->virtualNodesNumber(); ++node) {
552 auto vnode = pSubComp->virtualNode(node);
553 // Skip if already registered (e.g. parent reused its VN via
554 // setVirtualNodeAt).
555 bool alreadyRegistered = false;
556 for (auto registeredNode : mNodes) {
557 if (registeredNode == vnode) {
558 alreadyRegistered = true;
559 break;
560 }
561 }
562 if (alreadyRegistered)
563 continue;
564 mNodes.push_back(vnode);
565 SPDLOG_LOGGER_INFO(mSLog, "Collected virtual node {} of {}", node,
566 pSubComp->name());
567 }
568 }
569 }
570 }
571
572 // collect virtual nodes of variable components
573 for (auto comp : mVariableComps) {
574 auto pComp = std::dynamic_pointer_cast<SimPowerComp<VarType>>(comp);
575 if (!pComp)
576 continue;
577
578 // Check if component requires virtual node and if so get a reference
579 if (pComp->hasVirtualNodes()) {
580 for (UInt node = 0; node < pComp->virtualNodesNumber(); ++node) {
581 mNodes.push_back(pComp->virtualNode(node));
582 SPDLOG_LOGGER_INFO(mSLog,
583 "Collected virtual node {} of Varible Comp {}", node,
584 pComp->name());
585 }
586 }
587 }
588
589 // Update node number to create matrices and vectors
590 mNumNodes = (UInt)mNodes.size();
592 SPDLOG_LOGGER_INFO(mSLog, "Created virtual nodes:");
593 SPDLOG_LOGGER_INFO(mSLog, "Number of network nodes: {:d}", mNumNetNodes);
594 SPDLOG_LOGGER_INFO(mSLog, "Number of network and virtual nodes: {:d}",
595 mNumNodes);
596}
597
598template <typename VarType>
599void MnaSolver<VarType>::steadyStateInitialization() {
600 SPDLOG_LOGGER_INFO(mSLog, "--- Run steady-state initialization ---");
601
602 DataLogger initLeftVectorLog(mName + "_InitLeftVector",
603 mLogLevel != CPS::Logger::Level::off);
604 initLeftVectorLog.start();
605 DataLogger initRightVectorLog(mName + "_InitRightVector",
606 mLogLevel != CPS::Logger::Level::off);
607 initRightVectorLog.start();
608
609 TopologicalPowerComp::Behaviour initBehaviourPowerComps =
610 TopologicalPowerComp::Behaviour::Initialization;
611 SimSignalComp::Behaviour initBehaviourSignalComps =
612 SimSignalComp::Behaviour::Initialization;
613
614 // TODO: enable use of timestep distinct from simulation timestep
615 Real initTimeStep = mTimeStep;
616
617 Int timeStepCount = 0;
618 Real time = 0;
619 Real maxDiff = 1.0;
620 Real max = 1.0;
621 Matrix diff = Matrix::Zero(2 * mNumNodes, 1);
622 Matrix prevLeftSideVector = Matrix::Zero(2 * mNumNodes, 1);
623
624 SPDLOG_LOGGER_INFO(mSLog,
625 "Time step is {:f}s for steady-state initialization",
626 initTimeStep);
627
628 for (auto comp : mSystem.mComponents) {
629 auto powerComp = std::dynamic_pointer_cast<CPS::TopologicalPowerComp>(comp);
630 if (powerComp)
631 powerComp->setBehaviour(initBehaviourPowerComps);
632
633 auto sigComp = std::dynamic_pointer_cast<CPS::SimSignalComp>(comp);
634 if (sigComp)
635 sigComp->setBehaviour(initBehaviourSignalComps);
636 }
637
638 initializeSystem();
639 logSystemMatrices();
640
641 // Use sequential scheduler
643 CPS::Task::List tasks;
644 Scheduler::Edges inEdges, outEdges;
645
646 for (auto node : mNodes) {
647 for (auto task : node->mnaTasks())
648 tasks.push_back(task);
649 }
650 for (auto comp : mMNAComponents) {
651 for (auto task : comp->mnaTasks()) {
652 tasks.push_back(task);
653 }
654 }
655 // TODO signal components should be moved out of MNA solver
656 for (auto comp : mSimSignalComps) {
657 for (auto task : comp->getTasks()) {
658 tasks.push_back(task);
659 }
660 }
661 tasks.push_back(createSolveTask());
662
663 sched.resolveDeps(tasks, inEdges, outEdges);
664 sched.createSchedule(tasks, inEdges, outEdges);
665
666 while (time < mSteadStIniTimeLimit) {
667 // Reset source vector
668 mRightSideVector.setZero();
669
670 sched.step(time, timeStepCount);
671
672 if (mDomain == CPS::Domain::EMT) {
673 initLeftVectorLog.logEMTNodeValues(time, leftSideVector());
674 initRightVectorLog.logEMTNodeValues(time, rightSideVector());
675 } else {
676 initLeftVectorLog.logPhasorNodeValues(time, leftSideVector());
677 initRightVectorLog.logPhasorNodeValues(time, rightSideVector());
678 }
679
680 // Calculate new simulation time
681 time = time + initTimeStep;
682 ++timeStepCount;
683
684 // Calculate difference
685 diff = prevLeftSideVector - **mLeftSideVector;
686 prevLeftSideVector = **mLeftSideVector;
687 maxDiff = diff.lpNorm<Eigen::Infinity>();
688 max = (**mLeftSideVector).lpNorm<Eigen::Infinity>();
689 // If difference is smaller than some epsilon, break
690 if ((maxDiff / max) < mSteadStIniAccLimit)
691 break;
692 }
693
694 SPDLOG_LOGGER_INFO(mSLog, "Max difference: {:f} or {:f}% at time {:f}",
695 maxDiff, maxDiff / max, time);
696
697 // Reset system for actual simulation
698 mRightSideVector.setZero();
699
700 SPDLOG_LOGGER_INFO(mSLog, "--- Finished steady-state initialization ---");
701}
702
703template <typename VarType> Task::List MnaSolver<VarType>::getTasks() {
704 Task::List l;
705
706 for (auto comp : mMNAComponents) {
707 for (auto task : comp->mnaTasks()) {
708 l.push_back(task);
709 }
710 }
711 for (auto comp : mMNAIntfSwitches) {
712 for (auto task : comp->mnaTasks()) {
713 l.push_back(task);
714 }
715 }
716 for (auto node : mNodes) {
717 for (auto task : node->mnaTasks())
718 l.push_back(task);
719 }
720 // TODO signal components should be moved out of MNA solver
721 for (auto comp : mSimSignalComps) {
722 for (auto task : comp->getTasks()) {
723 l.push_back(task);
724 }
725 }
726 if (mFrequencyParallel) {
727 for (UInt i = 0; i < mSystem.mFrequencies.size(); ++i)
728 l.push_back(createSolveTaskHarm(i));
729 } else if (mSystemMatrixRecomputation) {
730 for (auto comp : this->mMNAIntfVariableComps) {
731 for (auto task : comp->mnaTasks())
732 l.push_back(task);
733 }
734 l.push_back(createSolveTaskRecomp());
736 l.push_back(createStateSpaceExtractionTask());
737 }
738 } else {
739 l.push_back(createSolveTask());
741 l.push_back(createStateSpaceExtractionTask());
742 }
743 l.push_back(createLogTask());
744 }
745 return l;
746}
747
748template <typename VarType>
749void MnaSolver<VarType>::log(Real time, Int timeStepCount) {
750 if (mLogLevel == Logger::Level::off)
751 return;
752
753 if (mDomain == CPS::Domain::EMT) {
754 mLeftVectorLog->logEMTNodeValues(time, leftSideVector());
755 mRightVectorLog->logEMTNodeValues(time, rightSideVector());
756 } else {
757 mLeftVectorLog->logPhasorNodeValues(time, leftSideVector());
758 mRightVectorLog->logPhasorNodeValues(time, rightSideVector());
759 }
760}
761
762} // namespace DPsim
763
764template class DPsim::MnaSolver<Real>;
765template class DPsim::MnaSolver<Complex>;
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
CPS::Domain mDomain
Simulation domain, which can be dynamic phasor (DP) or EMT.
Definition MNASolver.h:43
void identifyTopologyObjects()
Identify Nodes and SimPowerComps and SimSignalComps.
std::vector< Matrix > mRightSideVectorHarm
Source vector of known quantities.
Definition MNASolver.h:90
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.
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:211
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.
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:208
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.
Real mTimeStep
Time step for fixed step solvers.
Definition Solver.h:47
Bool mSystemMatrixRecomputation
Enable recomputation of system matrix during simulation.
Definition Solver.h:64
CPS::Logger::Log mSLog
Logger.
Definition Solver.h:45
CPS::Logger::Level mLogLevel
Logging level.
Definition Solver.h:41
Bool mIsInInitialization
Determines if solver is in initialization phase, which requires different behavior.
Definition Solver.h:59
Bool mFrequencyParallel
Activates parallelized computation of frequencies.
Definition Solver.h:49
Bool mSteadyStateInit
Activates steady state initialization.
Definition Solver.h:57