solprop.gasflow#

Steady gas flow in a hollow fibre: the fill, the choked end and the flow regime.

A hollow fibre filled from gas cells at its ends carries a steady laminar flow. For an isothermal ideal gas in Poiseuille flow the mass flow is constant along each stage and proportional to \(a^4\,p\,\mathrm{d}p/\mathrm{d}z\), which makes \(p^2\) linear in the hydraulic resistance \(R = \int a^{-4}\,\mathrm{d}z\) (solprop.profiles.PressureGradient, Pennetta et al., APL Photonics 4, 056105 (2019), eq. 6). The PV throughput of a stage is

\[Q = \frac{\pi\,(p_h^2 - p_l^2)}{16\,\eta\,R}.\]

Choked ends. An end that exhausts into a pumped chamber cannot pass more than the sonic throughput \(p_e \pi a_e^2 c\). Equating the two, as HiSol.jl’s choked_pressure does (which then drops \(p_e^2\)), gives

\[k (p_h^2 - p_e^2) = p_e, \quad k = \frac{1}{16\,\eta\,c\,a_e^2 R}, \quad p_e = \frac{2 k p_h^2}{1 + \sqrt{1 + 4 k^2 p_h^2}},\]

solved here in that non-cancelling form. steady_fill() applies \(\max(\text{declared}, \text{choked}, \text{floor})\) at each outer end that is the low end of its stage: a fill “to vacuum” ends at its choked pressure, not at zero and not at a numerical floor. c is the adiabatic sound speed at the fill temperature, as HiSol takes it; an independent integration of the one-dimensional compressible flow (tests/test_gasflow.py) brackets the result between an isothermal and an adiabatic wall, which is why the adiabatic speed is kept (docs/dev/decisions.md).

Flow regime. Gebhardt et al., Opt. Express 29, 6957 (2021), measured output-beam instabilities in differentially pumped capillaries from \(Re \approx 2040\), worst near 2400, and recommend \(Re < 2000\) (their eq. 4); the laminar fill itself is not valid there. With \(Re = 4\dot m/(\pi d \eta)\) and the mass flow constant along a stage, the narrowest point of a taper has the largest Reynolds number. At the other extreme the gas stops being a continuum: with \(Kn = \lambda / d\), first-order wall slip raises the throughput by \(8\,Kn\), and above 0.1 the flow is transitional. steady_fill() warns (GasFlowWarning) for a Reynolds number above 2000, for a stage whose Knudsen number at its mean pressure and narrowest bore exceeds 0.01, or for one with more than 1 % of its gas where the Knudsen number exceeds 0.1, and otherwise logs the exit’s Knudsen number. It does not replace the laminar law: a turbulent or rarefied fill is out of scope.

Pressures are in bar at this interface (docs/dev/conventions.md section 1); throughputs are in Pa m^3 s^-1, with mbar_litres_per_second() and standard_litres_per_minute() for the units vacuum practice quotes.

Module Attributes

VacuumEnd

How a fill treats an outer end below its choked pressure.

REYNOLDS_LAMINAR_LIMIT

Peak Reynolds number above which a fill warns that it is not laminar.

KNUDSEN_SLIP_LIMIT

Mean-pressure Knudsen number lambda(p_bar) / d_min above which a stage warns.

KNUDSEN_TRANSITION_LIMIT

Knudsen number lambda / d above which the gas is transitional, not a continuum.

TRANSITIONAL_COLUMN_LIMIT

Share of a stage's column density int p dz that may be transitional unwarned.

STANDARD_TEMPERATURE

Temperature in K of a standard litre, for standard_litres_per_minute().

STANDARD_PRESSURE

Pressure in Pa of a standard litre, for standard_litres_per_minute().

Functions

chamber_pressure(throughput, pump_speed)

Return the pressure at which a pump removes a given throughput.

choked_pressure(gas, radius, length, high_bar, *)

Return the choked exit pressure of a bore fed at one end and pumped at the other.

flow_report(fill, gas, *, radius[, temperature])

Return the steady laminar flow a fill implies, stage by stage.

knudsen_number(gas, pressure_bar, radius, *)

Return the Knudsen number lambda / d of a gas in a bore.

mass_flow(gas, throughput, pressure_bar, *)

Return the mass flow carried by a PV throughput measured at a given pressure.

