9 #include <dpsim/MNASolver.h>
10 #include <dpsim/SequentialScheduler.h>
13 using namespace DPsim;
18 template <
typename VarType>
20 CPS::Logger::Level logLevel)
21 :
Solver(name, logLevel), mDomain(domain) {
25 name +
"_LeftVector", logLevel == CPS::Logger::Level::trace);
27 name +
"_RightVector", logLevel == CPS::Logger::Level::trace);
30 template <
typename VarType>
37 SPDLOG_LOGGER_INFO(mSLog,
"---- Start initialization ----");
38 mLeftVectorLog->start();
39 mRightVectorLog->start();
44 if (mFrequencyParallel) {
45 SPDLOG_LOGGER_INFO(mSLog,
"Computing network harmonics in parallel.");
46 for (Int freq = 0; freq < mSystem.mFrequencies.size(); ++freq) {
53 SPDLOG_LOGGER_INFO(mSLog,
"-- Process topology");
54 for (
auto comp : mSystem.mComponents)
55 SPDLOG_LOGGER_INFO(mSLog,
"Added {:s} '{:s}' to simulation.", comp->type(),
59 if (mSystem.mComponents.size() == 0)
64 identifyTopologyObjects();
66 collectVirtualNodes();
67 assignMatrixNodeIndices();
69 SPDLOG_LOGGER_INFO(mSLog,
"-- Create empty MNA system matrices and vectors");
71 createEmptySystemMatrix();
75 initializeComponents();
77 if (mSteadyStateInit) {
78 mIsInInitialization =
true;
79 steadyStateInitialization();
81 mIsInInitialization =
false;
84 for (
auto comp : mSystem.mComponents) {
85 auto powerComp = std::dynamic_pointer_cast<CPS::TopologicalPowerComp>(comp);
87 powerComp->setBehaviour(TopologicalPowerComp::Behaviour::MNASimulation);
89 auto sigComp = std::dynamic_pointer_cast<CPS::SimSignalComp>(comp);
91 sigComp->setBehaviour(SimSignalComp::Behaviour::Simulation);
97 SPDLOG_LOGGER_INFO(mSLog,
"--- Initialization finished ---");
98 SPDLOG_LOGGER_INFO(mSLog,
"--- Initial system matrices and vectors ---");
105 SPDLOG_LOGGER_INFO(mSLog,
"-- Initialize components from power flow");
107 CPS::MNAInterface::List allMNAComps;
108 allMNAComps.insert(allMNAComps.end(), mMNAComponents.begin(),
109 mMNAComponents.end());
110 allMNAComps.insert(allMNAComps.end(), mMNAIntfVariableComps.begin(),
111 mMNAIntfVariableComps.end());
113 for (
auto comp : allMNAComps) {
114 auto pComp = std::dynamic_pointer_cast<SimPowerComp<Real>>(comp);
117 pComp->checkForUnconnectedTerminals();
118 if (mInitFromNodesAndTerminals)
119 pComp->initializeFromNodesAndTerminals(mSystem.mSystemFrequency);
123 for (
auto comp : mSimSignalComps)
124 comp->initialize(mSystem.mSystemOmega, mTimeStep);
127 for (
auto comp : allMNAComps) {
128 comp->mnaInitialize(mSystem.mSystemOmega, mTimeStep, mLeftSideVector);
129 const Matrix &stamp = comp->getRightVector()->get();
130 if (stamp.size() != 0) {
131 mRightVectorStamps.push_back(&stamp);
135 for (
auto comp : mMNAIntfSwitches)
136 comp->mnaInitialize(mSystem.mSystemOmega, mTimeStep, mLeftSideVector);
139 for (UInt nodeIdx = 0; nodeIdx < mNodes.size(); ++nodeIdx)
140 mNodes[nodeIdx]->initialize();
144 SPDLOG_LOGGER_INFO(mSLog,
"-- Initialize components from power flow");
146 CPS::MNAInterface::List allMNAComps;
147 allMNAComps.insert(allMNAComps.end(), mMNAComponents.begin(),
148 mMNAComponents.end());
149 allMNAComps.insert(allMNAComps.end(), mMNAIntfVariableComps.begin(),
150 mMNAIntfVariableComps.end());
153 for (
auto comp : allMNAComps) {
154 auto pComp = std::dynamic_pointer_cast<SimPowerComp<Complex>>(comp);
157 pComp->checkForUnconnectedTerminals();
158 if (mInitFromNodesAndTerminals)
159 pComp->initializeFromNodesAndTerminals(mSystem.mSystemFrequency);
163 for (
auto comp : mSimSignalComps)
164 comp->initialize(mSystem.mSystemOmega, mTimeStep);
166 SPDLOG_LOGGER_INFO(mSLog,
"-- Initialize MNA properties of components");
167 if (mFrequencyParallel) {
169 for (
auto comp : mMNAComponents) {
171 comp->mnaInitializeHarm(mSystem.mSystemOmega, mTimeStep,
172 mLeftSideVectorHarm);
173 const Matrix &stamp = comp->getRightVector()->get();
174 if (stamp.size() != 0)
175 mRightVectorStamps.push_back(&stamp);
178 for (UInt nodeIdx = 0; nodeIdx < mNodes.size(); ++nodeIdx) {
179 mNodes[nodeIdx]->mnaInitializeHarm(mLeftSideVectorHarm);
183 for (
auto comp : allMNAComps) {
184 comp->mnaInitialize(mSystem.mSystemOmega, mTimeStep, mLeftSideVector);
185 const Matrix &stamp = comp->getRightVector()->get();
186 if (stamp.size() != 0) {
187 mRightVectorStamps.push_back(&stamp);
191 for (
auto comp : mMNAIntfSwitches)
192 comp->mnaInitialize(mSystem.mSystemOmega, mTimeStep, mLeftSideVector);
195 for (UInt nodeIdx = 0; nodeIdx < mNodes.size(); ++nodeIdx)
196 mNodes[nodeIdx]->initialize();
201 SPDLOG_LOGGER_INFO(mSLog,
202 "-- Initialize MNA system matrices and source vector");
203 mRightSideVector.setZero();
207 if (mSwitches.size() >
sizeof(std::size_t) * 8) {
211 if (mFrequencyParallel)
212 initializeSystemWithParallelFrequencies();
213 else if (mSystemMatrixRecomputation)
214 initializeSystemWithVariableMatrix();
216 initializeSystemWithPrecomputedMatrices();
219 template <
typename VarType>
222 for (std::size_t sw = 0; sw < (1ULL << mSwitches.size()); ++sw) {
223 for (Int freq = 0; freq < mSystem.mFrequencies.size(); ++freq) {
224 switchedMatrixEmpty(sw, freq);
225 switchedMatrixStamp(sw, freq, mMNAComponents, mSwitches);
229 if (mSwitches.size() > 0)
230 updateSwitchStatus();
233 for (Int freq = 0; freq < mSystem.mFrequencies.size(); ++freq) {
234 for (
auto comp : mMNAComponents)
235 comp->mnaApplyRightSideVectorStampHarm(mRightSideVectorHarm[freq], freq);
239 template <
typename VarType>
242 for (std::size_t i = 0; i < (1ULL << mSwitches.size()); i++) {
243 switchedMatrixEmpty(i);
246 if (mSwitches.size() < 1) {
247 switchedMatrixStamp(0, mMNAComponents);
250 for (std::size_t i = 0; i < (1ULL << mSwitches.size()); i++) {
251 switchedMatrixStamp(i, mMNAComponents);
253 updateSwitchStatus();
260 for (
auto comp : mMNAComponents) {
261 comp->mnaApplyRightSideVectorStamp(mRightSideVector);
262 auto idObj = std::dynamic_pointer_cast<IdentifiedObject>(comp);
263 SPDLOG_LOGGER_DEBUG(mSLog,
"Stamping {:s} {:s} into source vector",
264 idObj->type(), idObj->name());
265 if (mSLog->should_log(spdlog::level::trace))
266 mSLog->trace(
"\n{:s}", Logger::matrixToString(mRightSideVector));
270 template <
typename VarType>
274 for (
auto varElem : mVariableComps)
275 for (
auto varEntry : varElem->mVariableSystemMatrixEntries)
276 mListVariableSystemMatrixEntries.push_back(varEntry);
277 SPDLOG_LOGGER_INFO(mSLog,
"List of index pairs of varying matrix entries: ");
278 for (
auto indexPair : mListVariableSystemMatrixEntries)
279 SPDLOG_LOGGER_INFO(mSLog,
"({}, {})", indexPair.first, indexPair.second);
281 stampVariableSystemMatrix();
287 for (
auto comp : mMNAComponents) {
288 comp->mnaApplyRightSideVectorStamp(mRightSideVector);
289 auto idObj = std::dynamic_pointer_cast<IdentifiedObject>(comp);
290 SPDLOG_LOGGER_DEBUG(mSLog,
"Stamping {:s} {:s} into source vector",
291 idObj->type(), idObj->name());
292 if (mSLog->should_log(spdlog::level::trace))
293 mSLog->trace(
"\n{:s}", Logger::matrixToString(mRightSideVector));
297 template <
typename VarType>
299 for (
auto varElem : mVariableComps) {
300 if (varElem->hasParameterChanged()) {
301 auto idObj = std::dynamic_pointer_cast<IdentifiedObject>(varElem);
303 mSLog,
"Component ({:s} {:s}) value changed -> Update System Matrix",
304 idObj->type(), idObj->name());
312 for (UInt i = 0; i < mSwitches.size(); ++i) {
313 mCurrentSwitchStatus.set(i, mSwitches[i]->mnaIsClosed());
318 for (
auto baseNode : mSystem.mNodes) {
320 if (!baseNode->isGround()) {
321 auto node = std::dynamic_pointer_cast<CPS::SimNode<VarType>>(baseNode);
322 mNodes.push_back(node);
323 SPDLOG_LOGGER_INFO(mSLog,
"Added node {:s}", node->name());
327 for (
auto comp : mSystem.mComponents) {
329 auto genComp = std::dynamic_pointer_cast<CPS::MNASyncGenInterface>(comp);
331 mSyncGen.push_back(genComp);
334 auto swComp = std::dynamic_pointer_cast<CPS::MNASwitchInterface>(comp);
336 mSwitches.push_back(swComp);
337 auto mnaComp = std::dynamic_pointer_cast<CPS::MNAInterface>(swComp);
339 mMNAIntfSwitches.push_back(mnaComp);
343 std::dynamic_pointer_cast<CPS::MNAVariableCompInterface>(comp);
345 mVariableComps.push_back(varComp);
346 auto mnaComp = std::dynamic_pointer_cast<CPS::MNAInterface>(varComp);
348 mMNAIntfVariableComps.push_back(mnaComp);
351 if (!(swComp || varComp)) {
352 auto mnaComp = std::dynamic_pointer_cast<CPS::MNAInterface>(comp);
354 mMNAComponents.push_back(mnaComp);
356 auto sigComp = std::dynamic_pointer_cast<CPS::SimSignalComp>(comp);
358 mSimSignalComps.push_back(sigComp);
364 UInt matrixNodeIndexIdx = 0;
365 for (UInt idx = 0; idx < mNodes.size(); ++idx) {
366 mNodes[idx]->setMatrixNodeIndex(0, matrixNodeIndexIdx);
367 SPDLOG_LOGGER_INFO(mSLog,
"Assigned index {} to phase A of node {}",
368 matrixNodeIndexIdx, idx);
369 ++matrixNodeIndexIdx;
370 if (mNodes[idx]->phaseType() == CPS::PhaseType::ABC) {
371 mNodes[idx]->setMatrixNodeIndex(1, matrixNodeIndexIdx);
372 SPDLOG_LOGGER_INFO(mSLog,
"Assigned index {} to phase B of node {}",
373 matrixNodeIndexIdx, idx);
374 ++matrixNodeIndexIdx;
375 mNodes[idx]->setMatrixNodeIndex(2, matrixNodeIndexIdx);
376 SPDLOG_LOGGER_INFO(mSLog,
"Assigned index {} to phase C of node {}",
377 matrixNodeIndexIdx, idx);
378 ++matrixNodeIndexIdx;
381 if (idx == mNumNetNodes - 1)
382 mNumNetMatrixNodeIndices = matrixNodeIndexIdx;
385 mNumMatrixNodeIndices = matrixNodeIndexIdx;
386 mNumVirtualMatrixNodeIndices =
387 mNumMatrixNodeIndices - mNumNetMatrixNodeIndices;
388 mNumHarmMatrixNodeIndices =
389 static_cast<UInt
>(mSystem.mFrequencies.size() - 1) *
390 mNumMatrixNodeIndices;
391 mNumTotalMatrixNodeIndices =
392 static_cast<UInt
>(mSystem.mFrequencies.size()) * mNumMatrixNodeIndices;
394 SPDLOG_LOGGER_INFO(mSLog,
"Assigned simulation nodes to topology nodes:");
395 SPDLOG_LOGGER_INFO(mSLog,
"Number of network simulation nodes: {:d}",
396 mNumNetMatrixNodeIndices);
397 SPDLOG_LOGGER_INFO(mSLog,
"Number of simulation nodes: {:d}",
398 mNumMatrixNodeIndices);
399 SPDLOG_LOGGER_INFO(mSLog,
"Number of harmonic simulation nodes: {:d}",
400 mNumHarmMatrixNodeIndices);
404 mRightSideVector = Matrix::Zero(mNumMatrixNodeIndices, 1);
405 **mLeftSideVector = Matrix::Zero(mNumMatrixNodeIndices, 1);
409 if (mFrequencyParallel) {
410 for (Int freq = 0; freq < mSystem.mFrequencies.size(); ++freq) {
411 mRightSideVectorHarm.push_back(
412 Matrix::Zero(2 * (mNumMatrixNodeIndices), 1));
414 Matrix::Zero(2 * (mNumMatrixNodeIndices), 1)));
417 mRightSideVector = Matrix::Zero(
418 2 * (mNumMatrixNodeIndices + mNumHarmMatrixNodeIndices), 1);
419 **mLeftSideVector = Matrix::Zero(
420 2 * (mNumMatrixNodeIndices + mNumHarmMatrixNodeIndices), 1);
426 mNumNetNodes = (UInt)mNodes.size();
428 UInt virtualNode = mNumNetNodes - 1;
430 for (
auto comp : mMNAComponents) {
431 auto pComp = std::dynamic_pointer_cast<SimPowerComp<VarType>>(comp);
436 if (pComp->hasVirtualNodes()) {
437 for (UInt node = 0; node < pComp->virtualNodesNumber(); ++node) {
438 mNodes.push_back(pComp->virtualNode(node));
439 SPDLOG_LOGGER_INFO(mSLog,
"Collected virtual node {} of {}",
440 virtualNode, node, pComp->name());
446 if (pComp->hasSubComponents()) {
447 for (
auto pSubComp : pComp->subComponents()) {
448 for (UInt node = 0; node < pSubComp->virtualNodesNumber(); ++node) {
449 mNodes.push_back(pSubComp->virtualNode(node));
450 SPDLOG_LOGGER_INFO(mSLog,
"Collected virtual node {} of {}",
451 virtualNode, node, pComp->name());
458 for (
auto comp : mVariableComps) {
459 auto pComp = std::dynamic_pointer_cast<SimPowerComp<VarType>>(comp);
464 if (pComp->hasVirtualNodes()) {
465 for (UInt node = 0; node < pComp->virtualNodesNumber(); ++node) {
466 mNodes.push_back(pComp->virtualNode(node));
467 SPDLOG_LOGGER_INFO(mSLog,
468 "Collected virtual node {} of Varible Comp {}", node,
475 mNumNodes = (UInt)mNodes.size();
476 mNumVirtualNodes = mNumNodes - mNumNetNodes;
477 SPDLOG_LOGGER_INFO(mSLog,
"Created virtual nodes:");
478 SPDLOG_LOGGER_INFO(mSLog,
"Number of network nodes: {:d}", mNumNetNodes);
479 SPDLOG_LOGGER_INFO(mSLog,
"Number of network and virtual nodes: {:d}",
483 template <
typename VarType>
485 SPDLOG_LOGGER_INFO(mSLog,
"--- Run steady-state initialization ---");
487 DataLogger initLeftVectorLog(mName +
"_InitLeftVector",
488 mLogLevel != CPS::Logger::Level::off);
489 initLeftVectorLog.start();
490 DataLogger initRightVectorLog(mName +
"_InitRightVector",
491 mLogLevel != CPS::Logger::Level::off);
492 initRightVectorLog.start();
494 TopologicalPowerComp::Behaviour initBehaviourPowerComps =
495 TopologicalPowerComp::Behaviour::Initialization;
496 SimSignalComp::Behaviour initBehaviourSignalComps =
497 SimSignalComp::Behaviour::Initialization;
500 Real initTimeStep = mTimeStep;
502 Int timeStepCount = 0;
506 Matrix diff = Matrix::Zero(2 * mNumNodes, 1);
507 Matrix prevLeftSideVector = Matrix::Zero(2 * mNumNodes, 1);
509 SPDLOG_LOGGER_INFO(mSLog,
510 "Time step is {:f}s for steady-state initialization",
513 for (
auto comp : mSystem.mComponents) {
514 auto powerComp = std::dynamic_pointer_cast<CPS::TopologicalPowerComp>(comp);
516 powerComp->setBehaviour(initBehaviourPowerComps);
518 auto sigComp = std::dynamic_pointer_cast<CPS::SimSignalComp>(comp);
520 sigComp->setBehaviour(initBehaviourSignalComps);
528 CPS::Task::List tasks;
531 for (
auto node : mNodes) {
532 for (
auto task : node->mnaTasks())
533 tasks.push_back(task);
535 for (
auto comp : mMNAComponents) {
536 for (
auto task : comp->mnaTasks()) {
537 tasks.push_back(task);
541 for (
auto comp : mSimSignalComps) {
542 for (
auto task : comp->getTasks()) {
543 tasks.push_back(task);
546 tasks.push_back(createSolveTask());
551 while (time < mSteadStIniTimeLimit) {
553 mRightSideVector.setZero();
555 sched.
step(time, timeStepCount);
557 if (mDomain == CPS::Domain::EMT) {
558 initLeftVectorLog.logEMTNodeValues(time, leftSideVector());
559 initRightVectorLog.logEMTNodeValues(time, rightSideVector());
561 initLeftVectorLog.logPhasorNodeValues(time, leftSideVector());
562 initRightVectorLog.logPhasorNodeValues(time, rightSideVector());
566 time = time + initTimeStep;
570 diff = prevLeftSideVector - **mLeftSideVector;
571 prevLeftSideVector = **mLeftSideVector;
572 maxDiff = diff.lpNorm<Eigen::Infinity>();
573 max = (**mLeftSideVector).lpNorm<Eigen::Infinity>();
575 if ((maxDiff / max) < mSteadStIniAccLimit)
579 SPDLOG_LOGGER_INFO(mSLog,
"Max difference: {:f} or {:f}% at time {:f}",
580 maxDiff, maxDiff / max, time);
583 mRightSideVector.setZero();
585 SPDLOG_LOGGER_INFO(mSLog,
"--- Finished steady-state initialization ---");
591 for (
auto comp : mMNAComponents) {
592 for (
auto task : comp->mnaTasks()) {
596 for (
auto comp : mMNAIntfSwitches) {
597 for (
auto task : comp->mnaTasks()) {
601 for (
auto node : mNodes) {
602 for (
auto task : node->mnaTasks())
606 for (
auto comp : mSimSignalComps) {
607 for (
auto task : comp->getTasks()) {
611 if (mFrequencyParallel) {
612 for (UInt i = 0; i < mSystem.mFrequencies.size(); ++i)
613 l.push_back(createSolveTaskHarm(i));
614 }
else if (mSystemMatrixRecomputation) {
615 for (
auto comp : this->mMNAIntfVariableComps) {
616 for (
auto task : comp->mnaTasks())
619 l.push_back(createSolveTaskRecomp());
621 l.push_back(createSolveTask());
622 l.push_back(createLogTask());
627 template <
typename VarType>
629 if (mLogLevel == Logger::Level::off)
632 if (mDomain == CPS::Domain::EMT) {
633 mLeftVectorLog->logEMTNodeValues(time, leftSideVector());
634 mRightVectorLog->logEMTNodeValues(time, rightSideVector());
636 mLeftVectorLog->logPhasorNodeValues(time, leftSideVector());
637 mRightVectorLog->logPhasorNodeValues(time, rightSideVector());
Solver class using Modified Nodal Analysis (MNA).
void identifyTopologyObjects()
Identify Nodes and SimPowerComps and SimSignalComps.
void initializeSystemWithVariableMatrix()
Initialization of system matrices and source vector.
virtual void initialize() override
Calls subroutines to set up everything that is required before simulation.
virtual void initializeSystem()
Initialization of system matrices and source vector.
Bool hasVariableComponentChanged()
Checks whether the status of variable MNA elements have changed.
virtual void log(Real time, Int timeStepCount) override
Logs left and right vector.
std::shared_ptr< DataLogger > mRightVectorLog
Right side vector logger.
void initializeComponents()
Initialization of individual components.
void updateSwitchStatus()
Collects the status of switches to select correct system matrix.
std::shared_ptr< DataLogger > mLeftVectorLog
Left side vector logger.
void initializeSystemWithParallelFrequencies()
Initialization of system matrices and source vector.
void collectVirtualNodes()
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.
void assignMatrixNodeIndices()
Assign simulation node index according to index in the vector.
void initializeSystemWithPrecomputedMatrices()
Initialization of system matrices and source vector.
void createEmptyVectors()
Create left and right side vector.
virtual CPS::Task::List getTasks() override
Get tasks for scheduler.
void resolveDeps(CPS::Task::List &tasks, Edges &inEdges, Edges &outEdges)
std::unordered_map< CPS::Task::Ptr, std::deque< CPS::Task::Ptr > > Edges
void step(Real time, Int timeStepCount)
Performs a single simulation step.
void createSchedule(const CPS::Task::List &tasks, const Edges &inEdges, const Edges &outEdges)
Creates the schedule for the given dependency graph.
Base class for more specific solvers such as MNA, ODE or IDA.