STAR
Cal STAR — firmware, DAQ server, and engine design
Loading...
Searching...
No Matches
feedtwin.vessels.tank.Tank Class Reference
Collaboration diagram for feedtwin.vessels.tank.Tank:
[legend]

Public Member Functions

None __init__ (self, Fluid liquid, Fluid gas, TankGeometry geometry, *, CollapseModel|None collapse=None, float wall_mass=0.0, float wall_capacity=0.0, float wall_conductance=0.0, VapourModel|None vapour=None, float wetted_conductance=0.0, float ambient_conductance=0.0, float ambient_temperature=293.15, bool wall_boiling=False, float nucleate_conductance=0.0, float leidenfrost_superheat=float("inf"), float boiling_onset=0.0, float surface_layer=0.0, float surface_mixing=0.0, bool wall_by_level=False)
float liquid_volume (self, TankState state)
float ullage_volume (self, TankState state)
float level (self, TankState state)
float interface_area (self, TankState state)
float fill_fraction (self, TankState state)
float pressure (self, TankState state)
float vapour_pressure (self, TankState state)
float gas_temperature (self, TankState state)
float gas_enthalpy (self, TankState state)
float liquid_density (self, TankState state)
LiquidThermal liquid_thermal (self, TankState state)
float outlet_pressure (self, TankState state, float gravity=GRAVITY)
TankState initial_state (self, *, float pressure, float liquid_mass, float liquid_temperature, float|None gas_temperature=None, float contact_time=0.0, bool split_wall=False)
float dry_wall_fraction (self, TankState state)
float surface_mass (self, TankState state, float|None rho_l=None)
TankRates rates (self, TankState state, *, float mdot_liquid_out=0.0, float mdot_gas_in=0.0, float enthalpy_gas_in=0.0, float heat_in=0.0, float stirring=1.0, float mdot_vapour_out=0.0)
TankState step (self, TankState state, TankRates rates, float dt)
dict[str, float] pressurant_for_expulsion (self, TankState state, float mdot_liquid, float duration, *, float inlet_temperature, int steps=500)
str __repr__ (self)

Public Attributes

 liquid = liquid
 gas = gas
 geometry = geometry
tuple collapse
 wall_mass = wall_mass
 wall_capacity = wall_capacity
 wall_conductance = wall_conductance
VapourModel vapour = NoVapour() if vapour is None else vapour
 wetted_conductance = wetted_conductance
 ambient_conductance = ambient_conductance
 ambient_temperature = ambient_temperature
 wall_boiling = wall_boiling
 nucleate_conductance = nucleate_conductance
 leidenfrost_superheat = leidenfrost_superheat
 boiling_onset = boiling_onset
 surface_layer = surface_layer
 surface_mixing = surface_mixing
 wall_by_level = wall_by_level

Protected Member Functions

float _ullage (self, TankState state, str prop)
float _vapour_r_specific (self)
tuple[float, float] _critical_point (self)

Protected Attributes

float|None _r_vapour = None
tuple _level_memo = (-1.0, 0.0)
tuple[float, float]|None _critical = None
tuple _density_memo = (-1.0, 0.0)
tuple _ullage_memo = (None, {})

Detailed Description

A partly-filled propellant tank.

