Skip to content

Commit 0ff72a1

Browse files
authored
BUG: Internal limit states (#640)
* repca fix * no floating point comaprison * two other misc limiter changes * REGCA HV limit fix * add stress test that engages many limits * polishing and remove aux file
1 parent a124b44 commit 0ff72a1

28 files changed

Lines changed: 3088 additions & 172 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@
1919
- Migrated PhasorDynamics models with real and Boolean parameters to `Model::ParameterReader`.
2020
- Added component labels and lookups that search subsystems.
2121
- Added ACTIVSg25k and ACTIVSg70k PhasorDynamics cases.
22+
- Fixed REPCA PI windup while saturated; the PI states now track the limited outputs.
23+
- Fixed REECB to limit the direct voltage reference (`VFlag` off) to [`Vmin`, `Vmax`].
24+
- Fixed ESDC1A rate feedback to use the limited field-voltage rate.
25+
- Fixed REGCA to hold the terminal voltage at `Vhvmax`, as PowerWorld does; `Khv` is accepted but unused.
26+
- Added the FourBusLimits validation case, which drives the REPCA, REECB, REGCA, and ESDC1A limits.
2227

2328
## v0.2
2429

‎GridKit/Model/PhasorDynamics/Controller/REECB/README.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ Smooth functions: [`antiwindup`](../../../../CommonMath.md#antiwindup), [`clamp`
205205
0 &= -Q^\mathrm{ref}+s_Q^\mathrm{ref}(s_\mathrm{pf}P^\mathrm{meas}\tan(\phi^\mathrm{ref})+s_\mathrm{pf}^\mathrm{off}k_\mathrm{base}Q^\mathrm{ext}) \\
206206
0 &= -e_Q+\text{clamp}(Q^\mathrm{ref};\,Q^{\min},Q^{\max})-k_\mathrm{base}Q^\mathrm{gen} \\
207207
0 &= -V_Q^\mathrm{PI}+\text{clamp}(K_\mathrm{qp}e_Q+x_Q^\mathrm{PI};\,V^{\min},V^{\max}) \\
208-
0 &= -e_V^\mathrm{PI}+s_Q^\mathrm{PI}V_Q^\mathrm{PI}+s_V^\mathrm{ref}Q^\mathrm{ext}-s_QV^\mathrm{meas} \\
208+
0 &= -e_V^\mathrm{PI}+s_Q^\mathrm{PI}V_Q^\mathrm{PI}+s_V^\mathrm{ref}\text{clamp}(Q^\mathrm{ext};\,V^{\min},V^{\max})-s_QV^\mathrm{meas} \\
209209
0 &= -r_P^\mathrm{ord}+\text{aslew}\!\left(\dfrac{k_\mathrm{base}P^\mathrm{ref}-P^\mathrm{ord}}{T_\mathrm{pord}};\,R_P^{\min},R_P^{\max}\right) \\
210210
0 &= -I_L^\mathrm{cap}+\text{sqrtramp}\big((I^{\max}-I^\mathrm{high})(I^{\max}+I^\mathrm{high})\big) \\
211211
0 &= -I_q^{\max}+s_\mathrm{pq}I_L^\mathrm{cap}+s_\mathrm{pq}^\mathrm{off}I^{\max} \\
@@ -321,7 +321,7 @@ give
321321
0 & s_Qs_V=0
322322
\end{cases} \\
323323
V_Q^\mathrm{PI} &\leftarrow \text{clamp}(K_\mathrm{qp}e_Q+x_Q^\mathrm{PI};\,V^{\min},V^{\max}) \\
324-
e_V^\mathrm{PI} &\leftarrow s_Q^\mathrm{PI}V_Q^\mathrm{PI}+s_V^\mathrm{ref}Q^\mathrm{ext}-s_QV^\mathrm{meas} \\
324+
e_V^\mathrm{PI} &\leftarrow s_Q^\mathrm{PI}V_Q^\mathrm{PI}+s_V^\mathrm{ref}\text{clamp}(Q^\mathrm{ext};\,V^{\min},V^{\max})-s_QV^\mathrm{meas} \\
325325
x_V^\mathrm{PI} &\leftarrow
326326
\begin{cases}
327327
-K_\mathrm{vp}e_V^\mathrm{PI} & s_Q=1\ \land\ I_q^{\max}\le\epsilon_\mathrm{init} \\
@@ -353,7 +353,7 @@ limit, latch, or signal is changed.
353353
\end{cases} \\
354354
Q^\mathrm{ext} &\leftarrow
355355
\begin{cases}
356-
V^\mathrm{meas} & s_V^\mathrm{ref}=1 \\
356+
\text{iclamp}(V^\mathrm{meas};\,V^{\min},V^{\max}) & s_V^\mathrm{ref}=1 \\
357357
0 & s_V^\mathrm{ref}=0\ \land\ s_\mathrm{pf}=1 \\
358358
Q^\mathrm{target}/k_\mathrm{base} & s_V^\mathrm{ref}=0\ \land\ s_\mathrm{pf}=0
359359
\end{cases} \\

‎GridKit/Model/PhasorDynamics/Controller/REECB/ReecbImpl.hpp‎

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -443,14 +443,13 @@ namespace GridKit
443443
RealT qmax = Qmax_;
444444
RealT vmin = Vmin_;
445445
RealT vmax = Vmax_;
446+
447+
const RealT infinity = std::numeric_limits<RealT>::infinity();
446448
if (QFlag_ && VFlag_)
447449
{
448450
qmin = std::min(Qmin_, qgen0);
449451
qmax = std::max(Qmax_, qgen0);
450-
vmin = std::min(Vmin_, vmeas0);
451-
vmax = std::max(Vmax_, vmeas0);
452452

453-
const RealT infinity = std::numeric_limits<RealT>::infinity();
454453
if (qmin == qgen0 && qmin < qmax)
455454
{
456455
qmin = std::nextafter(qmin, -infinity);
@@ -459,6 +458,12 @@ namespace GridKit
459458
{
460459
qmax = std::nextafter(qmax, infinity);
461460
}
461+
}
462+
if (QFlag_)
463+
{
464+
vmin = std::min(Vmin_, vmeas0);
465+
vmax = std::max(Vmax_, vmeas0);
466+
462467
if (vmin == vmeas0 && vmin < vmax)
463468
{
464469
vmin = std::nextafter(vmin, -infinity);
@@ -489,7 +494,11 @@ namespace GridKit
489494

490495
if (QFlag_ && !VFlag_)
491496
{
492-
qext0_port = vmeas0;
497+
if (!iclamp(vmeas0, vmin, vmax, qext0_port))
498+
{
499+
Log::error() << "Reecb: voltage limiter has no finite steady input\n";
500+
return false;
501+
}
493502
}
494503
else if (PfFlag_)
495504
{
@@ -541,7 +550,7 @@ namespace GridKit
541550
}
542551
else
543552
{
544-
epiv0 = qext0_port - vmeas0;
553+
epiv0 = Math::clamp(qext0_port, vmin, vmax) - vmeas0;
545554
}
546555

547556
if (iqmax0 <= INITIALIZATION_TOLERANCE)
@@ -991,7 +1000,7 @@ namespace GridKit
9911000
f[QREF] = -qref + qref_target;
9921001
f[EQ] = -eq + Math::clamp(qref, Qmin_, Qmax_) - qgen;
9931002
f[VPIQ] = -vpiq + Math::clamp(q_pi_state, Vmin_, Vmax_);
994-
f[EPIV] = -epiv + q_pi_on_ * vpiq + v_ref_on_ * extref - q_on_ * vmeas;
1003+
f[EPIV] = -epiv + q_pi_on_ * vpiq + v_ref_on_ * Math::clamp(extref, Vmin_, Vmax_) - q_on_ * vmeas;
9951004
f[RPORD] = -rpord + aslew(fpord, dPmin_, dPmax_);
9961005
f[ILCAP] = -ilcap + sqrtramp(circleSquare(Imax_, high));
9971006
f[IQMAX] = -iqmax + pq_on_ * ilcap + pq_off_ * Imax_;

‎GridKit/Model/PhasorDynamics/Controller/REPCA/README.md‎

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ A valid REPCA parameter set must satisfy the following conditions:
6868
\begin{aligned}
6969
S^\mathrm{base} &> 0 \\
7070
T_\mathrm{fltr}, T_\mathrm{ft}, T_\mathrm{fv}, T_\mathrm{p}, T_\mathrm{lag} &\ge 0 \\
71+
K_\mathrm{p}, K_\mathrm{pg} &\ge 0 \\
7172
D_\mathrm{bd1} &\le 0 \le D_\mathrm{bd2} \\
7273
e^{\min} &\le 0 \le e^{\max} \\
7374
Q^{\min} &\le Q^{\max} \\
@@ -188,6 +189,9 @@ $f^\mathrm{ref}$ | [p.u.] | Frequency reference
188189

189190
Smooth functions: [`above`](../../../../CommonMath.md#above), [`antiwindup`](../../../../CommonMath.md#antiwindup), [`clamp`](../../../../CommonMath.md#clamp), [`deadband2`](../../../../CommonMath.md#type-ii-deadband).
190191

192+
The PI controllers feed back the limited outputs.[^pi-feedback] Limiting the
193+
state as well keeps it constant at zero error.
194+
191195
### Internal Equations
192196

193197
#### Differential
@@ -196,10 +200,16 @@ Smooth functions: [`above`](../../../../CommonMath.md#above), [`antiwindup`](../
196200
\begin{aligned}
197201
0 &= -\dot{V}^\mathrm{meas} + \dfrac{1}{T_\mathrm{fltr}} (V^\mathrm{ctrl} - V^\mathrm{meas}) \\
198202
0 &= -\dot{Q}^\mathrm{meas} + \dfrac{1}{T_\mathrm{fltr}} (k_\mathrm{base}Q - Q^\mathrm{meas}) \\
199-
0 &= -\dot{x}_Q^\mathrm{PI} + s_\mathrm{frz}\, \text{antiwindup}(Q^\mathrm{PI}, K_\mathrm{i}e_\mathrm{RQ}^\mathrm{lim};\,Q^{\min}, Q^{\max}) \\
203+
0 &= -\dot{x}_Q^\mathrm{PI} + s_\mathrm{frz}\, \begin{cases}
204+
\dfrac{K_\mathrm{i}}{K_\mathrm{p}}\left(Q^\mathrm{PI}-\text{clamp}(x_Q^\mathrm{PI};\,Q^{\min},Q^{\max})\right) & K_\mathrm{p}>0 \\
205+
\text{antiwindup}(x_Q^\mathrm{PI}, K_\mathrm{i}e_\mathrm{RQ}^\mathrm{lim};\,Q^{\min},Q^{\max}) & K_\mathrm{p}=0
206+
\end{cases} \\
200207
0 &= -\dot{x}_Q^\mathrm{lag} + \dfrac{1}{T_\mathrm{fv}} (Q^\mathrm{PI} - x_Q^\mathrm{lag}) \\
201208
0 &= -\dot{P}^\mathrm{meas} + \dfrac{1}{T_\mathrm{p}} (k_\mathrm{base}P - P^\mathrm{meas}) \\
202-
0 &= -\dot{x}_P^\mathrm{PI} + \text{antiwindup}(P^\mathrm{PI}, K_\mathrm{ig}e_P^\mathrm{lim};\,P^{\min}, P^{\max}) \\
209+
0 &= -\dot{x}_P^\mathrm{PI} + \begin{cases}
210+
\dfrac{K_\mathrm{ig}}{K_\mathrm{pg}}\left(P^\mathrm{PI}-\text{clamp}(x_P^\mathrm{PI};\,P^{\min},P^{\max})\right) & K_\mathrm{pg}>0 \\
211+
\text{antiwindup}(x_P^\mathrm{PI}, K_\mathrm{ig}e_P^\mathrm{lim};\,P^{\min},P^{\max}) & K_\mathrm{pg}=0
212+
\end{cases} \\
203213
0 &= -\dot{P}^\mathrm{ref} + \dfrac{1}{T_\mathrm{lag}} (P^\mathrm{PI} - P^\mathrm{ref})
204214
\end{aligned}
205215
```
@@ -299,11 +309,8 @@ If an initial PI output falls outside its configured limits, REPCA expands the
299309
limits to include it and logs a warning. This matches PowerWorld's default
300310
`Modify Limits and Run` treatment of initial limit violations.
301311

302-
Initialization rejects an operating point if:
303-
304-
- a required input or derived value is not finite;
305-
- the gated reactive-power or active-power PI state rate is nonfinite or does
306-
not vanish within the implementation tolerance.
312+
Initialization rejects an operating point if a required input or derived value
313+
is not finite.
307314

308315
Initialization is atomic; candidates are validated before state or signal writes.
309316

@@ -342,3 +349,5 @@ $\text{droop}(0;D_\mathrm{dn},D_\mathrm{up})=0$.
342349
[^frequency-measurement]: Background for phase-derived, filtered frequency
343350
measurement: [PSCAD Frequency/Phase/Magnitude Meter](https://www.pscad.com/webhelp-pscad-v5.1.0-ol/Master_Library_Models/Meters/Frequency_Phase_Magnitude_Meter.htm) and
344351
[Ting et al., *Evaluating Methods for Measuring Grid Frequency in Low-Inertia Power Systems*](https://research-hub.nlr.gov/en/publications/evaluating-methods-for-measuring-grid-frequency-in-low-inertia-po-3/).
352+
353+
[^pi-feedback]: See PowerWorld's [DER_A feedback PI, p. 3](https://www.powerworld.com/WebHelp/Content/TransientModels_PDF/Generator/Machine/Machine%20Model%20DER_A.pdf#page=3).

‎GridKit/Model/PhasorDynamics/Controller/REPCA/Repca.hpp‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,11 @@ namespace GridKit
206206
RealT ref_off_{ZERO<RealT>};
207207
RealT freq_on_{ZERO<RealT>};
208208

209+
RealT q_feedback_gain_{ONE<RealT>};
210+
RealT q_integral_gain_{ZERO<RealT>};
211+
RealT p_feedback_gain_{ONE<RealT>};
212+
RealT p_integral_gain_{ZERO<RealT>};
213+
209214
ScalarT freqref_set_{ONE<RealT>};
210215
ScalarT vref_set_{ONE<RealT>};
211216
ScalarT qref_set_{ZERO<RealT>};

‎GridKit/Model/PhasorDynamics/Controller/REPCA/RepcaImpl.hpp‎

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,8 @@ namespace GridKit
155155
"system/component power-base conversion ratios must be finite and positive");
156156
}
157157

158+
checks.check(Kp_ >= ZERO<RealT>, "Kp must be non-negative");
159+
checks.check(Kpg_ >= ZERO<RealT>, "Kpg must be non-negative");
158160
checks.check(dbdlow_ <= ZERO<RealT> && ZERO<RealT> <= dbdupper_,
159161
"dbdlow <= 0 <= dbdupper is required");
160162
checks.check(emin_ <= ZERO<RealT> && ZERO<RealT> <= emax_,
@@ -335,15 +337,7 @@ namespace GridKit
335337
Log::error() << "Repca: reactive-power PI limiter has no finite steady input\n";
336338
return 1;
337339
}
338-
const ScalarT xqpi0 = qpi_input0 - Kp_ * erqlim0;
339-
const ScalarT q_aw_rate0 = Math::antiwindup(qpi0, Ki_ * erqlim0, qmin, qmax);
340-
const ScalarT xqpi_rate0 = sfrz0 * q_aw_rate0;
341-
if (!is_finite(q_aw_rate0) || !is_finite(xqpi_rate0)
342-
|| std::abs(static_cast<RealT>(xqpi_rate0)) > INITIALIZATION_TOLERANCE)
343-
{
344-
Log::error() << "Repca: reactive-power PI state rate is nonzero at initialization\n";
345-
return 1;
346-
}
340+
const ScalarT xqpi0 = qpi_input0 - Kp_ * erqlim0;
347341

348342
ScalarT frequency_error0{};
349343
ScalarT ep0{};
@@ -368,14 +362,7 @@ namespace GridKit
368362
Log::error() << "Repca: active-power PI limiter has no finite steady input\n";
369363
return 1;
370364
}
371-
const ScalarT xppi0 = ppi_input0 - Kpg_ * eplim0;
372-
const ScalarT p_aw_rate0 = Math::antiwindup(ppi0, Kig_ * eplim0, pmin, pmax);
373-
if (!is_finite(p_aw_rate0)
374-
|| std::abs(static_cast<RealT>(p_aw_rate0)) > INITIALIZATION_TOLERANCE)
375-
{
376-
Log::error() << "Repca: active-power PI antiwindup rate is nonzero at initialization\n";
377-
return 1;
378-
}
365+
const ScalarT xppi0 = ppi_input0 - Kpg_ * eplim0;
379366

380367
const ScalarT pext_output0 = Freqflag_ ? pext0_system : zero;
381368
const ScalarT freqref0 = freq + frequency_error0;
@@ -415,8 +402,6 @@ namespace GridKit
415402
&& is_finite(xqlag0)
416403
&& is_finite(qpi_input0)
417404
&& is_finite(xqpi0)
418-
&& is_finite(q_aw_rate0)
419-
&& is_finite(xqpi_rate0)
420405
&& is_finite(ef0)
421406
&& is_finite(pfreq0)
422407
&& is_finite(ep0)
@@ -425,7 +410,6 @@ namespace GridKit
425410
&& is_finite(ppi0)
426411
&& is_finite(ppi_input0)
427412
&& is_finite(xppi0)
428-
&& is_finite(p_aw_rate0)
429413
&& is_finite(pext_output0)
430414
&& is_finite(freqref0)
431415
&& is_finite(vref0)
@@ -793,12 +777,17 @@ namespace GridKit
793777
const ScalarT vldc_i = vi - Rc_ * ii - Xc_ * ir;
794778
const ScalarT pfreq = droop(ef, Ddn_, Dup_);
795779

780+
const ScalarT q_pi_rate = q_feedback_gain_ * (qpi - Math::clamp(xqpi, Qmin_, Qmax_))
781+
+ Math::antiwindup(xqpi, q_integral_gain_ * erqlim, Qmin_, Qmax_);
782+
const ScalarT p_pi_rate = p_feedback_gain_ * (ppi - Math::clamp(xppi, Pmin_, Pmax_))
783+
+ Math::antiwindup(xppi, p_integral_gain_ * eplim, Pmin_, Pmax_);
784+
796785
f[VMEAS] = -vmeas_dot + (vctrl - vmeas) / Tfltr_;
797786
f[QMEAS] = -qmeas_dot + (q - qmeas) / Tfltr_;
798-
f[XQPI] = -xqpi_dot + sfrz * Math::antiwindup(qpi, Ki_ * erqlim, Qmin_, Qmax_);
787+
f[XQPI] = -xqpi_dot + sfrz * q_pi_rate;
799788
f[XQLAG] = -xqlag_dot + (qpi - xqlag) / Tfv_;
800789
f[PMEAS] = -pmeas_dot + (p - pmeas) / Tp_;
801-
f[XPPI] = -xppi_dot + Math::antiwindup(ppi, Kig_ * eplim, Pmin_, Pmax_);
790+
f[XPPI] = -xppi_dot + p_pi_rate;
802791
f[PREF_STATE] = -pref_dot + (ppi - pref) / Tlag_;
803792

804793
f[V] = -v * v + vr * vr + vi * vi;
@@ -929,7 +918,7 @@ namespace GridKit
929918
* @brief Resolve parameter-derived constants
930919
*
931920
* Raises the explicit controller lags in place, computes the component
932-
* power base, and resolves selector masks.
921+
* power base, and resolves selector masks and PI rate gains.
933922
*/
934923
template <typename scalar_type, typename index_type>
935924
void Repca<scalar_type, index_type>::setDerivedParameters()
@@ -956,6 +945,22 @@ namespace GridKit
956945
ref_on_ = RefFlag_ ? ONE<RealT> : ZERO<RealT>;
957946
ref_off_ = ONE<RealT> - ref_on_;
958947
freq_on_ = Freqflag_ ? ONE<RealT> : ZERO<RealT>;
948+
949+
q_feedback_gain_ = ZERO<RealT>;
950+
q_integral_gain_ = Ki_;
951+
if (Kp_ > ZERO<RealT>)
952+
{
953+
q_feedback_gain_ = Ki_ / Kp_;
954+
q_integral_gain_ = ZERO<RealT>;
955+
}
956+
957+
p_feedback_gain_ = ZERO<RealT>;
958+
p_integral_gain_ = Kig_;
959+
if (Kpg_ > ZERO<RealT>)
960+
{
961+
p_feedback_gain_ = Kig_ / Kpg_;
962+
p_integral_gain_ = ZERO<RealT>;
963+
}
959964
}
960965

961966
/**

‎GridKit/Model/PhasorDynamics/Converter/REGCA/README.md‎

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,16 @@ $V_{L0}$ | [p.u.] | `VL0` | LVPL zero-crossing voltage
3232
$V_{L1}$ | [p.u.] | `VL1` | LVPL upper breakpoint voltage | 0.9 | Source label: `brkpt`
3333
$V_{A0}$ | [p.u.] | `VA0` | LVACM lower breakpoint voltage | 0.4 | Source label: `LVPnt0`
3434
$V_{A1}$ | [p.u.] | `VA1` | LVACM upper breakpoint voltage | 0.9 | Source label: `LVPnt1`
35-
$V_\mathrm{hv}^{\max}$ | [p.u.] | `Vhvmax` | HV reactive management activation threshold | 1.2 | Source label: `VLim`
36-
$Q^{\min}$ | [p.u.] | `Qmin` | PowerWorld compatibility field | | Optional; accepted but unused
37-
$K_\mathrm{hv}$ | [p.u.] | `Khv` | HV reactive management gain | 0.7 | Optional; defaults to 0.7; block name: `Khv`
38-
$X_\mathrm{e}$ | [p.u.] | `Xe` | PowerWorld compatibility field | | Optional; accepted but unused
35+
$V_\mathrm{hv}^{\max}$ | [p.u.] | `Vhvmax` | Terminal-voltage ceiling | 1.2 | Source label: `VLim`
36+
$Q^{\min}$ | [p.u.] | `Qmin` | Case-file compatibility field | | Optional; accepted but unused
37+
$K_\mathrm{hv}$ | [p.u.] | `Khv` | Case-file compatibility field | | Optional; accepted but unused
38+
$X_\mathrm{e}$ | [p.u.] | `Xe` | Case-file compatibility field | | Optional; accepted but unused
3939

4040
Omitted parameters default to 0 unless noted.
4141

42+
`Qmin`, `Khv`, and `Xe` are retained for compatibility with existing case
43+
files and do not affect this model's equations.
44+
4245
### Parameter Validation
4346

4447
A valid REGCA parameter set must satisfy the following conditions:
@@ -61,9 +64,7 @@ A valid REGCA parameter set must satisfy the following conditions:
6164
0
6265
&\le V_{L0} < V_{L1} \\
6366
0
64-
&\le V_{A0} < V_{A1} < V_\mathrm{hv}^{\max} \\
65-
0 \le K_\mathrm{hv}
66-
&< \infty
67+
&\le V_{A0} < V_{A1} < V_\mathrm{hv}^{\max}
6768
\end{aligned}
6869
```
6970

@@ -199,8 +200,7 @@ the sign that enables the corresponding limit.
199200
0 &= -k_\mathrm{base} V_T I_i
200201
- V_r(I_q - I_q^\mathrm{extra})
201202
+ V_i I_p\,\text{linseg}(V_T; V_{A0}, V_{A1}, 1) \\
202-
0 &= -I_q^\mathrm{extra}
203-
+ K_\mathrm{hv}\,\text{ramp}(V_T - V_\mathrm{hv}^{\max}) \\
203+
0 &= -\text{min}(I_q^\mathrm{extra}, V_\mathrm{hv}^{\max}-V_T) \\
204204
0 &= -I_L
205205
+ \text{linseg}(V_M; V_{L0}, V_{L1}, I_{L1})
206206
+ K_L\,\text{ramp}(V_M - V_{L1}) \\
@@ -233,8 +233,8 @@ the sign that enables the corresponding limit.
233233

234234
### Internal Initialization
235235

236-
Initialization requires $V_{A1}\le V_T$ and, with LVPL enabled, $I_p\le I_L$.
237-
All internal derivatives initialize to zero.
236+
Initialization requires $V_{A1}\le V_T<V_\mathrm{hv}^{\max}$ and, with LVPL enabled, $I_p\le I_L$.
237+
All internal derivatives initialize to zero. [Appendix C](#appendix-c-iramp) defines `iramp`.
238238

239239
```math
240240
\begin{aligned}
@@ -243,7 +243,7 @@ All internal derivatives initialize to zero.
243243
I_L &\leftarrow \text{linseg}(V_T;V_{L0},V_{L1},I_{L1})
244244
+K_L\,\text{ramp}(V_T-V_{L1}) \\
245245
I_p &\leftarrow \dfrac{k_\mathrm{base}P_0}{V_T\,\text{linseg}(V_T;V_{A0},V_{A1},1)} \\
246-
I_q^\mathrm{extra} &\leftarrow K_\mathrm{hv}\,\text{ramp}(V_T-V_\mathrm{hv}^{\max}) \\
246+
I_q^\mathrm{extra} &\leftarrow V_\mathrm{hv}^{\max}-V_T-\text{iramp}(V_\mathrm{hv}^{\max}-V_T) \\
247247
I_q &\leftarrow \dfrac{k_\mathrm{base}Q_0}{V_T}+I_q^\mathrm{extra} \\
248248
I_r &\leftarrow \dfrac{V_rP_0+V_iQ_0}{V_T^2} \\
249249
I_i &\leftarrow \dfrac{V_iP_0-V_rQ_0}{V_T^2} \\
@@ -333,3 +333,14 @@ The model evaluates this rule with the following smooth approximation:
333333

334334
With a stationary bound ($\dot u = 0$) this reduces to the `antiwindup` of
335335
CommonMath restricted to its upper limit, which admits an algebraic bound $u$.
336+
337+
## Appendix C: `iramp`
338+
339+
For a positive smooth-ramp output $v>0$ and smoothing parameter $\mu$,
340+
341+
```math
342+
\text{iramp}(v) = v+\dfrac{1}{\mu}\log(1-\exp(-\mu v))
343+
```
344+
345+
This is the positive-range inverse of GridKit's smooth
346+
[`ramp`](../../../../CommonMath.md#ramp).

‎GridKit/Model/PhasorDynamics/Converter/REGCA/Regca.hpp‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,8 @@ namespace GridKit
196196
ScalarT& Ir();
197197
ScalarT& Ii();
198198

199+
static RealT iramp(RealT value);
200+
199201
static constexpr RealT TIME_CONSTANT_MINIMUM = static_cast<RealT>(1.0e-3);
200202
static constexpr RealT KL_ = static_cast<RealT>(100.0);
201203
static void logTimeConstantWarning();
@@ -218,7 +220,6 @@ namespace GridKit
218220
RealT VA0_{0};
219221
RealT VA1_{0};
220222
RealT Vhvmax_{0};
221-
RealT Khv_{0.7};
222223

223224
// Derived parameters
224225
RealT use_lvpl_{0};

‎GridKit/Model/PhasorDynamics/Converter/REGCA/RegcaData.hpp‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,11 @@ namespace GridKit
3232
VL1, ///< LVPL upper breakpoint voltage
3333
VA0, ///< LVACM lower breakpoint voltage
3434
VA1, ///< LVACM upper breakpoint voltage
35-
Vhvmax, ///< Terminal-voltage activation threshold for HV reactive management
35+
Vhvmax, ///< Terminal-voltage ceiling for HV reactive management
3636

37-
Qmin, ///< Optional PowerWorld compatibility field, accepted and unused
38-
Khv, ///< Optional HV reactive management gain, defaults to 0.7
39-
Xe, ///< Optional PowerWorld compatibility field, accepted and unused
37+
Qmin, ///< Optional case-file compatibility field, accepted and unused
38+
Khv, ///< Optional case-file compatibility field, accepted and unused
39+
Xe, ///< Optional case-file compatibility field, accepted and unused
4040
};
4141

4242
/// Buses for the REGCA converter model.

0 commit comments

Comments
 (0)