use serde::{Deserialize, Serialize};
use std::cell::Cell;
use super::{Control, InitError, LimitState, Limits};
#[derive(Clone, Debug)]
pub struct Sexs {
k: f64,
ta_over_tb: f64,
tb: f64,
te: f64,
limits: Limits,
limit: Cell<LimitState>,
v_ref: f64,
}
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
pub struct SexsParams {
pub k: f64,
pub ta: f64,
pub tb: f64,
pub te: f64,
#[serde(default)]
pub limits: Limits,
}
const SEXS_STATES: [&str; 2] = ["avr_lead", "efd"];
impl Sexs {
pub fn new(params: SexsParams) -> Result<Self, InitError> {
if params.k <= 0.0 {
return Err(InitError::NonPositiveParameter {
name: "avr k",
value: params.k,
});
}
if params.tb <= 0.0 {
return Err(InitError::NonPositiveParameter {
name: "avr tb",
value: params.tb,
});
}
if params.te <= 0.0 {
return Err(InitError::NonPositiveParameter {
name: "avr te",
value: params.te,
});
}
if params.ta < 0.0 {
return Err(InitError::NonPositiveParameter {
name: "avr ta",
value: params.ta,
});
}
Ok(Self {
k: params.k,
ta_over_tb: params.ta / params.tb,
tb: params.tb,
te: params.te,
limits: params.limits,
limit: Cell::new(LimitState::Free),
v_ref: 0.0,
})
}
pub fn v_ref(&self) -> f64 {
self.v_ref
}
fn raw_field_derivative(&self, x: &[f64], u: f64) -> f64 {
let w = self.v_ref + u;
let y1 = x[0] + self.ta_over_tb * w;
(self.k * y1 - x[1]) / self.te
}
}
impl Control for Sexs {
fn n_states(&self) -> usize {
2
}
fn state_names(&self) -> &[&'static str] {
&SEXS_STATES
}
fn latch(&self, x: &[f64], u: f64) {
let raw = self.raw_field_derivative(x, u);
self.limit.set(self.limits.latch(x[1], raw));
}
fn derivatives(&self, x: &[f64], u: f64, out: &mut [f64]) {
let w = self.v_ref + u;
out[0] = (w * (1.0 - self.ta_over_tb) - x[0]) / self.tb;
out[1] = self
.limits
.held(self.limit.get(), self.raw_field_derivative(x, u));
}
fn jacobian(&self, _x: &[f64], _u: f64, dfdx: &mut [f64], dfdu: &mut [f64]) {
dfdx[0] = -1.0 / self.tb;
dfdx[1] = 0.0;
dfdu[0] = (1.0 - self.ta_over_tb) / self.tb;
if self.limit.get() == LimitState::Free {
dfdx[2] = self.k / self.te;
dfdx[3] = -1.0 / self.te;
dfdu[1] = self.k * self.ta_over_tb / self.te;
}
}
fn output(&self, x: &[f64], _u: f64) -> f64 {
self.limits.under(self.limit.get(), x[1]).0
}
fn output_jacobian(&self, x: &[f64], _u: f64, dydx: &mut [f64]) -> f64 {
dydx[0] = 0.0;
dydx[1] = self.limits.under(self.limit.get(), x[1]).1;
0.0
}
fn project(&self, x: &mut [f64]) {
x[1] = self.limits.clamp(x[1]).0;
}
fn initialize(&mut self, y: f64, u: f64) -> Result<Vec<f64>, InitError> {
self.limits.require("exciter field voltage", y)?;
let w = y / self.k;
self.v_ref = w - u;
Ok(vec![w * (1.0 - self.ta_over_tb), y])
}
}
#[derive(Clone, Debug)]
pub struct VrProportional {
k: f64,
limits: Limits,
limit: Cell<LimitState>,
v_ref: f64,
}
const NO_AVR_STATES: [&str; 0] = [];
impl VrProportional {
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: "avr gain",
value: k,
});
}
Ok(Self {
k,
limits,
limit: Cell::new(LimitState::Free),
v_ref: 0.0,
})
}
pub fn v_ref(&self) -> f64 {
self.v_ref
}
}
impl Control for VrProportional {
fn n_states(&self) -> usize {
0
}
fn state_names(&self) -> &[&'static str] {
&NO_AVR_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.k * (self.v_ref + 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.k * (self.v_ref + u))
.0
}
fn output_jacobian(&self, _x: &[f64], u: f64, _dydx: &mut [f64]) -> f64 {
self.k
* self
.limits
.under(self.limit.get(), self.k * (self.v_ref + u))
.1
}
fn initialize(&mut self, y: f64, u: f64) -> Result<Vec<f64>, InitError> {
self.limits.require("exciter field voltage", y)?;
self.v_ref = y / self.k - u;
Ok(Vec::new())
}
}