Args:
    liquid: The propellant.
    gas: The pressurant. A separate fluid: this is a two-species model with
        one interface, not a mixture. Vapour in the ullage is a Phase 14
        refinement and the seam for it is :attr:`collapse`.
    geometry: Tank shape -- see :mod:`feedtwin.vessels.geometry`.
    collapse: Interfacial heat transfer model. Defaults to transient
        conduction, which is a lower bound; ``NoCollapse()`` disables it,
        and either choice is recorded rather than assumed.
    wall_mass: Mass of tank wall in thermal contact with the ullage [kg].
    wall_capacity: Wall specific heat [J/(kg.K)].
    wall_conductance: Ullage-to-wall conductance hA [W/K].
    vapour: Propellant vapour in the ullage. Defaults to
        :class:`~feedtwin.vessels.vapour.NoVapour`, which is exactly the
        two-species behaviour this class had before the model existed.
    wetted_conductance: Liquid-to-wall conductance per unit wetted area
        [W/(m^2.K)]. **Chilldown.** Zero by default, which removes the term
        entirely; give it a value and a warm wall dumps heat into the
        liquid, which with a vapour model fitted is what boils a cryogen off
        during a load. Per unit area rather than lumped because the wetted
        area is a strong function of fill and the geometry already knows it.
    ambient_conductance: Air-to-wall film per unit outer area [W/(m^2.K)].
        **Heat leak.** Zero by default, which is a wall that has no outside
        -- once chilled it stays chilled and a loaded tank sits forever.
        Give it a value and the room leaks heat into the metal, split
        between the wetted and ullage-side lumps by area: the wetted wall
        passes it to the liquid through ``wetted_conductance``, and with a
        vapour model fitted a saturated cryogen boils, so a LOX tank with
        its vent shut climbs -- which is what one does.
    ambient_temperature: The room [K].
    wall_boiling: Let heat arriving at a superheated wetted wall boil the
        liquid **at the wall**, straight into the ullage, rather than
        warming the bulk. Off by default. Nucleate boiling needs the wall
        above saturation at the tank's *total* pressure -- a bubble has to
        push the whole ullage back -- so a LOX tank at atmosphere with
        its vent shut boils and climbs, and the same tank pressed to
        38 bar of helium does not: its liquid is a hundred kelvin
        subcooled at that pressure and the leak warms the bulk instead.
        Both are what the stand does. Needs a vapour model fitted.

Definition at line 163 of file tank.py.

Constructor & Destructor Documentation

◆ __init__()

None feedtwin.vessels.tank.Tank.__init__ ( self,
Fluid liquid,
Fluid gas,
TankGeometry geometry,
* ,
CollapseModel | None collapse = None,
float wall_mass = 0.0,
float wall_capacity = 0.0,
float wall_conductance = 0.0,
VapourModel | None vapour = None,
float wetted_conductance = 0.0,
float ambient_conductance = 0.0,
float ambient_temperature = 293.15,
bool wall_boiling = False,
float nucleate_conductance = 0.0,
float leidenfrost_superheat = float("inf"),
float boiling_onset = 0.0,
float surface_layer = 0.0,
float surface_mixing = 0.0,
bool wall_by_level = False )

Definition at line 207 of file tank.py.

Member Function Documentation

◆ __repr__()

str feedtwin.vessels.tank.Tank.__repr__ ( self)

Definition at line 1049 of file tank.py.

◆ _critical_point()

tuple[float, float] feedtwin.vessels.tank.Tank._critical_point ( self)
protected
``(T_crit, p_crit)`` of the propellant, read once; infinite for a
measured table with no critical point.

Definition at line 577 of file tank.py.

References _critical, and liquid.

Referenced by rates().

◆ _ullage()

float feedtwin.vessels.tank.Tank._ullage ( self,
TankState state,
str prop )
protected
One ullage property from ``(rho, u)``, with a legible failure.

An ullage driven cold and dense enough stops being a gas, and the
property layer says so in terms of its own internals -- a bare
``rhoV is invalid`` several frames down inside CoolProp. That is a real
physical event with two very different causes and it should name both:
a long unpressurised hold genuinely does chill an ullage into a
liquid-like state, and a too-large integration step gets there
spuriously. Either way, continuing would report a pressure from a state
this model does not represent.

Definition at line 326 of file tank.py.

References _ullage_memo, gas, and ullage_volume().

Referenced by gas_enthalpy(), gas_temperature(), pressure(), and vapour_pressure().

◆ _vapour_r_specific()

float feedtwin.vessels.tank.Tank._vapour_r_specific ( self)
protected
Specific gas constant of the propellant vapour [J/(kg.K)]: R / M.

