10#include <dpsim/MNASolver.h>
11#include <dpsim/SequentialScheduler.h>
22template <
typename VarType>
24 CPS::Logger::Level logLevel)
25 : Solver(name, logLevel),
mDomain(domain) {
29 name +
"_LeftVector", logLevel == CPS::Logger::Level::trace);
31 name +
"_RightVector", logLevel == CPS::Logger::Level::trace);
34template <
typename VarType>
39template <
typename VarType>
44template <
typename VarType>
48 throw std::logic_error(
49 "MNA state-space extractor has not been initialized.");
56 SPDLOG_LOGGER_INFO(
mSLog,
"---- Start initialization ----");
64 SPDLOG_LOGGER_INFO(
mSLog,
"Computing network harmonics in parallel.");
65 for (Int freq = 0; freq <
mSystem.mFrequencies.size(); ++freq) {
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(),
78 if (
mSystem.mComponents.size() == 0)
87 CPS::MNAInterface::List allMNAComps;
104 std::function<void(CPS::MNAInterface::Ptr)> createSubComponentsRec =
105 [&](CPS::MNAInterface::Ptr comp) {
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();
112 comp->createSubComponents();
115 for (
auto subComp : pComp->subComponents()) {
116 bool isNew = std::find(subCompsBefore.begin(), subCompsBefore.end(),
117 subComp) == subCompsBefore.end();
121 std::dynamic_pointer_cast<CPS::MNAInterface>(subComp))
122 createSubComponentsRec(subMna);
126 for (
auto comp : allMNAComps)
127 createSubComponentsRec(comp);
132 SPDLOG_LOGGER_INFO(
mSLog,
"-- Create empty MNA system matrices and vectors");
142 steadyStateInitialization();
147 for (
auto comp :
mSystem.mComponents) {
148 auto powerComp = std::dynamic_pointer_cast<CPS::TopologicalPowerComp>(comp);
150 powerComp->setBehaviour(TopologicalPowerComp::Behaviour::MNASimulation);
152 auto sigComp = std::dynamic_pointer_cast<CPS::SimSignalComp>(comp);
154 sigComp->setBehaviour(SimSignalComp::Behaviour::Simulation);
163 SPDLOG_LOGGER_INFO(
mSLog,
"--- Initialization finished ---");
164 SPDLOG_LOGGER_INFO(
mSLog,
"--- Initial system matrices and vectors ---");
171 SPDLOG_LOGGER_INFO(mSLog,
"-- Initialize components from power flow");
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());
179 for (
auto comp : allMNAComps) {
180 auto pComp = std::dynamic_pointer_cast<SimPowerComp<Real>>(comp);
183 pComp->checkForUnconnectedTerminals();
184 if (mInitFromNodesAndTerminals)
185 pComp->initializeFromNodesAndTerminals(mSystem.mSystemFrequency);
189 for (
auto comp : mSimSignalComps)
190 comp->initialize(mSystem.mSystemOmega, mTimeStep);
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);
201 for (
auto comp : mMNAIntfSwitches)
202 comp->mnaInitialize(mSystem.mSystemOmega, mTimeStep, mLeftSideVector);
205 for (UInt nodeIdx = 0; nodeIdx < mNodes.size(); ++nodeIdx)
206 mNodes[nodeIdx]->initialize();
210 SPDLOG_LOGGER_INFO(mSLog,
"-- Initialize components from power flow");
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());
219 for (
auto comp : allMNAComps) {
220 auto pComp = std::dynamic_pointer_cast<SimPowerComp<Complex>>(comp);
223 pComp->checkForUnconnectedTerminals();
224 if (mInitFromNodesAndTerminals)
225 pComp->initializeFromNodesAndTerminals(mSystem.mSystemFrequency);
229 for (
auto comp : mSimSignalComps)
230 comp->initialize(mSystem.mSystemOmega, mTimeStep);
232 SPDLOG_LOGGER_INFO(mSLog,
"-- Initialize MNA properties of components");
233 if (mFrequencyParallel) {
235 for (
auto comp : mMNAComponents) {
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);
244 for (UInt nodeIdx = 0; nodeIdx < mNodes.size(); ++nodeIdx) {
245 mNodes[nodeIdx]->mnaInitializeHarm(mLeftSideVectorHarm);
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);
257 for (
auto comp : mMNAIntfSwitches)
258 comp->mnaInitialize(mSystem.mSystemOmega, mTimeStep, mLeftSideVector);
261 for (UInt nodeIdx = 0; nodeIdx < mNodes.size(); ++nodeIdx)
262 mNodes[nodeIdx]->initialize();
267 SPDLOG_LOGGER_INFO(
mSLog,
268 "-- Initialize MNA system matrices and source vector");
273 if (
mSwitches.size() >
sizeof(std::size_t) * 8) {
285template <
typename VarType>
288 for (std::size_t sw = 0; sw < (1ULL <<
mSwitches.size()); ++sw) {
289 for (Int freq = 0; freq <
mSystem.mFrequencies.size(); ++freq) {
299 for (Int freq = 0; freq <
mSystem.mFrequencies.size(); ++freq) {
305template <
typename VarType>
308 for (std::size_t i = 0; i < (1ULL <<
mSwitches.size()); i++) {
316 for (std::size_t i = 0; i < (1ULL <<
mSwitches.size()); i++) {
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))
336template <
typename VarType>
341 for (
auto varEntry : varElem->mVariableSystemMatrixEntries)
343 SPDLOG_LOGGER_INFO(
mSLog,
"List of index pairs of varying matrix entries: ");
345 SPDLOG_LOGGER_INFO(
mSLog,
"({}, {})", indexPair.first, indexPair.second);
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))
363template <
typename VarType>
365 if (
mDomain != CPS::Domain::EMT) {
366 throw std::logic_error(
367 "MNA state-space extraction supports EMT domain only.");
370 if (!std::is_same<VarType, Real>::value) {
371 throw std::logic_error(
372 "MNA state-space extraction supports real-valued MNA systems only.");
376 throw std::logic_error(
377 "MNA state-space extraction does not support frequency-parallel "
381 CPS::MNAInterface::List stateSpaceComponents;
382 stateSpaceComponents.insert(stateSpaceComponents.end(),
384 stateSpaceComponents.insert(stateSpaceComponents.end(),
388 const UInt mnaVectorSize =
static_cast<UInt
>((**mLeftSideVector).rows());
396 "Initialized MNA state-space extractor with {:d} extraction states.",
400template <
typename VarType>
403 if (varElem->hasParameterChanged()) {
404 auto idObj = std::dynamic_pointer_cast<IdentifiedObject>(varElem);
406 mSLog,
"Component ({:s} {:s}) value changed -> Update System Matrix",
407 idObj->type(), idObj->name());
415 for (UInt i = 0; i <
mSwitches.size(); ++i) {
421 for (
auto baseNode :
mSystem.mNodes) {
423 if (!baseNode->isGround()) {
424 auto node = std::dynamic_pointer_cast<CPS::SimNode<VarType>>(baseNode);
426 SPDLOG_LOGGER_INFO(
mSLog,
"Added node {:s}", node->name());
430 for (
auto comp :
mSystem.mComponents) {
432 auto genComp = std::dynamic_pointer_cast<CPS::MNASyncGenInterface>(comp);
437 auto swComp = std::dynamic_pointer_cast<CPS::MNASwitchInterface>(comp);
440 auto mnaComp = std::dynamic_pointer_cast<CPS::MNAInterface>(swComp);
446 std::dynamic_pointer_cast<CPS::MNAVariableCompInterface>(comp);
449 auto mnaComp = std::dynamic_pointer_cast<CPS::MNAInterface>(varComp);
454 if (!(swComp || varComp)) {
455 auto mnaComp = std::dynamic_pointer_cast<CPS::MNAInterface>(comp);
459 auto sigComp = std::dynamic_pointer_cast<CPS::SimSignalComp>(comp);
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;
492 static_cast<UInt
>(
mSystem.mFrequencies.size() - 1) *
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}",
507 mRightSideVector = Matrix::Zero(mNumMatrixNodeIndices, 1);
508 **mLeftSideVector = Matrix::Zero(mNumMatrixNodeIndices, 1);
512 if (mFrequencyParallel) {
513 for (Int freq = 0; freq < mSystem.mFrequencies.size(); ++freq) {
514 mRightSideVectorHarm.push_back(
515 Matrix::Zero(2 * (mNumMatrixNodeIndices), 1));
517 Matrix::Zero(2 * (mNumMatrixNodeIndices), 1)));
520 mRightSideVector = Matrix::Zero(
521 2 * (mNumMatrixNodeIndices + mNumHarmMatrixNodeIndices), 1);
522 **mLeftSideVector = Matrix::Zero(
523 2 * (mNumMatrixNodeIndices + mNumHarmMatrixNodeIndices), 1);
534 auto pComp = std::dynamic_pointer_cast<SimPowerComp<VarType>>(comp);
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());
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);
555 bool alreadyRegistered =
false;
556 for (
auto registeredNode :
mNodes) {
557 if (registeredNode == vnode) {
558 alreadyRegistered =
true;
562 if (alreadyRegistered)
565 SPDLOG_LOGGER_INFO(
mSLog,
"Collected virtual node {} of {}", node,
574 auto pComp = std::dynamic_pointer_cast<SimPowerComp<VarType>>(comp);
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,
592 SPDLOG_LOGGER_INFO(
mSLog,
"Created virtual nodes:");
594 SPDLOG_LOGGER_INFO(
mSLog,
"Number of network and virtual nodes: {:d}",
598template <
typename VarType>
599void MnaSolver<VarType>::steadyStateInitialization() {
600 SPDLOG_LOGGER_INFO(mSLog,
"--- Run steady-state initialization ---");
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();
609 TopologicalPowerComp::Behaviour initBehaviourPowerComps =
610 TopologicalPowerComp::Behaviour::Initialization;
611 SimSignalComp::Behaviour initBehaviourSignalComps =
612 SimSignalComp::Behaviour::Initialization;
615 Real initTimeStep = mTimeStep;
617 Int timeStepCount = 0;
621 Matrix diff = Matrix::Zero(2 * mNumNodes, 1);
622 Matrix prevLeftSideVector = Matrix::Zero(2 * mNumNodes, 1);
624 SPDLOG_LOGGER_INFO(mSLog,
625 "Time step is {:f}s for steady-state initialization",
628 for (
auto comp : mSystem.mComponents) {
629 auto powerComp = std::dynamic_pointer_cast<CPS::TopologicalPowerComp>(comp);
631 powerComp->setBehaviour(initBehaviourPowerComps);
633 auto sigComp = std::dynamic_pointer_cast<CPS::SimSignalComp>(comp);
635 sigComp->setBehaviour(initBehaviourSignalComps);
643 CPS::Task::List tasks;
646 for (
auto node : mNodes) {
647 for (
auto task : node->mnaTasks())
648 tasks.push_back(task);
650 for (
auto comp : mMNAComponents) {
651 for (
auto task : comp->mnaTasks()) {
652 tasks.push_back(task);
656 for (
auto comp : mSimSignalComps) {
657 for (
auto task : comp->getTasks()) {
658 tasks.push_back(task);
661 tasks.push_back(createSolveTask());
666 while (time < mSteadStIniTimeLimit) {
668 mRightSideVector.setZero();
670 sched.
step(time, timeStepCount);
672 if (mDomain == CPS::Domain::EMT) {
673 initLeftVectorLog.logEMTNodeValues(time, leftSideVector());
674 initRightVectorLog.logEMTNodeValues(time, rightSideVector());
676 initLeftVectorLog.logPhasorNodeValues(time, leftSideVector());
677 initRightVectorLog.logPhasorNodeValues(time, rightSideVector());
681 time = time + initTimeStep;
685 diff = prevLeftSideVector - **mLeftSideVector;
686 prevLeftSideVector = **mLeftSideVector;
687 maxDiff = diff.lpNorm<Eigen::Infinity>();
688 max = (**mLeftSideVector).lpNorm<Eigen::Infinity>();
690 if ((maxDiff / max) < mSteadStIniAccLimit)
694 SPDLOG_LOGGER_INFO(mSLog,
"Max difference: {:f} or {:f}% at time {:f}",
695 maxDiff, maxDiff / max, time);
698 mRightSideVector.setZero();
700 SPDLOG_LOGGER_INFO(mSLog,
"--- Finished steady-state initialization ---");
707 for (
auto task : comp->mnaTasks()) {
712 for (
auto task : comp->mnaTasks()) {
716 for (
auto node :
mNodes) {
717 for (
auto task : node->mnaTasks())
722 for (
auto task : comp->getTasks()) {
727 for (UInt i = 0; i <
mSystem.mFrequencies.size(); ++i)
731 for (
auto task : comp->mnaTasks())
748template <
typename VarType>
753 if (
mDomain == CPS::Domain::EMT) {
Solver class using Modified Nodal Analysis (MNA).
std::bitset< SWITCH_NUM > mCurrentSwitchStatus
Current status of all switches encoded as bitset.
CPS::Domain mDomain
Simulation domain, which can be dynamic phasor (DP) or EMT.
void identifyTopologyObjects()
Identify Nodes and SimPowerComps and SimSignalComps.
std::vector< Matrix > mRightSideVectorHarm
Source vector of known quantities.
Matrix mRightSideVector
Source vector of known quantities.
Bool mStateSpaceExtraction
Enables extraction of the MNA-coupled discrete-time state matrix.
CPS::SystemTopology mSystem
System topology.
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.
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)
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.
UInt mNumNetMatrixNodeIndices
Number of network nodes, considering individual phases.
UInt mNumNetNodes
Number of network nodes, single line equivalent.
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.
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.
UInt mNumVirtualMatrixNodeIndices
Number of virtual nodes, considering individual phases.
CPS::MNASyncGenInterface::List mSyncGen
List of synchronous generators that need iterate to solve the differential equations.
CPS::MNAInterface::List mMNAIntfSwitches
List of switches if they must be accessed as MNAInterface objects.
std::shared_ptr< DataLogger > mRightVectorLog
Right side vector logger.
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.
CPS::MNAVariableCompInterface::List mVariableComps
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.
virtual void stampVariableSystemMatrix()=0
Stamps components into the variable system matrix.
std::shared_ptr< DataLogger > mLeftVectorLog
Left side vector logger.
UInt mNumMatrixNodeIndices
Number of network and virtual nodes, considering individual phases.
void initializeSystemWithParallelFrequencies()
Initialization of system matrices and source vector.
void collectVirtualNodes()
UInt mNumHarmMatrixNodeIndices
Number of nodes, excluding the primary frequency.
UInt mNumNodes
Number of network and virtual nodes, single line equivalent.
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.
UInt mNumVirtualNodes
Number of virtual nodes, single line equivalent.
void initializeSystemWithPrecomputedMatrices()
Initialization of system matrices and source vector.
CPS::Attribute< Matrix >::Ptr mLeftSideVector
Solution vector of unknown quantities.
CPS::SimSignalComp::List mSimSignalComps
List of signal type components that do not directly interact with the MNA solver.
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.
void doStateSpaceExtraction(Bool value=true)
Enable or disable MNA state-space extraction.
CPS::MNASwitchInterface::List mSwitches
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.
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)
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.
Real mTimeStep
Time step for fixed step solvers.
Bool mSystemMatrixRecomputation
Enable recomputation of system matrix during simulation.
CPS::Logger::Log mSLog
Logger.
CPS::Logger::Level mLogLevel
Logging level.
Bool mIsInInitialization
Determines if solver is in initialization phase, which requires different behavior.
Bool mFrequencyParallel
Activates parallelized computation of frequencies.
Bool mSteadyStateInit
Activates steady state initialization.