DPsim
Loading...
Searching...
No Matches
EMT_Ph3_SSN_GFM.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 <algorithm>
5#include <cmath>
6#include <stdexcept>
7
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), mNominalVoltage(0.0), mPRef(0.0),
15 mQRef(0.0), mVirtualInertia(0.0), mDampingCoefficient(0.0),
16 mVoltageDroopGain(0.0), mReactiveIntegralGain(0.0), mKpVoltage(0.0),
17 mKiVoltage(0.0), mKpCurrent(0.0), mKiCurrent(0.0),
18 mActiveDampingGain(0.0), mPowerFilterCutoff(0.0), mDelayBandwidth(0.0),
19 mVirtualResistance(0.0), mVirtualReactance(0.0),
20 mGridCurrentFeedforward(1.0), mReactivePowerDroop(0.0),
21 mReactiveDroopCutoff(0.0), mVoltageSetpoint(0.0),
22 mJacobianRelativeStep(1e-6), mJacobianAbsoluteStep(1e-8),
23 mPInst(mAttributes->create<Real>("p_inst")),
24 mQInst(mAttributes->create<Real>("q_inst")),
25 mOmegaGFM(mAttributes->create<Real>("omega_gfm")),
26 mThetaGFM(mAttributes->create<Real>("theta_gfm")),
27 mVoltageMagnitudeGFM(mAttributes->create<Real>("voltage_magnitude_gfm")),
28 mVcD(mAttributes->create<Real>("vc_d")),
29 mVcQ(mAttributes->create<Real>("vc_q")),
30 mIGridD(mAttributes->create<Real>("i_grid_d")),
31 mIGridQ(mAttributes->create<Real>("i_grid_q")),
32 mIfD(mAttributes->create<Real>("if_d")),
33 mIfQ(mAttributes->create<Real>("if_q")),
34 mVoltageReferenceD(mAttributes->create<Real>("v_ref_d")),
35 mVoltageReferenceQ(mAttributes->create<Real>("v_ref_q")) {
36
37 **mIntfVoltage = Matrix::Zero(3, 1);
38 **mIntfCurrent = Matrix::Zero(3, 1);
39
40 **mPInst = 0.0;
41 **mQInst = 0.0;
42 **mOmegaGFM = 0.0;
43 **mThetaGFM = 0.0;
44 **mVoltageMagnitudeGFM = 0.0;
45
46 **mVcD = 0.0;
47 **mVcQ = 0.0;
48
49 **mIGridD = 0.0;
50 **mIGridQ = 0.0;
51
52 **mIfD = 0.0;
53 **mIfQ = 0.0;
54
55 **mVoltageReferenceD = 0.0;
56 **mVoltageReferenceQ = 0.0;
57}
58std::vector<String> EMT::Ph3::SSN_GFM::getLocalStateNames() const {
59 return {
60 "p_filtered",
61 "q_filtered",
62 "omega",
63 "theta",
64 "voltage_magnitude",
65
66 "voltage_integrator_d",
67 "voltage_integrator_q",
68
69 "current_integrator_d",
70 "current_integrator_q",
71
72 "delay_voltage_d",
73 "delay_voltage_q",
74
75 "vc_a",
76 "vc_b",
77 "vc_c",
78
79 "if_a",
80 "if_b",
81 "if_c",
82 };
83}
84
85std::vector<EMT::SSNComp::LocalAbcStateBlock>
87 return {
88 {{static_cast<Int>(VcA), static_cast<Int>(VcB), static_cast<Int>(VcC)},
89 "vc"},
90
91 {{static_cast<Int>(IfA), static_cast<Int>(IfB), static_cast<Int>(IfC)},
92 "if"},
93 };
94}
95
97 Real lf, Real cf, Real rf, Real rc, Real nominalVoltage, Real omegaN,
98 Real pRef, Real qRef, Real virtualInertia, Real dampingCoefficient,
99 Real voltageDroopGain, Real reactiveIntegralGain, Real kpVoltage,
100 Real kiVoltage, Real kpCurrent, Real kiCurrent, Real activeDampingGain,
101 Real powerFilterCutoff, Real delayBandwidth) {
102
103 if (lf <= 0.0)
104 throw std::invalid_argument("Filter inductance lf must be positive.");
105
106 if (cf <= 0.0)
107 throw std::invalid_argument("Filter capacitance cf must be positive.");
108
109 if (rf < 0.0)
110 throw std::invalid_argument("Filter resistance rf must be non-negative.");
111
112 if (rc <= 0.0)
113 throw std::invalid_argument("Coupling resistance rc must be positive.");
114
115 if (nominalVoltage <= 0.0)
116 throw std::invalid_argument("Nominal voltage must be positive.");
117
118 if (omegaN <= 0.0)
119 throw std::invalid_argument(
120 "Nominal angular frequency omegaN must be positive.");
121
122 if (virtualInertia <= 0.0)
123 throw std::invalid_argument("Virtual inertia must be positive.");
124
125 if (dampingCoefficient < 0.0)
126 throw std::invalid_argument("Damping coefficient must be non-negative.");
127
128 if (kiVoltage == 0.0)
129 throw std::invalid_argument(
130 "Voltage-controller integral gain must be non-zero.");
131
132 if (kiCurrent == 0.0)
133 throw std::invalid_argument(
134 "Current-controller integral gain must be non-zero.");
135
136 if (powerFilterCutoff < 0.0)
137 throw std::invalid_argument(
138 "Power-filter cutoff frequency must be non-negative.");
139
140 if (delayBandwidth <= 0.0)
141 throw std::invalid_argument("Delay bandwidth must be positive.");
142
143 mLf = lf;
144 mCf = cf;
145 mRf = rf;
146 mRc = rc;
147
148 mNominalVoltage = nominalVoltage;
149 mOmegaN = omegaN;
150
151 mPRef = pRef;
152 mQRef = qRef;
153
154 mVirtualInertia = virtualInertia;
155 mDampingCoefficient = dampingCoefficient;
156 mVoltageDroopGain = voltageDroopGain;
157 mReactiveIntegralGain = reactiveIntegralGain;
158
159 mKpVoltage = kpVoltage;
160 mKiVoltage = kiVoltage;
161
162 mKpCurrent = kpCurrent;
163 mKiCurrent = kiCurrent;
164
165 mActiveDampingGain = activeDampingGain;
166 mPowerFilterCutoff = powerFilterCutoff;
167 mDelayBandwidth = delayBandwidth;
168
169 // Only establish the dimensions here. Linearizing at x = 0, u = 0 is
170 // invalid for this model because omega, voltage magnitude and the Park
171 // transformation do not represent a physical operating point there.
172 // The first actual local model is created after operating-point
173 // initialization in initializeFromNodesAndTerminals().
174 VTypeVariableSSNComp::setParameters(Matrix::Zero(mStateSize, mStateSize),
175 Matrix::Zero(mStateSize, mInputSize),
176 Matrix::Zero(mOutputSize, mStateSize),
177 Matrix::Zero(mOutputSize, mInputSize),
178 Matrix::Zero(mStateSize, 1),
179 Matrix::Zero(mOutputSize, 1));
180}
181
183 Real absoluteStep) {
184
185 if (relativeStep <= 0.0)
186 throw std::invalid_argument(
187 "Relative finite-difference step must be positive.");
188
189 if (absoluteStep <= 0.0)
190 throw std::invalid_argument(
191 "Absolute finite-difference step must be positive.");
192
193 mJacobianRelativeStep = relativeStep;
194 mJacobianAbsoluteStep = absoluteStep;
195}
196
198 Real virtualReactance) {
199 if (virtualResistance < 0.0)
200 throw std::invalid_argument("Virtual resistance must be non-negative.");
201
202 mVirtualResistance = virtualResistance;
203 mVirtualReactance = virtualReactance;
204}
205
207 mGridCurrentFeedforward = scale;
208}
209
211 if (droopGain < 0.0)
212 throw std::invalid_argument("Reactive-droop gain must be non-negative.");
213 if (cutoff < 0.0)
214 throw std::invalid_argument("Reactive-droop cutoff must be non-negative.");
215
216 mReactivePowerDroop = droopGain;
217 mReactiveDroopCutoff = cutoff;
218}
219
220Matrix EMT::Ph3::SSN_GFM::getParkTransformMatrix(Real theta) const {
221
222 theta = std::remainder(theta, 2.0 * PI);
223 Matrix transform(2, 3);
224
225 constexpr Real scale = 2.0 / 3.0;
226
227 transform.row(0) << scale * std::cos(theta),
228 scale * std::cos(theta - 2.0 * PI / 3.0),
229 scale * std::cos(theta + 2.0 * PI / 3.0);
230
231 transform.row(1) << -scale * std::sin(theta),
232 -scale * std::sin(theta - 2.0 * PI / 3.0),
233 -scale * std::sin(theta + 2.0 * PI / 3.0);
234
235 return transform;
236}
237
238Matrix EMT::Ph3::SSN_GFM::getInverseParkTransformMatrix(Real theta) const {
239
240 theta = std::remainder(theta, 2.0 * PI);
241 Matrix transform(3, 2);
242
243 transform << std::cos(theta), -std::sin(theta),
244
245 std::cos(theta - 2.0 * PI / 3.0), -std::sin(theta - 2.0 * PI / 3.0),
246
247 std::cos(theta + 2.0 * PI / 3.0), -std::sin(theta + 2.0 * PI / 3.0);
248
249 return transform;
250}
251
252Real EMT::Ph3::SSN_GFM::regularizedOmega(Real omega) const {
253 constexpr Real minimumOmega = 1.0;
254
255 if (std::abs(omega) >= minimumOmega)
256 return omega;
257
258 return omega >= 0.0 ? minimumOmega : -minimumOmega;
259}
260
261void EMT::Ph3::SSN_GFM::evaluateStateDerivative(const Matrix &x,
262 const Matrix &u,
263 Matrix &stateDerivative) const {
264
265 if (x.rows() != mStateSize || x.cols() != 1)
266 throw std::invalid_argument(
267 "SSN_GFM state vector has an invalid dimension.");
268
269 if (u.rows() != mInputSize || u.cols() != 1)
270 throw std::invalid_argument(
271 "SSN_GFM input vector has an invalid dimension.");
272
273 stateDerivative.setZero(mStateSize, 1);
274
275 const Real pFiltered = x(PFiltered, 0);
276 const Real qFiltered = x(QFiltered, 0);
277 const Real omega = x(Omega, 0);
278 const Real theta = x(Theta, 0);
279 const Real voltageMagnitude = x(VoltageMagnitude, 0);
280
281 const Real voltageIntegratorD = x(VoltageIntegratorD, 0);
282 const Real voltageIntegratorQ = x(VoltageIntegratorQ, 0);
283
284 const Real currentIntegratorD = x(CurrentIntegratorD, 0);
285 const Real currentIntegratorQ = x(CurrentIntegratorQ, 0);
286
287 const Real delayVoltageD = x(DelayVoltageD, 0);
288 const Real delayVoltageQ = x(DelayVoltageQ, 0);
289
290 const Matrix vcAbc = x.block(VcA, 0, 3, 1);
291 const Matrix ifAbc = x.block(IfA, 0, 3, 1);
292
293 const Matrix parkTransform = getParkTransformMatrix(theta);
294 const Matrix inverseParkTransform = getInverseParkTransformMatrix(theta);
295
296 // Positive physical grid current means inverter injection into the grid.
297 const Matrix iGridAbc = (vcAbc - u) / mRc;
298
299 const Matrix vcDq = parkTransform * vcAbc;
300 const Matrix ifDq = parkTransform * ifAbc;
301 const Matrix iGridDq = parkTransform * iGridAbc;
302
303 const Real vcD = vcDq(0, 0);
304 const Real vcQ = vcDq(1, 0);
305
306 const Real ifD = ifDq(0, 0);
307 const Real ifQ = ifDq(1, 0);
308
309 const Real iGridD = iGridDq(0, 0);
310 const Real iGridQ = iGridDq(1, 0);
311
312 // Capacitor current.
313 const Real iCapD = ifD - iGridD;
314 const Real iCapQ = ifQ - iGridQ;
315
316 // The Park transformation is amplitude invariant (2/3 scaling).
317 // Therefore three-phase instantaneous power requires the factor 3/2.
318 const Real pInstantaneous = 1.5 * (vcD * iGridD + vcQ * iGridQ);
319
320 const Real qInstantaneous = 1.5 * (vcQ * iGridD - vcD * iGridQ);
321
322 const Real pccVoltageMagnitude = std::sqrt(vcD * vcD + vcQ * vcQ);
323
324 // ----------------------------------------------------------------------
325 // 1. Measurement filters
326 // ----------------------------------------------------------------------
327
328 stateDerivative(PFiltered, 0) =
329 mPowerFilterCutoff * (pInstantaneous - pFiltered);
330
331 stateDerivative(QFiltered, 0) =
332 mPowerFilterCutoff * (qInstantaneous - qFiltered);
333
334 // ----------------------------------------------------------------------
335 // 2. VSG algorithm
336 //
337 // J * omega_dot =
338 // (P_ref - P_e) / omega
339 // - D * (omega - omega_n)
340 //
341 // theta_dot = omega
342 // ----------------------------------------------------------------------
343
344 stateDerivative(Omega, 0) = ((mPRef - pFiltered) / regularizedOmega(omega) -
345 mDampingCoefficient * (omega - mOmegaN)) /
346 mVirtualInertia;
347
348 stateDerivative(Theta, 0) = omega;
349
350 // ----------------------------------------------------------------------
351 // 3. Reactive-power/voltage excitation controller
352 //
353 // E_dot =
354 // Kq * (Q_ref - Q_e)
355 // + Ku * (U_n - U_pcc)
356 // ----------------------------------------------------------------------
357
358 if (mReactiveDroopCutoff > 0.0) {
359 // Proportional Q-V droop (opt-in, grid-connected): E lags a droop target
360 // E_set + Dq*(Qref-Qf). No integral, so no reactive windup on a stiff grid.
361 const Real droopTarget =
362 mVoltageSetpoint + mReactivePowerDroop * (mQRef - qFiltered);
363 stateDerivative(VoltageMagnitude, 0) =
364 mReactiveDroopCutoff * (droopTarget - voltageMagnitude);
365 } else {
366 // Integral excitation (default, islanded).
367 stateDerivative(VoltageMagnitude, 0) =
368 mReactiveIntegralGain * (mQRef - qFiltered) +
369 mVoltageDroopGain * (mNominalVoltage - pccVoltageMagnitude);
370 }
371
372 // EMF minus virtual-impedance drop Zv * if (Zv = Rv + jXv; zero = islanded).
373 // if (a filter-current state) avoids the 1/Rc sensitivity of iGrid.
374 const Real voltageReferenceD =
375 voltageMagnitude - (mVirtualResistance * ifD - mVirtualReactance * ifQ);
376 const Real voltageReferenceQ =
377 -(mVirtualResistance * ifQ + mVirtualReactance * ifD);
378
379 const Real voltageErrorD = voltageReferenceD - vcD;
380 const Real voltageErrorQ = voltageReferenceQ - vcQ;
381
382 stateDerivative(VoltageIntegratorD, 0) = voltageErrorD;
383 stateDerivative(VoltageIntegratorQ, 0) = voltageErrorQ;
384
385 // ----------------------------------------------------------------------
386 // 4. Voltage controller
387 //
388 // The feed-forward and decoupling terms correspond to:
389 //
390 // Cf * dv_d/dt = if_d - ig_d + omega * Cf * v_q
391 // Cf * dv_q/dt = if_q - ig_q - omega * Cf * v_d
392 // ----------------------------------------------------------------------
393
394 const Real currentReferenceD =
395 mGridCurrentFeedforward * iGridD - omega * mCf * vcQ +
396 mKpVoltage * voltageErrorD + mKiVoltage * voltageIntegratorD;
397
398 const Real currentReferenceQ =
399 mGridCurrentFeedforward * iGridQ + omega * mCf * vcD +
400 mKpVoltage * voltageErrorQ + mKiVoltage * voltageIntegratorQ;
401
402 const Real currentErrorD = currentReferenceD - ifD;
403 const Real currentErrorQ = currentReferenceQ - ifQ;
404
405 stateDerivative(CurrentIntegratorD, 0) = currentErrorD;
406 stateDerivative(CurrentIntegratorQ, 0) = currentErrorQ;
407
408 // ----------------------------------------------------------------------
409 // 5. Current controller and active damping
410 //
411 // Lf * dif_d/dt =
412 // v_inv_d - vc_d - Rf * if_d + omega * Lf * if_q
413 //
414 // Lf * dif_q/dt =
415 // v_inv_q - vc_q - Rf * if_q - omega * Lf * if_d
416 // ----------------------------------------------------------------------
417
418 const Real converterVoltageReferenceD =
419 vcD - omega * mLf * ifQ + mKpCurrent * currentErrorD +
420 mKiCurrent * currentIntegratorD - mActiveDampingGain * iCapD;
421
422 const Real converterVoltageReferenceQ =
423 vcQ + omega * mLf * ifD + mKpCurrent * currentErrorQ +
424 mKiCurrent * currentIntegratorQ - mActiveDampingGain * iCapQ;
425
426 // ----------------------------------------------------------------------
427 // 6. First-order converter/digital-delay approximation
428 // ----------------------------------------------------------------------
429
430 stateDerivative(DelayVoltageD, 0) =
431 mDelayBandwidth * (converterVoltageReferenceD - delayVoltageD);
432
433 stateDerivative(DelayVoltageQ, 0) =
434 mDelayBandwidth * (converterVoltageReferenceQ - delayVoltageQ);
435
436 Matrix converterVoltageDq(2, 1);
437 converterVoltageDq << delayVoltageD, delayVoltageQ;
438
439 // ----------------------------------------------------------------------
440 // 7. Electrical filter model in abc coordinates
441 //
442 // vc_dot = (if + (u - vc) / Rc) / Cf
443 // if_dot = (v_inv - vc - Rf * if) / Lf
444 // ----------------------------------------------------------------------
445
446 const Matrix converterVoltageAbc = inverseParkTransform * converterVoltageDq;
447
448 const Matrix vcDerivative = (ifAbc + (u - vcAbc) / mRc) / mCf;
449
450 const Matrix ifDerivative = (converterVoltageAbc - vcAbc - mRf * ifAbc) / mLf;
451
452 stateDerivative.block(VcA, 0, 3, 1) = vcDerivative;
453 stateDerivative.block(IfA, 0, 3, 1) = ifDerivative;
454}
455
456void EMT::Ph3::SSN_GFM::evaluateOutput(const Matrix &x, const Matrix &u,
457 Matrix &output) const {
458
459 if (x.rows() != mStateSize || x.cols() != 1)
460 throw std::invalid_argument(
461 "SSN_GFM state vector has an invalid dimension.");
462
463 if (u.rows() != mInputSize || u.cols() != 1)
464 throw std::invalid_argument(
465 "SSN_GFM input vector has an invalid dimension.");
466
467 const Matrix vcAbc = x.block(VcA, 0, 3, 1);
468
469 // SSN current entering the component.
470 //
471 // The physical inverter injection current is:
472 //
473 // i_injection = (vc - u) / Rc
474 //
475 // The component current used by the SSN stamp is the opposite:
476 //
477 // y = -i_injection = (u - vc) / Rc
478 output = (u - vcAbc) / mRc;
479}
480
482 const Matrix &u, Matrix &A,
483 Matrix &B, Matrix &C,
484 Matrix &D) const {
485
486 if (x.rows() != mStateSize || x.cols() != 1)
487 throw std::invalid_argument(
488 "SSN_GFM state vector has an invalid dimension.");
489
490 if (u.rows() != mInputSize || u.cols() != 1)
491 throw std::invalid_argument(
492 "SSN_GFM input vector has an invalid dimension.");
493
494 const Real pFiltered = x(PFiltered, 0);
495 const Real omega = x(Omega, 0);
496 const Real theta = x(Theta, 0);
497 const Real delayVoltageD = x(DelayVoltageD, 0);
498 const Real delayVoltageQ = x(DelayVoltageQ, 0);
499
500 const Matrix vcAbc = x.block(VcA, 0, 3, 1);
501 const Matrix ifAbc = x.block(IfA, 0, 3, 1);
502
503 const Matrix parkTransform = getParkTransformMatrix(theta);
504 const Matrix inverseParkTransform = getInverseParkTransformMatrix(theta);
505
506 const Matrix tD = parkTransform.row(0);
507 const Matrix tQ = parkTransform.row(1);
508 const Matrix sD = inverseParkTransform.col(0);
509 const Matrix sQ = inverseParkTransform.col(1);
510
511 const Matrix iGridAbc = (vcAbc - u) / mRc;
512
513 const Real vcD = (tD * vcAbc)(0, 0);
514 const Real vcQ = (tQ * vcAbc)(0, 0);
515 const Real ifD = (tD * ifAbc)(0, 0);
516 const Real ifQ = (tQ * ifAbc)(0, 0);
517 const Real iGridD = (tD * iGridAbc)(0, 0);
518 const Real iGridQ = (tQ * iGridAbc)(0, 0);
519
520 Matrix dVcDByX = Matrix::Zero(1, mStateSize);
521 Matrix dVcQByX = Matrix::Zero(1, mStateSize);
522 Matrix dIfDByX = Matrix::Zero(1, mStateSize);
523 Matrix dIfQByX = Matrix::Zero(1, mStateSize);
524 Matrix dIGridDByX = Matrix::Zero(1, mStateSize);
525 Matrix dIGridQByX = Matrix::Zero(1, mStateSize);
526
527 dVcDByX(0, Theta) = vcQ;
528 dVcDByX.block(0, VcA, 1, 3) = tD;
529 dVcQByX(0, Theta) = -vcD;
530 dVcQByX.block(0, VcA, 1, 3) = tQ;
531
532 dIfDByX(0, Theta) = ifQ;
533 dIfDByX.block(0, IfA, 1, 3) = tD;
534 dIfQByX(0, Theta) = -ifD;
535 dIfQByX.block(0, IfA, 1, 3) = tQ;
536
537 dIGridDByX(0, Theta) = iGridQ;
538 dIGridDByX.block(0, VcA, 1, 3) = tD / mRc;
539 dIGridQByX(0, Theta) = -iGridD;
540 dIGridQByX.block(0, VcA, 1, 3) = tQ / mRc;
541
542 const Matrix dIGridDByU = -tD / mRc;
543 const Matrix dIGridQByU = -tQ / mRc;
544
545 Matrix dPByX = 1.5 * (iGridD * dVcDByX + vcD * dIGridDByX + iGridQ * dVcQByX +
546 vcQ * dIGridQByX);
547 const Matrix dPByU = 1.5 * (vcD * dIGridDByU + vcQ * dIGridQByU);
548
549 Matrix dQByX = 1.5 * (iGridD * dVcQByX + vcQ * dIGridDByX - iGridQ * dVcDByX -
550 vcD * dIGridQByX);
551 const Matrix dQByU = 1.5 * (vcQ * dIGridDByU - vcD * dIGridQByU);
552
553 dPByX(0, Theta) = 0.0;
554 dQByX(0, Theta) = 0.0;
555
556 const Real pccVoltageMagnitude = std::sqrt(vcD * vcD + vcQ * vcQ);
557 Matrix dPccVoltageMagnitudeByX = Matrix::Zero(1, mStateSize);
558
559 if (pccVoltageMagnitude > 1e-12) {
560 dPccVoltageMagnitudeByX =
561 (vcD * dVcDByX + vcQ * dVcQByX) / pccVoltageMagnitude;
562 dPccVoltageMagnitudeByX(0, Theta) = 0.0;
563 }
564
565 Matrix unitOmega = Matrix::Zero(1, mStateSize);
566 Matrix unitVoltageMagnitude = Matrix::Zero(1, mStateSize);
567 Matrix unitVoltageIntegratorD = Matrix::Zero(1, mStateSize);
568 Matrix unitVoltageIntegratorQ = Matrix::Zero(1, mStateSize);
569 Matrix unitCurrentIntegratorD = Matrix::Zero(1, mStateSize);
570 Matrix unitCurrentIntegratorQ = Matrix::Zero(1, mStateSize);
571
572 unitOmega(0, Omega) = 1.0;
573 unitVoltageMagnitude(0, VoltageMagnitude) = 1.0;
574 unitVoltageIntegratorD(0, VoltageIntegratorD) = 1.0;
575 unitVoltageIntegratorQ(0, VoltageIntegratorQ) = 1.0;
576 unitCurrentIntegratorD(0, CurrentIntegratorD) = 1.0;
577 unitCurrentIntegratorQ(0, CurrentIntegratorQ) = 1.0;
578
579 const Matrix dVoltageErrorDByX = unitVoltageMagnitude -
580 mVirtualResistance * dIfDByX +
581 mVirtualReactance * dIfQByX - dVcDByX;
582 const Matrix dVoltageErrorQByX =
583 -mVirtualResistance * dIfQByX - mVirtualReactance * dIfDByX - dVcQByX;
584
585 const Matrix dCurrentReferenceDByX =
586 mGridCurrentFeedforward * dIGridDByX -
587 mCf * (vcQ * unitOmega + omega * dVcQByX) +
588 mKpVoltage * dVoltageErrorDByX + mKiVoltage * unitVoltageIntegratorD;
589 const Matrix dCurrentReferenceDByU = mGridCurrentFeedforward * dIGridDByU;
590
591 const Matrix dCurrentReferenceQByX =
592 mGridCurrentFeedforward * dIGridQByX +
593 mCf * (vcD * unitOmega + omega * dVcDByX) +
594 mKpVoltage * dVoltageErrorQByX + mKiVoltage * unitVoltageIntegratorQ;
595 const Matrix dCurrentReferenceQByU = mGridCurrentFeedforward * dIGridQByU;
596
597 const Matrix dCurrentErrorDByX = dCurrentReferenceDByX - dIfDByX;
598 const Matrix dCurrentErrorQByX = dCurrentReferenceQByX - dIfQByX;
599 const Matrix dCurrentErrorDByU = dCurrentReferenceDByU;
600 const Matrix dCurrentErrorQByU = dCurrentReferenceQByU;
601
602 const Matrix dCapacitorCurrentDByX = dIfDByX - dIGridDByX;
603 const Matrix dCapacitorCurrentQByX = dIfQByX - dIGridQByX;
604 const Matrix dCapacitorCurrentDByU = -dIGridDByU;
605 const Matrix dCapacitorCurrentQByU = -dIGridQByU;
606
607 const Matrix dConverterVoltageReferenceDByX =
608 dVcDByX - mLf * (ifQ * unitOmega + omega * dIfQByX) +
609 mKpCurrent * dCurrentErrorDByX + mKiCurrent * unitCurrentIntegratorD -
610 mActiveDampingGain * dCapacitorCurrentDByX;
611 const Matrix dConverterVoltageReferenceDByU =
612 mKpCurrent * dCurrentErrorDByU -
613 mActiveDampingGain * dCapacitorCurrentDByU;
614
615 const Matrix dConverterVoltageReferenceQByX =
616 dVcQByX + mLf * (ifD * unitOmega + omega * dIfDByX) +
617 mKpCurrent * dCurrentErrorQByX + mKiCurrent * unitCurrentIntegratorQ -
618 mActiveDampingGain * dCapacitorCurrentQByX;
619 const Matrix dConverterVoltageReferenceQByU =
620 mKpCurrent * dCurrentErrorQByU -
621 mActiveDampingGain * dCapacitorCurrentQByU;
622
623 A.setZero(mStateSize, mStateSize);
624 B.setZero(mStateSize, mInputSize);
625 C.setZero(mOutputSize, mStateSize);
626 D.setZero(mOutputSize, mInputSize);
627
628 A.row(PFiltered) = mPowerFilterCutoff * dPByX;
629 A(PFiltered, PFiltered) -= mPowerFilterCutoff;
630 B.row(PFiltered) = mPowerFilterCutoff * dPByU;
631
632 A.row(QFiltered) = mPowerFilterCutoff * dQByX;
633 A(QFiltered, QFiltered) -= mPowerFilterCutoff;
634 B.row(QFiltered) = mPowerFilterCutoff * dQByU;
635
636 const Real omegaDenominator = regularizedOmega(omega);
637 const Real omegaDenominatorDerivative =
638 std::abs(omega) >= std::abs(omegaDenominator) ? 1.0 : 0.0;
639
640 A(Omega, PFiltered) = -1.0 / (mVirtualInertia * omegaDenominator);
641 A(Omega, Omega) = (-(mPRef - pFiltered) * omegaDenominatorDerivative /
642 (omegaDenominator * omegaDenominator) -
643 mDampingCoefficient) /
644 mVirtualInertia;
645
646 A(Theta, Omega) = 1.0;
647
648 if (mReactiveDroopCutoff > 0.0) {
649 A(VoltageMagnitude, QFiltered) =
650 -mReactiveDroopCutoff * mReactivePowerDroop;
651 A(VoltageMagnitude, VoltageMagnitude) = -mReactiveDroopCutoff;
652 } else {
653 A(VoltageMagnitude, QFiltered) = -mReactiveIntegralGain;
654 A.row(VoltageMagnitude) -= mVoltageDroopGain * dPccVoltageMagnitudeByX;
655 }
656
657 A.row(VoltageIntegratorD) = dVoltageErrorDByX;
658 A.row(VoltageIntegratorQ) = dVoltageErrorQByX;
659
660 A.row(CurrentIntegratorD) = dCurrentErrorDByX;
661 B.row(CurrentIntegratorD) = dCurrentErrorDByU;
662 A.row(CurrentIntegratorQ) = dCurrentErrorQByX;
663 B.row(CurrentIntegratorQ) = dCurrentErrorQByU;
664
665 A.row(DelayVoltageD) = mDelayBandwidth * dConverterVoltageReferenceDByX;
666 A(DelayVoltageD, DelayVoltageD) -= mDelayBandwidth;
667 B.row(DelayVoltageD) = mDelayBandwidth * dConverterVoltageReferenceDByU;
668
669 A.row(DelayVoltageQ) = mDelayBandwidth * dConverterVoltageReferenceQByX;
670 A(DelayVoltageQ, DelayVoltageQ) -= mDelayBandwidth;
671 B.row(DelayVoltageQ) = mDelayBandwidth * dConverterVoltageReferenceQByU;
672
673 const Matrix identity = Matrix::Identity(3, 3);
674
675 A.block(VcA, VcA, 3, 3) = -identity / (mCf * mRc);
676 A.block(VcA, IfA, 3, 3) = identity / mCf;
677 B.block(VcA, 0, 3, 3) = identity / (mCf * mRc);
678
679 A.block(IfA, VcA, 3, 3) = -identity / mLf;
680 A.block(IfA, IfA, 3, 3) = -mRf * identity / mLf;
681 A.block(IfA, Theta, 3, 1) = (sQ * delayVoltageD - sD * delayVoltageQ) / mLf;
682 A.block(IfA, DelayVoltageD, 3, 1) = sD / mLf;
683 A.block(IfA, DelayVoltageQ, 3, 1) = sQ / mLf;
684
685 C.block(0, VcA, 3, 3) = -identity / mRc;
686 D = identity / mRc;
687}
688
690 const Matrix &u, Matrix &A,
691 Matrix &B, Matrix &C,
692 Matrix &D) const {
693
694 A.setZero(mStateSize, mStateSize);
695 B.setZero(mStateSize, mInputSize);
696 C.setZero(mOutputSize, mStateSize);
697 D.setZero(mOutputSize, mInputSize);
698
699 Matrix fPlus = Matrix::Zero(mStateSize, 1);
700 Matrix fMinus = Matrix::Zero(mStateSize, 1);
701
702 Matrix gPlus = Matrix::Zero(mOutputSize, 1);
703 Matrix gMinus = Matrix::Zero(mOutputSize, 1);
704
705 // State Jacobians A and C.
706 for (Int column = 0; column < mStateSize; ++column) {
707 const Real step =
708 mJacobianAbsoluteStep +
709 mJacobianRelativeStep * std::max(1.0, std::abs(x(column, 0)));
710
711 Matrix xPlus = x;
712 Matrix xMinus = x;
713
714 xPlus(column, 0) += step;
715 xMinus(column, 0) -= step;
716
717 evaluateStateDerivative(xPlus, u, fPlus);
718 evaluateStateDerivative(xMinus, u, fMinus);
719
720 evaluateOutput(xPlus, u, gPlus);
721 evaluateOutput(xMinus, u, gMinus);
722
723 A.col(column) = (fPlus - fMinus) / (2.0 * step);
724 C.col(column) = (gPlus - gMinus) / (2.0 * step);
725 }
726
727 // Input Jacobians B and D.
728 for (Int column = 0; column < mInputSize; ++column) {
729 const Real step =
730 mJacobianAbsoluteStep +
731 mJacobianRelativeStep * std::max(1.0, std::abs(u(column, 0)));
732
733 Matrix uPlus = u;
734 Matrix uMinus = u;
735
736 uPlus(column, 0) += step;
737 uMinus(column, 0) -= step;
738
739 evaluateStateDerivative(x, uPlus, fPlus);
740 evaluateStateDerivative(x, uMinus, fMinus);
741
742 evaluateOutput(x, uPlus, gPlus);
743 evaluateOutput(x, uMinus, gMinus);
744
745 B.col(column) = (fPlus - fMinus) / (2.0 * step);
746 D.col(column) = (gPlus - gMinus) / (2.0 * step);
747 }
748}
749
750void EMT::Ph3::SSN_GFM::buildStateSpaceModel(const Matrix &x, const Matrix &u,
751 Matrix &A, Matrix &B, Matrix &C,
752 Matrix &D, Matrix &E,
753 Matrix &F) const {
754
755 calculateAnalyticalJacobians(x, u, A, B, C, D);
756
757 Matrix stateDerivative = Matrix::Zero(mStateSize, 1);
758 Matrix output = Matrix::Zero(mOutputSize, 1);
759
760 evaluateStateDerivative(x, u, stateDerivative);
761 evaluateOutput(x, u, output);
762
763 // Local affine offsets:
764 //
765 // E = f(x0,u0) - A*x0 - B*u0
766 // F = g(x0,u0) - C*x0 - D*u0
767 E = stateDerivative - A * x - B * u;
768 F = output - C * x - D * u;
769}
770
772 Matrix eVector;
773 Matrix fVector;
774
775 buildStateSpaceModel(**mX, **mIntfVoltage, mA, mB, mC, mD, eVector, fVector);
776
777 setStateOffset(eVector);
778 setOutputOffset(fVector);
779
780 // The dq/abc transformations and nonlinear GFM controls make the local
781 // state-space model time varying. The SSN equivalent must therefore be
782 // recomputed every simulation step.
783 return true;
784}
785
787
788 const Matrix &x = **mX;
789
790 const Real theta = x(Theta, 0);
791
792 const Matrix parkTransform = getParkTransformMatrix(theta);
793
794 const Matrix vcAbc = x.block(VcA, 0, 3, 1);
795 const Matrix ifAbc = x.block(IfA, 0, 3, 1);
796
797 const Matrix iGridAbc = (vcAbc - u) / mRc;
798
799 const Matrix vcDq = parkTransform * vcAbc;
800 const Matrix ifDq = parkTransform * ifAbc;
801 const Matrix iGridDq = parkTransform * iGridAbc;
802
803 const Real vcD = vcDq(0, 0);
804 const Real vcQ = vcDq(1, 0);
805
806 const Real iGridD = iGridDq(0, 0);
807 const Real iGridQ = iGridDq(1, 0);
808
809 const Real ifD = ifDq(0, 0);
810 const Real ifQ = ifDq(1, 0);
811
812 **mVcD = vcD;
813 **mVcQ = vcQ;
814
815 **mIGridD = iGridD;
816 **mIGridQ = iGridQ;
817
818 **mIfD = ifD;
819 **mIfQ = ifQ;
820
821 **mPInst = 1.5 * (vcD * iGridD + vcQ * iGridQ);
822 **mQInst = 1.5 * (vcQ * iGridD - vcD * iGridQ);
823
824 **mOmegaGFM = x(Omega, 0);
825 **mThetaGFM = theta;
826 **mVoltageMagnitudeGFM = x(VoltageMagnitude, 0);
827
828 // Reference the controller tracks, including the virtual-impedance drop.
829 **mVoltageReferenceD = x(VoltageMagnitude, 0) -
830 (mVirtualResistance * ifD - mVirtualReactance * ifQ);
831 **mVoltageReferenceQ = -(mVirtualResistance * ifQ + mVirtualReactance * ifD);
832}
833
835
836 if (!mParametersSet)
837 throw std::logic_error("setParameters() must be called before "
838 "initializeFromNodesAndTerminals().");
839
840 const Real omegaInitialization = 2.0 * PI * frequency;
841 const Complex imaginaryUnit(0.0, 1.0);
842
843 const Complex powerReference(mPRef, mQRef);
844
845 // Terminal voltage phasors using the inherited convention:
846 //
847 // u = terminal1 - terminal0
848 const MatrixComp uPhasor = buildInitialInputFromNodes(frequency);
849
850 MatrixComp vcPhasor = uPhasor;
851 MatrixComp iInjectionPhasor = MatrixComp::Zero(3, 1);
852
853 // Determine capacitor-voltage and injected-current phasors through the
854 // coupling resistance:
855 //
856 // vc = u + Rc * iInjection
857 for (Int iteration = 0; iteration < mInitializationMaxIterations;
858 ++iteration) {
859
860 const Complex vcA = vcPhasor(0, 0);
861
862 if (std::abs(vcA) < mInitializationTolerance) {
863 iInjectionPhasor.setZero();
864 break;
865 }
866
867 // With peak-valued phase phasors and the amplitude-invariant Park
868 // transform, total three-phase power is
869 //
870 // S = 1.5 * V_phase_peak * conj(I_phase_peak).
871 const Complex currentA = std::conj(powerReference / (1.5 * vcA));
872
873 MatrixComp nextInjectionCurrent(3, 1);
874
875 nextInjectionCurrent << currentA, currentA * SHIFT_TO_PHASE_B,
876 currentA * SHIFT_TO_PHASE_C;
877
878 const MatrixComp nextVcPhasor = uPhasor + mRc * nextInjectionCurrent;
879
880 iInjectionPhasor = nextInjectionCurrent;
881
882 if ((nextVcPhasor - vcPhasor).norm() < mInitializationTolerance) {
883 vcPhasor = nextVcPhasor;
884 break;
885 }
886
887 vcPhasor = nextVcPhasor;
888 }
889
890 // Capacitor-current relation:
891 //
892 // if = iGrid + j*omega*Cf*vc
893 const MatrixComp ifPhasor =
894 iInjectionPhasor + imaginaryUnit * omegaInitialization * mCf * vcPhasor;
895
896 // Converter bridge voltage:
897 //
898 // vInv = vc + (Rf + j*omega*Lf)*if
899 const MatrixComp converterVoltagePhasor =
900 vcPhasor + (mRf + imaginaryUnit * omegaInitialization * mLf) * ifPhasor;
901
902 const Matrix vcAbc0 = vcPhasor.real();
903 const Matrix ifAbc0 = ifPhasor.real();
904 const Matrix iGridAbc0 = iInjectionPhasor.real();
905 const Matrix converterVoltageAbc0 = converterVoltagePhasor.real();
906
907 // Align the dq frame with the internal EMF behind the virtual impedance,
908 // E0 = vc + (Rv + jXv)*if. At the operating point the voltage-loop reference
909 // is E0 in dq, so aligning to it makes both loop errors vanish (true
910 // equilibrium) even for nonzero Zv. Zv = 0 reduces to aligning with vc.
911 const Complex virtualImpedance(mVirtualResistance, mVirtualReactance);
912 const MatrixComp emfPhasor = vcPhasor + virtualImpedance * ifPhasor;
913 const Real theta0 = std::arg(emfPhasor(0, 0));
914
915 const Matrix parkTransform = getParkTransformMatrix(theta0);
916
917 const Matrix vcDq0 = parkTransform * vcAbc0;
918 const Matrix ifDq0 = parkTransform * ifAbc0;
919 const Matrix iGridDq0 = parkTransform * iGridAbc0;
920 const Matrix converterVoltageDq0 = parkTransform * converterVoltageAbc0;
921
922 const Real vcD0 = vcDq0(0, 0);
923 const Real vcQ0 = vcDq0(1, 0);
924
925 const Real ifD0 = ifDq0(0, 0);
926 const Real ifQ0 = ifDq0(1, 0);
927
928 const Real iGridD0 = iGridDq0(0, 0);
929 const Real iGridQ0 = iGridDq0(1, 0);
930
931 const Real pInitial = 1.5 * (vcD0 * iGridD0 + vcQ0 * iGridQ0);
932
933 const Real qInitial = 1.5 * (vcQ0 * iGridD0 - vcD0 * iGridQ0);
934
935 const Real iCapD0 = ifD0 - iGridD0;
936 const Real iCapQ0 = ifQ0 - iGridQ0;
937
938 Matrix x0 = Matrix::Zero(mStateSize, 1);
939
940 x0(PFiltered, 0) = pInitial;
941 x0(QFiltered, 0) = qInitial;
942
943 x0(Omega, 0) = omegaInitialization;
944 x0(Theta, 0) = theta0;
945
946 // Internal EMF magnitude behind the virtual impedance (d-axis aligned).
947 x0(VoltageMagnitude, 0) = std::abs(emfPhasor(0, 0));
948
949 // Proportional-droop setpoint: the operating EMF, so the droop is centered at
950 // the initial point (E_dot = 0 when Qf = Qref at t = 0).
951 mVoltageSetpoint = x0(VoltageMagnitude, 0);
952
953 // Voltage-loop references at the operating point, same form as
954 // evaluateStateDerivative(). With the E0-aligned frame both errors are ~0.
955 const Real voltageReferenceD0 =
956 x0(VoltageMagnitude, 0) -
957 (mVirtualResistance * ifD0 - mVirtualReactance * ifQ0);
958 const Real voltageReferenceQ0 =
959 -(mVirtualResistance * ifQ0 + mVirtualReactance * ifD0);
960 const Real voltageErrorD0 = voltageReferenceD0 - vcD0;
961 const Real voltageErrorQ0 = voltageReferenceQ0 - vcQ0;
962
963 // Voltage controller:
964 //
965 // iRefD =
966 // iGridD - omega*Cf*vcQ
967 // + KpV*(E-vcD)
968 // + KiV*xiVd
969 //
970 // Set iRefD = ifD and solve for xiVd.
971 x0(VoltageIntegratorD, 0) =
972 (ifD0 - mGridCurrentFeedforward * iGridD0 +
973 omegaInitialization * mCf * vcQ0 - mKpVoltage * voltageErrorD0) /
974 mKiVoltage;
975
976 // iRefQ =
977 // iGridQ + omega*Cf*vcD
978 // + KpV*(0-vcQ)
979 // + KiV*xiVq
980 //
981 // Set iRefQ = ifQ and solve for xiVq.
982 x0(VoltageIntegratorQ, 0) =
983 (ifQ0 - mGridCurrentFeedforward * iGridQ0 -
984 omegaInitialization * mCf * vcD0 - mKpVoltage * voltageErrorQ0) /
985 mKiVoltage;
986
987 // Current controller steady-state integrators.
988 //
989 // vInvD =
990 // vcD - omega*Lf*ifQ
991 // + KpI*(iRefD-ifD)
992 // + KiI*xiId
993 // - Kc*iCapD
994 x0(CurrentIntegratorD, 0) =
995 (converterVoltageDq0(0, 0) - vcD0 + omegaInitialization * mLf * ifQ0 +
996 mActiveDampingGain * iCapD0) /
997 mKiCurrent;
998
999 // vInvQ =
1000 // vcQ + omega*Lf*ifD
1001 // + KpI*(iRefQ-ifQ)
1002 // + KiI*xiIq
1003 // - Kc*iCapQ
1004 x0(CurrentIntegratorQ, 0) =
1005 (converterVoltageDq0(1, 0) - vcQ0 - omegaInitialization * mLf * ifD0 +
1006 mActiveDampingGain * iCapQ0) /
1007 mKiCurrent;
1008
1009 // At steady state, the first-order delay output equals its input.
1010 x0(DelayVoltageD, 0) = converterVoltageDq0(0, 0);
1011 x0(DelayVoltageQ, 0) = converterVoltageDq0(1, 0);
1012
1013 x0.block(VcA, 0, 3, 1) = vcAbc0;
1014 x0.block(IfA, 0, 3, 1) = ifAbc0;
1015
1016 // Store the initialized state and terminal voltage.
1017 **mX = x0;
1018 **mIntfVoltage = uPhasor.real();
1019
1020 // Do not call updateComponentParameters() directly here.
1021 //
1022 // updateStateSpaceModel() performs:
1023 // 1. updateComponentParameters()
1024 // 2. recomputeDiscreteModel()
1025 //
1026 // This initializes mdA, mdB, mdE and mW.
1028
1029 // The MNA right-side-vector stamp uses mYHist. It must therefore already
1030 // be valid before the first network solution.
1032
1033 // Use the same Norton-equivalent equation as the SSN network stamp:
1034 //
1035 // y = W*u + yHist
1036 //
1037 // This avoids an initialization mismatch between mIntfCurrent and the
1038 // actual component stamp.
1039
1040 **mIntfCurrent = mW * (**mIntfVoltage) + mYHist;
1041
1043
1044 Matrix stateDerivative = Matrix::Zero(mStateSize, 1);
1045
1046 evaluateStateDerivative(**mX, **mIntfVoltage, stateDerivative);
1047
1048 const Matrix nonlinearOutput = [&]() {
1049 Matrix output = Matrix::Zero(mOutputSize, 1);
1050 evaluateOutput(**mX, **mIntfVoltage, output);
1051 return output;
1052 }();
1053
1054 const Matrix ssnOutput = mW * (**mIntfVoltage) + mYHist;
1055
1056 SPDLOG_LOGGER_INFO(
1057 mSLog,
1058 "\n--- SSN GFM initialization ---"
1059 "\nInput voltage u: {:s}"
1060 "\nInterface current y: {:s}"
1061 "\nState x: {:s}"
1062 "\nState derivative norm: {:.6e}"
1063 "\nNonlinear output: {:s}"
1064 "\nSSN output: {:s}"
1065 "\nOutput mismatch norm: {:.6e}"
1066 "\nW norm: {:.6e}"
1067 "\nHistory-vector norm: {:.6e}"
1068 "\nP/Q initial: [{:.6e}, {:.6e}]"
1069 "\nVc dq: [{:.6e}, {:.6e}]"
1070 "\nIGrid dq: [{:.6e}, {:.6e}]"
1071 "\nIf dq: [{:.6e}, {:.6e}]"
1072 "\nConverter voltage dq: [{:.6e}, {:.6e}]"
1073 "\n--- SSN GFM initialization finished ---",
1076 stateDerivative.norm(), Logger::matrixToString(nonlinearOutput),
1077 Logger::matrixToString(ssnOutput), (nonlinearOutput - ssnOutput).norm(),
1078 mW.norm(), mYHist.norm(), pInitial, qInitial, vcD0, vcQ0, iGridD0,
1079 iGridQ0, ifD0, ifQ0, converterVoltageDq0(0, 0),
1080 converterVoltageDq0(1, 0));
1081}
1082
1084
1086 Matrix stateDerivative = Matrix::Zero(mStateSize, 1);
1087
1088 evaluateStateDerivative(**mX, **mIntfVoltage, stateDerivative);
1089
1090 return stateDerivative;
1091}
1092
1094
void calculateNumericalJacobians(const Matrix &x, const Matrix &u, Matrix &A, Matrix &B, Matrix &C, Matrix &D) const
Numerically calculate A, B, C and D by central differences, as the reference for EMT_Ph3_SSN_GFM_Jaco...
void setNumericalLinearizationParameters(Real relativeStep, Real absoluteStep)
Configure finite-difference steps for local linearization.
SSN_GFM(String uid, String name, Logger::Level logLevel=Logger::Level::off)
Matrix getStateDerivative() const
Bool updateComponentParameters() override final
Matrix getInterfaceCurrent() const
std::vector< SSNComp::LocalAbcStateBlock > getLocalAbcStateBlocks() const override final
Matrix getInterfaceVoltage() const
void updateLogAttributes(const Matrix &u) const override final
Update derived attributes used for logging/inspection.
void setReactivePowerDroop(Real droopGain, Real cutoff)
Opt-in proportional Q-V droop replacing the integral excitation. droopGain is Dq [V/var]; cutoff [rad...
void setParameters(Real lf, Real cf, Real rf, Real rc, Real nominalVoltage, Real omegaN, Real pRef, Real qRef, Real virtualInertia, Real dampingCoefficient, Real voltageDroopGain, Real reactiveIntegralGain, Real kpVoltage, Real kiVoltage, Real kpCurrent, Real kiCurrent, Real activeDampingGain, Real powerFilterCutoff, Real delayBandwidth)
Configure the GFM model.
std::vector< String > getLocalStateNames() const override final
void calculateAnalyticalJacobians(const Matrix &x, const Matrix &u, Matrix &A, Matrix &B, Matrix &C, Matrix &D) const
Analytically calculate A, B, C and D, as the simulation does.
void setGridCurrentFeedforward(Real scale)
Scale on the voltage-loop grid-current feedforward (default 1).
void setVirtualImpedance(Real virtualResistance, Real virtualReactance=0.0)
Opt-in virtual output impedance Zv = Rv + jXv, subtracted from the EMF reference across the filter cu...
void initializeFromNodesAndTerminals(Real frequency) override final
Initializes Component variables according to power flow data stored in Nodes.
TwoTerminalVTypeVariableSSNComp(String uid, String name, Logger::Level logLevel=Logger::Level::off)
const Attribute< Matrix >::Ptr mX
Definition EMT_SSNComp.h:43
Matrix calculateHistoryVector() const override final
void setParameters(const Matrix &A, const Matrix &B, const Matrix &C, const Matrix &D)
static constexpr Real mInitializationTolerance
void updateStateSpaceModel() override final
Hook for variable/time-varying SSN components.
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
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