This used to be read off the equation of state at one bar and 300 K,
"where every propellant here is a well-behaved gas". Ethanol is not:
at one bar and 300 K it is a liquid at 780 kg/m^3, the "gas constant"
came out at 0.43 J/(kg.K) instead of 181, and a fuel tank primed with
its equilibrium vapour was seeded with 124 grams of ethanol vapour in
a 2.6 litre ullage -- half its gas by mass, which then slowed its vent
to twice the real time. The molar mass is what the ideal-gas fallback
actually needs, and the equation of state knows it for any species.

Definition at line 421 of file tank.py.

References _r_vapour, and liquid.

Referenced by initial_state(), and vapour_pressure().

◆ dry_wall_fraction()

float feedtwin.vessels.tank.Tank.dry_wall_fraction ( self,
TankState state )
Share of the inner wall above the liquid [-]: what the ullage touches.

Definition at line 560 of file tank.py.

References geometry, level(), max, and min.

Referenced by rates().

◆ fill_fraction()

float feedtwin.vessels.tank.Tank.fill_fraction ( self,
TankState state )

Definition at line 321 of file tank.py.

References geometry, and liquid_volume().

◆ gas_enthalpy()

float feedtwin.vessels.tank.Tank.gas_enthalpy ( self,
TankState state )

Definition at line 418 of file tank.py.

References _ullage().

◆ gas_temperature()

float feedtwin.vessels.tank.Tank.gas_temperature ( self,
TankState state )

Definition at line 415 of file tank.py.

References _ullage().

Referenced by rates().

◆ initial_state()

TankState feedtwin.vessels.tank.Tank.initial_state ( self,
* ,
float pressure,
float liquid_mass,
float liquid_temperature,
float | None gas_temperature = None,
float contact_time = 0.0,
bool split_wall = False )
Build a state from what is actually known before a test.

A propellant load, a liquid temperature, a tank pressure, and the
pressurant temperature. Everything else follows.

``contact_time`` is how long the interface has existed [s]; a tank that
was loaded minutes ago is not one whose surface appeared this instant,
and the collapse flux goes as ``1/sqrt(t)``. ``split_wall`` gives the
wall under the liquid its own temperature -- the liquid's -- while the
wall above stays at the gas temperature. Off, a single lump at the gas
temperature, exactly as before.

Definition at line 479 of file tank.py.

References _vapour_r_specific(), gas, geometry, liquid, max, surface_layer, and vapour.

◆ interface_area()

float feedtwin.vessels.tank.Tank.interface_area ( self,
TankState state )
Liquid surface area [m^2]. Falls away as a head empties.

Definition at line 317 of file tank.py.

References geometry, and level().

Referenced by rates(), and surface_mass().

◆ level()

float feedtwin.vessels.tank.Tank.level ( self,
TankState state )
Height of the liquid surface above the tank's lowest point [m].

Definition at line 307 of file tank.py.

References _level_memo, geometry, and liquid_volume().

Referenced by dry_wall_fraction(), interface_area(), outlet_pressure(), and rates().

◆ liquid_density()

float feedtwin.vessels.tank.Tank.liquid_density ( self,
TankState state )
Liquid density [kg/m^3], on the saturation line at its temperature.

Saturated rather than at tank pressure: for a cryogen the compressed-
liquid correction is a fraction of a percent, while asking for a
pressure the liquid is not actually at risks landing outside a tabular
backend's envelope for no gain.

Definition at line 439 of file tank.py.

References _density_memo, and liquid.

Referenced by liquid_volume(), outlet_pressure(), rates(), and surface_mass().

◆ liquid_thermal()

LiquidThermal feedtwin.vessels.tank.Tank.liquid_thermal ( self,
TankState state )

Definition at line 455 of file tank.py.

References liquid.

Referenced by rates().

◆ liquid_volume()

float feedtwin.vessels.tank.Tank.liquid_volume ( self,
TankState state )
Volume the propellant occupies [m^3], at its own density.

Definition at line 281 of file tank.py.

References liquid_density().

Referenced by fill_fraction(), level(), and ullage_volume().

