4#include <Eigen/Eigenvalues>
7#include <dpsim/StateSpaceModalAnalysis.h>
18Matrix parkTransformDQ0(Real theta) {
19 Matrix transform(3, 3);
21 const Real k = std::sqrt(2.0 / 3.0);
22 const Real k0 = 1.0 / std::sqrt(3.0);
24 transform.row(0) << k * std::cos(theta), k * std::cos(theta - 2.0 * PI / 3.0),
25 k * std::cos(theta + 2.0 * PI / 3.0);
27 transform.row(1) << -k * std::sin(theta),
28 -k * std::sin(theta - 2.0 * PI / 3.0),
29 -k * std::sin(theta + 2.0 * PI / 3.0);
31 transform.row(2) << k0, k0, k0;
36String fallbackStateName(UInt index) {
return "x" + std::to_string(index); }
40StateSpaceModalAnalysis::StateSpaceModalAnalysis(
42 : mExtractor(extractor) {}
45 if (!mExtractor.isInitialized())
46 throw std::logic_error(
"StateSpaceModalAnalysis requires an initialized "
47 "MNAStateSpaceExtractor.");
49 const Matrix Ad = buildDiscreteStateMatrixInAnalysisFrame();
52 mDiscreteEigenvalues.resize(0);
53 mContinuousEigenvalues.resize(0);
55 mRightEigenvectors.resize(0, 0);
56 mLeftEigenvectors.resize(0, 0);
57 mParticipationFactors.resize(0, 0);
64 if (Ad.rows() != Ad.cols())
65 throw std::logic_error(
66 "StateSpaceModalAnalysis requires a square state matrix.");
68 mStateNames = buildStateNamesInAnalysisFrame();
70 Eigen::EigenSolver<Matrix> eigenSolver(Ad,
true);
72 if (eigenSolver.info() != Eigen::Success)
73 throw std::runtime_error(
74 "StateSpaceModalAnalysis: eigenvalue computation failed.");
76 mDiscreteEigenvalues = eigenSolver.eigenvalues();
78 mContinuousEigenvalues.resize(mDiscreteEigenvalues.rows());
80 for (Eigen::Index idx = 0; idx < mDiscreteEigenvalues.rows(); ++idx)
81 mContinuousEigenvalues(idx) =
82 mapDiscreteToContinuous(mDiscreteEigenvalues(idx));
84 mRightEigenvectors = eigenSolver.eigenvectors();
86 Eigen::FullPivLU<CPS::MatrixComp> eigenvectorLu(mRightEigenvectors);
88 if (!eigenvectorLu.isInvertible())
89 throw std::runtime_error(
90 "StateSpaceModalAnalysis: cannot compute participation factors because "
91 "the eigenvector matrix is singular.");
93 mLeftEigenvectors = eigenvectorLu.inverse();
95 mParticipationFactors.resize(mRightEigenvectors.rows(),
96 mRightEigenvectors.cols());
98 for (Eigen::Index mode = 0; mode < mRightEigenvectors.cols(); ++mode) {
99 for (Eigen::Index state = 0; state < mRightEigenvectors.rows(); ++state) {
100 mParticipationFactors(state, mode) =
101 mRightEigenvectors(state, mode) * mLeftEigenvectors(mode, state);
107StateSpaceModalAnalysis::buildDiscreteStateMatrixInAnalysisFrame()
const {
108 const Matrix &nativeAd = mExtractor.getDiscreteStateMatrix();
110 if (mAnalysisFrame == StateSpaceAnalysisFrame::Native)
113 if (mAnalysisFrame == StateSpaceAnalysisFrame::GlobalDQ0) {
114 if (!mExtractor.hasExtractionTime()) {
115 throw std::logic_error(
116 "GlobalDQ0 modal analysis requires a valid extraction timestamp.");
119 if (mGlobalOmega <= 0.0) {
120 throw std::logic_error(
121 "GlobalDQ0 modal analysis requires a positive frame angular speed.");
124 const Real time = mExtractor.getLastExtractionTime();
125 const Real timeStep = mExtractor.getTimeStep();
127 const Real thetaNow = mGlobalTheta0 + mGlobalOmega * time;
128 const Real thetaNext = thetaNow + mGlobalOmega * timeStep;
130 const Matrix transformNow = buildGlobalDq0Transformation(thetaNow);
131 const Matrix transformNext = buildGlobalDq0Transformation(thetaNext);
138 return transformNext * nativeAd * transformNow.transpose();
141 throw std::logic_error(
"Unsupported state-space analysis frame.");
144Matrix StateSpaceModalAnalysis::buildGlobalDq0Transformation(Real theta)
const {
145 const UInt stateCount = mExtractor.getStateCount();
147 Matrix transform = Matrix::Identity(stateCount, stateCount);
149 const Matrix park = parkTransformDQ0(theta);
151 for (
const auto &abcBlock : mExtractor.getMetadata().abcStateBlocks) {
152 for (UInt row = 0; row < 3; ++row) {
153 for (UInt col = 0; col < 3; ++col) {
154 transform(abcBlock.indices[row], abcBlock.indices[col]) =
164StateSpaceModalAnalysis::buildStateNamesInAnalysisFrame()
const {
165 const UInt stateCount = mExtractor.getStateCount();
166 const auto &metadata = mExtractor.getMetadata();
168 std::vector<String> stateNames(stateCount);
170 for (UInt idx = 0; idx < stateCount; ++idx) {
171 if (idx < metadata.stateNames.size() && !metadata.stateNames[idx].empty())
172 stateNames[idx] = metadata.stateNames[idx];
174 stateNames[idx] = fallbackStateName(idx);
177 if (mAnalysisFrame == StateSpaceAnalysisFrame::Native)
180 if (mAnalysisFrame == StateSpaceAnalysisFrame::GlobalDQ0) {
181 for (
const auto &abcBlock : metadata.abcStateBlocks) {
182 if (abcBlock.name.empty())
183 throw std::logic_error(
184 "GlobalDQ0 modal analysis requires named abc state blocks.");
186 stateNames[abcBlock.indices[0]] = abcBlock.name +
"_d";
187 stateNames[abcBlock.indices[1]] = abcBlock.name +
"_q";
188 stateNames[abcBlock.indices[2]] = abcBlock.name +
"_0";
194 throw std::logic_error(
"Unsupported state-space analysis frame.");
198StateSpaceModalAnalysis::mapDiscreteToContinuous(
const CPS::Complex &z)
const {
199 const CPS::Complex one(1.0, 0.0);
200 const CPS::Complex denominator = z + one;
202 if (std::abs(denominator) <= DOUBLE_EPSILON)
203 return CPS::Complex(std::numeric_limits<Real>::infinity(), 0.0);
205 return (2.0 / mExtractor.getTimeStep()) * (z - one) / denominator;
void update()
Update modal quantities from the current extracted state matrix.