Skip to content

Commit 4861044

Browse files
committed
REGCA Implementation and Tests
1 parent 2dd9fc6 commit 4861044

14 files changed

Lines changed: 958 additions & 157 deletions

GridKit/CommonMath.hpp

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,31 @@ namespace GridKit
153153
}
154154

155155
/**
156-
* @brief Smooth two-sided deadband function
156+
* @brief Smooth Type 1 no-offset two-sided deadband function
157+
*
158+
* Smooth approximation to a deadband that returns zero inside the band and
159+
* passes the input through unchanged outside the band.
160+
*
161+
* @tparam ScalarT - scalar data type
162+
* @tparam RealT - Real data type (see GridKit::ScalarTraits<ScalarT>::RealT)
163+
*
164+
* @param[in] x - Input signal
165+
* @param[in] lower - Lower breakpoint
166+
* @param[in] upper - Upper breakpoint
167+
* @return Smooth no-offset deadbanded value
168+
*/
169+
template <class ScalarT, typename RealT>
170+
__attribute__((always_inline)) inline ScalarT deadband1(
171+
const ScalarT x,
172+
const RealT lower,
173+
const RealT upper)
174+
{
175+
assert(lower <= upper);
176+
return x * (sigmoid(lower - x) + sigmoid(x - upper));
177+
}
178+
179+
/**
180+
* @brief Smooth Type 2 offset two-sided deadband function
157181
*
158182
* Smooth approximation to x - min(max(x, lower), upper), composed from the
159183
* smooth ramp function.
@@ -164,10 +188,10 @@ namespace GridKit
164188
* @param[in] x - Input signal
165189
* @param[in] lower - Lower breakpoint
166190
* @param[in] upper - Upper breakpoint
167-
* @return Smooth deadbanded value
191+
* @return Smooth offset deadbanded value
168192
*/
169193
template <class ScalarT, typename RealT>
170-
__attribute__((always_inline)) inline ScalarT deadband(
194+
__attribute__((always_inline)) inline ScalarT deadband2(
171195
const ScalarT x,
172196
const RealT lower,
173197
const RealT upper)

GridKit/CommonMath.md

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ The scale $\mu=4\cdot f_{\text{sync}}=240$ is chosen so $\sigma$ behaves like a
4949
| `max` | Smooth binary maximum | `REECA` |
5050
| `min` | Smooth binary minimum | `REECA` |
5151
| `clamp` | Bounded saturation | `IEEEST` |
52-
| `deadband` | Signed two-sided deadband | `REECA` |
52+
| `deadband1` | Type 1 no-offset signed two-sided deadband | - |
53+
| `deadband2` | Type 2 offset signed two-sided deadband | `REECA` |
5354
| `slew` | Symmetric slew-rate limiter | - |
5455
| `linseg` | Saturated linear segment contribution | `REGCA`, `REECA` |
5556
| `above` | Above-lower-limit indicator | - |
@@ -84,7 +85,15 @@ x & \ell \le x \le u \\
8485
u & x > u
8586
\end{cases}
8687
\\
87-
\text{deadband}(x;\ell,u)
88+
\text{deadband1}(x;\ell,u)
89+
&=
90+
\begin{cases}
91+
x & x < \ell \\
92+
0 & \ell \le x \le u \\
93+
x & x > u
94+
\end{cases}
95+
\\
96+
\text{deadband2}(x;\ell,u)
8897
&=
8998
\begin{cases}
9099
x-\ell & x < \ell \\
@@ -156,7 +165,8 @@ f & x \ge u \land f < 0 \\
156165
\text{max}(x,y) &= y+\rho(x-y) \\
157166
\text{min}(x,y) &= x-\rho(x-y) \\
158167
\text{clamp}(x;\ell,u) &= \ell+\rho(x-\ell)-\rho(x-u) \\
159-
\text{deadband}(x;\ell,u) &= \rho(x-u)-\rho(\ell-x) \\
168+
\text{deadband1}(x;\ell,u) &= x\left[\sigma(\ell-x)+\sigma(x-u)\right] \\
169+
\text{deadband2}(x;\ell,u) &= \rho(x-u)-\rho(\ell-x) \\
160170
\text{slew}(f;r) &= -r+\rho(f+r)-\rho(f-r) \\
161171
\text{linseg}(x;a,b,h) &= \dfrac{h}{b-a}\left[\rho(x-a)-\rho(x-b)\right] \\
162172
\text{above}(x;\ell) &= \sigma(x-\ell) \\

GridKit/Model/PhasorDynamics/Converter/REECA/README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ Symbol | Units | Description |
161161
$V_T$ | [p.u.] | Terminal voltage magnitude |
162162
$V_{\mathrm{meas}}^{\mathrm{safe}}$ | [p.u.] | Safe filtered terminal voltage for divider blocks | Lower bounded by 0.01
163163
$s_{\mathrm{dip}}$ | [binary] | Voltage-dip/overvoltage freeze indicator | 1 when outside voltage thresholds
164-
$V_{\mathrm{err}}$ | [p.u.] | Deadbanded voltage error | Defined by CommonMath `deadband`
164+
$V_{\mathrm{err}}$ | [p.u.] | Deadbanded voltage error | Defined by CommonMath `deadband2`
165165
$I_{\mathrm{qv}}$ | [p.u.] | Reactive-current injection candidate | Converter base
166166
$Q_{\mathrm{ref}}$ | [p.u.] | Selected reactive-power reference | From power-factor or external reactive-power command
167167
$e_Q$ | [p.u.] | Reactive-power control error | Limited $Q_{\mathrm{ref}}$ minus $Q_{\mathrm{gen}}$
@@ -262,7 +262,7 @@ The algebraic targets use CommonMath helper notation where applicable:
262262
0 &= -V_T^2 + V_\mathrm r^2 + V_\mathrm i^2 \\
263263
0 &= -V_\mathrm{meas}^\mathrm{safe} + \max(V_\mathrm{meas}, 0.01) \\
264264
0 &= -s_\mathrm{dip} + \text{outside}(V_T, V_\mathrm{dip}, V_\mathrm{up}) \\
265-
0 &= -V_\mathrm{err} + \text{deadband}(V_\mathrm{ref0} - V_\mathrm{meas}, D_\mathrm{bd1}, D_\mathrm{bd2}) \\
265+
0 &= -V_\mathrm{err} + \text{deadband2}(V_\mathrm{ref0} - V_\mathrm{meas}, D_\mathrm{bd1}, D_\mathrm{bd2}) \\
266266
0 &= -I_\mathrm{qv} + \text{clamp}(K_\mathrm{qv} V_\mathrm{err}, I_\mathrm{qinj}^{\min}, I_\mathrm{qinj}^{\max}) \\
267267
0 &= -Q_\mathrm{ref}
268268
+ s_\mathrm{pf} P_\mathrm{meas}\tan(\phi_\mathrm{pf}^\mathrm{ref})
@@ -290,7 +290,7 @@ The algebraic targets use CommonMath helper notation where applicable:
290290

291291
The $V_T$, $I_{\mathrm{q}}^{\mathrm{circ}}$, and $I_{\mathrm{p}}^{\mathrm{circ}}$ variables use nonnegative branches of squared algebraic residuals. This preserves the $s_{PQ}=0$ Q-priority and $s_{PQ}=1$ P-priority current-circle behavior without explicit square roots; a consistent solution should satisfy the nonnegative branch and nonnegative radicands.
292292

293-
CommonMath defines the helper targets and smooth approximations for [min, max, clamp, deadband, and outside](../../../../CommonMath.md#derived-functions).
293+
CommonMath defines the helper targets and smooth approximations for [min, max, clamp, deadband2, and outside](../../../../CommonMath.md#derived-functions).
294294

295295
## Initialization
296296

@@ -324,7 +324,7 @@ Then evaluate the upstream algebraic chain:
324324
\begin{aligned}
325325
V_{\mathrm{meas},0}^{\mathrm{safe}} &= \text{max}(V_{\mathrm{meas},0}, 0.01) \\
326326
s_{\mathrm{dip},0} &= \text{outside}(V_{T,0}, V_{\mathrm{dip}}, V_{\mathrm{up}}) \\
327-
V_{\mathrm{err},0} &= \text{deadband}(V_{\mathrm{ref0}} - V_{\mathrm{meas},0}, D_{\mathrm{bd1}}, D_{\mathrm{bd2}}) \\
327+
V_{\mathrm{err},0} &= \text{deadband2}(V_{\mathrm{ref0}} - V_{\mathrm{meas},0}, D_{\mathrm{bd1}}, D_{\mathrm{bd2}}) \\
328328
I_{\mathrm{qv},0} &= \text{clamp}(K_{\mathrm{qv}} V_{\mathrm{err},0}, I_{\mathrm{qinj}}^{\min}, I_{\mathrm{qinj}}^{\max}) \\
329329
Q_{\mathrm{ref},0} &= s_{\mathrm{pf}} P_{\mathrm{meas},0}\tan(\phi_{\mathrm{pf},0}^{\mathrm{ref}}) + s_{\mathrm{pf}}^{\mathrm{off}} Q_{\mathrm{ext},0} \\
330330
e_{Q,0} &= \text{clamp}(Q_{\mathrm{ref},0}, Q^{\min}, Q^{\max}) - Q_{\mathrm{gen},0} \\

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ Symbol | Units | Description
2525
---------------------------------|----------|-------------------------------------------------------|---------------|------
2626
$P_{\mathrm{0}}$ | [p.u.] | Initial active power injection | | On system base
2727
$Q_{\mathrm{0}}$ | [p.u.] | Initial reactive power injection | | On system base
28-
$S^{\mathrm{conv}}$ | [MVA] | Converter/model power base | TBD |
28+
$S^{\text{base}}$ | [MVA] | REGCA model power base | TBD | JSON key: `mva_base`
2929
$T_{\mathrm{g}}$ | [sec] | Converter current-control lag time constant | TBD |
3030
$T_M$ | [sec] | Terminal voltage sensor time constant | TBD | Block name: `Tfltr`
3131
$R_{\mathrm{q}}^{\max}$ | [p.u./s] | Reactive-current recovery positive rate limit | TBD | Block name: `Iqrmax`
@@ -45,7 +45,7 @@ Implementations should reject or report invalid parameter sets:
4545

4646
```math
4747
\begin{aligned}
48-
S^{\mathrm{conv}} &> 0 &
48+
S^{\text{base}} &> 0 &
4949
T_{\mathrm{g}} &> 0 &
5050
T_M &> 0 \\
5151
R_{\mathrm{p}}^{\max} &> 0 &
@@ -241,8 +241,8 @@ REGCA currents:
241241

242242
```math
243243
\begin{aligned}
244-
I_{\mathrm{r}}^{\mathrm{inj}} &:= I_{\mathrm{r}}\dfrac{S^{\mathrm{conv}}}{S^{\mathrm{sys}}} \\
245-
I_{\mathrm{i}}^{\mathrm{inj}} &:= I_{\mathrm{i}}\dfrac{S^{\mathrm{conv}}}{S^{\mathrm{sys}}}
244+
I_{\mathrm{r}}^{\mathrm{inj}} &:= I_{\mathrm{r}}\dfrac{S^{\text{base}}}{S^{\text{sys}}} \\
245+
I_{\mathrm{i}}^{\mathrm{inj}} &:= I_{\mathrm{i}}\dfrac{S^{\text{base}}}{S^{\text{sys}}}
246246
\end{aligned}
247247
```
248248

@@ -257,9 +257,9 @@ steady-state initial values:
257257
\begin{aligned}
258258
V_T &= \sqrt{V_\mathrm{r}^2 + V_\mathrm{i}^2} \\
259259
I_\mathrm{r0} &= \dfrac{P_0 V_\mathrm{r} + Q_0 V_\mathrm{i}}{V_T^2}
260-
\dfrac{S^\mathrm{sys}}{S^\mathrm{conv}} \\
260+
\dfrac{S^{\text{sys}}}{S^{\text{base}}} \\
261261
I_\mathrm{i0} &= \dfrac{P_0 V_\mathrm{i} - Q_0 V_\mathrm{r}}{V_T^2}
262-
\dfrac{S^\mathrm{sys}}{S^\mathrm{conv}} \\
262+
\dfrac{S^{\text{sys}}}{S^{\text{base}}} \\
263263
V_{M0} &= V_T \\
264264
I_{L0} &= \text{linseg}(V_T;\ V_{L0},\ V_{L1},\ I_{L1}) \\
265265
I_\mathrm{p0} &= \dfrac{I_\mathrm{r0}}

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

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,12 @@ namespace GridKit
6868
using Component<ScalarT, IdxT>::J_cols_buffer_;
6969
using Component<ScalarT, IdxT>::J_rows_buffer_;
7070
using Component<ScalarT, IdxT>::J_vals_buffer_;
71-
using Component<ScalarT, IdxT>::mva_system_base_;
7271
using Component<ScalarT, IdxT>::nnz_;
7372
using Component<ScalarT, IdxT>::residual_indices_;
7473
using Component<ScalarT, IdxT>::size_;
7574
using Component<ScalarT, IdxT>::tag_;
7675
using Component<ScalarT, IdxT>::time_;
76+
using Component<ScalarT, IdxT>::va_system_base_;
7777
using Component<ScalarT, IdxT>::variable_indices_;
7878
using Component<ScalarT, IdxT>::wb_;
7979
using Component<ScalarT, IdxT>::y_;
@@ -117,6 +117,20 @@ namespace GridKit
117117
private:
118118
void initializeParameters(const model_data_type& data);
119119
void initializeMonitor();
120+
void setDerivedParameters();
121+
122+
ScalarT toComponentBase(ScalarT value) const
123+
{
124+
return value * va_system_base_ / va_converter_base_;
125+
}
126+
127+
ScalarT toSystemBase(ScalarT value) const
128+
{
129+
return value / toComponentBase(static_cast<ScalarT>(ONE<RealT>));
130+
}
131+
132+
ScalarT activeCurrentLowerRateBound(ScalarT ip) const;
133+
ScalarT activeCurrentUpperRateBound(ScalarT ip, ScalarT il) const;
120134

121135
ScalarT& Vr()
122136
{
@@ -142,7 +156,7 @@ namespace GridKit
142156

143157
RealT P0_{0};
144158
RealT Q0_{0};
145-
RealT Sconv_{0};
159+
RealT mva_base_{0};
146160
RealT Tg_{0};
147161
RealT TM_{0};
148162
RealT Rqmax_{0};
@@ -157,6 +171,17 @@ namespace GridKit
157171
RealT Vhvmax_{0};
158172
IdxT bus_id_{0};
159173

174+
IdxT parameter_error_count_{0};
175+
RealT Mp_{0};
176+
RealT va_converter_base_{0};
177+
RealT use_lvpl_{0};
178+
RealT bypass_lvpl_{1};
179+
RealT iq_use_upper_{0};
180+
RealT iq_use_lower_{1};
181+
182+
ScalarT ipcmd_set_{0};
183+
ScalarT iqcmd_set_{0};
184+
160185
ComponentSignals<ScalarT, IdxT, RegcaInternalVariables, RegcaExternalVariables> signals_;
161186
std::unique_ptr<MonitorT> monitor_;
162187

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

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,21 @@ namespace GridKit
1717
/// Parameter keys for the REGCA converter model.
1818
enum class RegcaParameters
1919
{
20-
P0, ///< Initial active power injection on system base
21-
Q0, ///< Initial reactive power injection on system base
22-
Sconv, ///< Converter/model power base
23-
Tg, ///< Converter current-control lag time constant
24-
TM, ///< Terminal voltage sensor time constant
25-
Rqmax, ///< Reactive-current recovery positive rate limit
26-
Rqmin, ///< Reactive-current recovery negative rate limit
27-
Rpmax, ///< Active-current magnitude recovery rate limit
28-
sL, ///< LVPL switch
29-
IL1, ///< LVPL upper-current ceiling
30-
VL0, ///< LVPL zero-crossing voltage
31-
VL1, ///< LVPL upper breakpoint voltage
32-
VA0, ///< LVACM lower breakpoint voltage
33-
VA1, ///< LVACM upper breakpoint voltage
34-
Vhvmax ///< Terminal-voltage ceiling for HV reactive management
20+
P0, ///< Initial active power injection on system base
21+
Q0, ///< Initial reactive power injection on system base
22+
mva_base, ///< MVA base of the REGCA model
23+
Tg, ///< Converter current-control lag time constant
24+
TM, ///< Terminal voltage sensor time constant
25+
Rqmax, ///< Reactive-current recovery positive rate limit
26+
Rqmin, ///< Reactive-current recovery negative rate limit
27+
Rpmax, ///< Active-current magnitude recovery rate limit
28+
sL, ///< LVPL switch
29+
IL1, ///< LVPL upper-current ceiling
30+
VL0, ///< LVPL zero-crossing voltage
31+
VL1, ///< LVPL upper breakpoint voltage
32+
VA0, ///< LVACM lower breakpoint voltage
33+
VA1, ///< LVACM upper breakpoint voltage
34+
Vhvmax ///< Terminal-voltage ceiling for HV reactive management
3535
};
3636

3737
/// Ports for the REGCA converter model.

GridKit/Model/PhasorDynamics/Converter/REGCA/RegcaEnzyme.cpp

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,85 @@ namespace GridKit
2121
Log::misc() << "Jacobian evaluation is experimental!" << std::endl;
2222

2323
J_.zeroMatrix();
24+
if (J_rows_buffer_ == nullptr)
25+
{
26+
// Reserve space for a dense internal block. Enzyme's sparse storage
27+
// keeps only structural nonzeros for each differentiated block.
28+
J_rows_buffer_ = new IdxT[static_cast<size_t>(size_) * static_cast<size_t>(size_)];
29+
J_cols_buffer_ = new IdxT[static_cast<size_t>(size_) * static_cast<size_t>(size_)];
30+
J_vals_buffer_ = new RealT[static_cast<size_t>(size_) * static_cast<size_t>(size_)];
31+
}
32+
33+
using RegcaT = GridKit::PhasorDynamics::Converter::Regca<ScalarT, IdxT>;
34+
using Fn = GridKit::Enzyme::Sparse::MemberFunctions;
35+
36+
GridKit::Enzyme::Sparse::DfDy<RegcaT,
37+
Fn::InternalResidualWithSignal,
38+
ScalarT,
39+
IdxT>::eval(this,
40+
f_.size(),
41+
y_.size(),
42+
(this->getResidualIndices()).data(),
43+
(this->getVariableIndices()).data(),
44+
y_.data(),
45+
yp_.data(),
46+
wb_.data(),
47+
ws_.data(),
48+
alpha_,
49+
J_rows_buffer_,
50+
J_cols_buffer_,
51+
J_vals_buffer_,
52+
J_);
53+
54+
GridKit::Enzyme::Sparse::DfDwb<RegcaT,
55+
Fn::InternalResidualWithSignal,
56+
ScalarT,
57+
IdxT>::eval(this,
58+
f_.size(),
59+
static_cast<size_t>(bus_->size()),
60+
(this->getResidualIndices()).data(),
61+
(bus_->getVariableIndices()).data(),
62+
y_.data(),
63+
yp_.data(),
64+
wb_.data(),
65+
ws_.data(),
66+
J_rows_buffer_,
67+
J_cols_buffer_,
68+
J_vals_buffer_,
69+
J_);
70+
71+
GridKit::Enzyme::Sparse::DfDws<RegcaT,
72+
Fn::InternalResidualWithSignal,
73+
ScalarT,
74+
IdxT>::eval(this,
75+
f_.size(),
76+
ws_.size(),
77+
(this->getResidualIndices()).data(),
78+
ws_indices_.data(),
79+
y_.data(),
80+
yp_.data(),
81+
wb_.data(),
82+
ws_.data(),
83+
J_rows_buffer_,
84+
J_cols_buffer_,
85+
J_vals_buffer_,
86+
J_);
87+
88+
GridKit::Enzyme::Sparse::DhDy<RegcaT,
89+
Fn::BusResidual,
90+
ScalarT,
91+
IdxT>::eval(this,
92+
static_cast<size_t>(bus_->size()),
93+
y_.size(),
94+
(bus_->getResidualIndices()).data(),
95+
(this->getVariableIndices()).data(),
96+
y_.data(),
97+
yp_.data(),
98+
wb_.data(),
99+
J_rows_buffer_,
100+
J_cols_buffer_,
101+
J_vals_buffer_,
102+
J_);
24103
return 0;
25104
}
26105

0 commit comments

Comments
 (0)