◆ outlet_pressure()

float feedtwin.vessels.tank.Tank.outlet_pressure ( self,
TankState state,
float gravity = GRAVITY )
Pressure at the tank outlet [Pa]: ullage plus the liquid column.

The head a full tank adds is not negligible against a feed-system
budget -- a metre of LOX is about 1.6 psi -- and it decays over a burn,
so a model that leaves it out predicts a drifting error rather than a
constant one. ``gravity`` is what the column feels along the tank's
axis: standard gravity on a stand, several times it in a vehicle under
thrust.

Definition at line 463 of file tank.py.

References level(), liquid_density(), feedtwin.session.burn.BurnTrace.pressure, feedtwin.session.core.BottleSim.pressure(), feedtwin.session.core.TankSim.pressure, feedtwin.session.core.TankSim.pressure(), feedtwin.session.network_trace.NetworkTrace.pressure, feedtwin.solve.network.Node.pressure, pressure(), and fsw::calibration::CalibrationPoint.pressure.

◆ pressurant_for_expulsion()

dict[str, float] feedtwin.vessels.tank.Tank.pressurant_for_expulsion ( self,
TankState state,
float mdot_liquid,
float duration,
* ,
float inlet_temperature,
int steps = 500 )
How much pressurant it takes to hold pressure through an expulsion.

The question a pressurisation system is actually sized by, and the one
where ullage collapse shows itself: with pressure held constant the
collapse never appears in the pressure trace at all, only in this
number.

Solves for the gas inflow that keeps ullage pressure at its starting
value, marching the real energy balance -- expansion work, interfacial
loss and wall exchange included.

Returns:
    ``mass`` [kg] of pressurant consumed, the ``heat_to_liquid`` [J]
    lost at the interface over the run, the ``expansion_work`` [J], and
    the ``pressure_error`` [Pa] left at the end, which should be small
    and is reported so it can be checked rather than trusted.

Definition at line 969 of file tank.py.

References gas, max, feedtwin.session.burn.BurnTrace.pressure, feedtwin.session.core.BottleSim.pressure(), feedtwin.session.core.TankSim.pressure, feedtwin.session.core.TankSim.pressure(), feedtwin.session.network_trace.NetworkTrace.pressure, feedtwin.solve.network.Node.pressure, pressure(), fsw::calibration::CalibrationPoint.pressure, rates(), feedtwin.session.core.Session.step(), feedtwin.session.model.AssemblyError.step, and step().

◆ pressure()

float feedtwin.vessels.tank.Tank.pressure ( self,
TankState state )
Ullage pressure [Pa]. What the regulator sees and the outlet feeds.

Pressurant plus propellant vapour, by Dalton. With no vapour model
fitted the second term is identically zero and this is the pressurant
pressure alone, exactly as before.

Definition at line 361 of file tank.py.

References _ullage(), and vapour_pressure().

Referenced by outlet_pressure(), pressurant_for_expulsion(), and rates().

◆ rates()

TankRates feedtwin.vessels.tank.Tank.rates ( self,
TankState state,
* ,
float mdot_liquid_out = 0.0,
float mdot_gas_in = 0.0,
float enthalpy_gas_in = 0.0,
float heat_in = 0.0,
float stirring = 1.0,
float mdot_vapour_out = 0.0 )
Time derivatives, for an integrator to march.

Args:
    mdot_liquid_out: Propellant leaving [kg/s].
    mdot_gas_in: Pressurant arriving [kg/s].
    enthalpy_gas_in: Specific enthalpy of arriving pressurant [J/kg].
        Warm gas into a cold ullage is a heat source and the number
        that makes helium behave differently from nitrogen.
    heat_in: External heat into the ullage [W].
    stirring: Multiplier on the ullage-to-wall conductance, 1 by
        default; see :meth:`GasVolume.rates`.
    mdot_vapour_out: Propellant vapour leaving through a vent [kg/s].
        A well-mixed ullage vents pressurant and vapour in proportion,
        and the caller knows the split. Without this a venting LOX
        tank lost only its pressurant: the vapour it boiled stayed,
        and a tank with a 3/8 in vent wide open climbed to 500 psig. Wall exchange and
        interfacial loss are computed here and must not be included.

