|
| float | _calibrate_choke () |
| float | _dynamic_head (float mdot, float bore, float rho) |
| float | cv_from_cd (float cd, float bore) |
| float | _curve_dp (Curve curve, float mdot) |
| HydraulicComponent | _segmented (ComponentInstance instance) |
| HydraulicComponent | _measured (ComponentInstance instance) |
| FlowConditions | conditions_from_fluid (object fluid, float p, float T, Mapping[str, float]|None signals=None, *, bool multiphase=False, str|None phase=None, float gravity=GRAVITY) |
| float | _off_the_dew_point (Fluid fluid, float p, float T) |
| float | _ideal_gamma (Fluid fluid, float T) |
| tuple[float, float, float, float, float]|None | _liquid_state (object fluid, float T) |
The liquid-side components: pipe, fitting, orifice, valve, check valve.
Every correlation here comes from ``fluids``, which is validated against the
books it implements -- Crane TP-410, IEC 60534, ISO 5167. This module contributes
the adapters and the bookkeeping, and deliberately nothing else. Where a number
in this package disagrees with the source, the fault is in an adapter.
The one thing worth reading before using any of these: **pressure drop is
expressed as a function of mass flow**, not the other way round. A network solve
carries flows and solves for pressures, so this is the direction that avoids an
inner root-find per component per iteration.
| float feedtwin.comps.elements._curve_dp |
( |
Curve | curve, |
|
|
float | mdot ) |
|
protected |
A measured curve, continued quadratically past the ends it was measured over.
:class:`MeasuredElement` raises outside its range, and for a whole component
declared ``measured`` that is right -- the run is the measurement, and there
is nothing else to fall back on.
A *segment* is different in two ways. It is one part of a run whose other
parts still have physics, so refusing the point throws away everything that
is known about the rest of the line. And a Newton iterate overshoots by its
nature: killing a transient because the solver probed 1.1x the top of a flow
bench sweep would make a measured segment worse to have than no measurement.
So it is continued, and continued *quadratically* -- ``dp ~ mdot^2`` is the
turbulent law the curve is a measurement of, so this is the shape the data
already has rather than a shape invented to fill a gap. Clamping instead
would be worse than either: a flat dp above the last point takes the flow
dependence out of the branch equation, and a branch whose loss does not
respond to its own flow makes the Jacobian singular.
Definition at line 1112 of file elements.py.
Referenced by feedtwin.comps.elements.SegmentedPipe._segment_dp(), and feedtwin.comps.elements.MeasuredElement.pressure_drop().
| tuple[float, float, float, float, float] | None feedtwin.comps.elements._liquid_state |
( |
object | fluid, |
|
|
float
| T ) |
|
protected |
``(rho, mu, gamma, p_sat, molar_mass)`` on the saturated-liquid line.
``None`` when the fluid has no liquid line at this temperature -- above the
critical point, which is where every pressurant lives -- and the caller
falls back to evaluating at ``(p, T)``.
Cached because with phase inference off these depend on **temperature
alone**: a Newton solve walks the pressures and holds the temperatures, so
a feed line's properties are constant across the entire solve and were
being recomputed a thousand times a tick. That was two thirds of the cost
of a live tick, and none of it bought a different number.
Definition at line 1333 of file elements.py.
Referenced by conditions_from_fluid().
| FlowConditions feedtwin.comps.elements.conditions_from_fluid |
( |
object | fluid, |
|
|
float | p, |
|
|
float | T, |
|
|
Mapping[str, float] | None | signals = None, |
|
|
* | , |
|
|
bool | multiphase = False, |
|
|
str | None | phase = None, |
|
|
float | gravity = GRAVITY ) |
Build :class:`FlowConditions` from a :class:`~feedtwin.props.Fluid`.
``phase`` is what the network *declares* the node to hold, and it wins over
the temperature rule below: ``"gas"`` is priced at ``(p, T)`` whatever the
temperature, ``"liquid"`` on the saturated-liquid line, ``None`` falls back
to the rule. A drawing knows which side of a tank a line is on; a
temperature does not, and an ethanol *vapour* line at 350 K is below
ethanol's critical temperature and would otherwise be priced as liquid.
The bridge between Phase 01 and Phase 03, kept as a free function so that
components never hold a fluid and stay testable with two floats.
Saturation pressure comes from the fluid where the state has one; above the
critical temperature there is no saturation line, and reporting zero there
disables the cavitation checks honestly rather than inventing a limit.
Phase, and why it is not inferred by default
-------------------------------------------
Asking the property layer for ``rho(p, T)`` lets it decide the phase, and
when the temperature reaching it is wrong that decision is *silently*
catastrophic: oxygen at the ambient default is a gas at 40 kg/m^3 where LOX
is 1140, so the leg solves at a thirtieth of the density and every number
after it stays plausible. It has happened twice here, from two different
causes, and both times it looked like a flow-rate discrepancy rather than a
property lookup.
So ``multiphase`` is **off**. With it off, a fluid that is below its
critical temperature is evaluated on its saturated-liquid line at ``T`` --
it cannot come back as a gas, whatever pressure the solve is currently
guessing. Real feed lines run subcooled and a few per cent stiffer than
saturated, which is well inside what a lumped ``K`` already assumes.
Turn it on when flashing and cavitation are the subject rather than the
hazard. It is not ready for that yet, which is the other reason it is off.
Definition at line 1169 of file elements.py.
References _ideal_gamma(), _liquid_state(), and _off_the_dew_point().