STAR
Cal STAR — firmware, DAQ server, and engine design
Loading...
Searching...
No Matches
feedtwin.comps.regulator Namespace Reference

Classes

class  Regulator
class  IdealRegulator
class  CurveRegulator

Functions

float _dynamic_head (float mdot, float bore, float rho)
HydraulicComponent _measured_regulator (object instance)

Variables

float REVERSE_STIFFNESS = 1.0e10
float SUPPLY_ZERO = 101325.0
str SEAT_XT_SIGNAL = "regulator_seat_xT"
str SEAT_MODEL_NAME = "regulator seat, compressible (IEC 60534-2-1)"
tuple SEAT_MODEL_SOURCE

Detailed Description

Pressure regulators, modelled on what a datasheet actually tells you.

A regulator is not a restriction. A pipe's pressure drop is a function of the
flow through it; a regulator's outlet is a *setpoint* it defends, and the drop
across it is whatever the inlet happens to be minus that setpoint. Modelled as
a resistance it is wrong in the most misleading way -- it will happily report a
tank pressure that tracks COPV pressure, which is the exact behaviour a
regulator exists to prevent.

Why there is no force balance here
----------------------------------
An earlier plan for this module proposed deriving droop from a force balance
across the sensing diaphragm: dome pressure on the diaphragm against spring
preload, seat and stem areas, flow force on the poppet. That was abandoned, and
the reason is worth recording so nobody rebuilds it.

The balance needs the diaphragm's **effective area as a function of lift**, and
effective area depends on how the convolution rolls under load. No vendor
publishes it, it changes with the elastomer and with age, and it cannot be
recovered from an outline drawing. Building the model means inventing three
unmeasurable numbers and tuning them until the answer reproduces the one number
the datasheet already gave you. More machinery, less confidence.

How a dome-loaded regulator is actually set
--------------------------------------------
A dome regulator has no knob. Its outlet follows the pressure in its dome, and
the dome is fed by a second, hand-loaded regulator upstream -- the *control*
regulator. Turning that knob is how the setpoint moves, during a test, by hand.
So the dome pressure is a **signal** here, not a fixed parameter: a scenario can
ramp it, and an operator can change it mid-run, which is exactly what happens on
the stand.

Between the dome and the outlet sits a **bias**. The Aqua Environment 1092-50 in
use here delivers 50 psi *above* what its dome is loaded to, by design. Modelled
as a parameter rather than folded into the setpoint, because it is a property of
the regulator and the dome pressure is a property of the test: fold them
together and every scenario has to remember to add 50, and one day one of them
will not.

.. code-block:: text

    p_set = p_dome + bias            dome-loaded
    p_set = setpoint                 hand-loaded, or a fixed bench setting

What the datasheet does give you is two more coefficients, and they cover the
two physically distinct effects:

**Supply-pressure effect.** Outlet *rises* as inlet *falls*, because inlet
pressure helps hold the poppet shut against the seat and that help goes away.
Datasheets quote it as a rate, and it is declared here in the units they quote
it in -- the Aqua Environment 1092-50 in use here is **17 psi per 1000 psi** of
inlet, written

.. code-block:: toml

    supply_coefficient = { value = 17, unit = "psi/1000psi" }

It is measured from **zero inlet**, not from a full bottle: the outlet is

.. code-block:: text

    p_out = p_dome + bias - S . p_in            (p_in gauge)

so a 1092-50 loaded to 500 psi with 4000 psi behind it holds
``500 + 50 - 4 x 17 = 482`` psi, and it climbs from there as the bottle falls
-- from the first gram, not from some charge pressure onwards. (The team,
2026-10-07. An earlier version measured the term from an ``inlet_reference``
or the COPV charge, which zeroed it at a full bottle and put the whole effect
on the far side of that pressure.) Over a COPV decay from 4500 to 1500 psi it
moves the outlet up 51 psi -- about 6% on chamber pressure, and the wrong sign
to guess at.

