代码仓库移植
This commit is contained in:
commit
0f57ba94f3
44 files changed
+7119
No files matched your search
@@ -0,0 +1,401 @@
|
||||
# Cryogenic LN2 Tank Simulation Module -- Design Specification
|
||||
|
||||
**Date:** 2026-04-16
|
||||
**Status:** Approved
|
||||
|
||||
## 1. Overview
|
||||
|
||||
A transient thermodynamic simulation module for a cryogenic liquid nitrogen (LN2) storage tank with helium pressurization. The module is an independent Python package within the existing pipe-system-simulation project, designed for future coupling with the pipe system.
|
||||
|
||||
### 1.1 Physical Scenario
|
||||
|
||||
A cylindrical cryogenic tank stores liquid nitrogen. It has:
|
||||
- Two inlets: LN2 inlet and He pressurization inlet
|
||||
- One outlet: LN2 outlet
|
||||
- A heat leak interface for connecting insulation models
|
||||
|
||||
The tank pressure is maintained at a constant 0.17 MPa (absolute) by adjusting helium flow. Liquid nitrogen flows in at 1.144 kg/s (77 K) and out at 1.1895 kg/s. The helium inlet temperature is 100 K, and its flow rate is determined by the constant-pressure constraint.
|
||||
|
||||
### 1.2 Key Design Decisions (from brainstorming)
|
||||
|
||||
| Decision | Choice | Rationale |
|
||||
|---|---|---|
|
||||
| Relationship to pipe system | Independent module (A) | Can run standalone, future coupling possible |
|
||||
| Tank model | Two-zone (liquid + ullage) (A) | Captures liquid/gas temperature difference |
|
||||
| Interphase mass transfer | Not considered | Only heat transfer between liquid and gas zones |
|
||||
| Fluid properties | CoolProp (C) | Highest accuracy for cryogenic conditions |
|
||||
| Heat leak interface | Plugin-style (C) | Flexible, extensible, decoupled |
|
||||
| Pressure control | Algebraic constraint (A) | Exact constant pressure, no PID tuning |
|
||||
| Time integration | scipy solve_ivp (A) | Adaptive stepping, good accuracy |
|
||||
| Liquid temperature | Uniform (homogeneous) | Justified by continuous flow and shallow liquid layer |
|
||||
|
||||
## 2. Physical Model
|
||||
|
||||
### 2.1 Two-Zone Model
|
||||
|
||||
The tank is divided into:
|
||||
- **Liquid zone** (lower): LN2 at uniform temperature T_liq
|
||||
- **Ullage zone** (upper): mixture of N2 vapor + He gas at uniform temperature T_ull
|
||||
|
||||
The two zones exchange heat across the liquid surface. No evaporation or condensation (no interphase mass transfer).
|
||||
|
||||
### 2.2 State Variables (ODE)
|
||||
|
||||
The ODE state vector has 3 components: **y = [m_liq, U_liq, U_ull]**
|
||||
|
||||
- `m_liq` [kg]: liquid nitrogen mass
|
||||
- `U_liq` [J]: liquid zone total internal energy
|
||||
- `U_ull` [J]: ullage zone total internal energy (N2 vapor + He combined)
|
||||
|
||||
### 2.3 Derived Quantities (not ODE states)
|
||||
|
||||
These are computed at each evaluation from the state + constraints:
|
||||
- `T_liq`: liquid temperature (from m_liq, U_liq via CoolProp)
|
||||
- `T_ull`: ullage temperature (from U_ull, m_N2_ull, m_He via CoolProp/ideal gas)
|
||||
- `V_liq = m_liq / rho_LN2(T_liq, P)`: liquid volume
|
||||
- `V_ull = V_total - V_liq`: ullage volume
|
||||
- `liquid_level = V_liq / A_cross`: liquid height in the cylinder
|
||||
- `m_He`: helium mass from constant-pressure constraint (see Section 2.6)
|
||||
- `m_dot_He`: helium mass flow rate (time derivative of m_He, also from constraint)
|
||||
- `T_out = T_liq`: LN2 outlet temperature (uniform assumption)
|
||||
|
||||
### 2.4 Liquid Zone Governing Equations
|
||||
|
||||
**Mass conservation:**
|
||||
```
|
||||
dm_liq/dt = m_dot_in_LN2 - m_dot_out_LN2
|
||||
= 1.144 - 1.1895
|
||||
= -0.0455 kg/s (constant)
|
||||
```
|
||||
|
||||
**Energy conservation:**
|
||||
```
|
||||
dU_liq/dt = m_dot_in_LN2 * h_in_LN2
|
||||
- m_dot_out_LN2 * h_liq
|
||||
- Q_liq_to_ull
|
||||
+ Q_leak_liq
|
||||
```
|
||||
|
||||
Where:
|
||||
- `h_in_LN2 = h_N2_liquid(T_in=77K, P=0.17MPa)` from CoolProp [J/kg]
|
||||
- `h_liq = h_N2_liquid(T_liq, P=0.17MPa)` from CoolProp [J/kg] -- also the outlet enthalpy
|
||||
- `Q_liq_to_ull`: heat transfer from liquid to ullage across the liquid surface [W]
|
||||
- `Q_leak_liq`: heat leak into liquid zone from environment [W]
|
||||
|
||||
### 2.5 Ullage Zone Governing Equations
|
||||
|
||||
**Mass conservation:**
|
||||
```
|
||||
m_N2_ull = constant (no mass transfer, set at initialization)
|
||||
dm_He/dt = m_dot_He (determined by constant-pressure constraint)
|
||||
```
|
||||
|
||||
**Energy conservation:**
|
||||
```
|
||||
dU_ull/dt = m_dot_He * h_in_He
|
||||
+ Q_liq_to_ull
|
||||
+ Q_leak_ull
|
||||
```
|
||||
|
||||
Where:
|
||||
- `h_in_He = h_He(T_in=100K, P=0.17MPa)` from CoolProp [J/kg]
|
||||
- `Q_liq_to_ull`: heat transfer from liquid surface (same magnitude, opposite sign as in liquid equation)
|
||||
- `Q_leak_ull`: heat leak into ullage zone from environment [W]
|
||||
|
||||
### 2.6 Constant-Pressure Constraint (Analytical m_dot_He Derivation)
|
||||
|
||||
Tank pressure is maintained at P_total = 0.17 MPa at all times. The ullage gas follows Dalton's law:
|
||||
```
|
||||
P_total = P_N2 + P_He = 0.17 MPa
|
||||
```
|
||||
|
||||
The N2 partial pressure depends on the fixed N2 vapor mass, ullage volume, and ullage temperature. The He mass required to provide the remaining pressure:
|
||||
```
|
||||
P_N2 = f(m_N2_ull, V_ull, T_ull) (CoolProp or ideal gas)
|
||||
P_He = P_total - P_N2
|
||||
m_He = P_He * V_ull / (R_He * T_ull) (He is well-approximated as ideal gas at these conditions)
|
||||
```
|
||||
|
||||
Where R_He = R_universal / M_He = 8314.46 / 4.0026 = 2077.1 J/(kg*K).
|
||||
|
||||
**m_He is NOT an ODE state variable.** It is a derived quantity from the algebraic constraint.
|
||||
|
||||
**Analytical derivation of m_dot_He (avoiding finite-difference instability):**
|
||||
|
||||
Since P_total = const, differentiating dP/dt = 0 and using the ideal gas relation
|
||||
for He (P_He * V_ull = m_He * R_He * T_ull) yields:
|
||||
|
||||
```
|
||||
m_He = P_He * V_ull / (R_He * T_ull)
|
||||
|
||||
dm_He/dt = (1 / R_He) * [ P_He * dV_ull/dt / T_ull
|
||||
+ V_ull * dP_He/dt / T_ull
|
||||
- P_He * V_ull * dT_ull/dt / T_ull^2 ]
|
||||
```
|
||||
|
||||
The terms dV_ull/dt, dP_He/dt, and dT_ull/dt can all be expressed analytically
|
||||
in terms of the current state and known quantities:
|
||||
|
||||
- `dV_ull/dt = -dV_liq/dt = (m_dot_out - m_dot_in) / rho_liq` (from liquid mass balance)
|
||||
- `dP_He/dt = -dP_N2/dt` (since P_total is constant); dP_N2/dt is computed from
|
||||
the N2 ideal gas law with fixed m_N2_ull and known dV_ull/dt, dT_ull/dt
|
||||
- `dT_ull/dt` is obtained from the ullage energy equation (which itself contains m_dot_He)
|
||||
|
||||
This creates a linear equation in m_dot_He that can be solved explicitly within each
|
||||
RHS evaluation. The key insight: dU_ull/dt = m_dot_He * h_in_He + Q_terms, and
|
||||
T_ull is a function of U_ull, so dT_ull/dt is linearly related to m_dot_He.
|
||||
Substituting into the dm_He/dt expression and solving for m_dot_He yields a
|
||||
closed-form formula with no finite differences, ensuring numerical stability
|
||||
with adaptive ODE solvers.
|
||||
|
||||
**Implementation note:** The analytical derivation will be implemented in
|
||||
`tank_model.py` as a dedicated method `_solve_he_flow_rate()` that returns
|
||||
m_dot_He as a function of the current state. This avoids the "algebraic loop"
|
||||
issue identified in review.
|
||||
|
||||
### 2.7 Heat Transfer Models
|
||||
|
||||
**Liquid-to-ullage surface heat transfer:**
|
||||
```
|
||||
Q_liq_to_ull = h_conv * A_surface * (T_liq - T_ull)
|
||||
```
|
||||
- `A_surface`: liquid surface area = cross-sectional area of cylinder = pi/4 * D^2
|
||||
- `h_conv`: surface convective heat transfer coefficient [W/(m^2*K)], configurable, default = 50 W/(m^2*K)
|
||||
|
||||
**Heat leak from environment (plugin interface):**
|
||||
Total heat leak Q_leak is computed by the configured HeatLeakModel, then distributed to liquid and ullage zones by wetted area ratio:
|
||||
```
|
||||
Q_leak = heat_leak_model.compute(T_inner, T_env)
|
||||
Q_leak_liq = Q_leak * A_wet / A_total
|
||||
Q_leak_ull = Q_leak * A_dry / A_total
|
||||
```
|
||||
|
||||
Where:
|
||||
- `A_wet = A_bottom + pi * D * liquid_level` (bottom cap + wetted side wall)
|
||||
- `A_dry = A_top + pi * D * (H - liquid_level)` (top cap + dry side wall)
|
||||
- `A_total = A_wet + A_dry`
|
||||
- `T_inner` passed to the model is a weighted average or conservative choice (e.g., T_liq for wet, T_ull for dry -- or simplified to just T_liq since most heat goes into the liquid)
|
||||
|
||||
Simplification: for the heat leak model input, use T_liq as T_inner since the liquid dominates thermal mass.
|
||||
|
||||
## 3. Heat Leak Interface
|
||||
|
||||
### 3.1 Base Class
|
||||
|
||||
```python
|
||||
class HeatLeakModel:
|
||||
def compute(self, T_inner: float, T_env: float) -> float:
|
||||
"""Return total heat leak Q [W], positive = heat flows into tank."""
|
||||
raise NotImplementedError
|
||||
```
|
||||
|
||||
### 3.2 MLI (Vacuum Multi-Layer Insulation)
|
||||
|
||||
```python
|
||||
class MLIHeatLeak(HeatLeakModel):
|
||||
def __init__(self, A_total, q_mli=1.0):
|
||||
"""
|
||||
A_total: total tank surface area [m^2]
|
||||
q_mli: specific heat flux [W/m^2], default 1.0 W/m^2 (typical MLI performance)
|
||||
"""
|
||||
```
|
||||
Computes: `Q = A_total * q_mli`
|
||||
|
||||
Note: MLI heat flux is largely independent of temperature difference in the typical operating range, so q_mli is a fixed parameter.
|
||||
|
||||
### 3.3 Foam/Wrap Insulation
|
||||
|
||||
```python
|
||||
class FoamHeatLeak(HeatLeakModel):
|
||||
def __init__(self, A_total, k_eff, delta):
|
||||
"""
|
||||
A_total: total tank surface area [m^2]
|
||||
k_eff: effective thermal conductivity [W/(m*K)]
|
||||
Can be a float (constant) or a callable k_eff(T) -> float
|
||||
that returns conductivity as a function of temperature.
|
||||
At cryogenic temperatures, k varies significantly with T.
|
||||
delta: insulation thickness [m]
|
||||
"""
|
||||
```
|
||||
Computes: `Q = A_total * k(T_mean) * (T_env - T_inner) / delta`
|
||||
|
||||
Where `T_mean = (T_env + T_inner) / 2` when k_eff is a function, or simply
|
||||
uses the constant value when k_eff is a float.
|
||||
|
||||
## 4. Tank Geometry
|
||||
|
||||
Cylindrical tank:
|
||||
- Total volume: V = 420.1 L = 0.4201 m^3
|
||||
- Height: H = 0.5 m
|
||||
- Cross-sectional area: A = V / H = 0.8402 m^2
|
||||
- Diameter: D = sqrt(4*A/pi) = 1.034 m
|
||||
- Side area: A_side = pi * D * H = 1.625 m^2
|
||||
- Top area = Bottom area = A = 0.8402 m^2
|
||||
- Total surface area: A_total = A_side + 2*A = 3.306 m^2
|
||||
|
||||
Liquid level at any time:
|
||||
```
|
||||
liquid_level = V_liq / A = (m_liq / rho_liq) / A
|
||||
fill_fraction = liquid_level / H
|
||||
```
|
||||
|
||||
## 5. Initial Conditions
|
||||
|
||||
| Quantity | Value | Notes |
|
||||
|---|---|---|
|
||||
| T_liq(0) | 78 K | Given |
|
||||
| T_ull(0) | 78 K | Given (same as liquid initially) |
|
||||
| P_total | 0.17 MPa | Constant throughout |
|
||||
| Ullage fraction | 30% | Gas pocket volume / total volume |
|
||||
| V_liq(0) | 0.2941 m^3 | 70% of 0.4201 |
|
||||
| V_ull(0) | 0.1260 m^3 | 30% of 0.4201 |
|
||||
| m_liq(0) | rho_LN2(78K, 0.17MPa) * 0.2941 | From CoolProp |
|
||||
| U_liq(0) | m_liq(0) * u_LN2(78K, 0.17MPa) | Specific internal energy from CoolProp |
|
||||
| m_N2_ull | rho_N2_vapor(78K, P_N2_sat) * V_ull(0) | N2 vapor at initial conditions, FIXED for all time |
|
||||
| P_N2(0) | N2 saturation pressure at 78K | From CoolProp |
|
||||
| P_He(0) | P_total - P_N2(0) | Helium makes up the pressure difference |
|
||||
| m_He(0) | P_He(0) * V_ull(0) / (R_He * 78) | Ideal gas for He |
|
||||
| U_ull(0) | m_N2_ull * u_N2_vapor(78K) + m_He(0) * u_He(78K) | Combined internal energy |
|
||||
|
||||
## 6. Inlet/Outlet Conditions
|
||||
|
||||
| Port | Flow rate | Temperature | Pressure | Fluid |
|
||||
|---|---|---|---|---|
|
||||
| LN2 inlet | 1.144 kg/s | 77 K | 0.17 MPa | Liquid nitrogen |
|
||||
| LN2 outlet | 1.1895 kg/s | T_liq (computed) | 0.17 MPa | Liquid nitrogen |
|
||||
| He inlet | m_dot_He (computed) | 100 K | 0.17 MPa | Helium gas |
|
||||
|
||||
Net liquid drain rate: 0.0455 kg/s.
|
||||
|
||||
## 7. Numerical Method
|
||||
|
||||
- **Time integration:** scipy.integrate.solve_ivp with RK45 (adaptive Runge-Kutta)
|
||||
- **Tolerances:** rtol = 1e-8, atol = 1e-10
|
||||
- **Simulation duration:** t_end = 3600 s (1 hour)
|
||||
- **Dense output:** enabled for smooth interpolation of results
|
||||
- **State vector:** y = [m_liq, U_liq, U_ull] (3 components)
|
||||
|
||||
RHS function evaluation at each call:
|
||||
1. Unpack y -> (m_liq, U_liq, U_ull)
|
||||
2. Compute T_liq from (m_liq, U_liq) via CoolProp
|
||||
3. Compute V_liq, V_ull, liquid_level from geometry
|
||||
4. Compute m_He from constant-pressure constraint
|
||||
5. Compute T_ull from (U_ull, m_N2_ull, m_He) -- iterative or CoolProp
|
||||
6. Compute all heat transfer terms (Q_liq_to_ull, Q_leak_liq, Q_leak_ull)
|
||||
7. Compute enthalpy terms for inlets/outlet
|
||||
8. Assemble and return dy/dt = [dm_liq/dt, dU_liq/dt, dU_ull/dt]
|
||||
|
||||
## 8. Code Structure
|
||||
|
||||
```
|
||||
src/cryo_tank/
|
||||
__init__.py
|
||||
config.py # Tank parameters, inlet/outlet conditions, simulation control
|
||||
properties.py # CoolProp wrappers for N2 and He properties
|
||||
heat_leak.py # HeatLeakModel base + MLIHeatLeak + FoamHeatLeak
|
||||
tank_model.py # CryoTank class: geometry, state, rhs(), derived quantities
|
||||
solver.py # run(tank, t_end) -> history dict
|
||||
output.py # Plotting and reporting
|
||||
main.py # Entry point
|
||||
```
|
||||
|
||||
Module dependencies:
|
||||
```
|
||||
main.py -> config.py, tank_model.py, solver.py, output.py
|
||||
tank_model.py -> properties.py, heat_leak.py
|
||||
solver.py -> tank_model.py (calls tank.rhs)
|
||||
output.py -> (only depends on history data dict)
|
||||
```
|
||||
|
||||
### 8.1 CoolProp Performance Optimization (properties.py)
|
||||
|
||||
CoolProp Python calls can be slow when invoked thousands of times in an ODE RHS.
|
||||
The following optimizations are mandatory in `properties.py`:
|
||||
|
||||
1. **Use CoolProp.AbstractState:** Create persistent `AbstractState` objects for
|
||||
N2 and He at module load time. Reuse them across calls (avoid per-call overhead).
|
||||
|
||||
2. **Lookup table with interpolation:** For the dominant hot-path properties
|
||||
(LN2 density, enthalpy, internal energy at P = 0.17 MPa as a function of T),
|
||||
build a 1D interpolation table at startup covering the expected temperature
|
||||
range (e.g., 70-90 K for liquid, 70-300 K for gas). Use `numpy.interp` for
|
||||
fast evaluation. Fall back to CoolProp only when T is outside the table range.
|
||||
|
||||
3. **He as ideal gas:** Helium at 0.17 MPa and 78-300 K is well-described by
|
||||
the ideal gas law. Use analytical expressions (cp, cv, h, u) instead of
|
||||
CoolProp for He wherever possible to avoid unnecessary library calls.
|
||||
|
||||
## 9. Output Quantities
|
||||
|
||||
All recorded as time series:
|
||||
|
||||
| Output | Symbol | Unit |
|
||||
|---|---|---|
|
||||
| Time | t | s |
|
||||
| Liquid temperature | T_liq | K |
|
||||
| Ullage temperature | T_ull | K |
|
||||
| Liquid mass | m_liq | kg |
|
||||
| Ullage N2 vapor mass | m_N2_ull | kg (constant) |
|
||||
| Helium mass | m_He | kg |
|
||||
| Helium flow rate | m_dot_He | kg/s |
|
||||
| Liquid volume / level | V_liq, liquid_level | m^3, m |
|
||||
| Fill fraction | fill_fraction | -- |
|
||||
| LN2 outlet temperature | T_out = T_liq | K |
|
||||
| Heat leak total | Q_leak | W |
|
||||
| Heat leak to liquid | Q_leak_liq | W |
|
||||
| Heat leak to ullage | Q_leak_ull | W |
|
||||
| Surface heat transfer | Q_liq_to_ull | W |
|
||||
| Tank pressure | P_total | Pa (constant 0.17 MPa) |
|
||||
|
||||
Output files:
|
||||
- Time-series plots (PNG): T_liq & T_ull vs t, liquid level vs t, m_dot_He vs t, heat fluxes vs t
|
||||
- History data (NPZ): all time series for post-processing
|
||||
- Summary report (HTML): key parameters and embedded figures
|
||||
|
||||
## 10. Constraints, Limits, and Robustness
|
||||
|
||||
### 10.1 Normal Operating Limits
|
||||
|
||||
- Tank must not be overpressurized: P_total <= 0.8 MPa (max bearing pressure). Should raise warning/error if pressure constraint cannot be maintained.
|
||||
- Liquid level must remain >= 0. When m_liq reaches 0, simulation should stop (use solve_ivp `events` mechanism to detect zero-crossing).
|
||||
|
||||
### 10.2 Near-Full Tank (fill_fraction > 95%)
|
||||
|
||||
When the ullage volume becomes very small, pressure sensitivity to mass/temperature
|
||||
changes grows dramatically. This can cause ODE solver instability.
|
||||
|
||||
Handling: when fill_fraction > 0.95, log a warning. The solver should still function
|
||||
because the analytical m_dot_He derivation avoids the finite-difference instability.
|
||||
If the solver fails to converge, reduce rtol/atol or switch to a stiffer solver (e.g., Radau).
|
||||
|
||||
### 10.3 Helium Backflow (m_dot_He < 0)
|
||||
|
||||
If the constant-pressure constraint computes m_dot_He < 0 (meaning pressure is too
|
||||
high and He should flow out), this indicates the operating regime has changed
|
||||
(e.g., heat leak is raising ullage temperature/pressure faster than liquid draining
|
||||
creates space). Two modes:
|
||||
|
||||
- **Strict mode (default):** Clamp m_dot_He = 0, allow pressure to drift above
|
||||
P_target. Log a warning with the overpressure magnitude. If P > 0.8 MPa (max
|
||||
bearing pressure), terminate simulation with an error.
|
||||
- **Vent mode (future):** Add a pressure relief mechanism. Not implemented in v1.
|
||||
|
||||
### 10.4 Model Applicability (No Mass Transfer Assumption)
|
||||
|
||||
The current model assumes no evaporation/condensation between liquid and gas zones.
|
||||
This is valid when:
|
||||
- Liquid temperature remains well below the saturation temperature at 0.17 MPa (~83.7 K)
|
||||
- The net liquid drain is fast relative to temperature rise from heat leak
|
||||
|
||||
If T_liq approaches saturation temperature, the model will log a warning:
|
||||
"T_liq approaching saturation (83.7 K); evaporation effects may be significant."
|
||||
|
||||
Future extension: add Hertz-Knudsen evaporation model as an optional feature.
|
||||
|
||||
## 11. Testing Strategy
|
||||
|
||||
- **Unit tests for properties.py:** verify CoolProp wrappers return physically reasonable values
|
||||
- **Unit tests for heat_leak.py:** verify MLI and Foam models compute correct Q for known inputs
|
||||
- **Unit tests for tank_model.py:** verify geometry calculations, initial state, RHS evaluation
|
||||
- **Integration test:** short run (10s), verify mass conservation (liquid mass change = net flow * dt)
|
||||
- **Steady-state test:** with zero net flow and zero heat leak, verify state remains constant
|
||||
Reference in new issue
Block a user