mbar_litres_per_second(throughput)

Convert a PV throughput to mbar l/s.

reynolds_number(gas, mass_flow, radius, z, *)

Return the Reynolds number of a steady flow at each position along a bore.

standard_litres_per_minute(throughput[, ...])

Convert a PV throughput at a gas temperature to standard litres per minute.

steady_fill(gas, positions, pressures_bar, ...)

Build the steady-flow fill of a bore: taper-aware, choked, regime-checked.

temperature_weight(gas, temperature, positions)

Return the flow weight eta(T) T of a gas along a fill, in Pa s K.

throughput(gas, radius, length, start_bar, ...)

Return the laminar PV throughput of a bore between two pressures.

Classes

FlowReport(stages, viscosity, sound_speed, ...)

The steady flow of a fill: per stage, and at the outer ends.

StageFlow(start, end, start_bar, end_bar, ...)

The steady flow through one stage of a fill.

class solprop.gasflow.FlowReport(stages, viscosity, sound_speed, choked_start_bar, choked_end_bar, start_is_choked, end_is_choked)[source]#

Bases: Displayed

The steady flow of a fill: per stage, and at the outer ends.

Attributes:
stagestuple of StageFlow

One entry per stage, in order along z.

viscosityfloat

The gas viscosity in Pa s.

sound_speedfloat

The dilute adiabatic sound speed in m s^-1.

choked_start_bar, choked_end_barfloat or None

The choked pressure in bar of the first and last outer end, where that end is the low end of its stage; None where it is not.

start_is_choked, end_is_chokedbool

Whether that end’s pressure is at (or, with vacuum_end="floor", below) its choked value, so that the exit throughput is the sonic limit.

choked_end_bar: float | None#
choked_start_bar: float | None#
end_is_choked: bool#
property peak_knudsen: float[source]#

Return the largest Knudsen number of the fill.

Returns:
float

The maximum over the stages.

property peak_reynolds: float[source]#

Return the largest Reynolds number of the fill.

Returns:
float

The maximum over the stages.

property peak_reynolds_position: float[source]#

Return where the largest Reynolds number is.

Returns:
float

The position in m.

sound_speed: float#
stages: tuple[StageFlow, ...]#
start_is_choked: bool#
viscosity: float#
solprop.gasflow.KNUDSEN_SLIP_LIMIT: Final = 0.01#

Mean-pressure Knudsen number lambda(p_bar) / d_min above which a stage warns.

p_bar is the mean of the stage’s end pressures and d_min its narrowest diameter. First-order wall slip raises a tube’s throughput to Q_Poiseuille (1 + 8 Kn(p_bar)), so above 0.01 the laminar throughput, the Reynolds number and the choked pressure are off by about ten per cent or more.

solprop.gasflow.KNUDSEN_TRANSITION_LIMIT: Final = 0.1#

Knudsen number lambda / d above which the gas is transitional, not a continuum.

The conventional transition-flow boundary for pipe flow.

solprop.gasflow.REYNOLDS_LAMINAR_LIMIT: Final = 2000.0#

Peak Reynolds number above which a fill warns that it is not laminar.

Gebhardt et al., Opt. Express 29, 6957 (2021), eq. (4) and Fig. 6: output-beam instabilities appear from Re = 2040 and they recommend Re < 2000; pipe flow becomes sustained-turbulent near Re = 2040 (Avila et al., Science 333, 192 (2011)).

solprop.gasflow.STANDARD_PRESSURE: Final = 101325.0#

Pressure in Pa of a standard litre, for standard_litres_per_minute().

solprop.gasflow.STANDARD_TEMPERATURE: Final = 273.15#

Temperature in K of a standard litre, for standard_litres_per_minute().

class solprop.gasflow.StageFlow(start, end, start_bar, end_bar, temperature, throughput, mass_flow, peak_reynolds, peak_reynolds_position, knudsen_start, knudsen_end, peak_knudsen, peak_knudsen_position, mean_knudsen, transitional_length, transitional_fraction)[source]#

Bases: Displayed

The steady flow through one stage of a fill.

Attributes:
start, endfloat

The stage’s ends in m.

start_bar, end_barfloat

The fill’s pressures in bar at the two ends.

temperaturefloat

Gas temperature in K.

throughputfloat

Laminar PV throughput in Pa m^3 s^-1, from the higher to the lower end.

