DPsim
Loading...
Searching...
No Matches
Base_ReducedOrderSynchronGenerator.cpp
1/* Copyright 2017-2021 Institute for Automation of Complex Power Systems,
2 * EONERC, RWTH Aachen University
3 *
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 *********************************************************************************/
8
9#include <dpsim-models/Base/Base_ReducedOrderSynchronGenerator.h>
10#include <dpsim-models/Signal/ExciterDC1Simp.h>
11
12using namespace CPS;
13
14template <>
15Base::ReducedOrderSynchronGenerator<Real>::ReducedOrderSynchronGenerator(
16 String uid, String name, Logger::Level logLevel)
17 : MNASimPowerComp<Real>(uid, name, true, true, logLevel),
18 mVdq0(mAttributes->create<Matrix>("Vdq0")),
19 mIdq0(mAttributes->create<Matrix>("Idq0")),
20 mElecTorque(mAttributes->create<Real>("Te")),
21 mMechTorque(mAttributes->create<Real>("Tm")),
22 mOmMech(mAttributes->create<Real>("w_r")),
23 mThetaMech(mAttributes->create<Real>("Theta")),
24 mDelta(mAttributes->create<Real>("delta")),
25 mEf(mAttributes->create<Real>("Ef")) {
26
27 mSimTime = 0.0;
28
29 // declare state variables
30 **mVdq0 = Matrix::Zero(3, 1);
31 **mIdq0 = Matrix::Zero(3, 1);
32
33 // default model is Norton equivalent
34 mModelAsNortonSource = true;
35 SPDLOG_LOGGER_DEBUG(this->mSLog,
36 "SG per default modelled as Norton equivalent");
37}
38
39template <>
40Base::ReducedOrderSynchronGenerator<Complex>::ReducedOrderSynchronGenerator(
41 String uid, String name, Logger::Level logLevel)
42 : MNASimPowerComp<Complex>(uid, name, true, true, logLevel),
43 mVdq(mAttributes->create<Matrix>("Vdq0")),
44 mIdq(mAttributes->create<Matrix>("Idq0")),
45 mElecTorque(mAttributes->create<Real>("Te")),
46 mMechTorque(mAttributes->create<Real>("Tm")),
47 mOmMech(mAttributes->create<Real>("w_r")),
48 mThetaMech(mAttributes->create<Real>("Theta")),
49 mDelta(mAttributes->create<Real>("delta")),
50 mEf(mAttributes->create<Real>("Ef")) {
51
52 mSimTime = 0.0;
53
54 // declare state variables
56 **mVdq = Matrix::Zero(2, 1);
57 **mIdq = Matrix::Zero(2, 1);
58
59 // default model is Norton equivalent
60 mModelAsNortonSource = true;
61 SPDLOG_LOGGER_DEBUG(this->mSLog,
62 "SG per default modelled as Norton equivalent");
63}
64
65template <typename VarType>
67 Bool modelAsCurrentSource) {
68 mModelAsNortonSource = modelAsCurrentSource;
71 this->setVirtualNodeNumber(0);
72 SPDLOG_LOGGER_DEBUG(this->mSLog, "Setting SG model to Norton equivalent");
73 } else {
74 this->setVirtualNodeNumber(2);
75 SPDLOG_LOGGER_DEBUG(this->mSLog, "Setting SG model to Thevenin equivalent");
76 }
77}
78
79template <typename VarType>
81 Real nomPower, Real nomVolt, Real nomFreq) {
82
84
85 // set base nominal values
86 mNomPower = nomPower;
87 mNomVolt = nomVolt;
88 mNomFreq = nomFreq;
89 mNomOmega = nomFreq * 2 * PI;
90
91 // Set base stator values
93 mBase_V = mNomVolt / sqrt(3) * sqrt(2);
95 mBase_I = mNomPower / ((3. / 2.) * mBase_V);
100}
101
102template <typename VarType>
104 VarType>::setOperationalParametersPerUnit(Real nomPower, Real nomVolt,
105 Real nomFreq, Real H, Real Ld,
106 Real Lq, Real L0, Real Ld_t,
107 Real Td0_t) {
108
109 setBaseParameters(nomPower, nomVolt, nomFreq);
110
111 mLd = Ld;
112 mLq = Lq;
113 mL0 = L0;
114 mLd_t = Ld_t;
115 mTd0_t = Td0_t;
116 mH = H;
117
118 SPDLOG_LOGGER_INFO(this->mSLog,
119 "Set base parameters: \n"
120 "nomPower: {:e}\nnomVolt: {:e}\nnomFreq: {:e}\n",
121 mNomPower, mNomVolt, mNomFreq);
122
123 SPDLOG_LOGGER_INFO(this->mSLog,
124 "Set operational parameters in per unit: \n"
125 "inertia: {:e}\n"
126 "Ld: {:e}\nLq: {:e}\nL0: {:e}\n"
127 "Ld_t: {:e}\nTd0_t: {:e}\n",
128 mH, mLd, mLq, mL0, mLd_t, mTd0_t);
129}
130
131template <typename VarType>
133 VarType>::setOperationalParametersPerUnit(Real nomPower, Real nomVolt,
134 Real nomFreq, Real H, Real Ld,
135 Real Lq, Real L0, Real Ld_t,
136 Real Lq_t, Real Td0_t,
137 Real Tq0_t) {
138
139 setBaseParameters(nomPower, nomVolt, nomFreq);
140
141 mLd = Ld;
142 mLq = Lq;
143 mL0 = L0;
144 mLd_t = Ld_t;
145 mLq_t = Lq_t;
146 mTd0_t = Td0_t;
147 mTq0_t = Tq0_t;
148 mH = H;
149
150 SPDLOG_LOGGER_INFO(this->mSLog,
151 "Set base parameters: \n"
152 "nomPower: {:e}\nnomVolt: {:e}\nnomFreq: {:e}\n",
153 mNomPower, mNomVolt, mNomFreq);
154
155 SPDLOG_LOGGER_INFO(this->mSLog,
156 "Set operational parameters in per unit: \n"
157 "inertia: {:e}\n"
158 "Ld: {:e}\nLq: {:e}\nL0: {:e}\n"
159 "Ld_t: {:e}\nLq_t: {:e}\n"
160 "Td0_t: {:e}\nTq0_t: {:e}\n",
162}
163
164template <typename VarType>
166 VarType>::setOperationalParametersPerUnit(Real nomPower, Real nomVolt,
167 Real nomFreq, Real H, Real Ld,
168 Real Lq, Real L0, Real Ld_t,
169 Real Lq_t, Real Td0_t, Real Tq0_t,
170 Real Ld_s, Real Lq_s, Real Td0_s,
171 Real Tq0_s, Real Taa) {
172
173 setBaseParameters(nomPower, nomVolt, nomFreq);
174
175 mLd = Ld;
176 mLq = Lq;
177 mL0 = L0;
178 mLd_t = Ld_t;
179 mLq_t = Lq_t;
180 mLd_s = Ld_s;
181 mLq_s = Lq_s;
182 mTd0_t = Td0_t;
183 mTq0_t = Tq0_t;
184 mTd0_s = Td0_s;
185 mTq0_s = Tq0_s;
186 mTaa = Taa;
187 mH = H;
188
189 SPDLOG_LOGGER_INFO(this->mSLog,
190 "Set base parameters: \n"
191 "nomPower: {:e}\nnomVolt: {:e}\nnomFreq: {:e}\n",
192 mNomPower, mNomVolt, mNomFreq);
193
194 SPDLOG_LOGGER_INFO(this->mSLog,
195 "Set operational parameters in per unit: \n"
196 "inertia: {:e}\n"
197 "Ld: {:e}\nLq: {:e}\nL0: {:e}\n"
198 "Ld_t: {:e}\nLq_t: {:e}\n"
199 "Td0_t: {:e}\nTq0_t: {:e}\n"
200 "Ld_s: {:e}\nLq_s: {:e}\n"
201 "Td0_s: {:e}\nTq0_s: {:e}\n"
202 "Taa: {:e}\n",
205}
206
207template <typename VarType>
209 Real scalingFactor) {
210 mH = mH * scalingFactor;
211 SPDLOG_LOGGER_INFO(
212 this->mSLog,
213 "Scaling inertia with factor {:e}:\n resulting inertia: {:e}\n",
214 scalingFactor, mH);
215}
216
217template <typename VarType>
218void Base::ReducedOrderSynchronGenerator<VarType>::calculateVBRconstants() {
219
220 Real Tf = 0;
221 if (mSGOrder == SGOrder::SG5Order) {
222 mYd = (mTd0_s / mTd0_t) * (mLd_s / mLd_t) * (mLd - mLd_t);
223 mYq = 0.0;
224 Tf = mTaa / mTd0_t;
225 } else if (mSGOrder == SGOrder::SG6aOrder) {
226 mYd = (mTd0_s / mTd0_t) * (mLd_s / mLd_t) * (mLd - mLd_t);
227 mYq = (mTq0_s / mTq0_t) * (mLq_s / mLq_t) * (mLq - mLq_t);
228 Tf = mTaa / mTd0_t;
229 } else {
230 mYd = 0;
231 mYq = 0;
232 }
233
234 Real Zq_t = mLd - mLd_t - mYd;
235 Real Zd_t = mLq - mLq_t - mYq;
236 Real Zq_s = mLd_t - mLd_s + mYd;
237 Real Zd_s = mLq_t - mLq_s + mYq;
238
239 mAd_t = mTimeStep * Zd_t / (2 * mTq0_t + mTimeStep);
240 mBd_t = (2 * mTq0_t - mTimeStep) / (2 * mTq0_t + mTimeStep);
241 mAq_t = -mTimeStep * Zq_t / (2 * mTd0_t + mTimeStep);
242 mBq_t = (2 * mTd0_t - mTimeStep) / (2 * mTd0_t + mTimeStep);
243 mDq_t = mTimeStep * (1 - Tf) / (2 * mTd0_t + mTimeStep);
244
245 if (mSGOrder == SGOrder::SG5Order) {
246 mAd_s = (mTimeStep * (mLq - mLq_s)) / (2 * mTq0_s + mTimeStep);
247 mCd_s = (2 * mTq0_s - mTimeStep) / (2 * mTq0_s + mTimeStep);
248 mAq_s = (-mTimeStep * Zq_s + mTimeStep * mAq_t) / (2 * mTd0_s + mTimeStep);
249 mBq_s = (mTimeStep * mBq_t + mTimeStep) / (2 * mTd0_s + mTimeStep);
250 mCq_s = (2 * mTd0_s - mTimeStep) / (2 * mTd0_s + mTimeStep);
251 mDq_s = (mTimeStep * mDq_t + Tf * mTimeStep) / (2 * mTd0_s + mTimeStep);
252 } else if (mSGOrder == SGOrder::SG6aOrder || mSGOrder == SGOrder::SG6bOrder) {
253 mAd_s = (mTimeStep * Zd_s + mTimeStep * mAd_t) / (2 * mTq0_s + mTimeStep);
254 mBd_s = (mTimeStep * mBd_t + mTimeStep) / (2 * mTq0_s + mTimeStep);
255 mCd_s = (2 * mTq0_s - mTimeStep) / (2 * mTq0_s + mTimeStep);
256 mAq_s = (-mTimeStep * Zq_s + mTimeStep * mAq_t) / (2 * mTd0_s + mTimeStep);
257 mBq_s = (mTimeStep * mBq_t + mTimeStep) / (2 * mTd0_s + mTimeStep);
258 mCq_s = (2 * mTd0_s - mTimeStep) / (2 * mTd0_s + mTimeStep);
259 mDq_s = (mTimeStep * mDq_t + Tf * mTimeStep) / (2 * mTd0_s + mTimeStep);
260 }
261}
262
263template <typename VarType>
265 VarType>::calculateResistanceMatrixConstants() {
266 if (mSGOrder == SGOrder::SG3Order) {
267 mA = -mLq;
268 mB = mLd_t - mAq_t;
269 }
270 if (mSGOrder == SGOrder::SG4Order) {
271 mA = -mAd_t - mLq_t;
272 mB = mLd_t - mAq_t;
273 }
274 if (mSGOrder == SGOrder::SG5Order || mSGOrder == SGOrder::SG6aOrder ||
275 mSGOrder == SGOrder::SG6bOrder) {
276 mA = -mLq_s - mAd_s;
277 mB = mLd_s - mAq_s;
278 }
279}
280
281template <typename VarType>
282void Base::ReducedOrderSynchronGenerator<VarType>::setInitialValues(
283 Complex initComplexElectricalPower, Real initMechanicalPower,
284 Complex initTerminalVoltage) {
285
286 mInitElecPower = initComplexElectricalPower;
287 mInitMechPower = initMechanicalPower;
288
289 mInitVoltage = initTerminalVoltage;
290 mInitVoltageAngle = Math::phase(mInitVoltage);
291
292 mInitCurrent = std::conj(mInitElecPower / mInitVoltage);
293 mInitCurrentAngle = Math::phase(mInitCurrent);
294
295 mInitVoltage = mInitVoltage / mBase_V_RMS;
296 mInitCurrent = mInitCurrent / mBase_I_RMS;
297
298 mInitialValuesSet = true;
299
300 SPDLOG_LOGGER_DEBUG(this->mSLog,
301 "\n--- Set initial values ---"
302 "\nInitial active power: {:}W = {:} p.u."
303 "\nInitial reactive power W: {:}W = {:} p.u."
304 "\nInitial terminal voltage magnitude: {:} p.u."
305 "\nInitial terminal voltage phase: {:} rad = ({:}°)"
306 "\nInitial current magnitude: {:} p.u."
307 "\nInitial current phase: {:} rad = ({:}°)"
308 "\n--- Set initial values finished ---\n",
309
310 mInitElecPower.real(), mInitElecPower.real() / mNomPower,
311 mInitElecPower.imag(), mInitElecPower.imag() / mNomPower,
312 Math::abs(mInitVoltage), Math::phase(mInitVoltage),
313 Math::phaseDeg(mInitVoltage), Math::abs(mInitCurrent),
314 Math::phase(mInitCurrent), Math::phaseDeg(mInitCurrent));
315 this->mSLog->flush();
316}
317
318template <>
320 Real frequency) {
321
322 this->updateMatrixNodeIndices();
323
324 if (!mInitialValuesSet)
325 this->setInitialValues(-this->terminal(0)->singlePower(),
326 -this->terminal(0)->singlePower().real(),
327 this->initialSingleVoltage(0));
328
329 // Initialize mechanical torque
330 **mMechTorque = mInitMechPower / mNomPower;
331 mMechTorque_prev = **mMechTorque;
332
333 // calculate steady state machine emf (i.e. voltage behind synchronous reactance)
334 Complex Eq0 = mInitVoltage + Complex(0, mLq) * mInitCurrent;
335
336 // Load angle
337 **mDelta = Math::phase(Eq0);
338
339 // convert currrents to dq reference frame
340 (**mIdq0)(0, 0) = Math::abs(mInitCurrent) * sin(**mDelta - mInitCurrentAngle);
341 (**mIdq0)(1, 0) = Math::abs(mInitCurrent) * cos(**mDelta - mInitCurrentAngle);
342
343 // convert voltages to dq reference frame
344 (**mVdq0)(0, 0) = Math::abs(mInitVoltage) * sin(**mDelta - mInitVoltageAngle);
345 (**mVdq0)(1, 0) = Math::abs(mInitVoltage) * cos(**mDelta - mInitVoltageAngle);
346
347 // calculate Ef
348 **mEf = Math::abs(Eq0) + (mLd - mLq) * (**mIdq0)(0, 0);
349 mEf_prev = **mEf;
350
351 // Initialize controllers
352 if (mHasExciter) {
353 mExciter->initializeStates(Math::abs(mInitVoltage), **mEf);
354 }
355 if (mHasTurbineGovernor) {
356 mTurbineGovernor->initializeStates(**mMechTorque);
357 }
358 if (mHasGovernorAndTurbine) {
359 mGovernor->initializeStates(**mMechTorque);
360 mTurbine->initializeStates(**mMechTorque);
361 }
362 if (mHasPSS) {
363 mPSS->initializeStates(mNomOmega / mBase_OmMech, **mMechTorque,
364 (**mVdq0)(0, 0), (**mVdq0)(1, 0));
365 }
366
367 // initial electrical torque
368 **mElecTorque =
369 (**mVdq0)(0, 0) * (**mIdq0)(0, 0) + (**mVdq0)(1, 0) * (**mIdq0)(1, 0);
370
371 // Initialize omega mech with nominal system frequency
372 **mOmMech = mNomOmega / mBase_OmMech;
373
374 // initialize theta and calculate transform matrix
375 **mThetaMech = **mDelta - PI / 2.;
376
377 // set initial interface current
378 (**mIntfCurrent)(0, 0) = (mInitCurrent * mBase_I).real();
379 (**mIntfCurrent)(1, 0) = (mInitCurrent * mBase_I * SHIFT_TO_PHASE_B).real();
380 (**mIntfCurrent)(2, 0) = (mInitCurrent * mBase_I * SHIFT_TO_PHASE_C).real();
381
382 // set initial interface voltage
383 (**mIntfVoltage)(0, 0) = (mInitVoltage * mBase_V).real();
384 (**mIntfVoltage)(1, 0) = (mInitVoltage * mBase_V * SHIFT_TO_PHASE_B).real();
385 (**mIntfVoltage)(2, 0) = (mInitVoltage * mBase_V * SHIFT_TO_PHASE_C).real();
386
387 SPDLOG_LOGGER_DEBUG(this->mSLog,
388 "\n--- Initialization from power flow ---"
389 "\nInitial Vd (per unit): {:f}"
390 "\nInitial Vq (per unit): {:f}"
391 "\nInitial Id (per unit): {:f}"
392 "\nInitial Iq (per unit): {:f}"
393 "\nInitial Ef (per unit): {:f}"
394 "\nInitial mechanical torque (per unit): {:f}"
395 "\nInitial electrical torque (per unit): {:f}"
396 "\nInitial initial mechanical theta (per unit): {:f}"
397 "\nInitial delta (per unit): {:f} (= {:f}°)"
398 "\n--- Initialization from power flow finished ---",
399
400 (**mVdq0)(0, 0), (**mVdq0)(1, 0), (**mIdq0)(0, 0),
401 (**mIdq0)(1, 0), **mEf, **mMechTorque, **mElecTorque,
402 **mThetaMech, **mDelta, **mDelta * 180 / PI);
403 this->mSLog->flush();
404}
405
406template <>
408 Complex>::initializeFromNodesAndTerminals(Real frequency) {
409
410 this->updateMatrixNodeIndices();
411
412 if (!mInitialValuesSet)
413 this->setInitialValues(-this->terminal(0)->singlePower(),
414 -this->terminal(0)->singlePower().real(),
415 this->initialSingleVoltage(0));
416
417 // Initialize mechanical torque
418 **mMechTorque = mInitMechPower / mNomPower;
419 mMechTorque_prev = **mMechTorque;
420
421 // calculate steady state machine emf (i.e. voltage behind synchronous reactance)
422 Complex Eq0 = mInitVoltage + Complex(0, mLq) * mInitCurrent;
423
424 // Load angle
425 **mDelta = Math::phase(Eq0);
426
427 // convert currrents to dq reference frame
428 (**mIdq)(0, 0) = Math::abs(mInitCurrent) * sin(**mDelta - mInitCurrentAngle);
429 (**mIdq)(1, 0) = Math::abs(mInitCurrent) * cos(**mDelta - mInitCurrentAngle);
430
431 // convert voltages to dq reference frame
432 (**mVdq)(0, 0) = Math::abs(mInitVoltage) * sin(**mDelta - mInitVoltageAngle);
433 (**mVdq)(1, 0) = Math::abs(mInitVoltage) * cos(**mDelta - mInitVoltageAngle);
434
435 // calculate Ef
436 **mEf = Math::abs(Eq0) + (mLd - mLq) * (**mIdq)(0, 0);
437 mEf_prev = **mEf;
438
439 // Initialize controllers
440 if (mHasExciter) {
441 mExciter->initializeStates(Math::abs(mInitVoltage), **mEf);
442 }
443 if (mHasTurbineGovernor) {
444 mTurbineGovernor->initializeStates(**mMechTorque);
445 }
446 if (mHasGovernorAndTurbine) {
447 mGovernor->initializeStates(**mMechTorque);
448 mTurbine->initializeStates(**mMechTorque);
449 }
450 if (mHasPSS) {
451 mPSS->initializeStates(mNomOmega / mBase_OmMech, **mMechTorque,
452 (**mVdq)(0, 0), (**mVdq)(1, 0));
453 }
454
455 // initial electrical torque
456 **mElecTorque =
457 (**mVdq)(0, 0) * (**mIdq)(0, 0) + (**mVdq)(1, 0) * (**mIdq)(1, 0);
458
459 // Initialize omega mech with nominal system frequency
460 **mOmMech = mNomOmega / mBase_OmMech;
461
462 // initialize theta and calculate transform matrix
463 **mThetaMech = **mDelta - PI / 2.;
464
465 // set initial value of current
466 (**mIntfCurrent)(0, 0) = mInitCurrent * mBase_I_RMS;
467
468 // set initial interface voltage
469 (**mIntfVoltage)(0, 0) = mInitVoltage * mBase_V_RMS;
470
471 SPDLOG_LOGGER_DEBUG(this->mSLog,
472 "\n--- Initialization from power flow ---"
473 "\nInitial Vd (per unit): {:f}"
474 "\nInitial Vq (per unit): {:f}"
475 "\nInitial Id (per unit): {:f}"
476 "\nInitial Iq (per unit): {:f}"
477 "\nInitial Ef (per unit): {:f}"
478 "\nInitial mechanical torque (per unit): {:f}"
479 "\nInitial electrical torque (per unit): {:f}"
480 "\nInitial initial mechanical theta (per unit): {:f}"
481 "\nInitial delta (per unit): {:f} (= {:f}°)"
482 "\n--- Initialization from power flow finished ---",
483
484 (**mVdq)(0, 0), (**mVdq)(1, 0), (**mIdq)(0, 0),
485 (**mIdq)(1, 0), **mEf, **mMechTorque, **mElecTorque,
486 **mThetaMech, **mDelta, **mDelta * 180 / PI);
487 this->mSLog->flush();
488}
489
490template <typename VarType>
491void Base::ReducedOrderSynchronGenerator<VarType>::mnaCompInitialize(
492 Real omega, Real timeStep, Attribute<Matrix>::Ptr leftVector) {
493
494 this->updateMatrixNodeIndices();
495 mTimeStep = timeStep;
496 calculateVBRconstants();
497 calculateResistanceMatrixConstants();
498 initializeResistanceMatrix();
499 specificInitialization();
500}
501
502template <>
503void Base::ReducedOrderSynchronGenerator<Complex>::mnaCompPreStep(
504 Real time, Int timeStepCount) {
505 mSimTime = time;
506
507 // update controller variables
508 if (mHasExciter) {
509 Real Vpss = mHasPSS ? mPSS->step(**mOmMech, **mElecTorque, (**mVdq)(0, 0),
510 (**mVdq)(1, 0), mTimeStep)
511 : 0.0;
512 mEf_prev = **mEf;
513 **mEf = mExciter->step((**mVdq)(0, 0), (**mVdq)(1, 0), mTimeStep, Vpss);
514 }
515 if (mHasGovernorAndTurbine) {
516 mMechTorque_prev = **mMechTorque;
517 Real Pgv = mGovernor->step(**mOmMech, mTimeStep);
518 **mMechTorque = mTurbine->step(Pgv, mTimeStep);
519 } else if (mHasTurbineGovernor) {
520 mMechTorque_prev = **mMechTorque;
521 **mMechTorque = mTurbineGovernor->step(**mOmMech, mTimeStep);
522 }
523
524 // calculate mechanical variables at t=k+1 with forward euler
525 **mElecTorque =
526 (**mVdq)(0, 0) * (**mIdq)(0, 0) + (**mVdq)(1, 0) * (**mIdq)(1, 0);
527 **mOmMech = **mOmMech +
528 mTimeStep * (1. / (2. * mH) * (mMechTorque_prev - **mElecTorque));
529 **mThetaMech = **mThetaMech + mTimeStep * (**mOmMech * mBase_OmMech);
530 **mDelta = **mDelta + mTimeStep * (**mOmMech - 1.) * mBase_OmMech;
531
532 // model specific calculation of electrical vars
533 stepInPerUnit();
534
535 // stamp model specific right side vector after calculation of electrical vars
536 (**mRightVector).setZero();
537 mnaCompApplyRightSideVectorStamp(**mRightVector);
538}
539
540template <>
541void Base::ReducedOrderSynchronGenerator<Real>::mnaCompPreStep(
542 Real time, Int timeStepCount) {
543 mSimTime = time;
544
545 // update controller variables
546 if (mHasExciter) {
547 Real Vpss = mHasPSS ? mPSS->step(**mOmMech, **mElecTorque, (**mVdq0)(0, 0),
548 (**mVdq0)(1, 0), mTimeStep)
549 : 0.0;
550 mEf_prev = **mEf;
551 **mEf = mExciter->step((**mVdq0)(0, 0), (**mVdq0)(1, 0), mTimeStep, Vpss);
552 }
553 if (mHasGovernorAndTurbine) {
554 mMechTorque_prev = **mMechTorque;
555 Real Pgv = mGovernor->step(**mOmMech, mTimeStep);
556 **mMechTorque = mTurbine->step(Pgv, mTimeStep);
557 } else if (mHasTurbineGovernor) {
558 mMechTorque_prev = **mMechTorque;
559 **mMechTorque = mTurbineGovernor->step(**mOmMech, mTimeStep);
560 }
561
562 // calculate mechanical variables at t=k+1 with forward euler
563 **mElecTorque =
564 ((**mVdq0)(0, 0) * (**mIdq0)(0, 0) + (**mVdq0)(1, 0) * (**mIdq0)(1, 0));
565 **mOmMech = **mOmMech +
566 mTimeStep * (1. / (2. * mH) * (mMechTorque_prev - **mElecTorque));
567 **mThetaMech = **mThetaMech + mTimeStep * (**mOmMech * mBase_OmMech);
568 **mDelta = **mDelta + mTimeStep * (**mOmMech - 1.) * mBase_OmMech;
569
570 // model specific calculation of electrical vars
571 stepInPerUnit();
572
573 // stamp model specific right side vector after calculation of electrical vars
574 (**mRightVector).setZero();
575 mnaCompApplyRightSideVectorStamp(**mRightVector);
576}
577
578template <typename VarType>
580 mnaCompAddPreStepDependencies(AttributeBase::List &prevStepDependencies,
581 AttributeBase::List &attributeDependencies,
582 AttributeBase::List &modifiedAttributes) {
583 modifiedAttributes.push_back(mRightVector);
584 prevStepDependencies.push_back(mIntfVoltage);
585}
586
587template <typename VarType>
588void Base::ReducedOrderSynchronGenerator<VarType>::mnaCompPreStep(
589 Real time, Int timeStepCount) {
590 mSimTime = time;
591 stepInPerUnit();
592 (**mRightVector).setZero();
593 mnaCompApplyRightSideVectorStamp(**mRightVector);
594}
595
596template <typename VarType>
598 mnaCompAddPostStepDependencies(AttributeBase::List &prevStepDependencies,
599 AttributeBase::List &attributeDependencies,
600 AttributeBase::List &modifiedAttributes,
601 Attribute<Matrix>::Ptr &leftVector) {
602 attributeDependencies.push_back(leftVector);
603 modifiedAttributes.push_back(mIntfVoltage);
604}
605
606template <typename VarType>
607void Base::ReducedOrderSynchronGenerator<VarType>::mnaCompPostStep(
608 Real time, Int timeStepCount, Attribute<Matrix>::Ptr &leftVector) {
609 mnaCompPostStep(**leftVector);
610}
611
612template <typename VarType>
614 std::shared_ptr<Base::Exciter> exciter,
615 std::shared_ptr<Base::ExciterParameters> params) {
616 mExciter = exciter;
617 mHasExciter = static_cast<bool>(mExciter);
618 if (!mExciter) {
619 SPDLOG_LOGGER_ERROR(
620 this->mSLog, "addExciter called with null exciter on {}", *this->mName);
621 return;
622 }
623 if (!params) {
624 SPDLOG_LOGGER_WARN(this->mSLog,
625 "addExciter called without parameters on {}",
626 *this->mName);
627 return;
628 }
629 mExciter->setParameters(params);
630}
631
632template <typename VarType>
634 std::shared_ptr<Base::Exciter> exciter) {
635 mExciter = exciter;
636 mHasExciter = static_cast<bool>(mExciter);
637 if (!mExciter) {
638 SPDLOG_LOGGER_ERROR(
639 this->mSLog, "addExciter called with null exciter on {}", *this->mName);
640 return;
641 }
642}
643
644// Deprecated: only for compatibility
645template <typename VarType>
647 Real Ta, Real Ka, Real Te, Real Ke, Real Tf, Real Kf, Real Tr) {
648 SPDLOG_LOGGER_WARN(
649 this->mSLog,
650 "Deprecated API addExciter(Ta,Ka,Te,Ke,Tf,Kf,Tr) called on {}. "
651 "This overload will be removed in the future. "
652 "Please create an ExciterDC1Simp and parameters explicitly.",
653 *this->mName);
654
655 auto params = std::make_shared<CPS::Signal::ExciterDC1SimpParameters>();
656 params->Ta = Ta;
657 params->Ka = Ka;
658 params->Tef = Te; // map Te → Tef
659 params->Kef = Ke; // map Ke → Kef
660 params->Tf = Tf;
661 params->Kf = Kf;
662 params->Tr = Tr;
663
664 // Keep saturation disabled unless explicitly set elsewhere
665 params->Aef = 0.0;
666 params->Bef = 0.0;
667
668 // Apply legacy amplifier limits for the deprecated path
669 params->MaxVa = 1.0;
670 params->MinVa = -0.9;
671
672 auto name = (**this->mName) + std::string("_Exciter");
673 mExciter =
674 std::make_shared<CPS::Signal::ExciterDC1Simp>(name, this->mLogLevel);
675 mExciter->setParameters(params);
676 mHasExciter = true;
677
678 SPDLOG_LOGGER_INFO(
679 this->mSLog,
680 "Attached ExciterDC1Simp to {} (Ka={:.3g}, Ta={:.3g}, Kef={:.3g}, "
681 "Tef={:.3g}, "
682 "Kf={:.3g}, Tf={:.3g}, Tr={:.3g}, MaxVa={:.3g}, MinVa={:.3g})",
683 *this->mName, params->Ka, params->Ta, params->Kef, params->Tef,
684 params->Kf, params->Tf, params->Tr, params->MaxVa, params->MinVa);
685}
686
687template <typename VarType>
689 Real T3, Real T4, Real T5, Real Tc, Real Ts, Real R, Real Pmin, Real Pmax,
690 Real OmRef, Real TmRef) {
691 mTurbineGovernor = Signal::TurbineGovernorType1::make(
692 **this->mName + "_TurbineGovernor", this->mLogLevel);
693 mTurbineGovernor->setParameters(T3, T4, T5, Tc, Ts, R, Pmin, Pmax, OmRef);
694 mTurbineGovernor->initializeStates(TmRef);
695 mHasTurbineGovernor = true;
696}
697
698template <typename VarType>
700 std::shared_ptr<Signal::TurbineGovernorType1> turbineGovernor) {
701 if (!turbineGovernor) {
702 SPDLOG_LOGGER_ERROR(
703 this->mSLog, "addGovernor called with null TurbineGovernorType1 on {}",
704 *this->mName);
705 return;
706 }
707 mTurbineGovernor = turbineGovernor;
708 mHasTurbineGovernor = true;
709}
710
711template <typename VarType>
713 std::shared_ptr<Base::Governor> governor,
714 std::shared_ptr<Base::GovernorParameters> govParams,
715 std::shared_ptr<Base::Turbine> turbine,
716 std::shared_ptr<Base::TurbineParameters> turbineParams) {
717 if (!governor) {
718 SPDLOG_LOGGER_ERROR(this->mSLog,
719 "addGovernorAndTurbine called with null governor on {}",
720 *this->mName);
721 return;
722 }
723 if (!turbine) {
724 SPDLOG_LOGGER_ERROR(this->mSLog,
725 "addGovernorAndTurbine called with null turbine on {}",
726 *this->mName);
727 return;
728 }
729 governor->setParameters(govParams);
730 turbine->setParameters(turbineParams);
731 mGovernor = governor;
732 mTurbine = turbine;
734}
735
736template <typename VarType>
738 std::shared_ptr<Base::Governor> governor,
739 std::shared_ptr<Base::Turbine> turbine) {
740 if (!governor) {
741 SPDLOG_LOGGER_ERROR(this->mSLog,
742 "addGovernorAndTurbine called with null governor on {}",
743 *this->mName);
744 return;
745 }
746 if (!turbine) {
747 SPDLOG_LOGGER_ERROR(this->mSLog,
748 "addGovernorAndTurbine called with null turbine on {}",
749 *this->mName);
750 return;
751 }
752 mGovernor = governor;
753 mTurbine = turbine;
755}
756
757template <typename VarType>
759 std::shared_ptr<Base::PSS> pss,
760 std::shared_ptr<Base::PSSParameters> parameters) {
761 if (!mHasExciter) {
762 SPDLOG_LOGGER_ERROR(this->mSLog, "Cannot attach PSS: no exciter present. "
763 "Attach an exciter first.");
765 }
766 if (!pss) {
767 SPDLOG_LOGGER_ERROR(this->mSLog, "addPSS called with null PSS on {}",
768 *this->mName);
769 return;
770 }
771 mPSS = pss;
772 mPSS->setParameters(parameters);
773 mHasPSS = true;
774}
775
776template <typename VarType>
778 std::shared_ptr<Base::PSS> pss) {
779 if (!mHasExciter) {
780 SPDLOG_LOGGER_ERROR(this->mSLog, "Cannot attach PSS: no exciter present. "
781 "Attach an exciter first.");
783 }
784 if (!pss) {
785 SPDLOG_LOGGER_ERROR(this->mSLog, "addPSS called with null PSS on {}",
786 *this->mName);
787 return;
788 }
789 mPSS = pss;
790 mHasPSS = true;
791}
792
793// Declare specializations to move definitions to .cpp
void addGovernorAndTurbine(std::shared_ptr< Base::Governor > governor, std::shared_ptr< Base::GovernorParameters > govParams, std::shared_ptr< Base::Turbine > turbine, std::shared_ptr< Base::TurbineParameters > turbineParams)
Add a modular governor + turbine pair (new API for SteamTurbineGovernor / SteamTurbine)
Real mTd0_s
Subtransient time constant of d-axis.
void setBaseParameters(Real nomPower, Real nomVolt, Real nomFreq)
void addPSS(std::shared_ptr< Base::PSS > pss, std::shared_ptr< Base::PSSParameters > parameters)
Attach a PSS (initialises parameters) to this generator.
Bool mHasTurbineGovernor
Determines if TurbineGovernorType1 is activated.
void setOperationalParametersPerUnit(Real nomPower, Real nomVolt, Real nomFreq, Real H, Real Ld, Real Lq, Real L0, Real Ld_t, Real Td0_t)
Initialization for 3 Order SynGen.
Bool mHasGovernorAndTurbine
Determines if modular Governor + Turbine pair is activated.
void mnaCompAddPostStepDependencies(AttributeBase::List &prevStepDependencies, AttributeBase::List &attributeDependencies, AttributeBase::List &modifiedAttributes, Attribute< Matrix >::Ptr &leftVector) override
Add MNA post step dependencies.
virtual void setModelAsNortonSource(Bool modelAsCurrentSource)
std::shared_ptr< Base::Governor > mGovernor
Modular governor (SteamTurbineGovernor / HydroTurbineGovernor)
void addExciter(std::shared_ptr< Base::Exciter > exciter, std::shared_ptr< Base::ExciterParameters > params)
Add voltage regulator and exciter.
std::shared_ptr< Base::PSS > mPSS
Power system stabilizer.
std::shared_ptr< Base::Exciter > mExciter
Signal component modelling voltage regulator and exciter.
Real mTaa
d-axis additional leakage time constant
void initializeFromNodesAndTerminals(Real frequency) override
Initializes Component variables according to power flow data stored in Nodes.
Real mTq0_s
Subtransient time constant of q-axis.
void mnaCompAddPreStepDependencies(AttributeBase::List &prevStepDependencies, AttributeBase::List &attributeDependencies, AttributeBase::List &modifiedAttributes) override
Add MNA pre step dependencies.
std::shared_ptr< Base::Turbine > mTurbine
Modular turbine (SteamTurbine / HydroTurbine)
void addGovernor(Real T3, Real T4, Real T5, Real Tc, Real Ts, Real R, Real Pmin, Real Pmax, Real OmRef, Real TmRef)
Add governor and turbine (TurbineGovernorType1 legacy API)
Bool mHasExciter
Determines if Exciter is activated.
std::shared_ptr< Signal::TurbineGovernorType1 > mTurbineGovernor
TurbineGovernorType1 (legacy)
const Attribute< String >::Ptr mName
Human readable name.
Base class for all MNA components that are transmitting power.
Attribute< Matrix >::Ptr mRightVector
const Attribute< MatrixVar< VarType > >::Ptr mIntfVoltage
Voltage between terminals.
Logger::Level mLogLevel
Component logger control for internal variables.
Logger::Log mSLog
Component logger.