DPsim
Loading...
Searching...
No Matches
DP_SSNComp.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-models/DP/DP_SSNComp.h>
5#include <dpsim-models/MathUtils.h>
6
7using namespace CPS;
8
9DP::SSNComp::SSNComp(String uid, String name, Int inputSize, Int outputSize,
10 Logger::Level logLevel)
11 : MNASimPowerComp<Complex>(uid, name, true, true, logLevel), mTimeStep(0.0),
12 mW(MatrixComp::Zero(outputSize, inputSize)),
13 mYHist(MatrixComp::Zero(outputSize, 1)), mInputSize(inputSize),
14 mOutputSize(outputSize), mX(mAttributes->create<MatrixComp>("x")) {
15 mParametersSet = false;
16}
17
18UInt DP::SSNComp::getStateCount() const { return static_cast<UInt>(mA.rows()); }
19
20const MatrixComp &DP::SSNComp::getDiscreteA() const { return mDiscreteA; }
21
22const MatrixComp &DP::SSNComp::getDiscreteB() const { return mDiscreteB; }
23
24const Matrix &DP::SSNComp::getC() const { return mC; }
25
26void DP::SSNComp::setParameters(const Matrix &A, const Matrix &B,
27 const Matrix &C, const Matrix &D) {
28 mParametersSet = false;
29
30 if (A.rows() != A.cols())
31 throw std::invalid_argument("A must be square.");
32
33 if (B.rows() != A.rows() || B.cols() != mInputSize)
34 throw std::invalid_argument("B has invalid dimensions.");
35
36 if (C.rows() != mOutputSize || C.cols() != A.rows())
37 throw std::invalid_argument("C has invalid dimensions.");
38
39 if (D.rows() != mOutputSize || D.cols() != mInputSize)
40 throw std::invalid_argument("D has invalid dimensions.");
41
42 mA = A;
43 mB = B;
44 mC = C;
45 mD = D;
46
47 **mX = MatrixComp::Zero(mA.rows(), 1);
48
49 mDiscreteA = MatrixComp::Zero(mA.rows(), mA.cols());
50 mDiscreteB = MatrixComp::Zero(mB.rows(), mB.cols());
51
52 mW = MatrixComp::Zero(mOutputSize, mInputSize);
53 mYHist = MatrixComp::Zero(mOutputSize, 1);
54
55 mParametersSet = true;
56}
57
58Matrix DP::SSNComp::buildAugmentedA(Real omega) const {
59 const Matrix::Index n = mA.rows();
60 const Matrix wI = omega * Matrix::Identity(n, n);
61
62 Matrix aAug = Matrix::Zero(2 * n, 2 * n);
63 aAug.topLeftCorner(n, n) = mA;
64 aAug.topRightCorner(n, n) = wI;
65 aAug.bottomLeftCorner(n, n) = -wI;
66 aAug.bottomRightCorner(n, n) = mA;
67 return aAug;
68}
69
71 const Matrix::Index n = mA.rows();
72
73 Matrix bAug = Matrix::Zero(2 * n, 2 * mInputSize);
74 bAug.topLeftCorner(n, mInputSize) = mB;
75 bAug.bottomRightCorner(n, mInputSize) = mB;
76 return bAug;
77}
78
80 const Matrix::Index n = mA.rows();
81
82 // Discretize the real-augmented model with the same helper as EMT-SSN.
83 const Matrix aAug = buildAugmentedA(omega);
84 const Matrix bAug = buildAugmentedB();
85 Matrix dAaug = Matrix::Zero(2 * n, 2 * n);
86 Matrix dBaug = Matrix::Zero(2 * n, 2 * mInputSize);
87 Math::calculateStateSpaceTrapezoidalMatrices(aAug, bAug, mTimeStep, dAaug,
88 dBaug);
89
90 // Fold the [[P, -Q], [Q, P]] rotation blocks back into complex P + jQ.
91 mDiscreteA = dAaug.topLeftCorner(n, n).cast<Complex>() +
92 Complex(0., 1.) * dAaug.bottomLeftCorner(n, n).cast<Complex>();
93 mDiscreteB =
94 dBaug.block(0, 0, n, mInputSize).cast<Complex>() +
95 Complex(0., 1.) * dBaug.block(n, 0, n, mInputSize).cast<Complex>();
96
97 mW = mC.cast<Complex>() * mDiscreteB + mD.cast<Complex>();
98}
99
100MatrixComp DP::SSNComp::calculateHistoryVector() const {
101 return mC.cast<Complex>() *
102 (mDiscreteA * (**mX) + mDiscreteB * (**inputAttribute()));
103}
104
106 Real omega) const {
107 MatrixComp h =
108 Complex(0., omega) * MatrixComp::Identity(mA.rows(), mA.cols()) -
109 mA.cast<Complex>();
110
111 return h.inverse() * mB.cast<Complex>() * u;
112}
113
114MatrixComp
115DP::SSNComp::calculateSteadyStateOutputFromInput(const MatrixComp &x,
116 const MatrixComp &u) const {
117 return mC.cast<Complex>() * x + mD.cast<Complex>() * u;
118}
119
120void DP::SSNComp::updateState(const MatrixComp &uOld, const MatrixComp &uNew) {
121 **mX = mDiscreteA * (**mX) + mDiscreteB * (uNew + uOld);
122}
123
125 // For linear components, the default implementation does nothing.
126}
127
128void DP::SSNComp::mnaCompInitialize(Real omega, Real timeStep,
129 Attribute<Matrix>::Ptr) {
130 if (!mParametersSet)
131 throw std::logic_error(
132 "setParameters() must be called before initialization.");
133
134 if (mNumFreqs != 1)
135 throw std::logic_error(
136 "DP SSN components currently support a single carrier frequency.");
137
138 mTimeStep = timeStep;
139 updateMatrixNodeIndices();
140
141 recomputeDiscreteModel(omega);
142 mYHist = calculateHistoryVector();
143}
144
145void DP::SSNComp::mnaCompAddPreStepDependencies(
146 AttributeBase::List &prevStepDependencies,
147 AttributeBase::List &attributeDependencies,
148 AttributeBase::List &modifiedAttributes) {
149 modifiedAttributes.push_back(mRightVector);
150 prevStepDependencies.push_back(mX);
151 prevStepDependencies.push_back(inputAttribute());
152}
153
154void DP::SSNComp::mnaCompPreStep(Real time, Int timeStepCount) {
155 updateStateSpaceModel();
156 mYHist = calculateHistoryVector();
157 mnaCompApplyRightSideVectorStamp(**mRightVector);
158}
159
160void DP::SSNComp::mnaCompAddPostStepDependencies(
161 AttributeBase::List &prevStepDependencies,
162 AttributeBase::List &attributeDependencies,
163 AttributeBase::List &modifiedAttributes,
164 Attribute<Matrix>::Ptr &leftVector) {
165 attributeDependencies.push_back(leftVector);
166 modifiedAttributes.push_back(inputAttribute());
167 modifiedAttributes.push_back(outputAttribute());
168 modifiedAttributes.push_back(mX);
169}
virtual void updateStateSpaceModel()
Hook for variable/time-varying SSN components.
Matrix mA
Continuous-time real physical model (user-provided).
Definition DP_SSNComp.h:27
const MatrixComp & getDiscreteA() const
Get the complex discrete state-transition operator of the envelope model.
UInt getStateCount() const
Get number of internal state variables of the SSN model.
Matrix buildAugmentedA(Real omega) const
Build A_aug = [[A, w I], [-w I, A]].
virtual MatrixComp calculateSteadyStateStateFromInput(const MatrixComp &u, Real omega) const
Steady-state envelope state X_ss = (j w I - A)^-1 B u.
virtual void recomputeDiscreteModel(Real omega)
Discretize the augmented model and fold it into complex dA, dB, W.
MatrixComp mW
Complex Norton matrix W = C dB + D.
Definition DP_SSNComp.h:37
const Matrix & getC() const
Get the continuous-time output matrix of the SSN model.
MatrixComp mDiscreteA
Complex envelope discrete operators (folded from the augmented model).
Definition DP_SSNComp.h:33
Matrix buildAugmentedB() const
Build B_aug = blkdiag(B, B).
const MatrixComp & getDiscreteB() const
Get the complex discrete input operator of the envelope model.
Base class for all MNA components that are transmitting power.
static void calculateStateSpaceTrapezoidalMatrices(const Matrix &A, const Matrix &B, const Matrix &C, const Real &dt, Matrix &Ad, Matrix &Bd, Matrix &Cd)
Calculate the discretized state space matrices Ad, Bd, Cd using trapezoidal rule.