The unit matters more than it looks. Outlet-pressure-per-inlet-pressure is
dimensionless by arithmetic, and an earlier version of this module took it as a
bare ``0.0147`` with unit ``-``. Nobody can check that against a datasheet:
17 psi/1000 psi, 1.7 psi/100 psi and a slipped decimal all look alike once the
units are gone, and the number that reaches the solver is three keystrokes from
being wrong by a factor of ten. It now carries its own ``pressure_ratio``
dimension, so ``-`` is rejected where one belongs and the vendor's own wording
is what gets typed in.

**Flow droop.** Outlet *falls* as flow rises, because the poppet must lift and
lifting takes a pressure difference across the sensing element. This is a
*separate* coefficient, and it turns out to be structurally load-bearing rather
than a refinement -- see below. It is tempting to think the seat's Cv already produces
it -- it does not. Cv sets where the regulator saturates and falls off a cliff;
at a tenth of rated flow the wide-open seat loss is a couple of psi while a real
unit has already drooped by ten. Both are modelled, and they are not the same
number.

The force balance is not forbidden, it is *deferred*: if a fitted droop
coefficient turns out to vary with something two coefficients cannot see, that
is the evidence that would justify building one.

Why zero flow droop is not "no droop" but "no answer"
-----------------------------------------------------
A regulator with ``flow_droop = 0`` holds its outlet at exactly the setpoint no
matter what passes through it. Put one between a COPV and a tank -- both of
whose pressures are states, hence boundary pressures at any instant -- and the
branch equation the network solver assembles is

.. code-block:: text

    (p_up - p_dn) - dp(mdot) = 0        with  dp(mdot) = p_up - p_set

which is satisfied for **every** value of ``mdot``. The flow is genuinely
indeterminate, the branch's Jacobian row is exactly zero, and Newton has nothing
to converge on. That is not a numerical quirk to regularise away; it is the
model saying that a perfect regulator does not determine its own flow.

Flow droop is what closes it. A non-zero coefficient gives the branch a slope of
``flow_droop / rated_flow`` and the flow becomes uniquely determined by how much
droop the downstream pressure is showing -- which is exactly the physical
mechanism. So :meth:`Regulator.check` reports a zero coefficient as an error
rather than a warning when the intent is a transient, and
:class:`IdealRegulator` exists precisely so that "I want a perfect setpoint" is a
deliberate, named choice with its consequence understood.

Function Documentation

◆ _dynamic_head()

float feedtwin.comps.regulator._dynamic_head ( float mdot,
float bore,
float rho )
protected

Definition at line 164 of file regulator.py.

Referenced by feedtwin.comps.regulator.Regulator._seat_dp().

◆ _measured_regulator()

HydraulicComponent feedtwin.comps.regulator._measured_regulator ( object instance)
protected

Definition at line 674 of file regulator.py.

Variable Documentation

◆ REVERSE_STIFFNESS

float feedtwin.comps.regulator.REVERSE_STIFFNESS = 1.0e10

Definition at line 143 of file regulator.py.

◆ SEAT_MODEL_NAME

str feedtwin.comps.regulator.SEAT_MODEL_NAME = "regulator seat, compressible (IEC 60534-2-1)"

Definition at line 155 of file regulator.py.

◆ SEAT_MODEL_SOURCE

tuple feedtwin.comps.regulator.SEAT_MODEL_SOURCE
Initial value:
1= (
2 "IEC 60534-2-1:2011, Industrial-process control valves - Part 2-1: Flow "
3 "capacity - Sizing equations for fluid flow under installed conditions: "
4 "W = N6 C Y sqrt(x p1 rho1), Y = 1 - x/(3 F_gamma xT), F_gamma = gamma/1.40, "
5 "choked at x >= F_gamma xT"
6)

Definition at line 156 of file regulator.py.

◆ SEAT_XT_SIGNAL

str feedtwin.comps.regulator.SEAT_XT_SIGNAL = "regulator_seat_xT"

Definition at line 153 of file regulator.py.

◆ SUPPLY_ZERO

float feedtwin.comps.regulator.SUPPLY_ZERO = 101325.0

Definition at line 147 of file regulator.py.