3#include <dpsim-models/CompositePowerComp.h>
4#include <dpsim/MNAStateSpaceContributor.h>
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>
44using SimPowerCompReal = CPS::SimPowerComp<CPS::Real>;
45using SimPowerCompComplex = CPS::SimPowerComp<CPS::Complex>;
47std::shared_ptr<CompositePowerComp<Real>>
48getSupportedRealComposite(
const MNAInterface::Ptr &component) {
49 if (
const auto composite =
50 std::dynamic_pointer_cast<EMT::Ph3::NetworkInjection>(component))
53 if (
const auto composite =
54 std::dynamic_pointer_cast<EMT::Ph3::PiLine>(component))
57 if (
const auto composite =
58 std::dynamic_pointer_cast<EMT::Ph3::RXLoad>(component))
61 if (
const auto composite =
62 std::dynamic_pointer_cast<EMT::Ph3::RxLine>(component))
65 if (
const auto composite =
66 std::dynamic_pointer_cast<EMT::Ph3::Shunt>(component))
69 if (
const auto composite =
70 std::dynamic_pointer_cast<EMT::Ph3::Transformer>(component))
76std::shared_ptr<CompositePowerComp<Complex>>
77getSupportedComplexComposite(
const MNAInterface::Ptr &component) {
78 if (
const auto composite =
79 std::dynamic_pointer_cast<DP::Ph1::NetworkInjection>(component))
82 if (
const auto composite =
83 std::dynamic_pointer_cast<DP::Ph1::PiLine>(component))
86 if (
const auto composite =
87 std::dynamic_pointer_cast<DP::Ph1::RXLoad>(component))
90 if (
const auto composite =
91 std::dynamic_pointer_cast<DP::Ph1::RxLine>(component))
94 if (
const auto composite =
95 std::dynamic_pointer_cast<DP::Ph1::Shunt>(component))
98 if (
const auto composite =
99 std::dynamic_pointer_cast<DP::Ph1::Transformer>(component))
107Matrix buildTwoTerminalInterfaceVoltageMapping(SimPowerCompReal &component,
108 UInt mnaVectorSize) {
109 Matrix K = Matrix::Zero(3, mnaVectorSize);
111 if (component.terminalNotGrounded(1)) {
112 for (UInt phase = 0; phase < 3; ++phase)
113 K(phase, component.matrixNodeIndex(1, phase)) = 1.0;
116 if (component.terminalNotGrounded(0)) {
117 for (UInt phase = 0; phase < 3; ++phase)
118 K(phase, component.matrixNodeIndex(0, phase)) = -1.0;
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.");
135 const UInt complexOffset = mnaVectorSize / 2;
136 Matrix K = Matrix::Zero(2, mnaVectorSize);
138 if (component.terminalNotGrounded(1)) {
139 const UInt nodeIdx = component.matrixNodeIndex(1);
141 K(1, nodeIdx + complexOffset) = 1.0;
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;
157void stampTwoTerminalCurrentInjectionMapping(
const Matrix &K, Matrix &CdMna,
159 const Matrix &outputMatrix) {
160 CdMna.block(0, stateOffset, CdMna.rows(), outputMatrix.cols()) +=
161 -K.transpose() * outputMatrix;
164Matrix realAugment(
const MatrixComp &matrix) {
165 const Matrix::Index rows = matrix.rows();
166 const Matrix::Index cols = matrix.cols();
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();
177Matrix realAugment(
const Complex &value) {
178 Matrix result = Matrix::Zero(2, 2);
179 result << value.real(), -value.imag(), value.imag(), value.real();
183Complex calculateDPInductorPreviousCurrentFactor(
const Complex &conductance) {
184 const Real omegaDtHalf = -conductance.imag() / conductance.real();
185 return Complex(1.0, -omegaDtHalf) / Complex(1.0, omegaDtHalf);
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.");
195 metadata.stateNames[stateIndex] = name;
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");
204 metadata.abcStateBlocks.push_back(
205 {{stateOffset + 0, stateOffset + 1, stateOffset + 2}, baseName});
210 const String &baseName) {
211 setStateName(metadata, stateOffset + 0, baseName +
"_re");
212 setStateName(metadata, stateOffset + 1, baseName +
"_im");
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,
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));
233class EMTPh3InductorStateSpaceContributor final
236 explicit EMTPh3InductorStateSpaceContributor(
237 std::shared_ptr<EMT::Ph3::Inductor> component)
238 : mComponent(std::move(component)) {}
240 UInt getStateCount()
const override {
return 3; }
242 void stamp(Matrix &AdLocal, Matrix &BdMna, Matrix &CdMna, UInt stateOffset,
243 UInt mnaVectorSize)
const override {
244 const Matrix &conductance = mComponent->getMNAConductance();
247 buildTwoTerminalInterfaceVoltageMapping(*mComponent, mnaVectorSize);
253 AdLocal.block(stateOffset, stateOffset, 3, 3) += Matrix::Identity(3, 3);
255 BdMna.block(stateOffset, 0, 3, mnaVectorSize) += 2.0 * conductance * K;
257 stampTwoTerminalCurrentInjectionMapping(K, CdMna, stateOffset,
258 Matrix::Identity(3, 3));
261 void contributeMetadata(StateSpaceMetadata &metadata,
262 UInt stateOffset)
const override {
263 addThreePhaseAbcStateMetadata(metadata, stateOffset, mComponent->name());
267 std::shared_ptr<EMT::Ph3::Inductor> mComponent;
270class EMTPh3CapacitorStateSpaceContributor final
273 explicit EMTPh3CapacitorStateSpaceContributor(
274 std::shared_ptr<EMT::Ph3::Capacitor> component)
275 : mComponent(std::move(component)) {}
277 UInt getStateCount()
const override {
return 3; }
279 void stamp(Matrix &AdLocal, Matrix &BdMna, Matrix &CdMna, UInt stateOffset,
280 UInt mnaVectorSize)
const override {
281 const Matrix &conductance = mComponent->getMNAConductance();
284 buildTwoTerminalInterfaceVoltageMapping(*mComponent, mnaVectorSize);
290 AdLocal.block(stateOffset, stateOffset, 3, 3) -= Matrix::Identity(3, 3);
292 BdMna.block(stateOffset, 0, 3, mnaVectorSize) -= 2.0 * conductance * K;
294 stampTwoTerminalCurrentInjectionMapping(K, CdMna, stateOffset,
295 Matrix::Identity(3, 3));
298 void contributeMetadata(StateSpaceMetadata &metadata,
299 UInt stateOffset)
const override {
300 addThreePhaseAbcStateMetadata(metadata, stateOffset, mComponent->name());
304 std::shared_ptr<EMT::Ph3::Capacitor> mComponent;
307class EMTPh3TwoTerminalVTypeSSNStateSpaceContributor final
310 EMTPh3TwoTerminalVTypeSSNStateSpaceContributor(
311 std::shared_ptr<EMT::VTypeSSNComp> component, Bool isVariable)
312 : mComponent(std::move(component)), mIsVariable(isVariable) {}
314 UInt getStateCount()
const override {
return mComponent->getStateCount(); }
316 Bool isVariable()
const override {
return mIsVariable; }
318 void stamp(Matrix &AdLocal, Matrix &BdMna, Matrix &CdMna, UInt stateOffset,
319 UInt mnaVectorSize)
const override {
320 const UInt localStateCount = getStateCount();
322 const Matrix &discreteA = mComponent->getDiscreteA();
323 const Matrix &discreteB = mComponent->getDiscreteB();
324 const Matrix &outputC = mComponent->getC();
327 buildTwoTerminalInterfaceVoltageMapping(*mComponent, mnaVectorSize);
334 AdLocal.block(stateOffset, stateOffset, localStateCount, localStateCount) +=
337 const Matrix inputUpdate =
338 (discreteA + Matrix::Identity(localStateCount, localStateCount)) *
341 BdMna.block(stateOffset, 0, localStateCount, mnaVectorSize) +=
344 stampTwoTerminalCurrentInjectionMapping(K, CdMna, stateOffset, outputC);
347 void contributeMetadata(StateSpaceMetadata &metadata,
348 UInt stateOffset)
const override {
349 const UInt localStateCount = getStateCount();
350 const String componentName = mComponent->name();
352 const auto localStateNames = mComponent->getLocalStateNames();
354 if (!localStateNames.empty() && localStateNames.size() != localStateCount) {
355 throw std::runtime_error(
356 "SSN component returned an invalid number of local state names.");
359 for (UInt idx = 0; idx < localStateCount; ++idx) {
360 if (!localStateNames.empty()) {
361 setStateName(metadata, stateOffset + idx,
362 componentName +
"." + localStateNames[idx]);
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.");
372 for (
auto &idx : abcBlock.indices) {
373 if (idx >= localStateCount) {
374 throw std::runtime_error(
375 "SSN component returned an invalid abc state index.");
381 metadata.abcStateBlocks.push_back(
382 {abcBlock.indices, componentName +
"." + abcBlock.name});
387 std::shared_ptr<EMT::VTypeSSNComp> mComponent;
388 Bool mIsVariable =
false;
391class DPPh1InductorStateSpaceContributor final
394 explicit DPPh1InductorStateSpaceContributor(
395 std::shared_ptr<DP::Ph1::Inductor> component)
396 : mComponent(std::move(component)) {}
398 UInt getStateCount()
const override {
return 2; }
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);
406 const Matrix K = buildSinglePhaseComplexInterfaceVoltageMapping(
407 *mComponent, mnaVectorSize);
414 AdLocal.block(stateOffset, stateOffset, 2, 2) +=
415 realAugment(prevCurrentFactor);
417 BdMna.block(stateOffset, 0, 2, mnaVectorSize) +=
418 realAugment((Complex(1.0, 0.0) + prevCurrentFactor) * conductance) * K;
420 stampTwoTerminalCurrentInjectionMapping(K, CdMna, stateOffset,
421 Matrix::Identity(2, 2));
424 void contributeMetadata(StateSpaceMetadata &metadata,
425 UInt stateOffset)
const override {
426 addSinglePhaseComplexStateMetadata(metadata, stateOffset,
431 std::shared_ptr<DP::Ph1::Inductor> mComponent;
434class DPPh1CapacitorStateSpaceContributor final
437 explicit DPPh1CapacitorStateSpaceContributor(
438 std::shared_ptr<DP::Ph1::Capacitor> component)
439 : mComponent(std::move(component)) {}
441 UInt getStateCount()
const override {
return 2; }
443 void stamp(Matrix &AdLocal, Matrix &BdMna, Matrix &CdMna, UInt stateOffset,
444 UInt mnaVectorSize)
const override {
445 const Complex conductance = mComponent->getMNAConductance();
447 const Matrix K = buildSinglePhaseComplexInterfaceVoltageMapping(
448 *mComponent, mnaVectorSize);
455 AdLocal.block(stateOffset, stateOffset, 2, 2) -= Matrix::Identity(2, 2);
457 BdMna.block(stateOffset, 0, 2, mnaVectorSize) -=
458 2.0 * conductance.real() * K;
460 stampTwoTerminalCurrentInjectionMapping(K, CdMna, stateOffset,
461 Matrix::Identity(2, 2));
464 void contributeMetadata(StateSpaceMetadata &metadata,
465 UInt stateOffset)
const override {
466 addSinglePhaseComplexStateMetadata(metadata, stateOffset,
471 std::shared_ptr<DP::Ph1::Capacitor> mComponent;
474class DPPh1TwoTerminalVTypeSSNStateSpaceContributor final
477 explicit DPPh1TwoTerminalVTypeSSNStateSpaceContributor(
478 std::shared_ptr<DP::VTypeSSNComp> component)
479 : mComponent(std::move(component)) {}
481 UInt getStateCount()
const override {
482 return 2 * mComponent->getStateCount();
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();
490 const MatrixComp &discreteA = mComponent->getDiscreteA();
491 const MatrixComp &discreteB = mComponent->getDiscreteB();
492 const MatrixComp outputC = mComponent->getC().cast<Complex>();
494 const Matrix K = buildSinglePhaseComplexInterfaceVoltageMapping(
495 *mComponent, mnaVectorSize);
502 AdLocal.block(stateOffset, stateOffset, realStateCount, realStateCount) +=
503 realAugment(discreteA);
505 const MatrixComp inputUpdate =
507 MatrixComp::Identity(complexStateCount, complexStateCount)) *
510 BdMna.block(stateOffset, 0, realStateCount, mnaVectorSize) +=
511 realAugment(inputUpdate) * K;
513 stampTwoTerminalCurrentInjectionMapping(K, CdMna, stateOffset,
514 realAugment(outputC));
517 void contributeMetadata(StateSpaceMetadata &metadata,
518 UInt stateOffset)
const override {
519 addComplexStateMetadata(metadata, stateOffset, mComponent->getStateCount(),
524 std::shared_ptr<DP::VTypeSSNComp> mComponent;
527class DPPh1MixedVTypeVariableSSNStateSpaceContributor final
530 explicit DPPh1MixedVTypeVariableSSNStateSpaceContributor(
531 std::shared_ptr<DP::Ph1::MixedVTypeVariableSSNComp> component)
532 : mComponent(std::move(component)) {}
534 UInt getStateCount()
const override {
return mComponent->getStateCount(); }
536 Bool isVariable()
const override {
return true; }
538 void stamp(Matrix &AdLocal, Matrix &BdMna, Matrix &CdMna, UInt stateOffset,
539 UInt mnaVectorSize)
const override {
540 const UInt localStateCount = getStateCount();
542 const Matrix &discreteA = mComponent->getDiscreteA();
543 const Matrix &discreteB = mComponent->getDiscreteB();
544 const Matrix &outputC = mComponent->getC();
546 const Matrix K = buildSinglePhaseComplexInterfaceVoltageMapping(
547 *mComponent, mnaVectorSize);
554 AdLocal.block(stateOffset, stateOffset, localStateCount, localStateCount) +=
557 const Matrix inputUpdate =
558 (discreteA + Matrix::Identity(localStateCount, localStateCount)) *
561 BdMna.block(stateOffset, 0, localStateCount, mnaVectorSize) +=
564 stampTwoTerminalCurrentInjectionMapping(K, CdMna, stateOffset, outputC);
567 void contributeMetadata(StateSpaceMetadata &metadata,
568 UInt stateOffset)
const override {
569 addRealStateMetadata(metadata, stateOffset, getStateCount(),
574 std::shared_ptr<DP::Ph1::MixedVTypeVariableSSNComp> mComponent;
579MNAStateSpaceContributor::Ptr
580MNAStateSpaceContributorFactory::create(
const MNAInterface::Ptr &component) {
585 std::dynamic_pointer_cast<EMT::Ph3::Inductor>(component)) {
586 return std::make_shared<EMTPh3InductorStateSpaceContributor>(inductor);
590 std::dynamic_pointer_cast<EMT::Ph3::Capacitor>(component)) {
591 return std::make_shared<EMTPh3CapacitorStateSpaceContributor>(capacitor);
594 if (
auto variableSsn =
595 std::dynamic_pointer_cast<EMT::Ph3::TwoTerminalVTypeVariableSSNComp>(
597 return std::make_shared<EMTPh3TwoTerminalVTypeSSNStateSpaceContributor>(
601 if (
auto ssn = std::dynamic_pointer_cast<EMT::Ph3::TwoTerminalVTypeSSNComp>(
603 return std::make_shared<EMTPh3TwoTerminalVTypeSSNStateSpaceContributor>(
607 if (std::dynamic_pointer_cast<EMT::Ph3::Resistor>(component))
610 if (std::dynamic_pointer_cast<EMT::Ph3::Switch>(component))
613 if (std::dynamic_pointer_cast<EMT::Ph3::VoltageSource>(component))
616 if (
auto inductor = std::dynamic_pointer_cast<DP::Ph1::Inductor>(component)) {
617 return std::make_shared<DPPh1InductorStateSpaceContributor>(inductor);
621 std::dynamic_pointer_cast<DP::Ph1::Capacitor>(component)) {
622 return std::make_shared<DPPh1CapacitorStateSpaceContributor>(capacitor);
625 if (
auto mixedVariableSsn =
626 std::dynamic_pointer_cast<DP::Ph1::MixedVTypeVariableSSNComp>(
628 return std::make_shared<DPPh1MixedVTypeVariableSSNStateSpaceContributor>(
632 if (
auto ssn = std::dynamic_pointer_cast<DP::Ph1::TwoTerminalVTypeSSNComp>(
634 return std::make_shared<DPPh1TwoTerminalVTypeSSNStateSpaceContributor>(ssn);
637 if (std::dynamic_pointer_cast<DP::Ph1::Resistor>(component))
640 if (std::dynamic_pointer_cast<DP::Ph1::Switch>(component))
643 if (std::dynamic_pointer_cast<DP::Ph1::VoltageSource>(component))
646 throw std::invalid_argument(
647 "Unsupported component in MNA state-space extraction.");
650MNAStateSpaceContributor::List MNAStateSpaceContributorFactory::createList(
651 const MNAInterface::List &components) {
652 MNAStateSpaceContributor::List contributors;
654 const auto appendContributor =
655 [&contributors](
const MNAInterface::Ptr &component) {
656 auto contributor = MNAStateSpaceContributorFactory::create(component);
659 contributors.push_back(std::move(contributor));
662 const auto appendCompositeContributors =
663 [&appendContributor](
const auto &composite) {
664 for (
const auto &subcomponent : composite->mnaSubComponents())
665 appendContributor(subcomponent);
668 for (
const auto &component : components) {
669 if (
const auto composite = getSupportedRealComposite(component)) {
670 appendCompositeContributors(composite);
674 if (
const auto composite = getSupportedComplexComposite(component)) {
675 appendCompositeContributors(composite);
679 appendContributor(component);