9#include <dpsim/MNASolver.h>
10#include <dpsim/SequentialScheduler.h>
20template <
typename VarType>
22 CPS::Logger::Level logLevel)
23 : Solver(name, logLevel),
mDomain(domain) {
27 name +
"_LeftVector", logLevel == CPS::Logger::Level::trace);
29 name +
"_RightVector", logLevel == CPS::Logger::Level::trace);
32template <
typename VarType>
37template <
typename VarType>
42template <
typename VarType>
46 throw std::logic_error(
47 "MNA state-space extractor has not been initialized.");
54 SPDLOG_LOGGER_INFO(
mSLog,
"---- Start initialization ----");
62 SPDLOG_LOGGER_INFO(
mSLog,
"Computing network harmonics in parallel.");
63 for (Int freq = 0; freq <
mSystem.mFrequencies.size(); ++freq) {
70 SPDLOG_LOGGER_INFO(
mSLog,
"-- Process topology");
71 for (
auto comp :
mSystem.mComponents)
72 SPDLOG_LOGGER_INFO(
mSLog,
"Added {:s} '{:s}' to simulation.", comp->type(),
76 if (
mSystem.mComponents.size() == 0)
86 SPDLOG_LOGGER_INFO(
mSLog,
"-- Create empty MNA system matrices and vectors");
96 steadyStateInitialization();
101 for (
auto comp :
mSystem.mComponents) {
102 auto powerComp = std::dynamic_pointer_cast<CPS::TopologicalPowerComp>(comp);
104 powerComp->setBehaviour(TopologicalPowerComp::Behaviour::MNASimulation);
106 auto sigComp = std::dynamic_pointer_cast<CPS::SimSignalComp>(comp);
108 sigComp->setBehaviour(SimSignalComp::Behaviour::Simulation);
117 SPDLOG_LOGGER_INFO(
mSLog,
"--- Initialization finished ---");
118 SPDLOG_LOGGER_INFO(
mSLog,
"--- Initial system matrices and vectors ---");
125 SPDLOG_LOGGER_INFO(mSLog,
"-- Initialize components from power flow");
127 CPS::MNAInterface::List allMNAComps;
128 allMNAComps.insert(allMNAComps.end(), mMNAComponents.begin(),
129 mMNAComponents.end());
130 allMNAComps.insert(allMNAComps.end(), mMNAIntfVariableComps.begin(),
131 mMNAIntfVariableComps.end());
133 for (
auto comp : allMNAComps) {
134 auto pComp = std::dynamic_pointer_cast<SimPowerComp<Real>>(comp);
137 pComp->checkForUnconnectedTerminals();
138 if (mInitFromNodesAndTerminals)
139 pComp->initializeFromNodesAndTerminals(mSystem.mSystemFrequency);
143 for (
auto comp : mSimSignalComps)
144 comp->initialize(mSystem.mSystemOmega, mTimeStep);
147 for (
auto comp : allMNAComps) {
148 comp->mnaInitialize(mSystem.mSystemOmega, mTimeStep, mLeftSideVector);
149 const Matrix &stamp = comp->getRightVector()->get();
150 if (stamp.size() != 0) {
151 mRightVectorStamps.push_back(&stamp);
155 for (
auto comp : mMNAIntfSwitches)
156 comp->mnaInitialize(mSystem.mSystemOmega, mTimeStep, mLeftSideVector);
159 for (UInt nodeIdx = 0; nodeIdx < mNodes.size(); ++nodeIdx)
160 mNodes[nodeIdx]->initialize();
164 SPDLOG_LOGGER_INFO(mSLog,
"-- Initialize components from power flow");
166 CPS::MNAInterface::List allMNAComps;
167 allMNAComps.insert(allMNAComps.end(), mMNAComponents.begin(),
168 mMNAComponents.end());
169 allMNAComps.insert(allMNAComps.end(), mMNAIntfVariableComps.begin(),
170 mMNAIntfVariableComps.end());
173 for (
auto comp : allMNAComps) {
174 auto pComp = std::dynamic_pointer_cast<SimPowerComp<Complex>>(comp);
177 pComp->checkForUnconnectedTerminals();
178 if (mInitFromNodesAndTerminals)
179 pComp->initializeFromNodesAndTerminals(mSystem.mSystemFrequency);
183 for (
auto comp : mSimSignalComps)
184 comp->initialize(mSystem.mSystemOmega, mTimeStep);
186 SPDLOG_LOGGER_INFO(mSLog,
"-- Initialize MNA properties of components");
187 if (mFrequencyParallel) {
189 for (
auto comp : mMNAComponents) {
191 comp->mnaInitializeHarm(mSystem.mSystemOmega, mTimeStep,
192 mLeftSideVectorHarm);
193 const Matrix &stamp = comp->getRightVector()->get();
194 if (stamp.size() != 0)
195 mRightVectorStamps.push_back(&stamp);
198 for (UInt nodeIdx = 0; nodeIdx < mNodes.size(); ++nodeIdx) {
199 mNodes[nodeIdx]->mnaInitializeHarm(mLeftSideVectorHarm);
203 for (
auto comp : allMNAComps) {
204 comp->mnaInitialize(mSystem.mSystemOmega, mTimeStep, mLeftSideVector);
205 const Matrix &stamp = comp->getRightVector()->get();
206 if (stamp.size() != 0) {
207 mRightVectorStamps.push_back(&stamp);
211 for (
auto comp : mMNAIntfSwitches)
212 comp->mnaInitialize(mSystem.mSystemOmega, mTimeStep, mLeftSideVector);
215 for (UInt nodeIdx = 0; nodeIdx < mNodes.size(); ++nodeIdx)
216 mNodes[nodeIdx]->initialize();
221 SPDLOG_LOGGER_INFO(
mSLog,
222 "-- Initialize MNA system matrices and source vector");
227 if (
mSwitches.size() >
sizeof(std::size_t) * 8) {
239template <
typename VarType>
242 for (std::size_t sw = 0; sw < (1ULL <<
mSwitches.size()); ++sw) {
243 for (Int freq = 0; freq <
mSystem.mFrequencies.size(); ++freq) {
253 for (Int freq = 0; freq <
mSystem.mFrequencies.size(); ++freq) {
259template <
typename VarType>
262 for (std::size_t i = 0; i < (1ULL <<
mSwitches.size()); i++) {
270 for (std::size_t i = 0; i < (1ULL <<
mSwitches.size()); i++) {
282 auto idObj = std::dynamic_pointer_cast<IdentifiedObject>(comp);
283 SPDLOG_LOGGER_DEBUG(
mSLog,
"Stamping {:s} {:s} into source vector",
284 idObj->type(), idObj->name());
285 if (
mSLog->should_log(spdlog::level::trace))
290template <
typename VarType>
295 for (
auto varEntry : varElem->mVariableSystemMatrixEntries)
297 SPDLOG_LOGGER_INFO(
mSLog,
"List of index pairs of varying matrix entries: ");
299 SPDLOG_LOGGER_INFO(
mSLog,
"({}, {})", indexPair.first, indexPair.second);
309 auto idObj = std::dynamic_pointer_cast<IdentifiedObject>(comp);
310 SPDLOG_LOGGER_DEBUG(
mSLog,
"Stamping {:s} {:s} into source vector",
311 idObj->type(), idObj->name());
312 if (
mSLog->should_log(spdlog::level::trace))
317template <
typename VarType>
319 if (
mDomain != CPS::Domain::EMT) {
320 throw std::logic_error(
321 "MNA state-space extraction supports EMT domain only.");
324 if (!std::is_same<VarType, Real>::value) {
325 throw std::logic_error(
326 "MNA state-space extraction supports real-valued MNA systems only.");
330 throw std::logic_error(
331 "MNA state-space extraction does not support frequency-parallel "
335 CPS::MNAInterface::List stateSpaceComponents;
336 stateSpaceComponents.insert(stateSpaceComponents.end(),
338 stateSpaceComponents.insert(stateSpaceComponents.end(),
342 const UInt mnaVectorSize =
static_cast<UInt
>((**mLeftSideVector).rows());
350 "Initialized MNA state-space extractor with {:d} extraction states.",
354template <
typename VarType>
357 if (varElem->hasParameterChanged()) {
358 auto idObj = std::dynamic_pointer_cast<IdentifiedObject>(varElem);
360 mSLog,
"Component ({:s} {:s}) value changed -> Update System Matrix",
361 idObj->type(), idObj->name());
369 for (UInt i = 0; i <
mSwitches.size(); ++i) {
375 for (
auto baseNode :
mSystem.mNodes) {
377 if (!baseNode->isGround()) {
378 auto node = std::dynamic_pointer_cast<CPS::SimNode<VarType>>(baseNode);
380 SPDLOG_LOGGER_INFO(
mSLog,
"Added node {:s}", node->name());
384 for (
auto comp :
mSystem.mComponents) {
386 auto genComp = std::dynamic_pointer_cast<CPS::MNASyncGenInterface>(comp);
391 auto swComp = std::dynamic_pointer_cast<CPS::MNASwitchInterface>(comp);
394 auto mnaComp = std::dynamic_pointer_cast<CPS::MNAInterface>(swComp);
400 std::dynamic_pointer_cast<CPS::MNAVariableCompInterface>(comp);
403 auto mnaComp = std::dynamic_pointer_cast<CPS::MNAInterface>(varComp);
408 if (!(swComp || varComp)) {
409 auto mnaComp = std::dynamic_pointer_cast<CPS::MNAInterface>(comp);
413 auto sigComp = std::dynamic_pointer_cast<CPS::SimSignalComp>(comp);
421 UInt matrixNodeIndexIdx = 0;
422 for (UInt idx = 0; idx <
mNodes.size(); ++idx) {
423 mNodes[idx]->setMatrixNodeIndex(0, matrixNodeIndexIdx);
424 SPDLOG_LOGGER_INFO(
mSLog,
"Assigned index {} to phase A of node {}",
425 matrixNodeIndexIdx, idx);
426 ++matrixNodeIndexIdx;
427 if (
mNodes[idx]->phaseType() == CPS::PhaseType::ABC) {
428 mNodes[idx]->setMatrixNodeIndex(1, matrixNodeIndexIdx);
429 SPDLOG_LOGGER_INFO(
mSLog,
"Assigned index {} to phase B of node {}",
430 matrixNodeIndexIdx, idx);
431 ++matrixNodeIndexIdx;
432 mNodes[idx]->setMatrixNodeIndex(2, matrixNodeIndexIdx);
433 SPDLOG_LOGGER_INFO(
mSLog,
"Assigned index {} to phase C of node {}",
434 matrixNodeIndexIdx, idx);
435 ++matrixNodeIndexIdx;
446 static_cast<UInt
>(
mSystem.mFrequencies.size() - 1) *
451 SPDLOG_LOGGER_INFO(
mSLog,
"Assigned simulation nodes to topology nodes:");
452 SPDLOG_LOGGER_INFO(
mSLog,
"Number of network simulation nodes: {:d}",
454 SPDLOG_LOGGER_INFO(
mSLog,
"Number of simulation nodes: {:d}",
456 SPDLOG_LOGGER_INFO(
mSLog,
"Number of harmonic simulation nodes: {:d}",
461 mRightSideVector = Matrix::Zero(mNumMatrixNodeIndices, 1);
462 **mLeftSideVector = Matrix::Zero(mNumMatrixNodeIndices, 1);
466 if (mFrequencyParallel) {
467 for (Int freq = 0; freq < mSystem.mFrequencies.size(); ++freq) {
468 mRightSideVectorHarm.push_back(
469 Matrix::Zero(2 * (mNumMatrixNodeIndices), 1));
471 Matrix::Zero(2 * (mNumMatrixNodeIndices), 1)));
474 mRightSideVector = Matrix::Zero(
475 2 * (mNumMatrixNodeIndices + mNumHarmMatrixNodeIndices), 1);
476 **mLeftSideVector = Matrix::Zero(
477 2 * (mNumMatrixNodeIndices + mNumHarmMatrixNodeIndices), 1);
488 auto pComp = std::dynamic_pointer_cast<SimPowerComp<VarType>>(comp);
493 if (pComp->hasVirtualNodes()) {
494 for (UInt node = 0; node < pComp->virtualNodesNumber(); ++node) {
495 mNodes.push_back(pComp->virtualNode(node));
496 SPDLOG_LOGGER_INFO(
mSLog,
"Collected virtual node {} of {}",
497 virtualNode, node, pComp->name());
503 if (pComp->hasSubComponents()) {
504 for (
auto pSubComp : pComp->subComponents()) {
505 for (UInt node = 0; node < pSubComp->virtualNodesNumber(); ++node) {
506 mNodes.push_back(pSubComp->virtualNode(node));
507 SPDLOG_LOGGER_INFO(
mSLog,
"Collected virtual node {} of {}",
508 virtualNode, node, pComp->name());
516 auto pComp = std::dynamic_pointer_cast<SimPowerComp<VarType>>(comp);
521 if (pComp->hasVirtualNodes()) {
522 for (UInt node = 0; node < pComp->virtualNodesNumber(); ++node) {
523 mNodes.push_back(pComp->virtualNode(node));
524 SPDLOG_LOGGER_INFO(
mSLog,
525 "Collected virtual node {} of Varible Comp {}", node,
534 SPDLOG_LOGGER_INFO(
mSLog,
"Created virtual nodes:");
536 SPDLOG_LOGGER_INFO(
mSLog,
"Number of network and virtual nodes: {:d}",
540template <
typename VarType>
541void MnaSolver<VarType>::steadyStateInitialization() {
542 SPDLOG_LOGGER_INFO(mSLog,
"--- Run steady-state initialization ---");
544 DataLogger initLeftVectorLog(mName +
"_InitLeftVector",
545 mLogLevel != CPS::Logger::Level::off);
546 initLeftVectorLog.start();
547 DataLogger initRightVectorLog(mName +
"_InitRightVector",
548 mLogLevel != CPS::Logger::Level::off);
549 initRightVectorLog.start();
551 TopologicalPowerComp::Behaviour initBehaviourPowerComps =
552 TopologicalPowerComp::Behaviour::Initialization;
553 SimSignalComp::Behaviour initBehaviourSignalComps =
554 SimSignalComp::Behaviour::Initialization;
557 Real initTimeStep = mTimeStep;
559 Int timeStepCount = 0;
563 Matrix diff = Matrix::Zero(2 * mNumNodes, 1);
564 Matrix prevLeftSideVector = Matrix::Zero(2 * mNumNodes, 1);
566 SPDLOG_LOGGER_INFO(mSLog,
567 "Time step is {:f}s for steady-state initialization",
570 for (
auto comp : mSystem.mComponents) {
571 auto powerComp = std::dynamic_pointer_cast<CPS::TopologicalPowerComp>(comp);
573 powerComp->setBehaviour(initBehaviourPowerComps);
575 auto sigComp = std::dynamic_pointer_cast<CPS::SimSignalComp>(comp);
577 sigComp->setBehaviour(initBehaviourSignalComps);
585 CPS::Task::List tasks;
588 for (
auto node : mNodes) {
589 for (
auto task : node->mnaTasks())
590 tasks.push_back(task);
592 for (
auto comp : mMNAComponents) {
593 for (
auto task : comp->mnaTasks()) {
594 tasks.push_back(task);
598 for (
auto comp : mSimSignalComps) {
599 for (
auto task : comp->getTasks()) {
600 tasks.push_back(task);
603 tasks.push_back(createSolveTask());
608 while (time < mSteadStIniTimeLimit) {
610 mRightSideVector.setZero();
612 sched.
step(time, timeStepCount);
614 if (mDomain == CPS::Domain::EMT) {
615 initLeftVectorLog.logEMTNodeValues(time, leftSideVector());
616 initRightVectorLog.logEMTNodeValues(time, rightSideVector());
618 initLeftVectorLog.logPhasorNodeValues(time, leftSideVector());
619 initRightVectorLog.logPhasorNodeValues(time, rightSideVector());
623 time = time + initTimeStep;
627 diff = prevLeftSideVector - **mLeftSideVector;
628 prevLeftSideVector = **mLeftSideVector;
629 maxDiff = diff.lpNorm<Eigen::Infinity>();
630 max = (**mLeftSideVector).lpNorm<Eigen::Infinity>();
632 if ((maxDiff / max) < mSteadStIniAccLimit)
636 SPDLOG_LOGGER_INFO(mSLog,
"Max difference: {:f} or {:f}% at time {:f}",
637 maxDiff, maxDiff / max, time);
640 mRightSideVector.setZero();
642 SPDLOG_LOGGER_INFO(mSLog,
"--- Finished steady-state initialization ---");
649 for (
auto task : comp->mnaTasks()) {
654 for (
auto task : comp->mnaTasks()) {
658 for (
auto node :
mNodes) {
659 for (
auto task : node->mnaTasks())
664 for (
auto task : comp->getTasks()) {
669 for (UInt i = 0; i <
mSystem.mFrequencies.size(); ++i)
673 for (
auto task : comp->mnaTasks())
690template <
typename VarType>
695 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.