DPsim
Loading...
Searching...
No Matches
EMT_Ph3_SSN_GFL.cpp
Go to the documentation of this file.
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 <cmath>
5#include <stdexcept>
6
9
10using namespace CPS;
11
13 : TwoTerminalVTypeVariableSSNComp(uid, name, logLevel), mLf(0.0), mCf(0.0),
14 mRf(0.0), mRc(0.0), mOmegaN(0.0), mKpPLL(0.0), mKiPLL(0.0),
15 mOmegaCutoff(0.0), mPRef(0.0), mQRef(0.0), mKpPowerCtrl(0.0),
16 mKiPowerCtrl(0.0), mKpCurrCtrl(0.0), mKiCurrCtrl(0.0),
17 mVcD(mAttributes->create<Real>("vc_d")),
18 mVcQ(mAttributes->create<Real>("vc_q")),
19 mIrcD(mAttributes->create<Real>("irc_d")),
20 mIrcQ(mAttributes->create<Real>("irc_q")),
21 mPInst(mAttributes->create<Real>("p_inst")),
22 mQInst(mAttributes->create<Real>("q_inst")),
23 mOmegaPLL(mAttributes->create<Real>("omega_pll")) {
24 **mIntfVoltage = Matrix::Zero(mInputSize, 1);
25 **mIntfCurrent = Matrix::Zero(mOutputSize, 1);
26
27 **mVcD = 0.0;
28 **mVcQ = 0.0;
29 **mIrcD = 0.0;
30 **mIrcQ = 0.0;
31 **mPInst = 0.0;
32 **mQInst = 0.0;
33 **mOmegaPLL = 0.0;
34}
35
36std::vector<String> EMT::Ph3::SSN_GFL::getLocalStateNames() const {
37 return {
38 "theta_pll", "phi_pll", "p_filtered", "q_filtered", "phi_d",
39 "phi_q", "gamma_d", "gamma_q", "vc_a", "vc_b",
40 "vc_c", "if_a", "if_b", "if_c",
41 };
42}
43
44std::vector<EMT::SSNComp::LocalAbcStateBlock>
46 return {
47 {{static_cast<UInt>(VcA), static_cast<UInt>(VcB), static_cast<UInt>(VcC)},
48 "vc"},
49 {{static_cast<UInt>(IfA), static_cast<UInt>(IfB), static_cast<UInt>(IfC)},
50 "if"},
51 };
52}
53
55 Real omegaN, Real kpPLL, Real kiPLL,
56 Real omegaCutoff, Real pRef, Real qRef,
57 Real kpPowerCtrl, Real kiPowerCtrl,
58 Real kpCurrCtrl, Real kiCurrCtrl) {
59 if (!Math::isFinite(lf) || !Math::isFinite(cf) || !Math::isFinite(rf) ||
60 !Math::isFinite(rc) || !Math::isFinite(omegaN) ||
61 !Math::isFinite(kpPLL) || !Math::isFinite(kiPLL) ||
62 !Math::isFinite(omegaCutoff) || !Math::isFinite(pRef) ||
63 !Math::isFinite(qRef) || !Math::isFinite(kpPowerCtrl) ||
64 !Math::isFinite(kiPowerCtrl) || !Math::isFinite(kpCurrCtrl) ||
65 !Math::isFinite(kiCurrCtrl))
66 throw std::invalid_argument("SSN_GFL parameters must be finite.");
67
68 if (lf <= 0.0)
69 throw std::invalid_argument("Filter inductance lf must be positive.");
70
71 if (cf <= 0.0)
72 throw std::invalid_argument("Filter capacitance cf must be positive.");
73
74 if (rf < 0.0)
75 throw std::invalid_argument("Filter resistance rf must be non-negative.");
76
77 if (rc <= 0.0)
78 throw std::invalid_argument("Coupling resistance rc must be positive.");
79
80 if (omegaN <= 0.0)
81 throw std::invalid_argument(
82 "Nominal angular frequency omegaN must be positive.");
83
84 if (omegaCutoff < 0.0)
85 throw std::invalid_argument(
86 "Power-filter cutoff frequency omegaCutoff must be non-negative.");
87
88 if (kiPLL == 0.0)
89 throw std::invalid_argument("PLL integral gain kiPLL must be non-zero.");
90
91 if (kiPowerCtrl == 0.0)
92 throw std::invalid_argument(
93 "Power-control integral gain kiPowerCtrl must be non-zero.");
94
95 if (kiCurrCtrl == 0.0)
96 throw std::invalid_argument(
97 "Current-control integral gain kiCurrCtrl must be non-zero.");
98
99 mLf = lf;
100 mCf = cf;
101 mRf = rf;
102 mRc = rc;
103
104 mOmegaN = omegaN;
105 mKpPLL = kpPLL;
106 mKiPLL = kiPLL;
107
108 mOmegaCutoff = omegaCutoff;
109 mPRef = pRef;
110 mQRef = qRef;
111 mKpPowerCtrl = kpPowerCtrl;
112 mKiPowerCtrl = kiPowerCtrl;
113 mKpCurrCtrl = kpCurrCtrl;
114 mKiCurrCtrl = kiCurrCtrl;
115
116 const Matrix x0 = Matrix::Zero(mStateSize, 1);
117 const Matrix u0 = Matrix::Zero(mInputSize, 1);
118
119 Matrix aMatrix;
120 Matrix bMatrix;
121 Matrix cMatrix;
122 Matrix dMatrix;
123 Matrix eVector;
124 Matrix fVector;
125
126 buildStateSpaceModel(x0, u0, aMatrix, bMatrix, cMatrix, dMatrix, eVector,
127 fVector);
128
129 VTypeVariableSSNComp::setParameters(aMatrix, bMatrix, cMatrix, dMatrix,
130 eVector, fVector);
131}
132
133Matrix EMT::Ph3::SSN_GFL::getParkTransformMatrix(Real theta) const {
134 Matrix transform(2, 3);
135 const Real scale = std::sqrt(2.0 / 3.0);
136
137 transform.row(0) << scale * std::cos(theta),
138 scale * std::cos(theta - 2.0 * PI / 3.0),
139 scale * std::cos(theta + 2.0 * PI / 3.0);
140
141 transform.row(1) << -scale * std::sin(theta),
142 -scale * std::sin(theta - 2.0 * PI / 3.0),
143 -scale * std::sin(theta + 2.0 * PI / 3.0);
144
145 return transform;
146}
147
148Matrix EMT::Ph3::SSN_GFL::getInverseParkTransformMatrix(Real theta) const {
149 Matrix transform(3, 2);
150 const Real scale = std::sqrt(2.0 / 3.0);
151
152 transform << scale * std::cos(theta), -scale * std::sin(theta),
153 scale * std::cos(theta - 2.0 * PI / 3.0),
154 -scale * std::sin(theta - 2.0 * PI / 3.0),
155 scale * std::cos(theta + 2.0 * PI / 3.0),
156 -scale * std::sin(theta + 2.0 * PI / 3.0);
157
158 return transform;
159}
160
161void EMT::Ph3::SSN_GFL::evaluateStateDerivative(const Matrix &x,
162 const Matrix &u,
163 Matrix &stateDerivative) const {
164 if (x.rows() != mStateSize || x.cols() != 1)
165 throw std::invalid_argument(
166 "SSN_GFL state vector has an invalid dimension.");
167
168 if (u.rows() != mInputSize || u.cols() != 1)
169 throw std::invalid_argument(
170 "SSN_GFL input vector has an invalid dimension.");
171
172 stateDerivative.setZero(mStateSize, 1);
173
174 const Real thetaPLL = x(ThetaPLL, 0);
175 const Real phiPLL = x(PhiPLL, 0);
176 const Real pFiltered = x(PFiltered, 0);
177 const Real qFiltered = x(QFiltered, 0);
178 const Real phiD = x(PhiD, 0);
179 const Real phiQ = x(PhiQ, 0);
180 const Real gammaD = x(GammaD, 0);
181 const Real gammaQ = x(GammaQ, 0);
182
183 const Matrix vcAbc = x.block(VcA, 0, 3, 1);
184 const Matrix ifAbc = x.block(IfA, 0, 3, 1);
185
186 const Matrix parkTransform = getParkTransformMatrix(thetaPLL);
187 const Matrix inverseParkTransform = getInverseParkTransformMatrix(thetaPLL);
188
189 // Positive current is physical inverter injection into the grid.
190 const Matrix iGridAbc = (vcAbc - u) / mRc;
191
192 const Matrix vcDq = parkTransform * vcAbc;
193 const Matrix iGridDq = parkTransform * iGridAbc;
194
195 const Real vcD = vcDq(0, 0);
196 const Real vcQ = vcDq(1, 0);
197 const Real iGridD = iGridDq(0, 0);
198 const Real iGridQ = iGridDq(1, 0);
199
200 // The power-invariant Park transformation requires no additional 3/2
201 // scaling. These equations intentionally match the reference GFL model.
202 const Real pInstantaneous = vcD * iGridD + vcQ * iGridQ;
203 const Real qInstantaneous = -vcD * iGridQ + vcQ * iGridD;
204
205 // ----------------------------------------------------------------------
206 // 1. PLL
207 // ----------------------------------------------------------------------
208 stateDerivative(ThetaPLL, 0) = mOmegaN + mKpPLL * vcQ + mKiPLL * phiPLL;
209 stateDerivative(PhiPLL, 0) = vcQ;
210
211 // ----------------------------------------------------------------------
212 // 2. Active- and reactive-power measurement filters
213 // ----------------------------------------------------------------------
214 stateDerivative(PFiltered, 0) = mOmegaCutoff * (pInstantaneous - pFiltered);
215 stateDerivative(QFiltered, 0) = mOmegaCutoff * (qInstantaneous - qFiltered);
216
217 // ----------------------------------------------------------------------
218 // 3. Outer power controller
219 // ----------------------------------------------------------------------
220 stateDerivative(PhiD, 0) = mPRef - pFiltered;
221 stateDerivative(PhiQ, 0) = qFiltered - mQRef;
222
223 const Real currentReferenceD =
224 mKpPowerCtrl * (mPRef - pFiltered) + mKiPowerCtrl * phiD;
225 const Real currentReferenceQ =
226 mKpPowerCtrl * (qFiltered - mQRef) + mKiPowerCtrl * phiQ;
227
228 // ----------------------------------------------------------------------
229 // 4. Inner current controller
230 // ----------------------------------------------------------------------
231 const Real currentErrorD = currentReferenceD - iGridD;
232 const Real currentErrorQ = currentReferenceQ - iGridQ;
233
234 stateDerivative(GammaD, 0) = currentErrorD;
235 stateDerivative(GammaQ, 0) = currentErrorQ;
236
237 const Real converterVoltageReferenceD =
238 mKpCurrCtrl * currentErrorD + mKiCurrCtrl * gammaD;
239 const Real converterVoltageReferenceQ =
240 mKpCurrCtrl * currentErrorQ + mKiCurrCtrl * gammaQ;
241
242 Matrix converterVoltageReferenceDq(2, 1);
243 converterVoltageReferenceDq << converterVoltageReferenceD,
244 converterVoltageReferenceQ;
245
246 const Matrix converterVoltageReferenceAbc =
247 inverseParkTransform * converterVoltageReferenceDq;
248
249 // ----------------------------------------------------------------------
250 // 5. Electrical filter plant
251 // ----------------------------------------------------------------------
252 const Matrix vcDerivative = ifAbc / mCf + (u - vcAbc) / (mCf * mRc);
253 const Matrix ifDerivative =
254 (converterVoltageReferenceAbc - vcAbc - mRf * ifAbc) / mLf;
255
256 stateDerivative.block(VcA, 0, 3, 1) = vcDerivative;
257 stateDerivative.block(IfA, 0, 3, 1) = ifDerivative;
258}
259
260void EMT::Ph3::SSN_GFL::evaluateOutput(const Matrix &x, const Matrix &u,
261 Matrix &output) const {
262 if (x.rows() != mStateSize || x.cols() != 1)
263 throw std::invalid_argument(
264 "SSN_GFL state vector has an invalid dimension.");
265
266 if (u.rows() != mInputSize || u.cols() != 1)
267 throw std::invalid_argument(
268 "SSN_GFL input vector has an invalid dimension.");
269
270 const Matrix vcAbc = x.block(VcA, 0, 3, 1);
271
272 // SSN current entering the component. The controller uses the opposite
273 // physical current direction as inverter injection into the grid.
274 output = (u - vcAbc) / mRc;
275}
276
277void EMT::Ph3::SSN_GFL::calculateAnalyticalJacobians(const Matrix &x,
278 const Matrix &u, Matrix &A,
279 Matrix &B, Matrix &C,
280 Matrix &D) const {
281 if (x.rows() != mStateSize || x.cols() != 1)
282 throw std::invalid_argument(
283 "SSN_GFL state vector has an invalid dimension.");
284
285 if (u.rows() != mInputSize || u.cols() != 1)
286 throw std::invalid_argument(
287 "SSN_GFL input vector has an invalid dimension.");
288
289 const Real thetaPLL = x(ThetaPLL, 0);
290 const Real pFiltered = x(PFiltered, 0);
291 const Real qFiltered = x(QFiltered, 0);
292 const Real phiD = x(PhiD, 0);
293 const Real phiQ = x(PhiQ, 0);
294 const Real gammaD = x(GammaD, 0);
295 const Real gammaQ = x(GammaQ, 0);
296 const Matrix vcAbc = x.block(VcA, 0, 3, 1);
297
298 const Matrix identity3 = Matrix::Identity(3, 3);
299
300 const Matrix parkTransform = getParkTransformMatrix(thetaPLL);
301 const Matrix tD = parkTransform.row(0);
302 const Matrix tQ = parkTransform.row(1);
303
304 const Matrix inverseParkTransform = getInverseParkTransformMatrix(thetaPLL);
305 const Matrix sD = inverseParkTransform.col(0);
306 const Matrix sQ = inverseParkTransform.col(1);
307
308 // Exact derivatives of the orthonormal Park transformation used by the
309 // reference model.
310 const Matrix dTdTheta = tQ;
311 const Matrix dTqTheta = -tD;
312 const Matrix dSdTheta = sQ;
313 const Matrix dSqTheta = -sD;
314
315 const Matrix iGridAbc = (vcAbc - u) / mRc;
316
317 const Real vcD = (tD * vcAbc)(0, 0);
318 const Real vcQ = (tQ * vcAbc)(0, 0);
319 const Real iGridD = (tD * iGridAbc)(0, 0);
320 const Real iGridQ = (tQ * iGridAbc)(0, 0);
321
322 const Matrix dVcDByVc = tD;
323 const Matrix dVcQByVc = tQ;
324 const Matrix dIGridDByVc = tD / mRc;
325 const Matrix dIGridQByVc = tQ / mRc;
326 const Matrix dIGridDByU = -tD / mRc;
327 const Matrix dIGridQByU = -tQ / mRc;
328
329 const Real dVcDByTheta = (dTdTheta * vcAbc)(0, 0);
330 const Real dVcQByTheta = (dTqTheta * vcAbc)(0, 0);
331 const Real dIGridDByTheta = (dTdTheta * iGridAbc)(0, 0);
332 const Real dIGridQByTheta = (dTqTheta * iGridAbc)(0, 0);
333
334 // Instantaneous active-power Jacobian.
335 const Real dPByTheta = iGridD * dVcDByTheta + vcD * dIGridDByTheta +
336 iGridQ * dVcQByTheta + vcQ * dIGridQByTheta;
337 const Matrix dPByVc = iGridD * dVcDByVc + vcD * dIGridDByVc +
338 iGridQ * dVcQByVc + vcQ * dIGridQByVc;
339 const Matrix dPByU = vcD * dIGridDByU + vcQ * dIGridQByU;
340
341 // Instantaneous reactive-power Jacobian.
342 const Real dQByTheta = -iGridQ * dVcDByTheta - vcD * dIGridQByTheta +
343 iGridD * dVcQByTheta + vcQ * dIGridDByTheta;
344 const Matrix dQByVc = -iGridQ * dVcDByVc - vcD * dIGridQByVc +
345 iGridD * dVcQByVc + vcQ * dIGridDByVc;
346 const Matrix dQByU = -vcD * dIGridQByU + vcQ * dIGridDByU;
347
348 const Real currentReferenceD =
349 -mKpPowerCtrl * pFiltered + mKiPowerCtrl * phiD + mKpPowerCtrl * mPRef;
350 const Real currentReferenceQ =
351 mKpPowerCtrl * qFiltered + mKiPowerCtrl * phiQ - mKpPowerCtrl * mQRef;
352
353 const Real converterVoltageReferenceD = -mKpCurrCtrl * iGridD +
354 mKiCurrCtrl * gammaD +
355 mKpCurrCtrl * currentReferenceD;
356 const Real converterVoltageReferenceQ = -mKpCurrCtrl * iGridQ +
357 mKiCurrCtrl * gammaQ +
358 mKpCurrCtrl * currentReferenceQ;
359
360 const Real dVoltageReferenceDByTheta = -mKpCurrCtrl * dIGridDByTheta;
361 const Matrix dVoltageReferenceDByVc = -mKpCurrCtrl * dIGridDByVc;
362 const Matrix dVoltageReferenceDByU = -mKpCurrCtrl * dIGridDByU;
363
364 const Real dVoltageReferenceQByTheta = -mKpCurrCtrl * dIGridQByTheta;
365 const Matrix dVoltageReferenceQByVc = -mKpCurrCtrl * dIGridQByVc;
366 const Matrix dVoltageReferenceQByU = -mKpCurrCtrl * dIGridQByU;
367
368 Matrix dConverterVoltageAbcByX = Matrix::Zero(3, mStateSize);
369 Matrix dConverterVoltageAbcByU = Matrix::Zero(3, mInputSize);
370
371 dConverterVoltageAbcByX.col(ThetaPLL) =
372 dSdTheta * converterVoltageReferenceD +
373 dSqTheta * converterVoltageReferenceQ + sD * dVoltageReferenceDByTheta +
374 sQ * dVoltageReferenceQByTheta;
375
376 dConverterVoltageAbcByX.col(PFiltered) += sD * (-mKpCurrCtrl * mKpPowerCtrl);
377 dConverterVoltageAbcByX.col(PhiD) += sD * (mKpCurrCtrl * mKiPowerCtrl);
378 dConverterVoltageAbcByX.col(GammaD) += sD * mKiCurrCtrl;
379
380 dConverterVoltageAbcByX.col(QFiltered) += sQ * (mKpCurrCtrl * mKpPowerCtrl);
381 dConverterVoltageAbcByX.col(PhiQ) += sQ * (mKpCurrCtrl * mKiPowerCtrl);
382 dConverterVoltageAbcByX.col(GammaQ) += sQ * mKiCurrCtrl;
383
384 dConverterVoltageAbcByX.block(0, VcA, 3, 3) +=
385 sD * dVoltageReferenceDByVc + sQ * dVoltageReferenceQByVc;
386
387 dConverterVoltageAbcByU =
388 sD * dVoltageReferenceDByU + sQ * dVoltageReferenceQByU;
389
390 A.setZero(mStateSize, mStateSize);
391 B.setZero(mStateSize, mInputSize);
392 C.setZero(mOutputSize, mStateSize);
393 D.setZero(mOutputSize, mInputSize);
394
395 // PLL rows.
396 A(ThetaPLL, ThetaPLL) = mKpPLL * dVcQByTheta;
397 A(ThetaPLL, PhiPLL) = mKiPLL;
398 A.block(ThetaPLL, VcA, 1, 3) = mKpPLL * dVcQByVc;
399
400 A(PhiPLL, ThetaPLL) = dVcQByTheta;
401 A.block(PhiPLL, VcA, 1, 3) = dVcQByVc;
402
403 // Power-filter rows.
404 A(PFiltered, ThetaPLL) = mOmegaCutoff * dPByTheta;
405 A(PFiltered, PFiltered) = -mOmegaCutoff;
406 A.block(PFiltered, VcA, 1, 3) = mOmegaCutoff * dPByVc;
407 B.block(PFiltered, 0, 1, 3) = mOmegaCutoff * dPByU;
408
409 A(QFiltered, ThetaPLL) = mOmegaCutoff * dQByTheta;
410 A(QFiltered, QFiltered) = -mOmegaCutoff;
411 A.block(QFiltered, VcA, 1, 3) = mOmegaCutoff * dQByVc;
412 B.block(QFiltered, 0, 1, 3) = mOmegaCutoff * dQByU;
413
414 // Outer power-loop integrators.
415 A(PhiD, PFiltered) = -1.0;
416 A(PhiQ, QFiltered) = 1.0;
417
418 // Inner current-loop integrators.
419 A(GammaD, PFiltered) = -mKpPowerCtrl;
420 A(GammaD, PhiD) = mKiPowerCtrl;
421 A(GammaD, ThetaPLL) = -dIGridDByTheta;
422 A.block(GammaD, VcA, 1, 3) = -dIGridDByVc;
423 B.block(GammaD, 0, 1, 3) = -dIGridDByU;
424
425 A(GammaQ, QFiltered) = mKpPowerCtrl;
426 A(GammaQ, PhiQ) = mKiPowerCtrl;
427 A(GammaQ, ThetaPLL) = -dIGridQByTheta;
428 A.block(GammaQ, VcA, 1, 3) = -dIGridQByVc;
429 B.block(GammaQ, 0, 1, 3) = -dIGridQByU;
430
431 // Electrical filter plant.
432 A.block(VcA, VcA, 3, 3) = -1.0 / (mCf * mRc) * identity3;
433 A.block(VcA, IfA, 3, 3) = 1.0 / mCf * identity3;
434 B.block(VcA, 0, 3, 3) = 1.0 / (mCf * mRc) * identity3;
435
436 A.block(IfA, 0, 3, mStateSize) = (1.0 / mLf) * dConverterVoltageAbcByX;
437 A.block(IfA, VcA, 3, 3) += -1.0 / mLf * identity3;
438 A.block(IfA, IfA, 3, 3) += -mRf / mLf * identity3;
439 B.block(IfA, 0, 3, 3) = (1.0 / mLf) * dConverterVoltageAbcByU;
440
441 // SSN output y = (u - vc) / Rc.
442 C.block(0, VcA, 3, 3) = -1.0 / mRc * identity3;
443 D = 1.0 / mRc * identity3;
444}
445
446void EMT::Ph3::SSN_GFL::buildStateSpaceModel(const Matrix &x, const Matrix &u,
447 Matrix &A, Matrix &B, Matrix &C,
448 Matrix &D, Matrix &E,
449 Matrix &F) const {
450 calculateAnalyticalJacobians(x, u, A, B, C, D);
451
452 Matrix stateDerivative = Matrix::Zero(mStateSize, 1);
453 Matrix output = Matrix::Zero(mOutputSize, 1);
454
455 evaluateStateDerivative(x, u, stateDerivative);
456 evaluateOutput(x, u, output);
457
458 // Local affine offsets at the operating point.
459 E = stateDerivative - A * x - B * u;
460 F = output - C * x - D * u;
461}
462
464 Matrix eVector;
465 Matrix fVector;
466
467 buildStateSpaceModel(**mX, **mIntfVoltage, mA, mB, mC, mD, eVector, fVector);
468
469 setStateOffset(eVector);
470 setOutputOffset(fVector);
471
472 // The PLL and dq/abc transformations make the local affine state-space
473 // model time varying. The SSN equivalent is therefore rebuilt every step.
474 return true;
475}
476
478 const Matrix &x = **mX;
479
480 const Matrix parkTransform = getParkTransformMatrix(x(ThetaPLL, 0));
481 const Matrix vcAbc = x.block(VcA, 0, 3, 1);
482 const Matrix iGridAbc = (vcAbc - u) / mRc;
483
484 **mVcD = (parkTransform.row(0) * vcAbc)(0, 0);
485 **mVcQ = (parkTransform.row(1) * vcAbc)(0, 0);
486 **mIrcD = (parkTransform.row(0) * iGridAbc)(0, 0);
487 **mIrcQ = (parkTransform.row(1) * iGridAbc)(0, 0);
488
489 **mPInst = **mVcD * **mIrcD + **mVcQ * **mIrcQ;
490 **mQInst = -**mVcD * **mIrcQ + **mVcQ * **mIrcD;
491
492 **mOmegaPLL = mOmegaN + mKpPLL * **mVcQ + mKiPLL * x(PhiPLL, 0);
493}
494
496 if (!mParametersSet)
497 throw std::logic_error("setParameters() must be called before "
498 "initializeFromNodesAndTerminals().");
499
500 // Initialize EMT abc filter states and dq-frame controller states.
501 const Real omega = 2.0 * PI * frequency;
502 const Complex imaginaryUnit(0.0, 1.0);
503 const Complex powerReference(mPRef, mQRef);
504
505 const MatrixComp uPhasor = buildInitialInputFromNodes(frequency);
506
507 MatrixComp vcPhasor = uPhasor;
508 MatrixComp injectionCurrentPhasor = MatrixComp::Zero(3, 1);
509
510 for (Int iteration = 0; iteration < mInitializationMaxIterations;
511 ++iteration) {
512 const Complex vcA = vcPhasor(0, 0);
513
514 if (std::abs(vcA) < mInitializationTolerance) {
515 injectionCurrentPhasor.setZero();
516 break;
517 }
518
519 // Peak-valued phase phasors use S = 1.5*V_peak*conj(I_peak).
520 const Complex currentA = std::conj(powerReference / (1.5 * vcA));
521
522 MatrixComp nextInjectionCurrent(3, 1);
523 nextInjectionCurrent << currentA, currentA * SHIFT_TO_PHASE_B,
524 currentA * SHIFT_TO_PHASE_C;
525
526 const MatrixComp nextVcPhasor = uPhasor + mRc * nextInjectionCurrent;
527
528 injectionCurrentPhasor = nextInjectionCurrent;
529
530 if ((nextVcPhasor - vcPhasor).norm() < mInitializationTolerance) {
531 vcPhasor = nextVcPhasor;
532 break;
533 }
534
535 vcPhasor = nextVcPhasor;
536 }
537
538 const MatrixComp ifPhasor =
539 imaginaryUnit * omega * mCf * vcPhasor + injectionCurrentPhasor;
540 const MatrixComp converterVoltageReferencePhasor =
541 vcPhasor + (mRf + imaginaryUnit * omega * mLf) * ifPhasor;
542
543 const Matrix vcAbc0 = vcPhasor.real();
544 const Matrix ifAbc0 = ifPhasor.real();
545 const Matrix injectionCurrentAbc0 = injectionCurrentPhasor.real();
546 const Matrix converterVoltageReferenceAbc0 =
547 converterVoltageReferencePhasor.real();
548
549 const Real theta0 = std::arg(vcPhasor(0, 0));
550 const Matrix parkTransform = getParkTransformMatrix(theta0);
551
552 const Matrix vcDq0 = parkTransform * vcAbc0;
553 const Matrix injectionCurrentDq0 = parkTransform * injectionCurrentAbc0;
554 const Matrix converterVoltageReferenceDq0 =
555 parkTransform * converterVoltageReferenceAbc0;
556
557 const Real vcD0 = vcDq0(0, 0);
558 const Real vcQ0 = vcDq0(1, 0);
559 const Real iGridD0 = injectionCurrentDq0(0, 0);
560 const Real iGridQ0 = injectionCurrentDq0(1, 0);
561
562 const Real pInitial = vcD0 * iGridD0 + vcQ0 * iGridQ0;
563 const Real qInitial = -vcD0 * iGridQ0 + vcQ0 * iGridD0;
564
565 Matrix x0 = Matrix::Zero(mStateSize, 1);
566
567 x0(ThetaPLL, 0) = theta0;
568 x0(PhiPLL, 0) = (omega - mOmegaN) / mKiPLL;
569 x0(PFiltered, 0) = pInitial;
570 x0(QFiltered, 0) = qInitial;
571
572 x0(PhiD, 0) = (iGridD0 + mKpPowerCtrl * (pInitial - mPRef)) / mKiPowerCtrl;
573 x0(PhiQ, 0) = (iGridQ0 - mKpPowerCtrl * (qInitial - mQRef)) / mKiPowerCtrl;
574
575 const Real currentReferenceD = -mKpPowerCtrl * pInitial +
576 mKiPowerCtrl * x0(PhiD, 0) +
577 mKpPowerCtrl * mPRef;
578 const Real currentReferenceQ = mKpPowerCtrl * qInitial +
579 mKiPowerCtrl * x0(PhiQ, 0) -
580 mKpPowerCtrl * mQRef;
581
582 x0(GammaD, 0) = (converterVoltageReferenceDq0(0, 0) +
583 mKpCurrCtrl * (iGridD0 - currentReferenceD)) /
584 mKiCurrCtrl;
585 x0(GammaQ, 0) = (converterVoltageReferenceDq0(1, 0) +
586 mKpCurrCtrl * (iGridQ0 - currentReferenceQ)) /
587 mKiCurrCtrl;
588
589 x0.block(VcA, 0, 3, 1) = vcAbc0;
590 x0.block(IfA, 0, 3, 1) = ifAbc0;
591
592 **mX = x0;
593 **mIntfVoltage = uPhasor.real();
594 **mIntfCurrent = ((uPhasor - vcPhasor) / mRc).real();
595
596 // Update local continuous-time matrices at the initialized operating point.
599
600 SPDLOG_LOGGER_INFO(mSLog,
601 "\n--- SSN GFL phasor/dq initialization ---"
602 "\nInput u: {:s}"
603 "\nOutput y: {:s}"
604 "\nState x: {:s}"
605 "\nP/Q init: [{:.6e}, {:.6e}]"
606 "\nVc dq: [{:.6e}, {:.6e}]"
607 "\nIgrid dq: [{:.6e}, {:.6e}]"
608 "\n--- SSN GFL initialization finished ---",
611 Logger::matrixToString(**mX), pInitial, qInitial, vcD0,
612 vcQ0, iGridD0, iGridQ0);
613}
614
616
618 Matrix stateDerivative = Matrix::Zero(mStateSize, 1);
619 evaluateStateDerivative(**mX, **mIntfVoltage, stateDerivative);
620 return stateDerivative;
621}
622
624
Matrix getInterfaceVoltage() const
std::vector< String > getLocalStateNames() const override final
Bool updateComponentParameters() override final
Matrix getInterfaceCurrent() const
std::vector< SSNComp::LocalAbcStateBlock > getLocalAbcStateBlocks() const override final
void updateLogAttributes(const Matrix &u) const override final
Update derived attributes used for logging/inspection.
void setParameters(Real lf, Real cf, Real rf, Real rc, Real omegaN, Real kpPLL, Real kiPLL, Real omegaCutoff, Real pRef, Real qRef, Real kpPowerCtrl, Real kiPowerCtrl, Real kpCurrCtrl, Real kiCurrCtrl)
Configure the GFL filter, PLL, power loop and current loop.
SSN_GFL(String uid, String name, Logger::Level logLevel=Logger::Level::off)
void initializeFromNodesAndTerminals(Real frequency) override final
Initializes Component variables according to power flow data stored in Nodes.
Matrix getStateDerivative() const
TwoTerminalVTypeVariableSSNComp(String uid, String name, Logger::Level logLevel=Logger::Level::off)
const Attribute< Matrix >::Ptr mX
Definition EMT_SSNComp.h:43
void setParameters(const Matrix &A, const Matrix &B, const Matrix &C, const Matrix &D)
static constexpr Real mInitializationTolerance
static constexpr Int mInitializationMaxIterations
String uid()
Returns unique id.
AttributeList::Ptr mAttributes
Attribute List.
spdlog::level::level_enum Level
Definition Logger.h:33
static String matrixToString(const Matrix &mat)
Definition Logger.cpp:31
static bool isFinite(Real value)
Definition MathUtils.cpp:63
const Attribute< MatrixVar< Real > >::Ptr mIntfCurrent
const Attribute< MatrixVar< Real > >::Ptr mIntfVoltage
bool mParametersSet
Flag indicating that parameters are set via setParameters() function.
Logger::Log mSLog
Component logger.
#define PI
Definition Definitions.h:43
#define SHIFT_TO_PHASE_C
Definition Definitions.h:47
#define SHIFT_TO_PHASE_B
Definition Definitions.h:46
Eigen::Matrix< Real, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor > Matrix
Dense matrix for real numbers.
Definition Definitions.h:81
std::string String
Definition Definitions.h:65
double Real
Definition Definitions.h:62
int Int
Definition Definitions.h:61
std::complex< Real > Complex
Definition Definitions.h:63
Eigen::Matrix< Complex, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor > MatrixComp
Dense matrix for complex numbers.
Definition Definitions.h:84
bool Bool
Definition Definitions.h:64
unsigned int UInt
Definition Definitions.h:60