Governor Model: H6E
Following checks and corrections are applied during Validation and AutoCorrection.
- Values of Tpe, Td, Tg, Tbd, and Tbs must be larger than Mult*TimeStep. If they are between 0 and 0.5*Mult*TimeStep, then they are changed to 0.0. If they are between 0.5*Mult*TimeStep they are changed to Mult*TimeStep
- Abs(Tsp) must be larger than Mult*TimeStep. If it is not it is changed to an absolute value of Mult*TimeStep. Reminder that Tsp can be negative indicating that the input is per unit bus frequency instead of per unit rotor speed.
- If 0.0 < Tw < Mult*TimeStep then Tw = Mult*TimeStep
- If Gmax < Gmin then the values are flipped by auto-correction
- If (Bgvmin >= 0.99999) OR (Bgvmin < 0) then an error message is displayed and the simulation will not run.
Following treatment is handled during the transient numerical simulation
- If Fd is not either 0 or 1, then it is treated as though it is a 1.
- If Ki <= 0.000001 then it is treated as a value of 0.000001
- If Gate > Gmax, then Gmax = Gate or if Gate < Gmin, then Gmin = Gate
- Negative values of velm, buv, blg, dbbd, blb, dbbs, and blv will be treated as the absolute value of the number specified
- Negative values of dturb and deff will be ignored and treated as a value of 0.0000
- Sprate <= 0 will be treated as indication to ignore sprate
Mult represents the user-specified value Minimum time constant size as multiple of time step option on the Validation page of the Transient Stability Dialog
TimeStep represents the integration time step being used as described on TimeStep
Model Equations and/or Block Diagrams
Parameters:
| trate | Turbine power base, MW |
| fd |
Flag for proportional elec power signal; 0 = Speed control mode - droop is based on gate servo stroke; 1 = Load control mode - droop is based on electric power |
| re | Permanent droop for electrical power feedback, pu |
| rg | Permanent droop for gate position feedback, pu |
| tpe | Electric power feedback transducer time const, sec |
| tsp | Shaft speed transducer time const, sec |
| kp | Governor proportional gain |
| ki | Governor integral gain |
| kd | Governor derivative gain |
| td | Derivative gain filter time constant, sec |
| velm | Maximum gate actuator velocity, pu/sec |
| gmax | Maximum gate actuator stroke, pu |
| gmin | Minimum gate actuator stroke, pu |
| buf | Upper limit of gate buffer region, pu |
| buv | Max gate closing rate in buffer region, pu |
| kg | Pilot servovalve gain |
| tg | Pilot servovalve time constant, sec |
| blg | Backlash in ring linkage |
| dbbd | Deliberate sliding deadband in digital blade cam output |
| tbd | Time constant of sliding deliberate dead band, sec |
| blb | Backlash in blade adjustment linkage |
| dbbs | Mechanical deadband in blade servovalve/motor |
| tbs | Blade servo time constant, sec |
| bgvmin | Flow area factor of blades when at minimum position |
| blv | Maximum stroking rate of blade servomotor |
| dturb | Turbine speed sensitivity constant |
| pgc | Electric motoring power with gates fully closed, pu |
| deff | Off blade angle power decrease factor |
| hdam | Operating head, pu |
| tw | water inertia time constant, sec |
| sprate | speed-load setpoint adjustment rate, pu/sec |
| gv0 | Abscissa value 0 for wicket gate and blade curves |
| gv1 | Abscissa value 1 for wicket gate and blade curves |
| gv2 | Abscissa value 2 for wicket gate and blade curves |
| gv3 | Abscissa value 3 for wicket gate and blade curves |
| gv4 | Abscissa value 4 for wicket gate and blade curves |
| gv5 | Abscissa value 5 for wicket gate and blade curves |
| gv6 | Abscissa value 6 for wicket gate and blade curves |
| gv7 | Abscissa value 7 for wicket gate and blade curves |
| gv8 | Abscissa value 8 for wicket gate and blade curves |
| gv9 | Abscissa value 9 for wicket gate and blade curves |
| pgv0 | Value 0 for Curve of wicket gate flow area |
| pgv1 | Value 1 for Curve of wicket gate flow area |
| pgv2 | Value 2 for Curve of wicket gate flow area |
| pgv3 | Value 3 for Curve of wicket gate flow area |
| pgv4 | Value 4 for Curve of wicket gate flow area |
| pgv5 | Value 5 for Curve of wicket gate flow area |
| pgv6 | Value 6 for Curve of wicket gate flow area |
| pgv7 | Value 7 for Curve of wicket gate flow area |
| pgv8 | Value 8 for Curve of wicket gate flow area |
| pgv9 | Value 9 for Curve of wicket gate flow area |
| bgv0 | Value 0 for Curve of Kaplan turbine blade flow area as function of gate servo stroke |
| bgv1 | Value 1 for Curve of Kaplan turbine blade flow area as function of gate servo stroke |
| bgv2 | Value 2 for Curve of Kaplan turbine blade flow area as function of gate servo stroke |
| bgv3 | Value 3 for Curve of Kaplan turbine blade flow area as function of gate servo stroke |
| bgv4 | Value 4 for Curve of Kaplan turbine blade flow area as function of gate servo stroke |
| bgv5 | Value 5 for Curve of Kaplan turbine blade flow area as function of gate servo stroke |
| bgv6 | Value 6 for Curve of Kaplan turbine blade flow area as function of gate servo stroke |
| bgv7 | Value 7 for Curve of Kaplan turbine blade flow area as function of gate servo stroke |
| bgv8 | Value 8 for Curve of Kaplan turbine blade flow area as function of gate servo stroke |
| bgv9 | Value 9 for Curve of Kaplan turbine blade flow area as function of gate servo stroke |
PowerWorld Implementation in Pseudo-Code
User Input Parameters for Model
ffd, ftrate, fre, frg, finvtpe, finvtsp, fkp, fki, fkd,
finvtd, fvelm, fgmax, fgmin, fbuf, fbuv, fkg, finvtg,
fblg, fdbbd, finvtbd, fblb, fdbbs, finvtbs, fbgvmin,
fblv, fdturb, fpgc, fdeff, fhdam, finvtw, fsprate : single;
// Note various parameters such as finvtpe are storing 1/tpe
fBackLash_blb, fBackLash_blg : BackLash Object; // see below
fP_Q, fB_GV : Nonlinear Lookup Tables
These tables are created from the input parameters GV0…GV9, PGV0…PGV9, and BGV0…BGV9.
See section below for details of how these are calculated.
Cached Values with model
These variables are availabe inside all functions.
cache_Spref, cache_LastTimeStepGateCommand, cache_GateCommand, cache_Hscroll,
cache_BC, cache_GV, cache_BB, cache_BH, cache_AB, cache_AF : double;
State and Derivative Value Syntax
Derivative[INDEX] : Derivative of state INDEX for model
StateVector[INDEX] : Value of state INDEX for model
IgnoreStateVector[INDEX] : boolean indicating if state INDEX is ignored
Nonlinear Lookup Curves (GV0…GV9, PGV0…PGV9, BGV0…BGV9)
After reading these input parameters three lookup tables are created
// force fbgvmin to between 0 and 1, but not equal to those numbers
if fbgvmin < 0.00001 then fbgvmin := 0.00001
else if fbgvmin > 0.99999 then fbgvmin := 0.99999;
for i := 0 to 9 do begin
// Calculate steady state Q for particular gate value
QGV[i] := GV[i]*( fbgvmin + (1-fbgvmin) * BGV[i]);
End;
// Create lookup function and add first point.
// The FALSE in constructor means to not extrapolite past the start and end point
fP_Q := PiecewiseLinearFunctionObject.Create(QGV[0],PGV[0],FALSE);
fB_GV := PiecewiseLinearFunctionObject.Create( GV[0],BGV[0],FALSE);
For i := 1 to 9 Do Begin
if (GV[i] > 0) then begin
fP_Q.AddPointInOrder(QGV[i],PGV[i]); // Power vs Q
fB_GV.AddPointInOrder(GV[i],BGV[i]); // B vs Gate
end;
End;
// PiecewiseLinearFunctionObject just encapsulates a lookup function.
// Method X(index) returns the X value of the X,Y part at a particular index
// Method Y(index) returns the Y value of the X,Y part at a particular index
// Method XtoY takes an input X value and calculates the output Y// Method YtoX takes an output Y value and calculates the input X
BackLash Object
fDeadband : Double;
fLastOutput: Double; // Tells the last output value
Public
Constructor CreateBackLash(tDeadband,tInitialOutput : Double);
Function Output(anInput : Double) : Double;
BackLash.CreateBackLash(tDeadband,tInitialOutput : Double);
Begin
fDeadBand := tDeadBand;
If fDeadband < 0 Then fDeadband := 0; // Must be non-negative
fLastOutput := tInitialOutput;
End;
Function BackLash.Output(anInput : Double) : Double;
Begin
If (anInput >= fLastOutput-fDeadband) and (anInput <= fLastOutput+fDeadBand) Then begin
Result := fLastOutput;
End
Else Begin
If anInput > fLastOutput+fDeadband
Then Result := anInput-fDeadBand // Going up, subtract off deadband
Else Result := anInput+fDeadBand; // Going down, add deadband
fLastOutput := Result;
End;
End;
Start of Each TimeStep
Begin
cache_LastTimeStepGateCommand := cache_GateCommand;
// Update the cache_Spref value using a rate limiter
if Pref > cache_Spref then begin
cache_spref := cache_spref + fsprate * timeStepSeconds;
if cache_spref > Pref then cache_spref := Pref;
end
else if Pref < cache_Spref then begin
cache_spref := cache_spref - fsprate * timeStepSeconds;
if cache_spref < Pref then cache_spref := Pref;
end;
end;
function to return the Pmech to feed to machine model
var local_Q, local_MinQ : double;
begin
local_Q := StateVector[9];
// hydraulic power
if fP_Q.Count > 0 then begin
local_MinQ := fP_Q.X(1);
if (local_Q < local_MinQ) and (local_MinQ > 0)
then result := cache_Hscroll * (fpgc * (local_Q - local_MinQ)/local_MinQ)
else result := cache_Hscroll * (fP_Q.XtoY( local_Q ) - fdeff*sqr(cache_bh - cache_bb));
end
else begin
result := cache_Hscroll * (local_Q - fdeff*sqr(cache_bh - cache_bb));
end;
result := result - deltaWPU * fDTurb * cache_GV;
// put turbine power on appropriate per unit base
result := result/GenObject.MVABase*fTrate;
end;
InitializeForIntegration Function
Var local_pq,
local_gv,
local_q,
local_af : Double; // local values for initialization
Local_Pmech : double;
Local_PElec : double;
Begin
Local_Pmech := GenObject.PMechObjectInitInternal;
Local_Pmech := Local_Pmech*GenObject.MVABase/fTrate;
Local_PElec := PElec;
Local_PElec := Local_PElec*GenObject.MVABase/fTrate;
local_pq := Local_Pmech/(1.0*fHdam);
if local_pq > fP_Q.LastY then begin // added initial limit violation check in February 2022
fHdam := local_Pmech / fP_Q.LastY; // added some log messages
local_pq := Local_Pmech/(1.0*fHdam);
end;
local_q := fP_Q.YtoX(local_pq);
local_af := local_q/sqrt(fHdam);
local_afmax := fgmax* (fbgvmin + (1-fbgvmin)*fB_GV.XtoY(fgmax) );
if (local_af > local_afmax)
and LOCAL_BackSolveForQ(local_afmax, local_Pmech, local_q)
then begin // added initial limit violation check in September 2022
local_gv := fgmax;
local_af := local_afmax;
fHdam := sqr(local_q/local_afmax); // added some log messages
end
else begin
local_gv := LOCAL_BackSolveForGate(local_af); // see on following pages
end;
// Modified fgmin and fgmax if Governor Response Limits flag ("base load") is used.
cache_GateCommand := local_gv;
cache_LastTimeStepGateCommand := local_gv;
cache_BC := fB_GV.XtoY(local_gv);
cache_GV := local_gv;
cache_BB := cache_BC;
cache_BH := cache_BC;
cache_AB := (fbgvmin + (1-fbgvmin)*cache_BB);
cache_AF := cache_AB * local_gv;
cache_Hscroll := GOVERNOR_DivideAndSquareForHead(local_q, cache_af, fHdam);
If fblb <> 0 Then fBackLash_blb := TTxBacklash.CreateBackLash(self,fblb,cache_BB);
If fblg <> 0 Then fBackLash_blg := TTxBacklash.CreateBackLash(self,fblg,local_GV);
StateVector[1] := Local_PElec;
StateVector[2] := 1.0; // speed
StateVector[4] := 0.0; // model input to derivative state as (speed - 1)
StateVector[5] := 0.0;
StateVector[6] := local_gv;
StateVector[7] := cache_BC;
StateVector[8] := cache_BC;
StateVector[9] := local_q;
// PIDInput = Pref - Speed - Relec*PElec - Rg*Gate
// PIDInput must be zero initially because we require Ki > 0
// 1.0 here represents the initial speed
if ffd = 0 then begin
Pref := 1.0 + local_gv*fRg;
StateVector[3] := local_gv;
end
else begin
Pref := 1.0 + local_PElec*fRe;
StateVector[3] := local_gv - fKp*(Pref - 1.0);
end;
cache_Spref := Pref;
// Set ignored states (this is a boolean vector PowerWorld uses to signify ignored states)
if fInvtpe = 0 then IgnoreStateVector[1] := True;
if fInvtsp = 0 then IgnoreStateVector[2] := True;
// 23 is the Ki integrator
if fInvTd = 0 then IgnoreStateVector[4] := True;
if fInvtg = 0 then IgnoreStateVector[5] := True;
// 56 is the gate integrator
if fInvtbd = 0 then IgnoreStateVector[7] := True;
if fInvtbs = 0 then IgnoreStateVector[8] := True;
// 89 is the water integrator
end;
function LOCAL_BackSolveForGate(local_af : double) : double;
var i : integer;
local_gk, local_gm, local_bk, local_bm,
local_slope,
local_A, local_B, local_C : double;
begin
Result := 0;
i := fB_GV.Count;
if i = 0 then begin // just assume Bgv = 1.0 always, which means that
result := local_af;
exit;
end;
if i = 1 then begin
local_bk := fB_GV.Y(1);
result := local_af/( fbgvmin + local_bk*(1-fbgvmin) );
exit;
end;
while i >= 0 do begin
local_gk := fB_GV.X(i);
local_bk := fB_GV.Y(i);
if local_af < local_gk*( fbgvmin + local_bk*(1-fbgvmin) ) then begin // just keep going
if i = 1 then begin // However if i = 1, this is degenerate case!
result := local_af/( fbgvmin + local_bk*(1-fbgvmin) );
exit;
end;
end
else begin
if (i = fB_GV.Count) then begin
result := local_af/( fbgvmin + local_bk*(1-fbgvmin) ); // just use this last Bk value
exit;
end
else begin
local_gm := fB_GV.X(i+1);
local_bm := fB_GV.Y(i+1);
local_slope := (local_bm - local_bk)/(local_gm - local_gk);
if abs(local_Slope) > 1E-6 then begin
{
we are trying to solve for gv given the value of af.
af = gv * [ bgvmin + (1-bgvmin)*Bgv ]
Solve this for Bgv to make it easier for next step and we have
af/gv - bgvmin
Bgv = --------------
1 - bgvmin
If we're one of the line segments of the Bgv vs Gv curve then we have
a line segement going from (gk, bk) sloping up to (gm, bm)
(bm-bk)
Bgv = bk + (gv-gk) * -------
(gm-gk)
Now equate these two function for Bgv and solve for gv (Note | just grouping stuff)
1 | -af | | bgvmin (bm-bk)| |(bm-bk)|
0 = --- * |--------- | + |---------- + bk - gk * -------| + gv * |-------|
gv |(1-bgvmin)| |(1-bgvmin) (gm-gk)| |(gm-gk)|
Now, multiply through by gv and make terms for "A, B, C" for quadratic function
0 = C + B*gv + C*sqr(gv) }
local_C := -local_AF/(1-fbgvmin);
local_B := fbgvmin/(1-fbgvmin) + local_bk - local_gk*local_slope;
local_A := local_Slope;
result := (- local_B + sqrt( sqr(local_B) - 4*local_A*local_C))/ (2*local_A);
exit;
end
else begin
{
We're on a slope with constant Bk, so back to af = gv * [ bgvmin + (1-bgvmin)*Bgv ]
Which can be solved as gv = af/bgvmin + bk*(1-bgvmin)
}
result := local_af/( fbgvmin + local_bk*(1-fbgvmin) );
exit;
end;
end;
end;
i := i - 1;
end;
end;
function LOCAL_BackSolveForQ(local_afmax, local_Pmech : double; var local_q : double) : boolean;
{
This function was added in September 2022 to handle situation where the maximum gate
Value is not enough to achieve the af value necessary to get Pmech desired.
We will again solve this by increasing the Hdam value
Solving Equations: sqr(q/afmax) = Hdam and P(q)*Hdam = Pmech
Substitute first equation into the second one --> P(q)*sqr(q/afmax) = Pmech
P(q)*sqr(q) = Pmech*sqr(afmax)
P(q)*sqr(q) - Pmech*sqr(afmax) = 0
If P(q) = q (meaning we have piecewise linear function at all this degenerate solution is
q * sqr(q/afmax) = Pmech --> q = (Pmech*sqr(afmax)) ^ (1/3)
Otherwise, we have only 1 point (or we end up on a horizontal segment), then this mean that P(q) is a constant
If P(q) = Pk (1 point, or we have a zero slope on this segment)
Pk * sqr(q/afmax) = Pmech --> q = sqrt(Pmech/Pk)*afmax
Otherwise we're looking at somewhere between 2 points
Assume we're on line segment between (qk, Pk) and (qm, Pm), so we can write
(Pm-Pk)
P(q) = Pk + (q-qk) * -------
(qm-qk)
(Pm-Pk) (Pm-Pk)
P(q) = q * ------- + Pk - qk * -------
(qm-qk) (qm-qk)
(Pm-Pk)
Define Slope = -------
(qm-qk)
P(q) = q*Slope + Pk - qk*Slope
P(q)*sqr(q) - Pmech*sqr(afmax) = 0
(q*slope + Pk - qk*Slope)*sqr(q) - Pmech*sqr(afmax) = 0
[Slope]*q^3 + [Pk - qk*Slope]*q^2 + [- Pmech*sqr(afmax)] = 0;
Slope = (Pm-Pk)/(qm-qk)
A = Slope // A > 0 (bad user input otherwise)
B = Pk - qk*Slope // B can be positive or negative
D = -Pmech*sqr(afmax) // D < 0 (Pmech can’t be negative!)
By assuming Slope > 0 this means that we have 1 sign change when looking through the
Polynomial coefficients, so by Descartes sign rule, we have exactly 1 positive real root
Degenerate situation when no P vs Q curve exists. In this situation P(q) = q always
(multiplier is just 1.00)
P(q)*sqr(q) - Pmech*sqr(afmax) = 0
q^3 = Pmech*sqr(afmax)
q = ( Pmech*sqr(afmax) ) ^(1/3) // cube root
Degenerate situation when Slope = 0, then this is
A=0; B=Pk; C=0; D=-Pmech*sqr(afmax)
Pk*sqr(q) = Pmech*sqr(afmax)
Solution is then
q = sqrt(Pmech/Pk)*afmax
}
procedure LOCAL_HandlePkConstant(local_Pk: double);
begin
if local_Pk < 0.0001 then local_Pk := 0.0001; // this should never happen!
Result := true;
Local_q := sqrt(local_Pmech/local_Pk) * local_afmax;
end;
var i : integer;
local_qk, local_qm, local_Pk, local_Pm,
_A, _B, _D, local_slope, _Pmech_sqrAfmax : double;
begin
result := false;
_Pmech_sqrAfmax := local_Pmech*sqr(local_afmax);
i := fP_Q.Count;
if i = 0 then begin
result := true;
local_q := GOVERNOR_CubeRoot(_Pmech_sqrAfmax);
exit;
end;
if i = 1 then begin
local_Pk := fP_Q.Y(1);
LOCAL_HandlePkConstant(local_Pk);
exit;
end;
Result := 0;
while i >= 0 do begin
local_qk := fP_Q.X(i);
local_Pk := fP_Q.Y(i);
if sqr(local_qk)*local_Pk > _Pmech_sqrAfmax then begin
// just keep going normally here,
if i = 1 then begin // However if i = 1, this is silly case!
LOCAL_HandlePkConstant(local_Pk);
exit;
end;
end
else begin
if (i = fP_Q.Count) then begin
LOCAL_HandlePkConstant(local_Pk);
end
else begin
local_qm := fP_Q.X(i+1);
local_Pm := fP_Q.Y(i+1);
local_slope := (local_Pm - local_Pk)/(local_qm - local_qk);
if abs(local_Slope) < 1E-6 then begin
LOCAL_HandlePkConstant(local_Pk);
end
else if local_Slope > 0 then begin // Solving A*q^3 + B*q^2 + D = 0
_A := local_Slope; // must be positive
_B := local_Pk - local_qk*local_Slope; // Could by pos or neg
_D := -_Pmech_sqrAfmax; // must be negative
// Because A>0 and D<0, then there is exactly 1 sign change
// By Descartes Rule of Signs, this means there is ONE positive answer
result := true;
Local_q := SolveCubicAndReturnPositiveRealAnswer(_A,_B,0,_D);
end;
end;
Exit; // exit no matter what here. If a local_Slope < 0, don’t do anything
end;
i := i - 1;
end;
end;
Derivative Function
procedure LOCAL_HandleNonWindup(theStateOffset : integer; theDeriv, themax, themin : double);
var local_s : double;
begin
local_s := StateVector[theStateOffset];
if ( local_s > themax) then StateVector[theStateOffset] := themax;
if ( local_s < themin) then StateVector[theStateOffset] := themin;
Derivative[theStateOffset] := theDeriv;
if ( local_s >= themax) and ( Derivative[theStateOffset] > 0 ) then
Derivative[theStateOffset] := 0;
if ( local_s <= themin) and ( Derivative[theStateOffset] < 0 ) then
Derivative[theStateOffset] := 0;
end;
var local_Pelec,
local_Speed,
local_PropIn,
local_IntIn,
local_SpeedError : double;
local_Deriv : double;
Begin
local_Pelec := PElec*GenObject.MVABase/fTrate;
if finvTsp < 0 then local_Speed := GenObject.GetBusFreqPU // bus frequency
else local_Speed := actualWPU; // rotor speed
If IgnoreStateVector[1] Then StateVector[1] := local_Pelec;
If IgnoreStateVector[2] Then StateVector[2] := local_Speed;
If IgnoreStateVector[4] Then StateVector[4] := StateVector[1] - 1.0;
Derivative[2] := abs(finvTsp)*(local_Speed - StateVector[2]);
Derivative[4] := finvTd*(StateVector[2] - 1.0 - StateVector[4]);
local_SpeedError := cache_Spref - StateVector[2] + GenObject.Paux/GetTRate; // AAA 03/13/20
if fFD = 0 then begin
Derivative[1] := 0;
// proportional control DOES include gate droop when FD = 0
local_PropIn := local_SpeedError - fRg*cache_LastTimeStepGateCommand;
local_IntIn := local_PropIn;
end
else begin
Derivative[1] := finvTpe*(local_Pelec - StateVector[1]);
// proportional control DOES NOT include Pelec droop term when FD = 1
local_PropIn := local_SpeedError;
local_IntIn := local_SpeedError - fRe*StateVector[1];
end;
LOCAL_HandleNonWindup(3, fKi*local_IntIn, fgmax - local_PropIn*fKp, fgmin - local_PropIn*fKp);
cache_GateCommand := fKp*local_PropIn + StateVector[3]; // proportional + Integrator
// Derivative block effect
If not IgnoreStateVector[4] Then begin
cache_GateCommand := cache_GateCommand -
fKd*finvTd*(StateVector[2] - 1.0 - StateVector[4]);
end;
if cache_GateCommand > fGmax then cache_GateCommand := fgmax;
if cache_GateCommand < fgmin then cache_GateCommand := fgmin;
local_Deriv := fInvTg*( fKg *(cache_GateCommand - StateVector[6]) - StateVector[5] );
LOCAL_HandleNonWindup(5, local_Deriv, fvelm, -fvelm);
LOCAL_HandleNonWindup(6, StateVector[5], fgmax, fgmin);
// gate buffer
if (StateVector[6] < fbuf) AND (Derivative[6] < -fbuv) then Derivative[6] := -fbuv;
// handle gate backlash
cache_gv := StateVector[6];
if fBacklash_blg <> nil then cache_gv := fBacklash_blg.output( cache_gv );
cache_BC := fB_GV.XtoY(cache_GateCommand);
if ( fInvtbd > 0 ) then begin
local_Deriv := cache_BC - StateVector[7];
if ( local_Deriv > fdbbd ) then local_Deriv := local_Deriv - fdbbd
else if ( local_Deriv < -fdbbd ) then local_Deriv := local_Deriv + fdbbd;
Derivative[7] := finvTbd * local_Deriv;
cache_bh := StateVector[7];
end
else begin
StateVector[7] := cache_bc;
Derivative[7] := 0;
// note this won't always update the bh value!
// That's fine, this is hysterisis. It only moves once the deviation is large enough
if (cache_bc - fdbbd) > cache_bh then cache_bh := cache_bc - fdbbd;
if (cache_bc + fdbbd) < cache_bh then cache_bh := cache_bc + fdbbd;
end;
if finvTbs > 0 then begin
local_Deriv := cache_bh - StateVector[78];
if ( local_Deriv > fdbbs ) then local_Deriv := local_Deriv - fdbbs
else if ( local_Deriv < -fdbbs ) then local_Deriv := local_Deriv + fdbbs;
local_Deriv := finvTbs * local_Deriv;
if local_Deriv > +fblv then local_Deriv := +fblv
else if local_Deriv < -fblv then local_Deriv := -fblv;
Derivative[8] := local_Deriv;
end
else begin
StateVector[8] := cache_bh;
Derivative[8] := 0;
end;
cache_bb := StateVector[8];
if fBacklash_blb <> nil then cache_bb := fBacklash_blb.output( cache_bb );
cache_ab := fbgvmin + (1 - fbgvmin)*cache_bb;
cache_af := cache_ab * cache_gv;
// when cache_af value input to water dynamics is an extremely small number,
// the dynamics can become extremely fast because we divide by cache_af.
// Model starts entering a region we have have 0/0 as Q value also goes to zero.
// Model these dynamics as algebraic and instantly change the state value to handle this.
// Otherwise numerical problems related to 0/0 will result
if cache_af < 0.005 then StateVector[9] := sqrt(fHdam)*cache_af;
cache_Hscroll := sqr(StateVector[9]/cache_af);
// Don’t allow the water flow to drop below 0.0001 per unit (more avoid 0/0 situations)
LOCAL_HandleNonWindup(9, finvTw*( fHdam - cache_hscroll ), 1E10, 0.0001);
end;