use serde::{Deserialize, Serialize};
use std::cell::Cell;
use super::{Control, InitError, LimitState, Limits};
#[derive(Clone, Debug)]
pub struct Tgov1 {
r: f64,
t1: f64,
t2_over_t3: f64,
t3: f64,
dt: f64,
limits: Limits,
rate: Limits,
limit: Cell<LimitState>,
held_rate: Cell<Option<f64>>,
p_ref: f64,
}
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
pub struct Tgov1Params {
pub r: f64,
pub t1: f64,
pub t2: f64,
pub t3: f64,
pub dt: f64,
#[serde(default)]
pub limits: Limits,
#[serde(default)]
pub rate: Limits,
}
const TGOV1_STATES: [&str; 2] = ["gov_valve", "gov_turbine"];
impl Tgov1 {
pub fn new(params: Tgov1Params) -> Result<Self, InitError> {
if params.r <= 0.0 {
return Err(InitError::NonPositiveParameter {
name: "gov r",
value: params.r,
});
}
if params.t1 <= 0.0 {
return Err(InitError::NonPositiveParameter {
name: "gov t1",
value: params.t1,
});
}
if params.t3 <= 0.0 {
return Err(InitError::NonPositiveParameter {
name: "gov t3",
value: params.t3,
});
}
Ok(Self {
r: params.r,
t1: params.t1,
t2_over_t3: params.t2 / params.t3,
t3: params.t3,
dt: params.dt,
limits: params.limits,
rate: params.rate,
limit: Cell::new(LimitState::Free),
held_rate: Cell::new(None),
p_ref: 0.0,
})
}
pub fn p_ref(&self) -> f64 {
self.p_ref
}
pub fn r(&self) -> f64 {
self.r
}
fn raw_valve_derivative(&self, x: &[f64], u: f64) -> f64 {
(self.p_ref - u / self.r - x[0]) / self.t1
}
pub fn rate(&self) -> Limits {
self.rate
}
fn valve_derivative(&self, x: &[f64], u: f64) -> f64 {
let raw = self
.held_rate
.get()
.unwrap_or_else(|| self.raw_valve_derivative(x, u));
self.limits.held(self.limit.get(), raw)
}
}
impl Control for Tgov1 {
fn n_states(&self) -> usize {
2
}
fn state_names(&self) -> &[&'static str] {
&TGOV1_STATES
}
fn latch(&self, x: &[f64], u: f64) {
let raw = self.raw_valve_derivative(x, u);
let (clipped, scale) = self.rate.clamp(raw);
self.held_rate.set((scale == 0.0).then_some(clipped));
self.limit.set(self.limits.latch(x[0], clipped));
}
fn derivatives(&self, x: &[f64], u: f64, out: &mut [f64]) {
out[0] = self.valve_derivative(x, u);
out[1] = (self.limits.under(self.limit.get(), x[0]).0 - x[1]) / self.t3;
}
fn jacobian(&self, x: &[f64], _u: f64, dfdx: &mut [f64], dfdu: &mut [f64]) {
if self.limit.get() == LimitState::Free && self.held_rate.get().is_none() {
dfdx[0] = -1.0 / self.t1;
dfdu[0] = -1.0 / (self.r * self.t1);
}
dfdx[1] = 0.0;
dfdx[2] = self.limits.under(self.limit.get(), x[0]).1 / self.t3;
dfdx[3] = -1.0 / self.t3;
dfdu[1] = 0.0;
}
fn output(&self, x: &[f64], u: f64) -> f64 {
let valve = self.limits.under(self.limit.get(), x[0]).0;
x[1] + self.t2_over_t3 * (valve - x[1]) - self.dt * u
}
fn output_jacobian(&self, x: &[f64], _u: f64, dydx: &mut [f64]) -> f64 {
dydx[0] = self.t2_over_t3 * self.limits.under(self.limit.get(), x[0]).1;
dydx[1] = 1.0 - self.t2_over_t3;
-self.dt
}
fn project(&self, x: &mut [f64]) {
x[0] = self.limits.clamp(x[0]).0;
}
fn initialize(&mut self, y: f64, u: f64) -> Result<Vec<f64>, InitError> {
let x = y + self.dt * u;
self.limits.require("governor valve position", x)?;
self.p_ref = x + u / self.r;
Ok(vec![x, x])
}
}
#[derive(Clone, Debug)]
pub struct GoverProportional {
k: f64,
limits: Limits,
limit: Cell<LimitState>,
p_ref: f64,
}
const NO_GOV_STATES: [&str; 0] = [];
impl GoverProportional {
pub fn new(k: f64) -> Result<Self, InitError> {
Self::limited(k, Limits::NONE)
}
pub fn limited(k: f64, limits: Limits) -> Result<Self, InitError> {
if k <= 0.0 {
return Err(InitError::NonPositiveParameter {
name: "gov gain",
value: k,
});
}
Ok(Self {
k,
limits,
limit: Cell::new(LimitState::Free),
p_ref: 0.0,
})
}
pub fn p_ref(&self) -> f64 {
self.p_ref
}
pub fn droop(&self) -> f64 {
1.0 / self.k
}
}
impl Control for GoverProportional {
fn n_states(&self) -> usize {
0
}
fn state_names(&self) -> &[&'static str] {
&NO_GOV_STATES
}
fn derivatives(&self, _x: &[f64], _u: f64, _out: &mut [f64]) {}
fn jacobian(&self, _x: &[f64], _u: f64, _dfdx: &mut [f64], _dfdu: &mut [f64]) {}
fn latch(&self, _x: &[f64], u: f64) {
let raw = self.p_ref - self.k * u;
self.limit.set(if raw > self.limits.upper() {
LimitState::AtMax
} else if raw < self.limits.lower() {
LimitState::AtMin
} else {
LimitState::Free
});
}
fn output(&self, _x: &[f64], u: f64) -> f64 {
self.limits
.under(self.limit.get(), self.p_ref - self.k * u)
.0
}
fn output_jacobian(&self, _x: &[f64], u: f64, _dydx: &mut [f64]) -> f64 {
-self.k
* self
.limits
.under(self.limit.get(), self.p_ref - self.k * u)
.1
}
fn initialize(&mut self, y: f64, u: f64) -> Result<Vec<f64>, InitError> {
self.limits.require("governor mechanical power", y)?;
self.p_ref = y + self.k * u;
Ok(Vec::new())
}
}