mass_flowfloat

Mass flow in kg s^-1, from the throughput at the stage’s low end (mass_flow()).

peak_reynoldsfloat

The largest Reynolds number on the stage.

peak_reynolds_positionfloat

Where it is, in m: the narrowest point of the bore.

knudsen_start, knudsen_endfloat

Knudsen numbers lambda / d at the two ends; infinite at zero pressure.

peak_knudsenfloat

The largest Knudsen number on the stage.

peak_knudsen_positionfloat

Where it is, in m.

mean_knudsenfloat

The Knudsen number at the stage’s mean end pressure and narrowest diameter, which sets the first-order slip correction 8 Kn of its throughput.

transitional_lengthfloat

The length in m over which the Knudsen number exceeds KNUDSEN_TRANSITION_LIMIT.

transitional_fractionfloat

The share of the stage’s column density int p dz that lies there.

end: float#
end_bar: float#
property exit_knudsen: float[source]#

Return the Knudsen number at the stage’s lower-pressure end.

Returns:
float

knudsen_end when the flow runs towards +z, else knudsen_start.

knudsen_end: float#
knudsen_start: float#
mass_flow: float#
mean_knudsen: float#
peak_knudsen: float#
peak_knudsen_position: float#
peak_reynolds: float#
peak_reynolds_position: float#
start: float#
start_bar: float#
temperature: float#
throughput: float#
property throughput_mbar_litres_per_second: float[source]#

Return the throughput in mbar l/s.

Returns:
float

mbar_litres_per_second() of throughput.

property throughput_slpm: float[source]#

Return the throughput in standard litres per minute.

Returns:
float

standard_litres_per_minute() of throughput.

transitional_fraction: float#
transitional_length: float#
solprop.gasflow.TRANSITIONAL_COLUMN_LIMIT: Final = 0.01#

Share of a stage’s column density int p dz that may be transitional unwarned.

A choked helium exit is transitional over micrometres (about 6 um of a 1 m helium gradient fibre), which carries a negligible part of the gas and of the nonlinear phase; a stage warns only when more than one per cent of its gas lies where the Knudsen number exceeds KNUDSEN_TRANSITION_LIMIT.

solprop.gasflow.VacuumEnd#

How a fill treats an outer end below its choked pressure.

"choked" raises it to the choked pressure of the flow, the default wherever the fill knows the gas and the bore; "floor" keeps the declared value, raised only to the numerical floor of solprop.profiles.PressureGradient, which is what a comparison with a fill whose end is a true vacuum needs. Either way a taper’s fill follows the bore: the choice concerns the ends only.

alias of Literal[‘choked’, ‘floor’]

solprop.gasflow.chamber_pressure(throughput, pump_speed)[source]#

Return the pressure at which a pump removes a given throughput.

Solves p S(p) = Q, as HiSol.jl’s gradient_end_pressure does: the pressure of the chamber an end exhausts into. If it is below the fill’s choked pressure the fibre end is choked and stays at that value.

Parameters:
throughputfloat

PV throughput in Pa m^3 s^-1, >= 0.

pump_speedfloat or callable

Pumping speed in m^3 s^-1, > 0, or a function of the chamber pressure in bar returning it (a pump’s characteristic curve).

Returns:
float

The chamber pressure in bar.

Raises:
TranslationError

If the throughput or a speed is invalid, or a characteristic’s throughput p S(p) does not reach throughput between 1e-12 bar and 10 bar.

Examples

A 35 m^3/h pump taking 15.3 mbar l/s:

>>> from solprop.gasflow import chamber_pressure
>>> f"{chamber_pressure(1.534, 35.0 / 3600.0) * 1e3:.3f}"
'1.578'
solprop.gasflow.choked_pressure(gas, radius, length, high_bar, *, exit_end='end', temperature=293.15)[source]#

Return the choked exit pressure of a bore fed at one end and pumped at the other.

Parameters:
gasGas, str, Species, Mapping or Mixture

One built-in gas, gases to mole fractions, or a mixture with a shared fill.

radiusfloat or ZProfile

Core radius in m, > 0, over [0, length].

lengthfloat

Length in m, > 0.

high_barfloat

Pressure in bar at the fed end, >= 0.

exit_end{‘end’, ‘start’}, optional

