pub struct FopdtProcess { /* private fields */ }Expand description
A first-order-plus-dead-time process model, advanced one tick at a time.
Uses the exact analytical solution for a first-order lag driven by a piecewise-constant
(zero-order-hold) input over each tick –
pv_new = pv*decay + (1-decay)*(bias + gain*mv_effective),
decay = exp(-tick_interval_s / time_constant_s) – rather than numerically integrating
an ODE every tick, as Model/ProcessModelOPC.py’s scipy.integrate.odeint call does.
This is not an approximation: it is the closed-form solution of
tau * d(pv)/dt = -(pv - pv0) + gain*(mv - mv0) for an input held constant over
[t, t+dt], which is exactly what “the controller wrote this MV and it stays there until
the next write” means physically. Cross-checked numerically (in a disposable scratch
script, not part of this repo) against the reference script’s own odeint-based
integration across a range of gain/tau/dt combinations before relying on it – the two
agree to within odeint’s own numerical tolerance (~1e-5).
Implementations§
Source§impl FopdtProcess
impl FopdtProcess
Sourcepub fn new(
config: FopdtConfig,
initial_pv: f32,
initial_mv: f32,
seed: u64,
) -> FopdtProcess
pub fn new( config: FopdtConfig, initial_pv: f32, initial_mv: f32, seed: u64, ) -> FopdtProcess
Builds a process starting at rest: initial_pv is assumed to already be the correct
steady-state PV for initial_mv (i.e. nothing changes until a FopdtProcess::set_mv
call moves the MV away from initial_mv). seed makes the noise sequence
reproducible – the same config/seed/write sequence always produces the same PV
sequence (on a given platform and rand version; see [rand::rngs::StdRng]’s own
caveat that its algorithm is not a portability guarantee across those).
Sourcepub fn pv(&self) -> f32
pub fn pv(&self) -> f32
The current PV, as of the last FopdtProcess::step call (or initial_pv, if
step has never been called).
Sourcepub fn mv(&self) -> f32
pub fn mv(&self) -> f32
The most recently recorded MV (see FopdtProcess::set_mv) – not delayed; this is
what a real DCS’s own MV readback tag would report immediately. Only the PV’s
response to it is delayed.
Sourcepub fn set_mv(&mut self, mv: f32)
pub fn set_mv(&mut self, mv: f32)
Records a new controller output, effective from the next FopdtProcess::step call
onward (after passing through the dead-time delay line, if configured).
Sourcepub fn step(&mut self) -> f32
pub fn step(&mut self) -> f32
Advances the process by one tick_interval_s, using whichever MV was most recently
recorded via FopdtProcess::set_mv – delayed by dead_time_s, if configured – as
the input, and returns the resulting PV (including noise, if configured).
Trait Implementations§
Auto Trait Implementations§
impl Freeze for FopdtProcess
impl RefUnwindSafe for FopdtProcess
impl Send for FopdtProcess
impl Sync for FopdtProcess
impl Unpin for FopdtProcess
impl UnsafeUnpin for FopdtProcess
impl UnwindSafe for FopdtProcess
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
§impl<T> IntoRequest<T> for T
impl<T> IntoRequest<T> for T
§fn into_request(self) -> Request<T>
fn into_request(self) -> Request<T>
T in a tonic::Request§impl<L> LayerExt<L> for L
impl<L> LayerExt<L> for L
§fn named_layer<S>(&self, service: S) -> Layered<<L as Layer<S>>::Service, S>where
L: Layer<S>,
fn named_layer<S>(&self, service: S) -> Layered<<L as Layer<S>>::Service, S>where
L: Layer<S>,
Layered].