DPsim
Loading...
Searching...
No Matches
MNAStateSpaceContributor.cpp
1// SPDX-FileCopyrightText: 2026 Institute for Automation of Complex Power Systems, EONERC, RWTH Aachen University
2// SPDX-License-Identifier: MPL-2.0
3#include <dpsim-models/CompositePowerComp.h>
4#include <dpsim/MNAStateSpaceContributor.h>
5
6#include <dpsim-models/DP/DP_Ph1_Capacitor.h>
7#include <dpsim-models/DP/DP_Ph1_Inductor.h>
8#include <dpsim-models/DP/DP_Ph1_MixedVTypeVariableSSNComp.h>
9#include <dpsim-models/DP/DP_Ph1_NetworkInjection.h>
10#include <dpsim-models/DP/DP_Ph1_PiLine.h>
11#include <dpsim-models/DP/DP_Ph1_RXLoad.h>
12#include <dpsim-models/DP/DP_Ph1_Resistor.h>
13#include <dpsim-models/DP/DP_Ph1_RxLine.h>
14#include <dpsim-models/DP/DP_Ph1_Shunt.h>
15#include <dpsim-models/DP/DP_Ph1_Switch.h>
16#include <dpsim-models/DP/DP_Ph1_Transformer.h>
17#include <dpsim-models/DP/DP_Ph1_TwoTerminalVTypeSSNComp.h>
18#include <dpsim-models/DP/DP_Ph1_VoltageSource.h>
19#include <dpsim-models/DP/DP_VTypeSSNComp.h>
20#include <dpsim-models/EMT/EMT_Ph3_Capacitor.h>
21#include <dpsim-models/EMT/EMT_Ph3_Inductor.h>
22#include <dpsim-models/EMT/EMT_Ph3_NetworkInjection.h>
23#include <dpsim-models/EMT/EMT_Ph3_PiLine.h>
24#include <dpsim-models/EMT/EMT_Ph3_RXLoad.h>
25#include <dpsim-models/EMT/EMT_Ph3_Resistor.h>
26#include <dpsim-models/EMT/EMT_Ph3_RxLine.h>
27#include <dpsim-models/EMT/EMT_Ph3_Shunt.h>
28#include <dpsim-models/EMT/EMT_Ph3_Switch.h>
29#include <dpsim-models/EMT/EMT_Ph3_Transformer.h>
30#include <dpsim-models/EMT/EMT_Ph3_TwoTerminalVTypeSSNComp.h>
31#include <dpsim-models/EMT/EMT_Ph3_TwoTerminalVTypeVariableSSNComp.h>
32#include <dpsim-models/EMT/EMT_Ph3_VoltageSource.h>
33#include <dpsim-models/EMT/EMT_VTypeSSNComp.h>
34#include <dpsim-models/SimPowerComp.h>
35#include <stdexcept>
36#include <string>
37#include <utility>
38
39using namespace CPS;
40
41namespace DPsim {
42namespace {
43
44using SimPowerCompReal = CPS::SimPowerComp<CPS::Real>;
45using SimPowerCompComplex = CPS::SimPowerComp<CPS::Complex>;
46
47std::shared_ptr<CompositePowerComp<Real>>
48getSupportedRealComposite(const MNAInterface::Ptr &component) {
49 if (const auto composite =
50 std::dynamic_pointer_cast<EMT::Ph3::NetworkInjection>(component))
51 return composite;
52
53 if (const auto composite =
54 std::dynamic_pointer_cast<EMT::Ph3::PiLine>(component))
55 return composite;
56
57 if (const auto composite =
58 std::dynamic_pointer_cast<EMT::Ph3::RXLoad>(component))
59 return composite;
60
61 if (const auto composite =
62 std::dynamic_pointer_cast<EMT::Ph3::RxLine>(component))
63 return composite;
64
65 if (const auto composite =
66 std::dynamic_pointer_cast<EMT::Ph3::Shunt>(component))
67 return composite;
68
69 if (const auto composite =
70 std::dynamic_pointer_cast<EMT::Ph3::Transformer>(component))
71 return composite;
72
73 return nullptr;
74}
75
76std::shared_ptr<CompositePowerComp<Complex>>
77getSupportedComplexComposite(const MNAInterface::Ptr &component) {
78 if (const auto composite =
79 std::dynamic_pointer_cast<DP::Ph1::NetworkInjection>(component))
80 return composite;
81
82 if (const auto composite =
83 std::dynamic_pointer_cast<DP::Ph1::PiLine>(component))
84 return composite;
85
86 if (const auto composite =
87 std::dynamic_pointer_cast<DP::Ph1::RXLoad>(component))
88 return composite;
89
90 if (const auto composite =
91 std::dynamic_pointer_cast<DP::Ph1::RxLine>(component))
92 return composite;
93
94 if (const auto composite =
95 std::dynamic_pointer_cast<DP::Ph1::Shunt>(component))
96 return composite;
97
98 if (const auto composite =
99 std::dynamic_pointer_cast<DP::Ph1::Transformer>(component))
100 return composite;
101
102 return nullptr;
103}
104
107Matrix buildTwoTerminalInterfaceVoltageMapping(SimPowerCompReal &component,
108 UInt mnaVectorSize) {
109 Matrix K = Matrix::Zero(3, mnaVectorSize);
110
111 if (component.terminalNotGrounded(1)) {
112 for (UInt phase = 0; phase < 3; ++phase)
113 K(phase, component.matrixNodeIndex(1, phase)) = 1.0;
114 }
115
116 if (component.terminalNotGrounded(0)) {
117 for (UInt phase = 0; phase < 3; ++phase)
118 K(phase, component.matrixNodeIndex(0, phase)) = -1.0;
119 }
120
121 return K;
122}
123
126Matrix
127buildSinglePhaseComplexInterfaceVoltageMapping(SimPowerCompComplex &component,
128 UInt mnaVectorSize) {
129 if (mnaVectorSize % 2 != 0) {
130 throw std::logic_error(
131 "DP MNA state-space extraction requires a real-imaginary stacked "
132 "MNA vector with even size.");
133 }
134
135 const UInt complexOffset = mnaVectorSize / 2;
136 Matrix K = Matrix::Zero(2, mnaVectorSize);
137
138 if (component.terminalNotGrounded(1)) {
139 const UInt nodeIdx = component.matrixNodeIndex(1);
140 K(0, nodeIdx) = 1.0;
141 K(1, nodeIdx + complexOffset) = 1.0;
142 }
143
144 if (component.terminalNotGrounded(0)) {
145 const UInt nodeIdx = component.matrixNodeIndex(0);
146 K(0, nodeIdx) = -1.0;
147 K(1, nodeIdx + complexOffset) = -1.0;
148 }
149
150 return K;
151}
152
157void stampTwoTerminalCurrentInjectionMapping(const Matrix &K, Matrix &CdMna,
158 UInt stateOffset,
159 const Matrix &outputMatrix) {
160 CdMna.block(0, stateOffset, CdMna.rows(), outputMatrix.cols()) +=
161 -K.transpose() * outputMatrix;
162}
163
164Matrix realAugment(const MatrixComp &matrix) {
165 const Matrix::Index rows = matrix.rows();
166 const Matrix::Index cols = matrix.cols();
167
168 Matrix result = Matrix::Zero(2 * rows, 2 * cols);
169 result.topLeftCorner(rows, cols) = matrix.real();
170 result.topRightCorner(rows, cols) = -matrix.imag();
171 result.bottomLeftCorner(rows, cols) = matrix.imag();
172 result.bottomRightCorner(rows, cols) = matrix.real();
173
174 return result;
175}
176
177Matrix realAugment(const Complex &value) {
178 Matrix result = Matrix::Zero(2, 2);
179 result << value.real(), -value.imag(), value.imag(), value.real();
180 return result;
181}
182
183Complex calculateDPInductorPreviousCurrentFactor(const Complex &conductance) {
184 const Real omegaDtHalf = -conductance.imag() / conductance.real();
185 return Complex(1.0, -omegaDtHalf) / Complex(1.0, omegaDtHalf);
186}
187
188void setStateName(StateSpaceMetadata &metadata, UInt stateIndex,
189 const String &name) {
190 if (stateIndex >= metadata.stateNames.size())
191 throw std::runtime_error(
192 "MNA state-space contributor tried to set a state name outside the "
193 "extracted state vector.");
194
195 metadata.stateNames[stateIndex] = name;
196}
197
198void addThreePhaseAbcStateMetadata(StateSpaceMetadata &metadata,
199 UInt stateOffset, const String &baseName) {
200 setStateName(metadata, stateOffset + 0, baseName + "_a");
201 setStateName(metadata, stateOffset + 1, baseName + "_b");
202 setStateName(metadata, stateOffset + 2, baseName + "_c");
203
204 metadata.abcStateBlocks.push_back(
205 {{stateOffset + 0, stateOffset + 1, stateOffset + 2}, baseName});
206}
207
208void addSinglePhaseComplexStateMetadata(StateSpaceMetadata &metadata,
209 UInt stateOffset,
210 const String &baseName) {
211 setStateName(metadata, stateOffset + 0, baseName + "_re");
212 setStateName(metadata, stateOffset + 1, baseName + "_im");
213}
214
215void addComplexStateMetadata(StateSpaceMetadata &metadata, UInt stateOffset,
216 UInt complexStateCount,
217 const String &componentName) {
218 for (UInt idx = 0; idx < complexStateCount; ++idx) {
219 const String stateName = componentName + ".x" + std::to_string(idx);
220 setStateName(metadata, stateOffset + idx, stateName + "_re");
221 setStateName(metadata, stateOffset + complexStateCount + idx,
222 stateName + "_im");
223 }
224}
225
226void addRealStateMetadata(StateSpaceMetadata &metadata, UInt stateOffset,
227 UInt stateCount, const String &componentName) {
228 for (UInt idx = 0; idx < stateCount; ++idx)
229 setStateName(metadata, stateOffset + idx,
230 componentName + ".x" + std::to_string(idx));
231}
232
233class EMTPh3InductorStateSpaceContributor final
234 : public MNAStateSpaceContributor {
235public:
236 explicit EMTPh3InductorStateSpaceContributor(
237 std::shared_ptr<EMT::Ph3::Inductor> component)
238 : mComponent(std::move(component)) {}
239
240 UInt getStateCount() const override { return 3; }
241
242 void stamp(Matrix &AdLocal, Matrix &BdMna, Matrix &CdMna, UInt stateOffset,
243 UInt mnaVectorSize) const override {
244 const Matrix &conductance = mComponent->getMNAConductance();
245
246 const Matrix K =
247 buildTwoTerminalInterfaceVoltageMapping(*mComponent, mnaVectorSize);
248
249 // History state h:
250 // i[k+1] = G vIntf[k+1] + h[k]
251 // h[k+1] = h[k] + 2 G vIntf[k+1]
252 // Therefore: AdLocal = I, BdMna = 2 G K, CdMna = -K^T.
253 AdLocal.block(stateOffset, stateOffset, 3, 3) += Matrix::Identity(3, 3);
254
255 BdMna.block(stateOffset, 0, 3, mnaVectorSize) += 2.0 * conductance * K;
256
257 stampTwoTerminalCurrentInjectionMapping(K, CdMna, stateOffset,
258 Matrix::Identity(3, 3));
259 }
260
261 void contributeMetadata(StateSpaceMetadata &metadata,
262 UInt stateOffset) const override {
263 addThreePhaseAbcStateMetadata(metadata, stateOffset, mComponent->name());
264 }
265
266private:
267 std::shared_ptr<EMT::Ph3::Inductor> mComponent;
268};
269
270class EMTPh3CapacitorStateSpaceContributor final
271 : public MNAStateSpaceContributor {
272public:
273 explicit EMTPh3CapacitorStateSpaceContributor(
274 std::shared_ptr<EMT::Ph3::Capacitor> component)
275 : mComponent(std::move(component)) {}
276
277 UInt getStateCount() const override { return 3; }
278
279 void stamp(Matrix &AdLocal, Matrix &BdMna, Matrix &CdMna, UInt stateOffset,
280 UInt mnaVectorSize) const override {
281 const Matrix &conductance = mComponent->getMNAConductance();
282
283 const Matrix K =
284 buildTwoTerminalInterfaceVoltageMapping(*mComponent, mnaVectorSize);
285
286 // History state h:
287 // i[k+1] = G vIntf[k+1] + h[k]
288 // h[k+1] = -h[k] - 2 G vIntf[k+1]
289 // Therefore: AdLocal = -I, BdMna = -2 G K, CdMna = -K^T.
290 AdLocal.block(stateOffset, stateOffset, 3, 3) -= Matrix::Identity(3, 3);
291
292 BdMna.block(stateOffset, 0, 3, mnaVectorSize) -= 2.0 * conductance * K;
293
294 stampTwoTerminalCurrentInjectionMapping(K, CdMna, stateOffset,
295 Matrix::Identity(3, 3));
296 }
297
298 void contributeMetadata(StateSpaceMetadata &metadata,
299 UInt stateOffset) const override {
300 addThreePhaseAbcStateMetadata(metadata, stateOffset, mComponent->name());
301 }
302
303private:
304 std::shared_ptr<EMT::Ph3::Capacitor> mComponent;
305};
306
307class EMTPh3TwoTerminalVTypeSSNStateSpaceContributor final
308 : public MNAStateSpaceContributor {
309public:
310 EMTPh3TwoTerminalVTypeSSNStateSpaceContributor(
311 std::shared_ptr<EMT::VTypeSSNComp> component, Bool isVariable)
312 : mComponent(std::move(component)), mIsVariable(isVariable) {}
313
314 UInt getStateCount() const override { return mComponent->getStateCount(); }
315
316 Bool isVariable() const override { return mIsVariable; }
317
318 void stamp(Matrix &AdLocal, Matrix &BdMna, Matrix &CdMna, UInt stateOffset,
319 UInt mnaVectorSize) const override {
320 const UInt localStateCount = getStateCount();
321
322 const Matrix &discreteA = mComponent->getDiscreteA();
323 const Matrix &discreteB = mComponent->getDiscreteB();
324 const Matrix &outputC = mComponent->getC();
325
326 const Matrix K =
327 buildTwoTerminalInterfaceVoltageMapping(*mComponent, mnaVectorSize);
328
329 // History-coordinate state s = discreteA x + discreteB vIntf:
330 // s[k+1] = discreteA s[k] + (discreteA + I) discreteB vIntf[k+1]
331 // yHist[k] = C s[k]
332 // Therefore: AdLocal = discreteA, BdMna = (discreteA + I) discreteB K,
333 // CdMna = -K^T C.
334 AdLocal.block(stateOffset, stateOffset, localStateCount, localStateCount) +=
335 discreteA;
336
337 const Matrix inputUpdate =
338 (discreteA + Matrix::Identity(localStateCount, localStateCount)) *
339 discreteB;
340
341 BdMna.block(stateOffset, 0, localStateCount, mnaVectorSize) +=
342 inputUpdate * K;
343
344 stampTwoTerminalCurrentInjectionMapping(K, CdMna, stateOffset, outputC);
345 }
346
347 void contributeMetadata(StateSpaceMetadata &metadata,
348 UInt stateOffset) const override {
349 const UInt localStateCount = getStateCount();
350 const String componentName = mComponent->name();
351
352 const auto localStateNames = mComponent->getLocalStateNames();
353
354 if (!localStateNames.empty() && localStateNames.size() != localStateCount) {
355 throw std::runtime_error(
356 "SSN component returned an invalid number of local state names.");
357 }
358
359 for (UInt idx = 0; idx < localStateCount; ++idx) {
360 if (!localStateNames.empty()) {
361 setStateName(metadata, stateOffset + idx,
362 componentName + "." + localStateNames[idx]);
363 }
364 }
365
366 for (auto abcBlock : mComponent->getLocalAbcStateBlocks()) {
367 if (abcBlock.name.empty()) {
368 throw std::runtime_error(
369 "SSN component returned an abc state block with an empty name.");
370 }
371
372 for (auto &idx : abcBlock.indices) {
373 if (idx >= localStateCount) {
374 throw std::runtime_error(
375 "SSN component returned an invalid abc state index.");
376 }
377
378 idx += stateOffset;
379 }
380
381 metadata.abcStateBlocks.push_back(
382 {abcBlock.indices, componentName + "." + abcBlock.name});
383 }
384 }
385
386private:
387 std::shared_ptr<EMT::VTypeSSNComp> mComponent;
388 Bool mIsVariable = false;
389};
390
391class DPPh1InductorStateSpaceContributor final
392 : public MNAStateSpaceContributor {
393public:
394 explicit DPPh1InductorStateSpaceContributor(
395 std::shared_ptr<DP::Ph1::Inductor> component)
396 : mComponent(std::move(component)) {}
397
398 UInt getStateCount() const override { return 2; }
399
400 void stamp(Matrix &AdLocal, Matrix &BdMna, Matrix &CdMna, UInt stateOffset,
401 UInt mnaVectorSize) const override {
402 const Complex conductance = mComponent->getMNAConductance();
403 const Complex prevCurrentFactor =
404 calculateDPInductorPreviousCurrentFactor(conductance);
405
406 const Matrix K = buildSinglePhaseComplexInterfaceVoltageMapping(
407 *mComponent, mnaVectorSize);
408
409 // Complex history state h:
410 // i[k+1] = Y_L vIntf[k+1] + h[k]
411 // h[k+1] = alpha h[k] + (1 + alpha) Y_L vIntf[k+1]
412 // Therefore: AdLocal = alpha, BdMna = (1 + alpha) Y_L K,
413 // CdMna = -K^T in real-imaginary augmented form.
414 AdLocal.block(stateOffset, stateOffset, 2, 2) +=
415 realAugment(prevCurrentFactor);
416
417 BdMna.block(stateOffset, 0, 2, mnaVectorSize) +=
418 realAugment((Complex(1.0, 0.0) + prevCurrentFactor) * conductance) * K;
419
420 stampTwoTerminalCurrentInjectionMapping(K, CdMna, stateOffset,
421 Matrix::Identity(2, 2));
422 }
423
424 void contributeMetadata(StateSpaceMetadata &metadata,
425 UInt stateOffset) const override {
426 addSinglePhaseComplexStateMetadata(metadata, stateOffset,
427 mComponent->name());
428 }
429
430private:
431 std::shared_ptr<DP::Ph1::Inductor> mComponent;
432};
433
434class DPPh1CapacitorStateSpaceContributor final
435 : public MNAStateSpaceContributor {
436public:
437 explicit DPPh1CapacitorStateSpaceContributor(
438 std::shared_ptr<DP::Ph1::Capacitor> component)
439 : mComponent(std::move(component)) {}
440
441 UInt getStateCount() const override { return 2; }
442
443 void stamp(Matrix &AdLocal, Matrix &BdMna, Matrix &CdMna, UInt stateOffset,
444 UInt mnaVectorSize) const override {
445 const Complex conductance = mComponent->getMNAConductance();
446
447 const Matrix K = buildSinglePhaseComplexInterfaceVoltageMapping(
448 *mComponent, mnaVectorSize);
449
450 // Complex history state h:
451 // i[k+1] = Y_C vIntf[k+1] + h[k]
452 // h[k+1] = -h[k] - (Y_C + conj(Y_C)) vIntf[k+1]
453 // Therefore: AdLocal = -1, BdMna = -2 Re(Y_C) K,
454 // CdMna = -K^T in real-imaginary augmented form.
455 AdLocal.block(stateOffset, stateOffset, 2, 2) -= Matrix::Identity(2, 2);
456
457 BdMna.block(stateOffset, 0, 2, mnaVectorSize) -=
458 2.0 * conductance.real() * K;
459
460 stampTwoTerminalCurrentInjectionMapping(K, CdMna, stateOffset,
461 Matrix::Identity(2, 2));
462 }
463
464 void contributeMetadata(StateSpaceMetadata &metadata,
465 UInt stateOffset) const override {
466 addSinglePhaseComplexStateMetadata(metadata, stateOffset,
467 mComponent->name());
468 }
469
470private:
471 std::shared_ptr<DP::Ph1::Capacitor> mComponent;
472};
473
474class DPPh1TwoTerminalVTypeSSNStateSpaceContributor final
475 : public MNAStateSpaceContributor {
476public:
477 explicit DPPh1TwoTerminalVTypeSSNStateSpaceContributor(
478 std::shared_ptr<DP::VTypeSSNComp> component)
479 : mComponent(std::move(component)) {}
480
481 UInt getStateCount() const override {
482 return 2 * mComponent->getStateCount();
483 }
484
485 void stamp(Matrix &AdLocal, Matrix &BdMna, Matrix &CdMna, UInt stateOffset,
486 UInt mnaVectorSize) const override {
487 const UInt complexStateCount = mComponent->getStateCount();
488 const UInt realStateCount = getStateCount();
489
490 const MatrixComp &discreteA = mComponent->getDiscreteA();
491 const MatrixComp &discreteB = mComponent->getDiscreteB();
492 const MatrixComp outputC = mComponent->getC().cast<Complex>();
493
494 const Matrix K = buildSinglePhaseComplexInterfaceVoltageMapping(
495 *mComponent, mnaVectorSize);
496
497 // Complex history-coordinate state s = discreteA x + discreteB vIntf:
498 // s[k+1] = discreteA s[k] + (discreteA + I) discreteB vIntf[k+1]
499 // yHist[k] = C s[k]
500 // Therefore: AdLocal = discreteA, BdMna = (discreteA + I) discreteB K,
501 // CdMna = -K^T C in real-imaginary augmented form.
502 AdLocal.block(stateOffset, stateOffset, realStateCount, realStateCount) +=
503 realAugment(discreteA);
504
505 const MatrixComp inputUpdate =
506 (discreteA +
507 MatrixComp::Identity(complexStateCount, complexStateCount)) *
508 discreteB;
509
510 BdMna.block(stateOffset, 0, realStateCount, mnaVectorSize) +=
511 realAugment(inputUpdate) * K;
512
513 stampTwoTerminalCurrentInjectionMapping(K, CdMna, stateOffset,
514 realAugment(outputC));
515 }
516
517 void contributeMetadata(StateSpaceMetadata &metadata,
518 UInt stateOffset) const override {
519 addComplexStateMetadata(metadata, stateOffset, mComponent->getStateCount(),
520 mComponent->name());
521 }
522
523private:
524 std::shared_ptr<DP::VTypeSSNComp> mComponent;
525};
526
527class DPPh1MixedVTypeVariableSSNStateSpaceContributor final
528 : public MNAStateSpaceContributor {
529public:
530 explicit DPPh1MixedVTypeVariableSSNStateSpaceContributor(
531 std::shared_ptr<DP::Ph1::MixedVTypeVariableSSNComp> component)
532 : mComponent(std::move(component)) {}
533
534 UInt getStateCount() const override { return mComponent->getStateCount(); }
535
536 Bool isVariable() const override { return true; }
537
538 void stamp(Matrix &AdLocal, Matrix &BdMna, Matrix &CdMna, UInt stateOffset,
539 UInt mnaVectorSize) const override {
540 const UInt localStateCount = getStateCount();
541
542 const Matrix &discreteA = mComponent->getDiscreteA();
543 const Matrix &discreteB = mComponent->getDiscreteB();
544 const Matrix &outputC = mComponent->getC();
545
546 const Matrix K = buildSinglePhaseComplexInterfaceVoltageMapping(
547 *mComponent, mnaVectorSize);
548
549 // Real packed history-coordinate state s = discreteA x + discreteB vIntf:
550 // s[k+1] = discreteA s[k] + (discreteA + I) discreteB vIntf[k+1]
551 // yHist[k] = C s[k]
552 // Therefore: AdLocal = discreteA, BdMna = (discreteA + I) discreteB K,
553 // CdMna = -K^T C.
554 AdLocal.block(stateOffset, stateOffset, localStateCount, localStateCount) +=
555 discreteA;
556
557 const Matrix inputUpdate =
558 (discreteA + Matrix::Identity(localStateCount, localStateCount)) *
559 discreteB;
560
561 BdMna.block(stateOffset, 0, localStateCount, mnaVectorSize) +=
562 inputUpdate * K;
563
564 stampTwoTerminalCurrentInjectionMapping(K, CdMna, stateOffset, outputC);
565 }
566
567 void contributeMetadata(StateSpaceMetadata &metadata,
568 UInt stateOffset) const override {
569 addRealStateMetadata(metadata, stateOffset, getStateCount(),
570 mComponent->name());
571 }
572
573private:
574 std::shared_ptr<DP::Ph1::MixedVTypeVariableSSNComp> mComponent;
575};
576
577} // namespace
578
579MNAStateSpaceContributor::Ptr
580MNAStateSpaceContributorFactory::create(const MNAInterface::Ptr &component) {
581 if (!component)
582 return nullptr;
583
584 if (auto inductor =
585 std::dynamic_pointer_cast<EMT::Ph3::Inductor>(component)) {
586 return std::make_shared<EMTPh3InductorStateSpaceContributor>(inductor);
587 }
588
589 if (auto capacitor =
590 std::dynamic_pointer_cast<EMT::Ph3::Capacitor>(component)) {
591 return std::make_shared<EMTPh3CapacitorStateSpaceContributor>(capacitor);
592 }
593
594 if (auto variableSsn =
595 std::dynamic_pointer_cast<EMT::Ph3::TwoTerminalVTypeVariableSSNComp>(
596 component)) {
597 return std::make_shared<EMTPh3TwoTerminalVTypeSSNStateSpaceContributor>(
598 variableSsn, true);
599 }
600
601 if (auto ssn = std::dynamic_pointer_cast<EMT::Ph3::TwoTerminalVTypeSSNComp>(
602 component)) {
603 return std::make_shared<EMTPh3TwoTerminalVTypeSSNStateSpaceContributor>(
604 ssn, false);
605 }
606
607 if (std::dynamic_pointer_cast<EMT::Ph3::Resistor>(component))
608 return nullptr;
609
610 if (std::dynamic_pointer_cast<EMT::Ph3::Switch>(component))
611 return nullptr;
612
613 if (std::dynamic_pointer_cast<EMT::Ph3::VoltageSource>(component))
614 return nullptr;
615
616 if (auto inductor = std::dynamic_pointer_cast<DP::Ph1::Inductor>(component)) {
617 return std::make_shared<DPPh1InductorStateSpaceContributor>(inductor);
618 }
619
620 if (auto capacitor =
621 std::dynamic_pointer_cast<DP::Ph1::Capacitor>(component)) {
622 return std::make_shared<DPPh1CapacitorStateSpaceContributor>(capacitor);
623 }
624
625 if (auto mixedVariableSsn =
626 std::dynamic_pointer_cast<DP::Ph1::MixedVTypeVariableSSNComp>(
627 component)) {
628 return std::make_shared<DPPh1MixedVTypeVariableSSNStateSpaceContributor>(
629 mixedVariableSsn);
630 }
631
632 if (auto ssn = std::dynamic_pointer_cast<DP::Ph1::TwoTerminalVTypeSSNComp>(
633 component)) {
634 return std::make_shared<DPPh1TwoTerminalVTypeSSNStateSpaceContributor>(ssn);
635 }
636
637 if (std::dynamic_pointer_cast<DP::Ph1::Resistor>(component))
638 return nullptr;
639
640 if (std::dynamic_pointer_cast<DP::Ph1::Switch>(component))
641 return nullptr;
642
643 if (std::dynamic_pointer_cast<DP::Ph1::VoltageSource>(component))
644 return nullptr;
645
646 throw std::invalid_argument(
647 "Unsupported component in MNA state-space extraction.");
648}
649
650MNAStateSpaceContributor::List MNAStateSpaceContributorFactory::createList(
651 const MNAInterface::List &components) {
652 MNAStateSpaceContributor::List contributors;
653
654 const auto appendContributor =
655 [&contributors](const MNAInterface::Ptr &component) {
656 auto contributor = MNAStateSpaceContributorFactory::create(component);
657
658 if (contributor)
659 contributors.push_back(std::move(contributor));
660 };
661
662 const auto appendCompositeContributors =
663 [&appendContributor](const auto &composite) {
664 for (const auto &subcomponent : composite->mnaSubComponents())
665 appendContributor(subcomponent);
666 };
667
668 for (const auto &component : components) {
669 if (const auto composite = getSupportedRealComposite(component)) {
670 appendCompositeContributors(composite);
671 continue;
672 }
673
674 if (const auto composite = getSupportedComplexComposite(component)) {
675 appendCompositeContributors(composite);
676 continue;
677 }
678
679 appendContributor(component);
680 }
681
682 return contributors;
683}
684
685} // namespace DPsim