Which end exhausts: z = length (the default) or z = 0. Its radius sets the sonic throughput.

temperaturefloat, optional

Gas temperature in K.

Returns:
float

The exit pressure in bar below which the exit throughput would exceed the sonic limit; the module docstring gives the relation.

Raises:
TranslationError

If a pressure, the length, the radius or exit_end is invalid.

MaterialError

If the gas has no transport data at this temperature.

Examples

>>> from solprop.gasflow import choked_pressure
>>> f"{choked_pressure('N2', 250e-6, 5.0, 4.2) * 1e3:.1f}"
'224.3'
>>> f"{choked_pressure('Ar', 15e-6, 1.0, 20.0) * 1e3:.1f}"
'79.1'
solprop.gasflow.flow_report(fill, gas, *, radius, temperature=293.15)[source]#

Return the steady laminar flow a fill implies, stage by stage.

Parameters:
fillsolprop.profiles.PressureGradient

The fill, in bar.

gasGas, str, Species, Mapping or Mixture

One built-in gas, gases to mole fractions, or a mixture with a shared fill.

radiusfloat or ZProfile

The core radius in m the fill runs through, > 0.

temperaturefloat or ZProfile, optional

Gas temperature in K, or a path of it along z in m. Along a path the flow law is weighted by eta(T) T, the Reynolds number takes the local viscosity, the Knudsen numbers the local mean free path, a choked end the viscosity and sound speed at its own temperature, and each stage’s throughput is quoted at its low end’s temperature; the report’s viscosity and sound_speed are those at the fill’s first position.

Returns:
FlowReport

Per stage the throughput, mass flow, peak Reynolds number and Knudsen numbers, and the choked state of the outer ends.

Raises:
TranslationError

If the radius is not positive or cannot be integrated.

MaterialError

If the gas has no transport data at this temperature, or a mixture has independent populations.

Examples

>>> from solprop.gasflow import flow_report, steady_fill
>>> fill = steady_fill("N2", [0.0, 5.0], [2.9, 0.0], radius=250e-6)
>>> report = flow_report(fill, "N2", radius=250e-6)
>>> round(report.peak_reynolds), report.end_is_choked
(1223, True)
solprop.gasflow.knudsen_number(gas, pressure_bar, radius, *, temperature=293.15)[source]#

Return the Knudsen number lambda / d of a gas in a bore.

Parameters:
gasGas, str, Species, Mapping or Mixture

One built-in gas, gases to mole fractions, or a mixture with a shared fill.

pressure_bararray_like or float

Pressure in bar, >= 0; zero gives an infinite Knudsen number.

radiusarray_like or float

Core radius in m, > 0, broadcast against pressure_bar.

temperaturefloat, optional

Gas temperature in K.

Returns:
numpy.ndarray

lambda(p, T) / (2 a) with the mean free path of solprop.physdata.transport.mean_free_path(), in the broadcast shape.

Raises:
TranslationError

If a pressure is negative or a radius is not positive.

MaterialError

If the gas has no transport data at this temperature.

Examples

>>> from solprop.gasflow import knudsen_number
>>> f"{float(knudsen_number('N2', 0.01, 250e-6)):.4f}"
'0.0130'
solprop.gasflow.mass_flow(gas, throughput, pressure_bar, *, temperature=293.15)[source]#

Return the mass flow carried by a PV throughput measured at a given pressure.

mdot = Q rho(p, T) / p with the real-gas mass density at the reference state (pressure_bar, temperature); a mixture sums its species’ partial densities. A throughput is conventionally quoted at the low-pressure end, where the gas is close to ideal and this is Q M / (R T), which is also the limit taken at zero pressure.

Parameters:
gasGas, str, Species, Mapping or Mixture

One built-in gas, gases to mole fractions, or a mixture with a shared fill.

throughputfloat

PV throughput in Pa m^3 s^-1, >= 0.

pressure_barfloat

Pressure in bar at which the throughput is measured, >= 0.

temperaturefloat, optional

Gas temperature in K.

Returns:
float

Mass flow in kg s^-1.

Raises:
TranslationError

If the throughput or the pressure is invalid.

MaterialError

If the gas has no transport data at this temperature.

Examples

>>> from solprop.gasflow import mass_flow
>>> f"{mass_flow('N2', 1.0, 0.0):.4e}"
'1.1493e-05'
solprop.gasflow.mbar_litres_per_second(throughput)[source]#