Definition at line 592 of file tank.py.

References _critical_point(), ambient_conductance, ambient_temperature, boiling_onset, collapse, dry_wall_fraction(), exchange(), gas_temperature(), geometry, interface_area(), leidenfrost_superheat, level(), liquid, liquid_density(), liquid_thermal(), max, min, nucleate_conductance, feedtwin.session.burn.BurnTrace.pressure, feedtwin.session.core.BottleSim.pressure(), feedtwin.session.core.TankSim.pressure, feedtwin.session.core.TankSim.pressure(), feedtwin.session.network_trace.NetworkTrace.pressure, feedtwin.solve.network.Node.pressure, pressure(), fsw::calibration::CalibrationPoint.pressure, surface_layer, surface_mass(), feedtwin.session.core.Setup.surface_mixing, surface_mixing, ullage_volume(), vapour, vapour_pressure(), feedtwin.session.core.Setup.wall_boiling, wall_boiling, wall_by_level, wall_capacity, wall_conductance, wall_mass, and wetted_conductance.

Referenced by feedtwin.vessels.volume.GasVolume.blowdown(), and pressurant_for_expulsion().

◆ step()

TankState feedtwin.vessels.tank.Tank.step ( self,
TankState state,
TankRates rates,
float dt )
One explicit Euler step.

Explicit on purpose, and stable only because the caller makes it so:
the session sub-steps the vessels, sizes its coupling from the
regulator-ullage time constant and the ullage's mass, and closes the
press path implicitly inside the network solve (ullage storage).

Rebuilt with ``replace``: a field added to :class:`TankState` later is
carried through the step rather than reset to its default every step,
which is how ``vapour_mass`` was once zeroed (docs/PHYSICS-BENCHMARK.md
4.3).

Definition at line 931 of file tank.py.

References max.

Referenced by engine.pipeline.thermal.wall_conduction.WallModel.advance(), feedtwin.vessels.volume.GasVolume.blowdown(), pressurant_for_expulsion(), and engine.pipeline.thermal.wall_conduction.WallModel.soak().

◆ surface_mass()

float feedtwin.vessels.tank.Tank.surface_mass ( self,
TankState state,
float | None rho_l = None )
Liquid in the surface layer [kg]: a slab ``surface_layer`` deep over
the interface, never more than the liquid there is.

Definition at line 568 of file tank.py.

References interface_area(), liquid_density(), min, and surface_layer.

Referenced by rates().

◆ ullage_volume()

float feedtwin.vessels.tank.Tank.ullage_volume ( self,
TankState state )
What is left for the gas [m^3].

Raises:
    ValueError: the tank is overfull. A negative ullage is not a small
        error to clip -- it means the load, the density or the geometry
        disagree, and continuing would silently produce a pressure from
        a volume that does not exist.

Definition at line 286 of file tank.py.

References geometry, and liquid_volume().

Referenced by _ullage(), rates(), and vapour_pressure().

◆ vapour_pressure()

float feedtwin.vessels.tank.Tank.vapour_pressure ( self,
TankState state )
The propellant vapour's own partial pressure in the ullage [Pa].

Its own equation of state at the shared ullage temperature and its own
partial density -- not a mixture EOS. See
:mod:`feedtwin.vessels.vapour` for why that approximation is the right
one here.

Definition at line 370 of file tank.py.

References _ullage(), _vapour_r_specific(), liquid, and ullage_volume().

Referenced by pressure(), and rates().

Member Data Documentation

◆ _critical

tuple[float, float] | None feedtwin.vessels.tank.Tank._critical = None
protected

Definition at line 271 of file tank.py.

Referenced by _critical_point(), and feedtwin.props.backends.coolprop._Dome.near().

◆ _density_memo

