refactor: organize src modules by category
This commit is contained in:
1 parent
d57c632db8
commit
4a22ef1334
20 files changed
+194
-144
No files matched your search
@@ -1,12 +1,13 @@
|
||||
# Repository Guidelines
|
||||
|
||||
## Project Structure & Module Organization
|
||||
Core simulation code lives in `src/`. The base pipe blowdown model is split across `src/tank.py`, `src/pipe.py`, `src/riemann.py`, `src/solver.py`, and `src/output.py`, with `src/main.py` as the entry point. The cryogenic tank variant is isolated under `src/cryo_tank/`. Tests mirror that layout in `tests/` and `tests/cryo_tank/`. Use `cases/` for scenario inputs, `docs/` for design notes and generated reports, `scripts/` for utilities, and `results/` for run artifacts.
|
||||
Core simulation code lives in `src/`. The base pipe blowdown model is grouped under `src/tank_pipe/`, the high-pressure gas cylinder model is under `src/cylinder/`, and the cryogenic tank variant is isolated under `src/cryo_tank/`. `src/main.py` remains a compatibility entry point for the tank-pipe simulation. Tests live in `tests/` and `tests/cryo_tank/`. Use `cases/` for scenario inputs, `docs/` for design notes and generated reports, `scripts/` for utilities, and `results/` for run artifacts.
|
||||
|
||||
## Build, Test, and Development Commands
|
||||
There is no checked-in build system; run modules directly from the repository root.
|
||||
|
||||
- `python3 src/main.py`: run the 0D-1D tank-pipe simulation and write plots/reports to `results/`.
|
||||
- `python3 src/main.py`: run the 0D-1D tank-pipe simulation through the compatibility entry point.
|
||||
- `python3 src/tank_pipe/main.py`: run the 0D-1D tank-pipe simulation from its package entry point.
|
||||
- `python3 src/cryo_tank/main.py`: run the cryogenic LN2 tank simulation.
|
||||
- `pytest -q`: run the full test suite.
|
||||
- `pytest -q tests/test_integration.py`: run the main conservation tests only.
|
||||
|
||||
@@ -4,12 +4,20 @@ Test repository for pipe system simulation work.
|
||||
|
||||
## Structure
|
||||
|
||||
- `src/tank_pipe/` 0D-1D tank-pipe blowdown simulation
|
||||
- `src/cylinder/` high-pressure gas cylinder model
|
||||
- `src/cryo_tank/` cryogenic LN2 tank simulation
|
||||
- `src/main.py` compatibility entry point for the tank-pipe simulation
|
||||
- `docs/` design notes and reports
|
||||
- `src/` source code
|
||||
- `cases/` test cases and input data
|
||||
- `scripts/` utility scripts
|
||||
- `results/` generated outputs and post-processing summaries
|
||||
|
||||
## Status
|
||||
## Common Commands
|
||||
|
||||
Initial repository scaffold created.
|
||||
```bash
|
||||
python3 src/main.py
|
||||
python3 src/tank_pipe/main.py
|
||||
python3 src/cryo_tank/main.py
|
||||
pytest -q
|
||||
```
|
||||
@@ -1,3 +1,20 @@
|
||||
# src
|
||||
|
||||
Source code for pipe system simulation models and utilities.
|
||||
|
||||
## Layout
|
||||
|
||||
- `main.py`: compatibility entry point for the tank-pipe simulation.
|
||||
- `tank_pipe/`: 0D-1D tank-pipe blowdown model, including configuration, tank, pipe, Riemann solver, time solver, and output helpers.
|
||||
- `cylinder/`: high-pressure gas cylinder model.
|
||||
- `cryo_tank/`: cryogenic LN2 tank model with helium pressurization, heat leak, solver, output helpers, and module README.
|
||||
|
||||
## Entry Points
|
||||
|
||||
Run from the repository root:
|
||||
|
||||
```bash
|
||||
python3 src/main.py
|
||||
python3 src/tank_pipe/main.py
|
||||
python3 src/cryo_tank/main.py
|
||||
```
|
||||
@@ -0,0 +1,17 @@
|
||||
"""High-pressure gas cylinder models."""
|
||||
|
||||
from cylinder.gas_cylinder import (
|
||||
DEFAULT_FLUID,
|
||||
DEFAULT_P_INIT,
|
||||
DEFAULT_T_INIT,
|
||||
DEFAULT_VOLUME,
|
||||
HighPressureGasCylinder,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"DEFAULT_FLUID",
|
||||
"DEFAULT_P_INIT",
|
||||
"DEFAULT_T_INIT",
|
||||
"DEFAULT_VOLUME",
|
||||
"HighPressureGasCylinder",
|
||||
]
|
||||
File renamed without changes.
+2
-126
@@ -1,130 +1,6 @@
|
||||
# src/main.py
|
||||
"""
|
||||
Entry point: assemble tanks + pipe from config constants, run the solver,
|
||||
verify total mass/energy conservation, persist history, and generate
|
||||
plots + animation.
|
||||
"""Compatibility entry point for the tank-pipe simulation."""
|
||||
|
||||
Run from project root:
|
||||
python3 src/main.py
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Ensure imports work when running from project root
|
||||
_HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
sys.path.insert(0, _HERE)
|
||||
|
||||
import numpy as np
|
||||
|
||||
from config import (
|
||||
GAMMA, R_GAS,
|
||||
V1, P1_INIT, T1_INIT,
|
||||
V2, P2_INIT, T2_INIT,
|
||||
L, D, N_CELLS,
|
||||
MU, ROUGHNESS,
|
||||
T_END, CFL, RIEMANN_SOLVER,
|
||||
ANIMATION_STRIDE, OUTPUT_DIR,
|
||||
)
|
||||
from tank import Tank
|
||||
from pipe import Pipe
|
||||
from solver import run
|
||||
from output import (
|
||||
save_history,
|
||||
plot_tank_pressure,
|
||||
plot_tank_temperature,
|
||||
plot_pipe_final_profiles,
|
||||
make_pipe_animation,
|
||||
write_summary_report,
|
||||
)
|
||||
|
||||
|
||||
def _total_mass(tank1, tank2, pipe):
|
||||
pipe_mass = float(np.sum(pipe.W[0, :] * pipe.area * pipe.dx))
|
||||
return tank1.mass + tank2.mass + pipe_mass
|
||||
|
||||
|
||||
def _total_energy(tank1, tank2, pipe):
|
||||
pipe_energy = float(np.sum(pipe.W[2, :] * pipe.area * pipe.dx))
|
||||
return tank1.U + tank2.U + pipe_energy
|
||||
|
||||
|
||||
def main():
|
||||
os.makedirs(OUTPUT_DIR, exist_ok=True)
|
||||
|
||||
# --- Assemble ---
|
||||
tank1 = Tank(V=V1, P_init=P1_INIT, T_init=T1_INIT, gamma=GAMMA, R_gas=R_GAS)
|
||||
tank2 = Tank(V=V2, P_init=P2_INIT, T_init=T2_INIT, gamma=GAMMA, R_gas=R_GAS)
|
||||
pipe = Pipe(L=L, D=D, N=N_CELLS, P_init=P2_INIT, T_init=T2_INIT,
|
||||
gamma=GAMMA, R_gas=R_GAS, mu=MU, roughness=ROUGHNESS,
|
||||
riemann_solver=RIEMANN_SOLVER)
|
||||
|
||||
m_init = _total_mass(tank1, tank2, pipe)
|
||||
U_init = _total_energy(tank1, tank2, pipe)
|
||||
print(f"Initial total mass: {m_init:.6e} kg")
|
||||
print(f"Initial total energy: {U_init:.6e} J")
|
||||
print(f"Initial P1 = {tank1.P/1e6:.3f} MPa, P2 = {tank2.P/1e6:.3f} MPa")
|
||||
print(f"Pipe: L={L} m, D={D*1e3:.1f} mm, N={N_CELLS} cells, dx={pipe.dx*1e3:.1f} mm")
|
||||
print(f"Riemann solver: {RIEMANN_SOLVER.upper()}")
|
||||
if MU > 0:
|
||||
print(f"Friction: mu={MU:.2e} Pa·s, roughness={ROUGHNESS:.2e} m (eps/D={ROUGHNESS/D:.4f})")
|
||||
else:
|
||||
print("Friction: OFF")
|
||||
print(f"Running to t_end={T_END} s with CFL={CFL}...")
|
||||
print()
|
||||
|
||||
# --- Run ---
|
||||
history = run(tank1, tank2, pipe,
|
||||
t_end=T_END, cfl=CFL,
|
||||
verbose=True, log_every=200)
|
||||
|
||||
n_steps = len(history['t'])
|
||||
print()
|
||||
print(f"Simulation complete: {n_steps} steps")
|
||||
|
||||
# --- Conservation sanity check (per spec §6.1, §8) ---
|
||||
m_final = _total_mass(tank1, tank2, pipe)
|
||||
U_final = _total_energy(tank1, tank2, pipe)
|
||||
rel_err_m = abs(m_final - m_init) / m_init
|
||||
rel_err_U = abs(U_final - U_init) / U_init
|
||||
print(f"Final total mass: {m_final:.6e} kg (rel err = {rel_err_m:.2e})")
|
||||
print(f"Final total energy: {U_final:.6e} J (rel err = {rel_err_U:.2e})")
|
||||
print(f"Final P1 = {tank1.P/1e6:.3f} MPa, P2 = {tank2.P/1e6:.3f} MPa")
|
||||
assert rel_err_m < 1e-10, f"Total mass not conserved: rel_err={rel_err_m:.2e}"
|
||||
assert rel_err_U < 1e-10, f"Total energy not conserved: rel_err={rel_err_U:.2e}"
|
||||
|
||||
# --- Persist + visualize ---
|
||||
save_history(history, pipe,
|
||||
os.path.join(OUTPUT_DIR, "history.npz"),
|
||||
GAMMA, R_GAS)
|
||||
plot_tank_pressure(history,
|
||||
os.path.join(OUTPUT_DIR, "tank_pressure.png"))
|
||||
plot_tank_temperature(history,
|
||||
os.path.join(OUTPUT_DIR, "tank_temperature.png"))
|
||||
plot_pipe_final_profiles(history, pipe,
|
||||
os.path.join(OUTPUT_DIR, "pipe_final_profiles.png"),
|
||||
GAMMA, R_GAS)
|
||||
make_pipe_animation(history, pipe,
|
||||
os.path.join(OUTPUT_DIR, "pipe_animation.gif"),
|
||||
GAMMA, R_GAS, stride=ANIMATION_STRIDE)
|
||||
write_summary_report(
|
||||
history, pipe,
|
||||
os.path.join(OUTPUT_DIR, "summary_report.html"),
|
||||
GAMMA, R_GAS,
|
||||
config={
|
||||
'V1': V1, 'P1_INIT': P1_INIT, 'T1_INIT': T1_INIT,
|
||||
'V2': V2, 'P2_INIT': P2_INIT, 'T2_INIT': T2_INIT,
|
||||
'L': L, 'D': D, 'N_CELLS': N_CELLS,
|
||||
'T_END': T_END, 'CFL': CFL,
|
||||
},
|
||||
)
|
||||
|
||||
print(f"Outputs written to {OUTPUT_DIR}/")
|
||||
print(f" - history.npz")
|
||||
print(f" - tank_pressure.png")
|
||||
print(f" - tank_temperature.png")
|
||||
print(f" - pipe_final_profiles.png")
|
||||
print(f" - pipe_animation.gif")
|
||||
print(f" - summary_report.html")
|
||||
from tank_pipe.main import main
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
"""0D-1D tank-pipe blowdown simulation package."""
|
||||
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,130 @@
|
||||
# src/tank_pipe/main.py
|
||||
"""
|
||||
Entry point: assemble tanks + pipe from config constants, run the solver,
|
||||
verify total mass/energy conservation, persist history, and generate
|
||||
plots + animation.
|
||||
|
||||
Run from project root:
|
||||
python3 src/tank_pipe/main.py
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
_HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
sys.path.insert(0, os.path.dirname(_HERE))
|
||||
|
||||
import numpy as np
|
||||
|
||||
from tank_pipe.config import (
|
||||
GAMMA, R_GAS,
|
||||
V1, P1_INIT, T1_INIT,
|
||||
V2, P2_INIT, T2_INIT,
|
||||
L, D, N_CELLS,
|
||||
MU, ROUGHNESS,
|
||||
T_END, CFL, RIEMANN_SOLVER,
|
||||
ANIMATION_STRIDE, OUTPUT_DIR,
|
||||
)
|
||||
from tank_pipe.tank import Tank
|
||||
from tank_pipe.pipe import Pipe
|
||||
from tank_pipe.solver import run
|
||||
from tank_pipe.output import (
|
||||
save_history,
|
||||
plot_tank_pressure,
|
||||
plot_tank_temperature,
|
||||
plot_pipe_final_profiles,
|
||||
make_pipe_animation,
|
||||
write_summary_report,
|
||||
)
|
||||
|
||||
|
||||
def _total_mass(tank1, tank2, pipe):
|
||||
pipe_mass = float(np.sum(pipe.W[0, :] * pipe.area * pipe.dx))
|
||||
return tank1.mass + tank2.mass + pipe_mass
|
||||
|
||||
|
||||
def _total_energy(tank1, tank2, pipe):
|
||||
pipe_energy = float(np.sum(pipe.W[2, :] * pipe.area * pipe.dx))
|
||||
return tank1.U + tank2.U + pipe_energy
|
||||
|
||||
|
||||
def main():
|
||||
os.makedirs(OUTPUT_DIR, exist_ok=True)
|
||||
|
||||
# --- Assemble ---
|
||||
tank1 = Tank(V=V1, P_init=P1_INIT, T_init=T1_INIT, gamma=GAMMA, R_gas=R_GAS)
|
||||
tank2 = Tank(V=V2, P_init=P2_INIT, T_init=T2_INIT, gamma=GAMMA, R_gas=R_GAS)
|
||||
pipe = Pipe(L=L, D=D, N=N_CELLS, P_init=P2_INIT, T_init=T2_INIT,
|
||||
gamma=GAMMA, R_gas=R_GAS, mu=MU, roughness=ROUGHNESS,
|
||||
riemann_solver=RIEMANN_SOLVER)
|
||||
|
||||
m_init = _total_mass(tank1, tank2, pipe)
|
||||
U_init = _total_energy(tank1, tank2, pipe)
|
||||
print(f"Initial total mass: {m_init:.6e} kg")
|
||||
print(f"Initial total energy: {U_init:.6e} J")
|
||||
print(f"Initial P1 = {tank1.P/1e6:.3f} MPa, P2 = {tank2.P/1e6:.3f} MPa")
|
||||
print(f"Pipe: L={L} m, D={D*1e3:.1f} mm, N={N_CELLS} cells, dx={pipe.dx*1e3:.1f} mm")
|
||||
print(f"Riemann solver: {RIEMANN_SOLVER.upper()}")
|
||||
if MU > 0:
|
||||
print(f"Friction: mu={MU:.2e} Pa·s, roughness={ROUGHNESS:.2e} m (eps/D={ROUGHNESS/D:.4f})")
|
||||
else:
|
||||
print("Friction: OFF")
|
||||
print(f"Running to t_end={T_END} s with CFL={CFL}...")
|
||||
print()
|
||||
|
||||
# --- Run ---
|
||||
history = run(tank1, tank2, pipe,
|
||||
t_end=T_END, cfl=CFL,
|
||||
verbose=True, log_every=200)
|
||||
|
||||
n_steps = len(history['t'])
|
||||
print()
|
||||
print(f"Simulation complete: {n_steps} steps")
|
||||
|
||||
# --- Conservation sanity check (per spec §6.1, §8) ---
|
||||
m_final = _total_mass(tank1, tank2, pipe)
|
||||
U_final = _total_energy(tank1, tank2, pipe)
|
||||
rel_err_m = abs(m_final - m_init) / m_init
|
||||
rel_err_U = abs(U_final - U_init) / U_init
|
||||
print(f"Final total mass: {m_final:.6e} kg (rel err = {rel_err_m:.2e})")
|
||||
print(f"Final total energy: {U_final:.6e} J (rel err = {rel_err_U:.2e})")
|
||||
print(f"Final P1 = {tank1.P/1e6:.3f} MPa, P2 = {tank2.P/1e6:.3f} MPa")
|
||||
assert rel_err_m < 1e-10, f"Total mass not conserved: rel_err={rel_err_m:.2e}"
|
||||
assert rel_err_U < 1e-10, f"Total energy not conserved: rel_err={rel_err_U:.2e}"
|
||||
|
||||
# --- Persist + visualize ---
|
||||
save_history(history, pipe,
|
||||
os.path.join(OUTPUT_DIR, "history.npz"),
|
||||
GAMMA, R_GAS)
|
||||
plot_tank_pressure(history,
|
||||
os.path.join(OUTPUT_DIR, "tank_pressure.png"))
|
||||
plot_tank_temperature(history,
|
||||
os.path.join(OUTPUT_DIR, "tank_temperature.png"))
|
||||
plot_pipe_final_profiles(history, pipe,
|
||||
os.path.join(OUTPUT_DIR, "pipe_final_profiles.png"),
|
||||
GAMMA, R_GAS)
|
||||
make_pipe_animation(history, pipe,
|
||||
os.path.join(OUTPUT_DIR, "pipe_animation.gif"),
|
||||
GAMMA, R_GAS, stride=ANIMATION_STRIDE)
|
||||
write_summary_report(
|
||||
history, pipe,
|
||||
os.path.join(OUTPUT_DIR, "summary_report.html"),
|
||||
GAMMA, R_GAS,
|
||||
config={
|
||||
'V1': V1, 'P1_INIT': P1_INIT, 'T1_INIT': T1_INIT,
|
||||
'V2': V2, 'P2_INIT': P2_INIT, 'T2_INIT': T2_INIT,
|
||||
'L': L, 'D': D, 'N_CELLS': N_CELLS,
|
||||
'T_END': T_END, 'CFL': CFL,
|
||||
},
|
||||
)
|
||||
|
||||
print(f"Outputs written to {OUTPUT_DIR}/")
|
||||
print(f" - history.npz")
|
||||
print(f" - tank_pressure.png")
|
||||
print(f" - tank_temperature.png")
|
||||
print(f" - pipe_final_profiles.png")
|
||||
print(f" - pipe_animation.gif")
|
||||
print(f" - summary_report.html")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,4 +1,4 @@
|
||||
# src/output.py
|
||||
# src/tank_pipe/output.py
|
||||
"""
|
||||
Output helpers: persistence (.npz), static plots (.png/.html), animation (.gif).
|
||||
Uses matplotlib's Agg backend so it works in headless environments.
|
||||
@@ -1,4 +1,4 @@
|
||||
# src/pipe.py
|
||||
# src/tank_pipe/pipe.py
|
||||
"""
|
||||
1D finite-volume pipe for compressible Euler equations:
|
||||
dW/dt + dF(W)/dx = 0
|
||||
@@ -12,8 +12,8 @@ Discretization:
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
from riemann import hll_flux, get_riemann_solver
|
||||
from friction import darcy_friction_factor
|
||||
from tank_pipe.riemann import hll_flux, get_riemann_solver
|
||||
from tank_pipe.friction import darcy_friction_factor
|
||||
|
||||
|
||||
class Pipe:
|
||||
@@ -1,4 +1,4 @@
|
||||
# src/riemann.py
|
||||
# src/tank_pipe/riemann.py
|
||||
"""
|
||||
Riemann flux solvers for the 1D compressible Euler equations.
|
||||
|
||||
File renamed without changes.
@@ -1,4 +1,4 @@
|
||||
# src/tank.py
|
||||
# src/tank_pipe/tank.py
|
||||
"""
|
||||
0D lumped-parameter tank for ideal gas. The tank's *primary* state is
|
||||
(mass, U) where U is total internal energy in joules. Pressure, temperature,
|
||||
@@ -1,7 +1,7 @@
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from gas_cylinder import (
|
||||
from cylinder.gas_cylinder import (
|
||||
DEFAULT_FLUID,
|
||||
DEFAULT_P_INIT,
|
||||
DEFAULT_T_INIT,
|
||||
|
||||
@@ -5,9 +5,9 @@ are the load-bearing tests for the "flux doubling" coupling mechanism.
|
||||
"""
|
||||
import numpy as np
|
||||
import pytest
|
||||
from tank import Tank
|
||||
from pipe import Pipe
|
||||
from solver import run
|
||||
from tank_pipe.tank import Tank
|
||||
from tank_pipe.pipe import Pipe
|
||||
from tank_pipe.solver import run
|
||||
|
||||
|
||||
GAMMA = 1.4
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
# tests/test_pipe.py
|
||||
import numpy as np
|
||||
import pytest
|
||||
from pipe import Pipe
|
||||
from tank_pipe.pipe import Pipe
|
||||
|
||||
|
||||
GAMMA = 1.4
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# tests/test_riemann.py
|
||||
import numpy as np
|
||||
from riemann import hll_flux
|
||||
from tank_pipe.riemann import hll_flux
|
||||
|
||||
|
||||
GAMMA = 1.4
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
# tests/test_tank.py
|
||||
import numpy as np
|
||||
import pytest
|
||||
from tank import Tank
|
||||
from tank_pipe.tank import Tank
|
||||
|
||||
|
||||
GAMMA = 1.4
|
||||
|
||||
Reference in new issue
Block a user