Convert a PV throughput to mbar l/s.

Parameters:
throughputfloat

PV throughput in Pa m^3 s^-1.

Returns:
float

The throughput in mbar l s^-1 (1 Pa m^3/s = 10 mbar l/s).

Examples

>>> from solprop.gasflow import mbar_litres_per_second
>>> mbar_litres_per_second(1.0)
10.0
solprop.gasflow.reynolds_number(gas, mass_flow, radius, z, *, temperature=293.15)[source]#

Return the Reynolds number of a steady flow at each position along a bore.

Re = 4 mdot / (pi d eta) = 2 mdot / (pi a eta): Gebhardt et al.’s 4 M Q / (R T pi d eta) written with the mass flow, which is constant along a stage, so the narrowest point has the largest value.

Parameters:
gasGas, str, Species, Mapping or Mixture

One built-in gas, gases to mole fractions, or a mixture with a shared fill.

mass_flowfloat

Mass flow in kg s^-1, >= 0.

radiusfloat or ZProfile

Core radius in m, > 0.

znumpy.ndarray

Positions in m, shape (n_z,).

temperaturefloat, optional

Gas temperature in K.

Returns:
numpy.ndarray

The Reynolds number at each position, shape (n_z,).

Raises:
TranslationError

If the mass flow is negative or the radius is not positive.

MaterialError

If the gas has no transport data at this temperature.

Examples

>>> import numpy as np
>>> from solprop.gasflow import reynolds_number
>>> reynolds_number("N2", 1e-5, 250e-6, np.array([0.0])).round(1)
array([1450.2])
solprop.gasflow.standard_litres_per_minute(throughput, temperature=293.15)[source]#

Convert a PV throughput at a gas temperature to standard litres per minute.

A standard litre is the ideal-gas amount in one litre at STANDARD_TEMPERATURE and STANDARD_PRESSURE (0 degrees C, one atmosphere). The throughput Q = p dV/dt at temperature T carries Q/(k_B T) molecules per second, so the flow is Q (T_std/T) / p_std standard cubic metres per second, returned in litres per minute. HiSol.jl’s slpm omits the ratio T_std/T (7 % at 293.15 K). A mass-flow controller calibrated to another standard temperature T_ref (20 degrees C, for example) reads T_ref/T_std times this number.

Parameters:
throughputfloat

PV throughput in Pa m^3 s^-1 at temperature.

temperaturefloat, optional

Temperature in K at which the throughput is measured, > 0.

Returns:
float

The flow in standard litres per minute.

Examples

>>> from solprop.gasflow import standard_litres_per_minute
>>> f"{standard_litres_per_minute(1.0, 273.15):.4f}"
'0.5922'
solprop.gasflow.steady_fill(gas, positions, pressures_bar, *, radius, temperature=293.15, vacuum_end='choked', floor=None)[source]#

Build the steady-flow fill of a bore: taper-aware, choked, regime-checked.

The fill follows the bore (solprop.profiles.PressureGradient with radius). With vacuum_end="choked" each outer end that is the low end of its stage takes max(declared, choked, floor): a declared zero replaced by the choked pressure is logged at INFO, a nonzero declared pressure raised to it at WARNING, naming both. Interior stage pressures are never choked (the chamber between two fibres is not modelled); an interior zero keeps the numerical floor, which from_stages() logs. With vacuum_end="floor" the declared pressures are kept, exactly as before; the fill still follows a varying bore (a uniform one is bitwise the previous fill).

The flow is then checked (flow_report()) and warns with GasFlowWarning for a peak Reynolds number above REYNOLDS_LAMINAR_LIMIT, or for a stage whose Knudsen number at its mean pressure and narrowest bore exceeds KNUDSEN_SLIP_LIMIT, or with more than TRANSITIONAL_COLUMN_LIMIT of its column density where the Knudsen number exceeds KNUDSEN_TRANSITION_LIMIT; otherwise each stage’s exit Knudsen number and transitional length are logged at INFO.

Parameters:
gasGas, str, Species, Mapping or Mixture

One built-in gas, gases to mole fractions (a shared fill), or a mixture with a shared fill.

positionsarray_like

Stage boundaries in m, strictly increasing, at least two.

pressures_bararray_like

Declared pressure in bar at each boundary, >= 0.

