DPsim
Loading...
Searching...
No Matches
MNAStateSpaceExtractor.cpp
1// SPDX-FileCopyrightText: 2026 Institute for Automation of Complex Power Systems, EONERC, RWTH Aachen University
2// SPDX-License-Identifier: MPL-2.0
3
4#include <dpsim/DirectLinearSolver.h>
5#include <dpsim/MNAStateSpaceExtractor.h>
6
7#include <stdexcept>
8#include <string>
9
10namespace DPsim {
11
12void MNAStateSpaceExtractor::initialize(
13 const CPS::MNAInterface::List &components, UInt mnaVectorSize,
14 Real timeStep) {
15 reset();
16
17 if (timeStep <= 0.0)
18 throw std::invalid_argument(
19 "MNAStateSpaceExtractor requires a positive time step.");
20
21 mMnaVectorSize = mnaVectorSize;
22 mTimeStep = timeStep;
23
24 const auto contributors =
25 MNAStateSpaceContributorFactory::createList(components);
26
27 UInt nextStateOffset = 0;
28
29 for (const auto &contributor : contributors) {
30 const UInt localStateCount = contributor->getStateCount();
31
32 ContributorEntry entry;
33 entry.contributor = contributor;
34 entry.stateOffset = nextStateOffset;
35 mContributorEntries.push_back(entry);
36
37 nextStateOffset += localStateCount;
38
39 if (contributor->isVariable())
40 mHasVariableContributors = true;
41 }
42
43 mStateCount = nextStateOffset;
44
45 allocateMatrices();
46 collectMetadata();
47
48 stampStaticMatrices();
49 restampVariableMatrices();
50 rebuildCombinedMatrices();
51
52 mStateMatrixValid = false;
53 mInitialized = true;
54}
55
56void MNAStateSpaceExtractor::reset() {
57 mInitialized = false;
58
59 mMnaVectorSize = 0;
60 mStateCount = 0;
61 mTimeStep = 0.0;
62
63 mHasVariableContributors = false;
64 mStateMatrixValid = false;
65 mMetadata = StateSpaceMetadata{};
66 mLastExtractionTime = 0.0;
67 mHasExtractionTime = false;
68
69 mContributorEntries.clear();
70
71 mAdLocalStatic.resize(0, 0);
72 mBdMnaStatic.resize(0, 0);
73 mCdMnaStatic.resize(0, 0);
74
75 mAdLocalVariable.resize(0, 0);
76 mBdMnaVariable.resize(0, 0);
77 mCdMnaVariable.resize(0, 0);
78
79 mAdLocal.resize(0, 0);
80 mBdMna.resize(0, 0);
81 mCdMna.resize(0, 0);
82
83 mAd.resize(0, 0);
84}
85
86void MNAStateSpaceExtractor::extract(DirectLinearSolver &linearSolver,
87 Bool variableModelChanged,
88 Bool systemMatrixChanged, Real time) {
89 if (!mInitialized)
90 throw std::logic_error(
91 "MNAStateSpaceExtractor::extract() called before initialize().");
92
93 mLastExtractionTime = time;
94 mHasExtractionTime = true;
95
96 if (mStateCount == 0) {
97 mStateMatrixValid = true;
98 return;
99 }
100
101 if (variableModelChanged && mHasVariableContributors) {
102 restampVariableMatrices();
103 rebuildCombinedMatrices();
104 mStateMatrixValid = false;
105 }
106
107 if (systemMatrixChanged)
108 mStateMatrixValid = false;
109
110 if (!mStateMatrixValid)
111 computeStateMatrix(linearSolver);
112}
113
114void MNAStateSpaceExtractor::allocateMatrices() {
115 mAdLocalStatic = Matrix::Zero(mStateCount, mStateCount);
116 mBdMnaStatic = Matrix::Zero(mStateCount, mMnaVectorSize);
117 mCdMnaStatic = Matrix::Zero(mMnaVectorSize, mStateCount);
118
119 mAdLocalVariable = Matrix::Zero(mStateCount, mStateCount);
120 mBdMnaVariable = Matrix::Zero(mStateCount, mMnaVectorSize);
121 mCdMnaVariable = Matrix::Zero(mMnaVectorSize, mStateCount);
122
123 mAdLocal = Matrix::Zero(mStateCount, mStateCount);
124 mBdMna = Matrix::Zero(mStateCount, mMnaVectorSize);
125 mCdMna = Matrix::Zero(mMnaVectorSize, mStateCount);
126
127 mAd = Matrix::Zero(mStateCount, mStateCount);
128}
129
130void MNAStateSpaceExtractor::collectMetadata() {
131 mMetadata = StateSpaceMetadata{};
132 mMetadata.stateNames.resize(mStateCount);
133
134 for (const auto &entry : mContributorEntries) {
135 entry.contributor->contributeMetadata(mMetadata, entry.stateOffset);
136 }
137
138 for (UInt idx = 0; idx < mStateCount; ++idx) {
139 if (mMetadata.stateNames[idx].empty())
140 mMetadata.stateNames[idx] = "x" + std::to_string(idx);
141 }
142
143 for (const auto &abcBlock : mMetadata.abcStateBlocks) {
144 if (abcBlock.name.empty()) {
145 throw std::runtime_error(
146 "MNAStateSpaceExtractor: abc metadata block has an empty name.");
147 }
148
149 for (const auto idx : abcBlock.indices) {
150 if (idx >= mStateCount) {
151 throw std::runtime_error(
152 "MNAStateSpaceExtractor: abc metadata index is outside the "
153 "extracted state vector.");
154 }
155 }
156 }
157}
158
159void MNAStateSpaceExtractor::stampStaticMatrices() {
160 mAdLocalStatic.setZero();
161 mBdMnaStatic.setZero();
162 mCdMnaStatic.setZero();
163
164 for (const auto &entry : mContributorEntries) {
165 if (!entry.contributor->isVariable()) {
166 entry.contributor->stamp(mAdLocalStatic, mBdMnaStatic, mCdMnaStatic,
167 entry.stateOffset, mMnaVectorSize);
168 }
169 }
170}
171
172void MNAStateSpaceExtractor::restampVariableMatrices() {
173 mAdLocalVariable.setZero();
174 mBdMnaVariable.setZero();
175 mCdMnaVariable.setZero();
176
177 for (const auto &entry : mContributorEntries) {
178 if (entry.contributor->isVariable()) {
179 entry.contributor->stamp(mAdLocalVariable, mBdMnaVariable, mCdMnaVariable,
180 entry.stateOffset, mMnaVectorSize);
181 }
182 }
183}
184
185void MNAStateSpaceExtractor::rebuildCombinedMatrices() {
186 mAdLocal = mAdLocalStatic + mAdLocalVariable;
187 mBdMna = mBdMnaStatic + mBdMnaVariable;
188 mCdMna = mCdMnaStatic + mCdMnaVariable;
189}
190
191void MNAStateSpaceExtractor::computeStateMatrix(
192 DirectLinearSolver &linearSolver) {
193 // DirectLinearSolver::solve takes a non-const Matrix&, so use a local copy.
194 Matrix rhs = mCdMna;
195 const Matrix mnaToStateSolution = linearSolver.solve(rhs);
196
197 if (mnaToStateSolution.rows() != mMnaVectorSize ||
198 mnaToStateSolution.cols() != mStateCount) {
199 throw std::runtime_error(
200 "MNAStateSpaceExtractor: linear solver returned unexpected "
201 "dimensions.");
202 }
203
204 mAd = mAdLocal + mBdMna * mnaToStateSolution;
205
206 mStateMatrixValid = true;
207}
208
209} // namespace DPsim