4#include <Eigen/Eigenvalues>
22 const Real k = std::sqrt(2.0 / 3.0);
23 const Real k0 = 1.0 / std::sqrt(3.0);
25 transform.row(0) << k * std::cos(theta), k * std::cos(theta - 2.0 *
PI / 3.0),
26 k * std::cos(theta + 2.0 *
PI / 3.0);
28 transform.row(1) << -k * std::sin(theta),
29 -k * std::sin(theta - 2.0 *
PI / 3.0),
30 -k * std::sin(theta + 2.0 *
PI / 3.0);
32 transform.row(2) << k0, k0, k0;
37String fallbackStateName(
UInt index) {
return "x" + std::to_string(index); }
43 : mExtractor(extractor) {}
46 if (!mExtractor.isInitialized())
47 throw std::logic_error(
"StateSpaceModalAnalysis requires an initialized "
48 "MNAStateSpaceExtractor.");
50 const Matrix Ad = buildDiscreteStateMatrixInAnalysisFrame();
53 mDiscreteEigenvalues.resize(0);
54 mContinuousEigenvalues.resize(0);
56 mRightEigenvectors.resize(0, 0);
57 mLeftEigenvectors.resize(0, 0);
58 mParticipationFactors.resize(0, 0);
65 if (Ad.rows() != Ad.cols())
66 throw std::logic_error(
67 "StateSpaceModalAnalysis requires a square state matrix.");
69 mStateNames = buildStateNamesInAnalysisFrame();
71 Eigen::EigenSolver<Matrix> eigenSolver(Ad,
true);
73 if (eigenSolver.info() != Eigen::Success)
74 throw std::runtime_error(
75 "StateSpaceModalAnalysis: eigenvalue computation failed.");
77 mDiscreteEigenvalues = eigenSolver.eigenvalues();
79 mContinuousEigenvalues.resize(mDiscreteEigenvalues.rows());
81 for (Eigen::Index idx = 0; idx < mDiscreteEigenvalues.rows(); ++idx)
82 mContinuousEigenvalues(idx) =
83 mapDiscreteToContinuous(mDiscreteEigenvalues(idx));
85 mRightEigenvectors = eigenSolver.eigenvectors();
87 Eigen::FullPivLU<CPS::MatrixComp> eigenvectorLu(mRightEigenvectors);
89 if (!eigenvectorLu.isInvertible())
90 throw std::runtime_error(
91 "StateSpaceModalAnalysis: cannot compute participation factors because "
92 "the eigenvector matrix is singular.");
94 mLeftEigenvectors = eigenvectorLu.inverse();
97 mRightEigenvectors, mLeftEigenvectors.transpose());
101StateSpaceModalAnalysis::buildDiscreteStateMatrixInAnalysisFrame()
const {
109 throw std::logic_error(
110 "GlobalDQ0 modal analysis requires a valid extraction timestamp.");
113 if (mGlobalOmega <= 0.0) {
114 throw std::logic_error(
115 "GlobalDQ0 modal analysis requires a positive frame angular speed.");
118 const Real time = mExtractor.getLastExtractionTime();
119 const Real timeStep = mExtractor.getTimeStep();
121 const Real thetaNow = mGlobalTheta0 + mGlobalOmega * time;
122 const Real thetaNext = thetaNow + mGlobalOmega * timeStep;
124 const Matrix transformNow = buildGlobalDq0Transformation(thetaNow);
125 const Matrix transformNext = buildGlobalDq0Transformation(thetaNext);
132 return transformNext * nativeAd * transformNow.transpose();
135 throw std::logic_error(
"Unsupported state-space analysis frame.");
138Matrix StateSpaceModalAnalysis::buildGlobalDq0Transformation(
Real theta)
const {
139 const UInt stateCount = mExtractor.getStateCount();
141 Matrix transform = Matrix::Identity(stateCount, stateCount);
143 const Matrix park = parkTransformDQ0(theta);
145 for (
const auto &abcBlock : mExtractor.getMetadata().abcStateBlocks) {
146 for (
UInt row = 0; row < 3; ++row) {
147 for (
UInt col = 0; col < 3; ++col) {
148 transform(abcBlock.indices[row], abcBlock.indices[col]) =
158StateSpaceModalAnalysis::buildStateNamesInAnalysisFrame()
const {
159 const UInt stateCount = mExtractor.getStateCount();
160 const auto &metadata = mExtractor.getMetadata();
162 std::vector<String> stateNames(stateCount);
164 for (
UInt idx = 0; idx < stateCount; ++idx) {
165 if (idx < metadata.stateNames.size() && !metadata.stateNames[idx].empty())
166 stateNames[idx] = metadata.stateNames[idx];
168 stateNames[idx] = fallbackStateName(idx);
175 for (
const auto &abcBlock : metadata.abcStateBlocks) {
176 if (abcBlock.name.empty())
177 throw std::logic_error(
178 "GlobalDQ0 modal analysis requires named abc state blocks.");
180 stateNames[abcBlock.indices[0]] = abcBlock.name +
"_d";
181 stateNames[abcBlock.indices[1]] = abcBlock.name +
"_q";
182 stateNames[abcBlock.indices[2]] = abcBlock.name +
"_0";
188 throw std::logic_error(
"Unsupported state-space analysis frame.");
192StateSpaceModalAnalysis::mapDiscreteToContinuous(
const CPS::Complex &z)
const {
197 return CPS::Complex(std::numeric_limits<Real>::infinity(), 0.0);
199 return (2.0 / mExtractor.getTimeStep()) * (z - one) / denominator;
static auto elementwiseProduct(const Eigen::MatrixBase< DerivedA > &a, const Eigen::MatrixBase< DerivedB > &b)
Elementwise product of two same-shaped Eigen expressions.
StateSpaceModalAnalysis(const MNAStateSpaceExtractor &extractor)
void update()
Update modal quantities from the current extracted state matrix.
std::complex< Real > Complex