tuple feedtwin.vessels.tank.Tank._density_memo = (-1.0, 0.0)
protected

Definition at line 276 of file tank.py.

Referenced by liquid_density().

◆ _level_memo

tuple feedtwin.vessels.tank.Tank._level_memo = (-1.0, 0.0)
protected

Definition at line 270 of file tank.py.

Referenced by level().

◆ _r_vapour

float | None feedtwin.vessels.tank.Tank._r_vapour = None
protected

Definition at line 266 of file tank.py.

Referenced by _vapour_r_specific().

◆ _ullage_memo

tuple feedtwin.vessels.tank.Tank._ullage_memo = (None, {})
protected

Definition at line 277 of file tank.py.

Referenced by _ullage().

◆ ambient_conductance

feedtwin.vessels.tank.Tank.ambient_conductance = ambient_conductance

Definition at line 240 of file tank.py.

Referenced by rates().

◆ ambient_temperature

feedtwin.vessels.tank.Tank.ambient_temperature = ambient_temperature

Definition at line 241 of file tank.py.

Referenced by rates().

◆ boiling_onset

feedtwin.vessels.tank.Tank.boiling_onset = boiling_onset

Definition at line 251 of file tank.py.

Referenced by rates().

◆ collapse

tuple feedtwin.vessels.tank.Tank.collapse
Initial value:
= (
ConductionCollapse() if collapse is None else collapse
)

Definition at line 232 of file tank.py.

Referenced by rates().

◆ gas

feedtwin.vessels.tank.Tank.gas = gas

Definition at line 230 of file tank.py.

Referenced by _ullage(), initial_state(), and pressurant_for_expulsion().

◆ geometry

feedtwin.vessels.tank.Tank.geometry = geometry

◆ leidenfrost_superheat

feedtwin.vessels.tank.Tank.leidenfrost_superheat = leidenfrost_superheat

Definition at line 247 of file tank.py.

Referenced by rates().

◆ liquid

feedtwin.vessels.tank.Tank.liquid = liquid

◆ nucleate_conductance

feedtwin.vessels.tank.Tank.nucleate_conductance = nucleate_conductance

Definition at line 246 of file tank.py.

Referenced by rates().

◆ surface_layer

feedtwin.vessels.tank.Tank.surface_layer = surface_layer

Definition at line 256 of file tank.py.

Referenced by initial_state(), rates(), and surface_mass().

◆ surface_mixing

feedtwin.vessels.tank.Tank.surface_mixing = surface_mixing

Definition at line 259 of file tank.py.

Referenced by rates().

◆ vapour

feedtwin.vessels.tank.Tank.vapour = NoVapour() if vapour is None else vapour

Definition at line 238 of file tank.py.

Referenced by initial_state(), and rates().

◆ wall_boiling

feedtwin.vessels.tank.Tank.wall_boiling = wall_boiling

Definition at line 242 of file tank.py.

Referenced by rates().

◆ wall_by_level

feedtwin.vessels.tank.Tank.wall_by_level = wall_by_level

Definition at line 265 of file tank.py.

Referenced by rates().

◆ wall_capacity

feedtwin.vessels.tank.Tank.wall_capacity = wall_capacity

Definition at line 236 of file tank.py.

Referenced by rates(), and feedtwin.vessels.volume.GasVolume.rates().

◆ wall_conductance

feedtwin.vessels.tank.Tank.wall_conductance = wall_conductance

Definition at line 237 of file tank.py.

Referenced by rates(), and feedtwin.vessels.volume.GasVolume.rates().

◆ wall_mass

feedtwin.vessels.tank.Tank.wall_mass = wall_mass

Definition at line 235 of file tank.py.

Referenced by rates(), and feedtwin.vessels.volume.GasVolume.rates().

◆ wetted_conductance

feedtwin.vessels.tank.Tank.wetted_conductance = wetted_conductance

Definition at line 239 of file tank.py.

Referenced by rates().


The documentation for this class was generated from the following file:
  • lib/feedtwin/feedtwin/vessels/tank.py