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 ----");
42 if (mFrequencyParallel) {
43 SPDLOG_LOGGER_INFO(mSLog,
"Computing network harmonics in parallel.");
44 for (Int freq = 0; freq < mSystem.mFrequencies.size(); ++freq) {
51 SPDLOG_LOGGER_INFO(mSLog,
"-- Process topology");
52 for (
auto comp : mSystem.mComponents)
53 SPDLOG_LOGGER_INFO(mSLog,
"Added {:s} '{:s}' to simulation.", comp->type(),
57 if (mSystem.mComponents.size() == 0)
62 identifyTopologyObjects();
64 collectVirtualNodes();
65 assignMatrixNodeIndices();
67 SPDLOG_LOGGER_INFO(mSLog,
"-- Create empty MNA system matrices and vectors");
69 createEmptySystemMatrix();
73 initializeComponents();
75 if (mSteadyStateInit) {
76 mIsInInitialization =
true;
77 steadyStateInitialization();
79 mIsInInitialization =
false;
82 for (
auto comp : mSystem.mComponents) {
83 auto powerComp = std::dynamic_pointer_cast<CPS::TopologicalPowerComp>(comp);
85 powerComp->setBehaviour(TopologicalPowerComp::Behaviour::MNASimulation);
87 auto sigComp = std::dynamic_pointer_cast<CPS::SimSignalComp>(comp);
89 sigComp->setBehaviour(SimSignalComp::Behaviour::Simulation);
95 SPDLOG_LOGGER_INFO(mSLog,
"--- Initialization finished ---");
96 SPDLOG_LOGGER_INFO(mSLog,
"--- Initial system matrices and vectors ---");
103 SPDLOG_LOGGER_INFO(mSLog,
"-- Initialize components from power flow");
105 CPS::MNAInterface::List allMNAComps;
106 allMNAComps.insert(allMNAComps.end(), mMNAComponents.begin(),
107 mMNAComponents.end());
108 allMNAComps.insert(allMNAComps.end(), mMNAIntfVariableComps.begin(),
109 mMNAIntfVariableComps.end());
111 for (
auto comp : allMNAComps) {
112 auto pComp = std::dynamic_pointer_cast<SimPowerComp<Real>>(comp);
115 pComp->checkForUnconnectedTerminals();
116 if (mInitFromNodesAndTerminals)
117 pComp->initializeFromNodesAndTerminals(mSystem.mSystemFrequency);
121 for (
auto comp : mSimSignalComps)
122 comp->initialize(mSystem.mSystemOmega, mTimeStep);
125 for (
auto comp : allMNAComps) {
126 comp->mnaInitialize(mSystem.mSystemOmega, mTimeStep, mLeftSideVector);
127 const Matrix &stamp = comp->getRightVector()->get();
128 if (stamp.size() != 0) {
129 mRightVectorStamps.push_back(&stamp);
133 for (
auto comp : mMNAIntfSwitches)
134 comp->mnaInitialize(mSystem.mSystemOmega, mTimeStep, mLeftSideVector);
137 for (UInt nodeIdx = 0; nodeIdx < mNodes.size(); ++nodeIdx)
138 mNodes[nodeIdx]->initialize();
142 SPDLOG_LOGGER_INFO(mSLog,
"-- Initialize components from power flow");
144 CPS::MNAInterface::List allMNAComps;
145 allMNAComps.insert(allMNAComps.end(), mMNAComponents.begin(),
146 mMNAComponents.end());
147 allMNAComps.insert(allMNAComps.end(), mMNAIntfVariableComps.begin(),
148 mMNAIntfVariableComps.end());
151 for (
auto comp : allMNAComps) {
152 auto pComp = std::dynamic_pointer_cast<SimPowerComp<Complex>>(comp);
155 pComp->checkForUnconnectedTerminals();
156 if (mInitFromNodesAndTerminals)
157 pComp->initializeFromNodesAndTerminals(mSystem.mSystemFrequency);
161 for (
auto comp : mSimSignalComps)
162 comp->initialize(mSystem.mSystemOmega, mTimeStep);
164 SPDLOG_LOGGER_INFO(mSLog,
"-- Initialize MNA properties of components");
165 if (mFrequencyParallel) {
167 for (
auto comp : mMNAComponents) {
169 comp->mnaInitializeHarm(mSystem.mSystemOmega, mTimeStep,
170 mLeftSideVectorHarm);
171 const Matrix &stamp = comp->getRightVector()->get();
172 if (stamp.size() != 0)
173 mRightVectorStamps.push_back(&stamp);
176 for (UInt nodeIdx = 0; nodeIdx < mNodes.size(); ++nodeIdx) {
177 mNodes[nodeIdx]->mnaInitializeHarm(mLeftSideVectorHarm);
181 for (
auto comp : allMNAComps) {
182 comp->mnaInitialize(mSystem.mSystemOmega, mTimeStep, mLeftSideVector);
183 const Matrix &stamp = comp->getRightVector()->get();
184 if (stamp.size() != 0) {
185 mRightVectorStamps.push_back(&stamp);
189 for (
auto comp : mMNAIntfSwitches)
190 comp->mnaInitialize(mSystem.mSystemOmega, mTimeStep, mLeftSideVector);
193 for (UInt nodeIdx = 0; nodeIdx < mNodes.size(); ++nodeIdx)
194 mNodes[nodeIdx]->initialize();
199 SPDLOG_LOGGER_INFO(mSLog,
200 "-- Initialize MNA system matrices and source vector");
201 mRightSideVector.setZero();
205 if (mSwitches.size() >
sizeof(std::size_t) * 8) {
209 if (mFrequencyParallel)
210 initializeSystemWithParallelFrequencies();
211 else if (mSystemMatrixRecomputation)
212 initializeSystemWithVariableMatrix();
214 initializeSystemWithPrecomputedMatrices();
217 template <
typename VarType>
220 for (std::size_t sw = 0; sw < (1ULL << mSwitches.size()); ++sw) {
221 for (Int freq = 0; freq < mSystem.mFrequencies.size(); ++freq) {
222 switchedMatrixEmpty(sw, freq);
223 switchedMatrixStamp(sw, freq, mMNAComponents, mSwitches);
227 if (mSwitches.size() > 0)
228 updateSwitchStatus();
231 for (Int freq = 0; freq < mSystem.mFrequencies.size(); ++freq) {
232 for (
auto comp : mMNAComponents)
233 comp->mnaApplyRightSideVectorStampHarm(mRightSideVectorHarm[freq], freq);
237 template <
typename VarType>
240 for (std::size_t i = 0; i < (1ULL << mSwitches.size()); i++) {
241 switchedMatrixEmpty(i);
244 if (mSwitches.size() < 1) {
245 switchedMatrixStamp(0, mMNAComponents);
248 for (std::size_t i = 0; i < (1ULL << mSwitches.size()); i++) {
249 switchedMatrixStamp(i, mMNAComponents);
251 updateSwitchStatus();
258 for (
auto comp : mMNAComponents) {
259 comp->mnaApplyRightSideVectorStamp(mRightSideVector);
260 auto idObj = std::dynamic_pointer_cast<IdentifiedObject>(comp);
261 SPDLOG_LOGGER_DEBUG(mSLog,
"Stamping {:s} {:s} into source vector",
262 idObj->type(), idObj->name());
263 if (mSLog->should_log(spdlog::level::trace))
264 mSLog->trace(
"\n{:s}", Logger::matrixToString(mRightSideVector));
268 template <
typename VarType>
272 for (
auto varElem : mVariableComps)
273 for (
auto varEntry : varElem->mVariableSystemMatrixEntries)
274 mListVariableSystemMatrixEntries.push_back(varEntry);
275 SPDLOG_LOGGER_INFO(mSLog,
"List of index pairs of varying matrix entries: ");
276 for (
auto indexPair : mListVariableSystemMatrixEntries)
277 SPDLOG_LOGGER_INFO(mSLog,
"({}, {})", indexPair.first, indexPair.second);
279 stampVariableSystemMatrix();
285 for (
auto comp : mMNAComponents) {
286 comp->mnaApplyRightSideVectorStamp(mRightSideVector);
287 auto idObj = std::dynamic_pointer_cast<IdentifiedObject>(comp);
288 SPDLOG_LOGGER_DEBUG(mSLog,
"Stamping {:s} {:s} into source vector",
289 idObj->type(), idObj->name());
290 if (mSLog->should_log(spdlog::level::trace))
291 mSLog->trace(
"\n{:s}", Logger::matrixToString(mRightSideVector));
295 template <
typename VarType>
297 for (
auto varElem : mVariableComps) {
298 if (varElem->hasParameterChanged()) {
299 auto idObj = std::dynamic_pointer_cast<IdentifiedObject>(varElem);
301 mSLog,
"Component ({:s} {:s}) value changed -> Update System Matrix",
302 idObj->type(), idObj->name());
310 for (UInt i = 0; i < mSwitches.size(); ++i) {
311 mCurrentSwitchStatus.set(i, mSwitches[i]->mnaIsClosed());
316 for (
auto baseNode : mSystem.mNodes) {
318 if (!baseNode->isGround()) {
319 auto node = std::dynamic_pointer_cast<CPS::SimNode<VarType>>(baseNode);
320 mNodes.push_back(node);
321 SPDLOG_LOGGER_INFO(mSLog,
"Added node {:s}", node->name());
325 for (
auto comp : mSystem.mComponents) {
327 auto genComp = std::dynamic_pointer_cast<CPS::MNASyncGenInterface>(comp);
329 mSyncGen.push_back(genComp);
332 auto swComp = std::dynamic_pointer_cast<CPS::MNASwitchInterface>(comp);
334 mSwitches.push_back(swComp);
335 auto mnaComp = std::dynamic_pointer_cast<CPS::MNAInterface>(swComp);
337 mMNAIntfSwitches.push_back(mnaComp);
341 std::dynamic_pointer_cast<CPS::MNAVariableCompInterface>(comp);
343 mVariableComps.push_back(varComp);
344 auto mnaComp = std::dynamic_pointer_cast<CPS::MNAInterface>(varComp);
346 mMNAIntfVariableComps.push_back(mnaComp);
349 if (!(swComp || varComp)) {
350 auto mnaComp = std::dynamic_pointer_cast<CPS::MNAInterface>(comp);
352 mMNAComponents.push_back(mnaComp);
354 auto sigComp = std::dynamic_pointer_cast<CPS::SimSignalComp>(comp);
356 mSimSignalComps.push_back(sigComp);
362 UInt matrixNodeIndexIdx = 0;
363 for (UInt idx = 0; idx < mNodes.size(); ++idx) {
364 mNodes[idx]->setMatrixNodeIndex(0, matrixNodeIndexIdx);
365 SPDLOG_LOGGER_INFO(mSLog,
"Assigned index {} to phase A of node {}",
366 matrixNodeIndexIdx, idx);
367 ++matrixNodeIndexIdx;
368 if (mNodes[idx]->phaseType() == CPS::PhaseType::ABC) {
369 mNodes[idx]->setMatrixNodeIndex(1, matrixNodeIndexIdx);
370 SPDLOG_LOGGER_INFO(mSLog,
"Assigned index {} to phase B of node {}",
371 matrixNodeIndexIdx, idx);
372 ++matrixNodeIndexIdx;
373 mNodes[idx]->setMatrixNodeIndex(2, matrixNodeIndexIdx);
374 SPDLOG_LOGGER_INFO(mSLog,
"Assigned index {} to phase C of node {}",
375 matrixNodeIndexIdx, idx);
376 ++matrixNodeIndexIdx;
379 if (idx == mNumNetNodes - 1)
380 mNumNetMatrixNodeIndices = matrixNodeIndexIdx;
383 mNumMatrixNodeIndices = matrixNodeIndexIdx;
384 mNumVirtualMatrixNodeIndices =
385 mNumMatrixNodeIndices - mNumNetMatrixNodeIndices;
386 mNumHarmMatrixNodeIndices =
387 static_cast<UInt
>(mSystem.mFrequencies.size() - 1) *
388 mNumMatrixNodeIndices;
389 mNumTotalMatrixNodeIndices =
390 static_cast<UInt
>(mSystem.mFrequencies.size()) * mNumMatrixNodeIndices;
392 SPDLOG_LOGGER_INFO(mSLog,
"Assigned simulation nodes to topology nodes:");
393 SPDLOG_LOGGER_INFO(mSLog,
"Number of network simulation nodes: {:d}",
394 mNumNetMatrixNodeIndices);
395 SPDLOG_LOGGER_INFO(mSLog,
"Number of simulation nodes: {:d}",
396 mNumMatrixNodeIndices);
397 SPDLOG_LOGGER_INFO(mSLog,
"Number of harmonic simulation nodes: {:d}",
398 mNumHarmMatrixNodeIndices);
402 mRightSideVector = Matrix::Zero(mNumMatrixNodeIndices, 1);
403 **mLeftSideVector = Matrix::Zero(mNumMatrixNodeIndices, 1);
407 if (mFrequencyParallel) {
408 for (Int freq = 0; freq < mSystem.mFrequencies.size(); ++freq) {
409 mRightSideVectorHarm.push_back(
410 Matrix::Zero(2 * (mNumMatrixNodeIndices), 1));
412 Matrix::Zero(2 * (mNumMatrixNodeIndices), 1)));
415 mRightSideVector = Matrix::Zero(
416 2 * (mNumMatrixNodeIndices + mNumHarmMatrixNodeIndices), 1);
417 **mLeftSideVector = Matrix::Zero(
418 2 * (mNumMatrixNodeIndices + mNumHarmMatrixNodeIndices), 1);
424 mNumNetNodes = (UInt)mNodes.size();
426 UInt virtualNode = mNumNetNodes - 1;
428 for (
auto comp : mMNAComponents) {
429 auto pComp = std::dynamic_pointer_cast<SimPowerComp<VarType>>(comp);
434 if (pComp->hasVirtualNodes()) {
435 for (UInt node = 0; node < pComp->virtualNodesNumber(); ++node) {
436 mNodes.push_back(pComp->virtualNode(node));
437 SPDLOG_LOGGER_INFO(mSLog,
"Collected virtual node {} of {}",
438 virtualNode, node, pComp->name());
444 if (pComp->hasSubComponents()) {
445 for (
auto pSubComp : pComp->subComponents()) {
446 for (UInt node = 0; node < pSubComp->virtualNodesNumber(); ++node) {
447 mNodes.push_back(pSubComp->virtualNode(node));
448 SPDLOG_LOGGER_INFO(mSLog,
"Collected virtual node {} of {}",
449 virtualNode, node, pComp->name());
456 for (
auto comp : mVariableComps) {
457 auto pComp = std::dynamic_pointer_cast<SimPowerComp<VarType>>(comp);
462 if (pComp->hasVirtualNodes()) {
463 for (UInt node = 0; node < pComp->virtualNodesNumber(); ++node) {
464 mNodes.push_back(pComp->virtualNode(node));
465 SPDLOG_LOGGER_INFO(mSLog,
466 "Collected virtual node {} of Varible Comp {}", node,
473 mNumNodes = (UInt)mNodes.size();
474 mNumVirtualNodes = mNumNodes - mNumNetNodes;
475 SPDLOG_LOGGER_INFO(mSLog,
"Created virtual nodes:");
476 SPDLOG_LOGGER_INFO(mSLog,
"Number of network nodes: {:d}", mNumNetNodes);
477 SPDLOG_LOGGER_INFO(mSLog,
"Number of network and virtual nodes: {:d}",
481 template <
typename VarType>
483 SPDLOG_LOGGER_INFO(mSLog,
"--- Run steady-state initialization ---");
485 DataLogger initLeftVectorLog(mName +
"_InitLeftVector",
486 mLogLevel != CPS::Logger::Level::off);
487 DataLogger initRightVectorLog(mName +
"_InitRightVector",
488 mLogLevel != CPS::Logger::Level::off);
490 TopologicalPowerComp::Behaviour initBehaviourPowerComps =
491 TopologicalPowerComp::Behaviour::Initialization;
492 SimSignalComp::Behaviour initBehaviourSignalComps =
493 SimSignalComp::Behaviour::Initialization;
496 Real initTimeStep = mTimeStep;
498 Int timeStepCount = 0;
502 Matrix diff = Matrix::Zero(2 * mNumNodes, 1);
503 Matrix prevLeftSideVector = Matrix::Zero(2 * mNumNodes, 1);
505 SPDLOG_LOGGER_INFO(mSLog,
506 "Time step is {:f}s for steady-state initialization",
509 for (
auto comp : mSystem.mComponents) {
510 auto powerComp = std::dynamic_pointer_cast<CPS::TopologicalPowerComp>(comp);
512 powerComp->setBehaviour(initBehaviourPowerComps);
514 auto sigComp = std::dynamic_pointer_cast<CPS::SimSignalComp>(comp);
516 sigComp->setBehaviour(initBehaviourSignalComps);
524 CPS::Task::List tasks;
527 for (
auto node : mNodes) {
528 for (
auto task : node->mnaTasks())
529 tasks.push_back(task);
531 for (
auto comp : mMNAComponents) {
532 for (
auto task : comp->mnaTasks()) {
533 tasks.push_back(task);
537 for (
auto comp : mSimSignalComps) {
538 for (
auto task : comp->getTasks()) {
539 tasks.push_back(task);
542 tasks.push_back(createSolveTask());
547 while (time < mSteadStIniTimeLimit) {
549 mRightSideVector.setZero();
551 sched.
step(time, timeStepCount);
553 if (mDomain == CPS::Domain::EMT) {
554 initLeftVectorLog.logEMTNodeValues(time, leftSideVector());
555 initRightVectorLog.logEMTNodeValues(time, rightSideVector());
557 initLeftVectorLog.logPhasorNodeValues(time, leftSideVector());
558 initRightVectorLog.logPhasorNodeValues(time, rightSideVector());
562 time = time + initTimeStep;
566 diff = prevLeftSideVector - **mLeftSideVector;
567 prevLeftSideVector = **mLeftSideVector;
568 maxDiff = diff.lpNorm<Eigen::Infinity>();
569 max = (**mLeftSideVector).lpNorm<Eigen::Infinity>();
571 if ((maxDiff / max) < mSteadStIniAccLimit)
575 SPDLOG_LOGGER_INFO(mSLog,
"Max difference: {:f} or {:f}% at time {:f}",
576 maxDiff, maxDiff / max, time);
579 mRightSideVector.setZero();
581 SPDLOG_LOGGER_INFO(mSLog,
"--- Finished steady-state initialization ---");
587 for (
auto comp : mMNAComponents) {
588 for (
auto task : comp->mnaTasks()) {
592 for (
auto comp : mMNAIntfSwitches) {
593 for (
auto task : comp->mnaTasks()) {
597 for (
auto node : mNodes) {
598 for (
auto task : node->mnaTasks())
602 for (
auto comp : mSimSignalComps) {
603 for (
auto task : comp->getTasks()) {
607 if (mFrequencyParallel) {
608 for (UInt i = 0; i < mSystem.mFrequencies.size(); ++i)
609 l.push_back(createSolveTaskHarm(i));
610 }
else if (mSystemMatrixRecomputation) {
611 for (
auto comp : this->mMNAIntfVariableComps) {
612 for (
auto task : comp->mnaTasks())
615 l.push_back(createSolveTaskRecomp());
617 l.push_back(createSolveTask());
618 l.push_back(createLogTask());
623 template <
typename VarType>
625 if (mLogLevel == Logger::Level::off)
628 if (mDomain == CPS::Domain::EMT) {
629 mLeftVectorLog->logEMTNodeValues(time, leftSideVector());
630 mRightVectorLog->logEMTNodeValues(time, rightSideVector());
632 mLeftVectorLog->logPhasorNodeValues(time, leftSideVector());
633 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.