|
STAR
Cal STAR — firmware, DAQ server, and engine design
|
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, {}) |
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.
| 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 ) |
|
protected |
|
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().
|
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().
| float feedtwin.vessels.tank.Tank.dry_wall_fraction | ( | self, | |
| TankState | state ) |
| float feedtwin.vessels.tank.Tank.fill_fraction | ( | self, | |
| TankState | state ) |
Definition at line 321 of file tank.py.
References geometry, and liquid_volume().
| float feedtwin.vessels.tank.Tank.gas_enthalpy | ( | self, | |
| TankState | state ) |
| float feedtwin.vessels.tank.Tank.gas_temperature | ( | self, | |
| TankState | 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.
| float feedtwin.vessels.tank.Tank.interface_area | ( | self, | |
| TankState | state ) |
| 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().
| 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().
| LiquidThermal feedtwin.vessels.tank.Tank.liquid_thermal | ( | self, | |
| TankState | state ) |
| 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().
| 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.
| 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().
| 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().
| 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().
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().
| 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().
| 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().
| 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().
|
protected |
Definition at line 271 of file tank.py.
Referenced by _critical_point(), and feedtwin.props.backends.coolprop._Dome.near().
|
protected |
Definition at line 276 of file tank.py.
Referenced by liquid_density().
|
protected |
|
protected |
Definition at line 266 of file tank.py.
Referenced by _vapour_r_specific().
|
protected |
| feedtwin.vessels.tank.Tank.ambient_conductance = ambient_conductance |
| feedtwin.vessels.tank.Tank.ambient_temperature = ambient_temperature |
| feedtwin.vessels.tank.Tank.boiling_onset = boiling_onset |
| tuple feedtwin.vessels.tank.Tank.collapse |
| feedtwin.vessels.tank.Tank.gas = gas |
Definition at line 230 of file tank.py.
Referenced by _ullage(), initial_state(), and pressurant_for_expulsion().
| feedtwin.vessels.tank.Tank.geometry = geometry |
Definition at line 231 of file tank.py.
Referenced by dry_wall_fraction(), fill_fraction(), initial_state(), interface_area(), level(), combined_gui.PressureBarWidget.paintEvent(), rates(), and ullage_volume().
| feedtwin.vessels.tank.Tank.leidenfrost_superheat = leidenfrost_superheat |
| feedtwin.vessels.tank.Tank.liquid = liquid |
Definition at line 229 of file tank.py.
Referenced by _critical_point(), _vapour_r_specific(), initial_state(), liquid_density(), liquid_thermal(), rates(), and vapour_pressure().
| feedtwin.vessels.tank.Tank.nucleate_conductance = nucleate_conductance |
| feedtwin.vessels.tank.Tank.surface_layer = surface_layer |
Definition at line 256 of file tank.py.
Referenced by initial_state(), rates(), and surface_mass().
| feedtwin.vessels.tank.Tank.surface_mixing = surface_mixing |
| 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().
| feedtwin.vessels.tank.Tank.wall_boiling = wall_boiling |
| feedtwin.vessels.tank.Tank.wall_by_level = wall_by_level |
| 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().
| 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().
| 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().
| feedtwin.vessels.tank.Tank.wetted_conductance = wetted_conductance |