45using SimPowerCompReal = CPS::SimPowerComp<CPS::Real>;
46using SimPowerCompComplex = CPS::SimPowerComp<CPS::Complex>;
48std::shared_ptr<CompositePowerComp<Real>>
50 if (
const auto composite =
51 std::dynamic_pointer_cast<EMT::Ph3::NetworkInjection>(component))
54 if (
const auto composite =
55 std::dynamic_pointer_cast<EMT::Ph3::PiLine>(component))
58 if (
const auto composite =
59 std::dynamic_pointer_cast<EMT::Ph3::RXLoad>(component))
62 if (
const auto composite =
63 std::dynamic_pointer_cast<EMT::Ph3::RxLine>(component))
66 if (
const auto composite =
67 std::dynamic_pointer_cast<EMT::Ph3::Shunt>(component))
70 if (
const auto composite =
71 std::dynamic_pointer_cast<EMT::Ph3::Transformer>(component))
77std::shared_ptr<CompositePowerComp<Complex>>
79 if (
const auto composite =
80 std::dynamic_pointer_cast<DP::Ph1::NetworkInjection>(component))
83 if (
const auto composite =
84 std::dynamic_pointer_cast<DP::Ph1::PiLine>(component))
87 if (
const auto composite =
88 std::dynamic_pointer_cast<DP::Ph1::RXLoad>(component))
91 if (
const auto composite =
92 std::dynamic_pointer_cast<DP::Ph1::RxLine>(component))
95 if (
const auto composite =
96 std::dynamic_pointer_cast<DP::Ph1::Shunt>(component))
99 if (
const auto composite =
100 std::dynamic_pointer_cast<DP::Ph1::Transformer>(component))
108Matrix buildTwoTerminalInterfaceVoltageMapping(SimPowerCompReal &component,
109 UInt mnaVectorSize) {
110 Matrix K = Matrix::Zero(3, mnaVectorSize);
112 if (component.terminalNotGrounded(1)) {
113 for (
UInt phase = 0; phase < 3; ++phase)
114 K(phase, component.matrixNodeIndex(1, phase)) = 1.0;
117 if (component.terminalNotGrounded(0)) {
118 for (
UInt phase = 0; phase < 3; ++phase)
119 K(phase, component.matrixNodeIndex(0, phase)) = -1.0;
128buildSinglePhaseComplexInterfaceVoltageMapping(SimPowerCompComplex &component,
129 UInt mnaVectorSize) {
130 if (mnaVectorSize % 2 != 0) {
131 throw std::logic_error(
132 "DP MNA state-space extraction requires a real-imaginary stacked "
133 "MNA vector with even size.");
136 const UInt complexOffset = mnaVectorSize / 2;
137 Matrix K = Matrix::Zero(2, mnaVectorSize);
139 if (component.terminalNotGrounded(1)) {
140 const UInt nodeIdx = component.matrixNodeIndex(1);
142 K(1, nodeIdx + complexOffset) = 1.0;
145 if (component.terminalNotGrounded(0)) {
146 const UInt nodeIdx = component.matrixNodeIndex(0);
147 K(0, nodeIdx) = -1.0;
148 K(1, nodeIdx + complexOffset) = -1.0;
158void stampTwoTerminalCurrentInjectionMapping(
const Matrix &K,
Matrix &CdMna,
160 const Matrix &outputMatrix) {
161 CdMna.block(0, stateOffset, CdMna.rows(), outputMatrix.cols()) +=
162 -K.transpose() * outputMatrix;
166 const Matrix::Index rows = matrix.rows();
167 const Matrix::Index cols = matrix.cols();
169 Matrix result = Matrix::Zero(2 * rows, 2 * cols);
170 result.topLeftCorner(rows, cols) = matrix.real();
171 result.topRightCorner(rows, cols) = -matrix.imag();
172 result.bottomLeftCorner(rows, cols) = matrix.imag();
173 result.bottomRightCorner(rows, cols) = matrix.real();
179 Matrix result = Matrix::Zero(2, 2);
180 result << value.real(), -value.imag(), value.imag(), value.real();
184Complex calculateDPInductorPreviousCurrentFactor(
const Complex &conductance) {
185 const Real omegaDtHalf = -conductance.imag() / conductance.real();
191 if (stateIndex >= metadata.stateNames.size())
192 throw std::runtime_error(
193 "MNA state-space contributor tried to set a state name outside the "
194 "extracted state vector.");
196 metadata.stateNames[stateIndex] = name;
201 setStateName(metadata, stateOffset + 0, baseName +
"_a");
202 setStateName(metadata, stateOffset + 1, baseName +
"_b");
203 setStateName(metadata, stateOffset + 2, baseName +
"_c");
205 metadata.abcStateBlocks.push_back(
206 {{stateOffset + 0, stateOffset + 1, stateOffset + 2}, baseName});
212 setStateName(metadata, stateOffset + 0, baseName +
"_re");
213 setStateName(metadata, stateOffset + 1, baseName +
"_im");
217 UInt complexStateCount,
218 const String &componentName) {
219 for (
UInt idx = 0; idx < complexStateCount; ++idx) {
220 const String stateName = componentName +
".x" + std::to_string(idx);
221 setStateName(metadata, stateOffset + idx, stateName +
"_re");
222 setStateName(metadata, stateOffset + complexStateCount + idx,
228 UInt stateCount,
const String &componentName) {
229 for (
UInt idx = 0; idx < stateCount; ++idx)
230 setStateName(metadata, stateOffset + idx,
231 componentName +
".x" + std::to_string(idx));
234class EMTPh3InductorStateSpaceContributor final
237 explicit EMTPh3InductorStateSpaceContributor(
238 std::shared_ptr<EMT::Ph3::Inductor> component)
239 : mComponent(std::move(component)) {}
241 UInt getStateCount()
const override {
return 3; }
244 UInt mnaVectorSize)
const override {
245 const Matrix &conductance = mComponent->getMNAConductance();
248 buildTwoTerminalInterfaceVoltageMapping(*mComponent, mnaVectorSize);
254 AdLocal.block(stateOffset, stateOffset, 3, 3) += Matrix::Identity(3, 3);
256 BdMna.block(stateOffset, 0, 3, mnaVectorSize) += 2.0 * conductance * K;
258 stampTwoTerminalCurrentInjectionMapping(K, CdMna, stateOffset,
259 Matrix::Identity(3, 3));
262 void contributeMetadata(StateSpaceMetadata &metadata,
263 UInt stateOffset)
const override {
264 addThreePhaseAbcStateMetadata(metadata, stateOffset, mComponent->name());
268 std::shared_ptr<EMT::Ph3::Inductor> mComponent;
271class EMTPh3CapacitorStateSpaceContributor final
274 explicit EMTPh3CapacitorStateSpaceContributor(
275 std::shared_ptr<EMT::Ph3::Capacitor> component)
276 : mComponent(std::move(component)) {}
278 UInt getStateCount()
const override {
return 3; }
281 UInt mnaVectorSize)
const override {
282 const Matrix &conductance = mComponent->getMNAConductance();
285 buildTwoTerminalInterfaceVoltageMapping(*mComponent, mnaVectorSize);
291 AdLocal.block(stateOffset, stateOffset, 3, 3) -= Matrix::Identity(3, 3);
293 BdMna.block(stateOffset, 0, 3, mnaVectorSize) -= 2.0 * conductance * K;
295 stampTwoTerminalCurrentInjectionMapping(K, CdMna, stateOffset,
296 Matrix::Identity(3, 3));
299 void contributeMetadata(StateSpaceMetadata &metadata,
300 UInt stateOffset)
const override {
301 addThreePhaseAbcStateMetadata(metadata, stateOffset, mComponent->name());
305 std::shared_ptr<EMT::Ph3::Capacitor> mComponent;
308class EMTPh3TwoTerminalVTypeSSNStateSpaceContributor final
311 EMTPh3TwoTerminalVTypeSSNStateSpaceContributor(
312 std::shared_ptr<EMT::VTypeSSNComp> component,
Bool isVariable)
313 : mComponent(std::move(component)), mIsVariable(isVariable) {}
315 UInt getStateCount()
const override {
return mComponent->getStateCount(); }
317 Bool contributesToUpdatedMatrices()
const override {
return mIsVariable; }
320 UInt mnaVectorSize)
const override {
321 const UInt localStateCount = getStateCount();
323 const Matrix &discreteA = mComponent->getDiscreteA();
324 const Matrix &discreteB = mComponent->getDiscreteB();
325 const Matrix &outputC = mComponent->getC();
328 buildTwoTerminalInterfaceVoltageMapping(*mComponent, mnaVectorSize);
335 AdLocal.block(stateOffset, stateOffset, localStateCount, localStateCount) +=
338 const Matrix inputUpdate =
339 (discreteA + Matrix::Identity(localStateCount, localStateCount)) *
342 BdMna.block(stateOffset, 0, localStateCount, mnaVectorSize) +=
345 stampTwoTerminalCurrentInjectionMapping(K, CdMna, stateOffset, outputC);
348 void contributeMetadata(StateSpaceMetadata &metadata,
349 UInt stateOffset)
const override {
350 const UInt localStateCount = getStateCount();
351 const String componentName = mComponent->name();
353 const auto localStateNames = mComponent->getLocalStateNames();
355 if (!localStateNames.empty() && localStateNames.size() != localStateCount) {
356 throw std::runtime_error(
357 "SSN component returned an invalid number of local state names.");
360 for (
UInt idx = 0; idx < localStateCount; ++idx) {
361 if (!localStateNames.empty()) {
362 setStateName(metadata, stateOffset + idx,
363 componentName +
"." + localStateNames[idx]);
367 for (
auto abcBlock : mComponent->getLocalAbcStateBlocks()) {
368 if (abcBlock.name.empty()) {
369 throw std::runtime_error(
370 "SSN component returned an abc state block with an empty name.");
373 for (
auto &idx : abcBlock.indices) {
374 if (idx >= localStateCount) {
375 throw std::runtime_error(
376 "SSN component returned an invalid abc state index.");
382 metadata.abcStateBlocks.push_back(
383 {abcBlock.indices, componentName +
"." + abcBlock.name});
388 std::shared_ptr<EMT::VTypeSSNComp> mComponent;
389 Bool mIsVariable =
false;
392class EMTPh3TwoTerminalVTypeSplitSSNStateSpaceContributor final
395 explicit EMTPh3TwoTerminalVTypeSplitSSNStateSpaceContributor(
396 std::shared_ptr<EMT::Ph3::TwoTerminalVTypeSplitSSNComp> component)
397 : mComponent(std::move(component)) {}
399 UInt getStateCount()
const override {
400 return mComponent->getSplitStateCount();
403 Bool contributesToUpdatedMatrices()
const override {
404 return mComponent->requiresStateSpaceMatrixUpdate();
407 Bool requiresUpdate()
const override {
408 return mComponent->requiresStateSpaceMatrixUpdate();
412 if (requiresUpdate())
413 return {mComponent->getSplitStateAttribute()};
419 UInt mnaVectorSize)
const override {
420 const UInt localStateCount = getStateCount();
421 const Matrix &discreteA = mComponent->getSplitDiscreteA();
422 const Matrix &discreteB = mComponent->getSplitDiscreteB();
423 const Matrix &historyC = mComponent->getSplitHistoryC();
425 buildTwoTerminalInterfaceVoltageMapping(*mComponent, mnaVectorSize);
427 AdLocal.block(stateOffset, stateOffset, localStateCount, localStateCount) +=
429 BdMna.block(stateOffset, 0, localStateCount, mnaVectorSize) +=
431 stampTwoTerminalCurrentInjectionMapping(K, CdMna, stateOffset, historyC);
434 void contributeMetadata(StateSpaceMetadata &metadata,
435 UInt stateOffset)
const override {
436 const UInt localStateCount = getStateCount();
437 const String componentName = mComponent->name();
438 const auto localStateNames = mComponent->getSplitLocalStateNames();
440 if (!localStateNames.empty() && localStateNames.size() != localStateCount) {
441 throw std::runtime_error(
442 "Split SSN component returned an invalid number of local state "
446 for (
UInt idx = 0; idx < localStateCount; ++idx) {
447 if (!localStateNames.empty())
448 setStateName(metadata, stateOffset + idx,
449 componentName +
"." + localStateNames[idx]);
452 for (
auto abcBlock : mComponent->getSplitLocalAbcStateBlocks()) {
453 if (abcBlock.name.empty()) {
454 throw std::runtime_error(
455 "Split SSN component returned an abc state block with an empty "
459 for (
auto &idx : abcBlock.indices) {
460 if (idx >= localStateCount) {
461 throw std::runtime_error(
462 "Split SSN component returned an invalid abc state index.");
467 metadata.abcStateBlocks.push_back(
468 {abcBlock.indices, componentName +
"." + abcBlock.name});
473 std::shared_ptr<EMT::Ph3::TwoTerminalVTypeSplitSSNComp> mComponent;
476class DPPh1InductorStateSpaceContributor final
479 explicit DPPh1InductorStateSpaceContributor(
480 std::shared_ptr<DP::Ph1::Inductor> component)
481 : mComponent(std::move(component)) {}
483 UInt getStateCount()
const override {
return 2; }
486 UInt mnaVectorSize)
const override {
487 const Complex conductance = mComponent->getMNAConductance();
488 const Complex prevCurrentFactor =
489 calculateDPInductorPreviousCurrentFactor(conductance);
491 const Matrix K = buildSinglePhaseComplexInterfaceVoltageMapping(
492 *mComponent, mnaVectorSize);
499 AdLocal.block(stateOffset, stateOffset, 2, 2) +=
500 realAugment(prevCurrentFactor);
502 BdMna.block(stateOffset, 0, 2, mnaVectorSize) +=
503 realAugment((
Complex(1.0, 0.0) + prevCurrentFactor) * conductance) * K;
505 stampTwoTerminalCurrentInjectionMapping(K, CdMna, stateOffset,
506 Matrix::Identity(2, 2));
509 void contributeMetadata(StateSpaceMetadata &metadata,
510 UInt stateOffset)
const override {
511 addSinglePhaseComplexStateMetadata(metadata, stateOffset,
516 std::shared_ptr<DP::Ph1::Inductor> mComponent;
519class DPPh1CapacitorStateSpaceContributor final
522 explicit DPPh1CapacitorStateSpaceContributor(
523 std::shared_ptr<DP::Ph1::Capacitor> component)
524 : mComponent(std::move(component)) {}
526 UInt getStateCount()
const override {
return 2; }
529 UInt mnaVectorSize)
const override {
530 const Complex conductance = mComponent->getMNAConductance();
532 const Matrix K = buildSinglePhaseComplexInterfaceVoltageMapping(
533 *mComponent, mnaVectorSize);
540 AdLocal.block(stateOffset, stateOffset, 2, 2) -= Matrix::Identity(2, 2);
542 BdMna.block(stateOffset, 0, 2, mnaVectorSize) -=
543 2.0 * conductance.real() * K;
545 stampTwoTerminalCurrentInjectionMapping(K, CdMna, stateOffset,
546 Matrix::Identity(2, 2));
549 void contributeMetadata(StateSpaceMetadata &metadata,
550 UInt stateOffset)
const override {
551 addSinglePhaseComplexStateMetadata(metadata, stateOffset,
556 std::shared_ptr<DP::Ph1::Capacitor> mComponent;
559class DPPh1TwoTerminalVTypeSSNStateSpaceContributor final
562 explicit DPPh1TwoTerminalVTypeSSNStateSpaceContributor(
563 std::shared_ptr<DP::VTypeSSNComp> component)
564 : mComponent(std::move(component)) {}
566 UInt getStateCount()
const override {
567 return 2 * mComponent->getStateCount();
571 UInt mnaVectorSize)
const override {
572 const UInt complexStateCount = mComponent->getStateCount();
573 const UInt realStateCount = getStateCount();
575 const MatrixComp &discreteA = mComponent->getDiscreteA();
576 const MatrixComp &discreteB = mComponent->getDiscreteB();
579 const Matrix K = buildSinglePhaseComplexInterfaceVoltageMapping(
580 *mComponent, mnaVectorSize);
587 AdLocal.block(stateOffset, stateOffset, realStateCount, realStateCount) +=
588 realAugment(discreteA);
592 MatrixComp::Identity(complexStateCount, complexStateCount)) *
595 BdMna.block(stateOffset, 0, realStateCount, mnaVectorSize) +=
596 realAugment(inputUpdate) * K;
598 stampTwoTerminalCurrentInjectionMapping(K, CdMna, stateOffset,
599 realAugment(outputC));
602 void contributeMetadata(StateSpaceMetadata &metadata,
603 UInt stateOffset)
const override {
604 addComplexStateMetadata(metadata, stateOffset, mComponent->getStateCount(),
609 std::shared_ptr<DP::VTypeSSNComp> mComponent;
612class DPPh1MixedVTypeVariableSSNStateSpaceContributor final
615 explicit DPPh1MixedVTypeVariableSSNStateSpaceContributor(
616 std::shared_ptr<DP::Ph1::MixedVTypeVariableSSNComp> component)
617 : mComponent(std::move(component)) {}
619 UInt getStateCount()
const override {
return mComponent->getStateCount(); }
621 Bool contributesToUpdatedMatrices()
const override {
return true; }
624 UInt mnaVectorSize)
const override {
625 const UInt localStateCount = getStateCount();
627 const Matrix &discreteA = mComponent->getDiscreteA();
628 const Matrix &discreteB = mComponent->getDiscreteB();
629 const Matrix &outputC = mComponent->getC();
631 const Matrix K = buildSinglePhaseComplexInterfaceVoltageMapping(
632 *mComponent, mnaVectorSize);
639 AdLocal.block(stateOffset, stateOffset, localStateCount, localStateCount) +=
642 const Matrix inputUpdate =
643 (discreteA + Matrix::Identity(localStateCount, localStateCount)) *
646 BdMna.block(stateOffset, 0, localStateCount, mnaVectorSize) +=
649 stampTwoTerminalCurrentInjectionMapping(K, CdMna, stateOffset, outputC);
652 void contributeMetadata(StateSpaceMetadata &metadata,
653 UInt stateOffset)
const override {
654 addRealStateMetadata(metadata, stateOffset, getStateCount(),
659 std::shared_ptr<DP::Ph1::MixedVTypeVariableSSNComp> mComponent;
670 std::dynamic_pointer_cast<EMT::Ph3::Inductor>(component)) {
671 return std::make_shared<EMTPh3InductorStateSpaceContributor>(inductor);
675 std::dynamic_pointer_cast<EMT::Ph3::Capacitor>(component)) {
676 return std::make_shared<EMTPh3CapacitorStateSpaceContributor>(capacitor);
679 if (
auto variableSsn =
680 std::dynamic_pointer_cast<EMT::Ph3::TwoTerminalVTypeVariableSSNComp>(
682 return std::make_shared<EMTPh3TwoTerminalVTypeSSNStateSpaceContributor>(
687 std::dynamic_pointer_cast<EMT::Ph3::TwoTerminalVTypeSplitSSNComp>(
689 return std::make_shared<
690 EMTPh3TwoTerminalVTypeSplitSSNStateSpaceContributor>(splitSsn);
693 if (
auto ssn = std::dynamic_pointer_cast<EMT::Ph3::TwoTerminalVTypeSSNComp>(
695 return std::make_shared<EMTPh3TwoTerminalVTypeSSNStateSpaceContributor>(
699 if (std::dynamic_pointer_cast<EMT::Ph3::Resistor>(component))
702 if (std::dynamic_pointer_cast<EMT::Ph3::Switch>(component))
705 if (std::dynamic_pointer_cast<EMT::Ph3::VoltageSource>(component))
708 if (
auto inductor = std::dynamic_pointer_cast<DP::Ph1::Inductor>(component)) {
709 return std::make_shared<DPPh1InductorStateSpaceContributor>(inductor);
713 std::dynamic_pointer_cast<DP::Ph1::Capacitor>(component)) {
714 return std::make_shared<DPPh1CapacitorStateSpaceContributor>(capacitor);
717 if (
auto mixedVariableSsn =
718 std::dynamic_pointer_cast<DP::Ph1::MixedVTypeVariableSSNComp>(
720 return std::make_shared<DPPh1MixedVTypeVariableSSNStateSpaceContributor>(
724 if (
auto ssn = std::dynamic_pointer_cast<DP::Ph1::TwoTerminalVTypeSSNComp>(
726 return std::make_shared<DPPh1TwoTerminalVTypeSSNStateSpaceContributor>(ssn);
729 if (std::dynamic_pointer_cast<DP::Ph1::Resistor>(component))
732 if (std::dynamic_pointer_cast<DP::Ph1::Switch>(component))
735 if (std::dynamic_pointer_cast<DP::Ph1::VoltageSource>(component))
738 throw std::invalid_argument(
739 "Unsupported component in MNA state-space extraction.");
746 const auto appendContributor =
748 auto contributor = MNAStateSpaceContributorFactory::create(component);
751 contributors.push_back(std::move(contributor));
754 const auto appendCompositeContributors =
755 [&appendContributor](
const auto &composite) {
756 for (
const auto &subcomponent : composite->mnaSubComponents())
757 appendContributor(subcomponent);
760 for (
const auto &component : components) {
761 if (
const auto composite = getSupportedRealComposite(component)) {
762 appendCompositeContributors(composite);
766 if (
const auto composite = getSupportedComplexComposite(component)) {
767 appendCompositeContributors(composite);
771 appendContributor(component);
std::shared_ptr< MNAInterface > Ptr
static MNAStateSpaceContributor::List createList(const CPS::MNAInterface::List &components)
std::shared_ptr< MNAStateSpaceContributor > Ptr
CPS::MatrixComp MatrixComp