radiusfloat or ZProfile

Core radius in m, > 0, over the fill.

temperaturefloat or ZProfile, optional

Gas temperature in K, which sets the viscosity and the sound speed, or a path of it along z in m: the fill then follows the flow weight eta(T) T (solprop.profiles.PressureGradient), a choked end takes the viscosity and the sound speed at its own temperature, and the regime checks the local ones (flow_report()). A constant path is the number, bit for bit.

vacuum_end{‘choked’, ‘floor’}, optional

How an outer end below its choked pressure is treated (VacuumEnd).

floorfloat or None, optional

The numerical floor in bar; None is solprop.profiles.DEFAULT_PRESSURE_FLOOR_FRACTION of the highest stage pressure, and 0.0 with vacuum_end="floor" a true vacuum end.

Returns:
solprop.profiles.PressureGradient

The fill in bar.

Raises:
TranslationError

If the stage table, the radius or vacuum_end is invalid.

MaterialError

If the gas has no transport data at this temperature (a custom species, or a temperature outside the data’s range): pass vacuum_end="floor" to build the fill without the choke, whose flow regime is then not checked. A temperature path needs the data whatever vacuum_end is.

Warns:
GasFlowWarning

If the fill is not laminar, or is transitional between continuum and molecular flow.

Examples

>>> from solprop.gasflow import steady_fill
>>> fill = steady_fill("He", [0.0, 3.0], [5.0, 0.0], radius=125e-6)
>>> f"{fill.pressures[-1] * 1e3:.2f} mbar"
'41.18 mbar'
solprop.gasflow.temperature_weight(gas, temperature, positions)[source]#

Return the flow weight eta(T) T of a gas along a fill, in Pa s K.

A steady laminar flow of an ideal gas carries the mass flow mdot = pi a^4 M d(p^2)/dz / (16 R eta(T) T), so p^2 is linear in int eta(T) T a^-4 dz; solprop.profiles.PressureGradient takes this weight as its weight. Along a temperature path the viscosity is a converged Chebyshev series in T over the temperatures the path takes on the fill, and the weight a solprop.profiles.SeriesOfPath of the path.

Parameters:
gasGas, str, Species, Mapping or Mixture

One built-in gas, gases to mole fractions, or a mixture with a shared fill.

temperaturefloat or ZProfile

Gas temperature in K, or a path of it along z in m.

positionsarray_like

The fill’s stage boundaries in m, strictly increasing: the span over which the temperature range is taken.

Returns:
ZProfile

The weight along z: a Constant for a constant temperature, a SeriesOfPath otherwise.

Raises:
TranslationError

If the positions are invalid.

MaterialError

If the temperature is not positive, or the gas has no transport data over its range.

Examples

>>> import numpy as np
>>> from solprop.gasflow import temperature_weight
>>> from solprop.profiles import Linear
>>> weight = temperature_weight("N2", Linear(293.15, 350.0, 1.0), [0.0, 1.0])
>>> f"{float(weight(np.array([0.0]))[0]):.4e}"
'5.1476e-03'
solprop.gasflow.throughput(gas, radius, length, start_bar, end_bar, *, temperature=293.15)[source]#

Return the laminar PV throughput of a bore between two pressures.

Q = pi (p_h^2 - p_l^2) / (16 eta R) with R = int_0^length a^-4 dz.

Parameters:
gasGas, str, Species, Mapping or Mixture

One built-in gas, gases to mole fractions, or a mixture with a shared fill.

radiusfloat or ZProfile

Core radius in m, > 0, over [0, length].

lengthfloat

Length in m, > 0.

start_bar, end_barfloat

Pressures in bar at z = 0 and z = length, >= 0.

temperaturefloat, optional

Gas temperature in K.

Returns:
float

The throughput in Pa m^3 s^-1, from the higher to the lower end, >= 0.

Raises:
TranslationError

If a pressure, the length or the radius is invalid.

MaterialError

If the gas has no transport data at this temperature.

Examples

Gebhardt et al.’s 5 m, 500 um nitrogen capillary at 4.2 bar into vacuum:

>>> from solprop.gasflow import mbar_litres_per_second, throughput
>>> q = throughput("N2", 250e-6, 5.0, 4.2, 0.0)
>>> f"{mbar_litres_per_second(q):.2f}"
'15.41'