代码仓库移植
This commit is contained in:
commit
0f57ba94f3
44 files changed
+7119
No files matched your search
+115
@@ -0,0 +1,115 @@
|
||||
"""
|
||||
Time-loop driver for the 0D-1D coupled tank-pipe simulation.
|
||||
|
||||
Per time step (per spec §3.1):
|
||||
1. Compute CFL-limited dt from pipe's max wave speed
|
||||
2. Freeze ghost states from current tank states
|
||||
3. Compute two boundary HLL fluxes (left and right)
|
||||
4. Advance pipe by one step using those two fluxes (pipe.step handles
|
||||
the internal fluxes itself)
|
||||
5. Advance both tanks using the SAME two boundary fluxes * area
|
||||
-> this "flux doubling" is the mechanism that makes system mass
|
||||
and energy strictly conserved to machine precision
|
||||
6. Advance time
|
||||
7. Append snapshot to history
|
||||
"""
|
||||
import numpy as np
|
||||
|
||||
|
||||
def run(tank1, tank2, pipe, t_end, cfl, verbose=False, log_every=100):
|
||||
"""
|
||||
Run the coupled tank-pipe simulation from t=0 to t=t_end.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
tank1, tank2 : Tank
|
||||
Upstream and downstream tanks. tank1 connects to pipe.W[:, 0],
|
||||
tank2 connects to pipe.W[:, -1].
|
||||
pipe : Pipe
|
||||
1D pipe instance with initial state already set.
|
||||
t_end : float
|
||||
End time in seconds.
|
||||
cfl : float
|
||||
CFL number in (0, 1].
|
||||
verbose : bool, default False
|
||||
If True, print step-progress info every `log_every` steps.
|
||||
log_every : int, default 100
|
||||
Logging interval when verbose=True.
|
||||
|
||||
Returns
|
||||
-------
|
||||
dict
|
||||
History with keys 't', 'P1', 'T1', 'P2', 'T2' (all 1D arrays
|
||||
of shape (n_steps,)), and 'W_hist' of shape (n_steps, 3, N).
|
||||
"""
|
||||
history = {
|
||||
't': [],
|
||||
'P1': [], 'T1': [],
|
||||
'P2': [], 'T2': [],
|
||||
'W_hist': [],
|
||||
}
|
||||
|
||||
t = 0.0
|
||||
step = 0
|
||||
|
||||
while t < t_end:
|
||||
# --- Phase 1: CFL time step ---
|
||||
a_max = pipe.max_wave_speed()
|
||||
dt = cfl * pipe.dx / a_max
|
||||
dt = min(dt, t_end - t)
|
||||
if dt < 1e-12:
|
||||
raise RuntimeError(
|
||||
f"dt degenerate at step {step}: dt={dt:.3e}, a_max={a_max:.3e}"
|
||||
)
|
||||
|
||||
# --- Phase 2: freeze tank ghost states (snapshot for this step) ---
|
||||
W_ghost_L = tank1.ghost_state()
|
||||
W_ghost_R = tank2.ghost_state()
|
||||
|
||||
# --- Phase 3: two boundary fluxes (solver-level, same solver as pipe) ---
|
||||
flux_L = pipe._flux_fn(W_ghost_L, pipe.W[:, 0], pipe.gamma)
|
||||
flux_R = pipe._flux_fn(pipe.W[:, -1], W_ghost_R, pipe.gamma)
|
||||
|
||||
# --- Phase 4: advance pipe (internal fluxes handled inside) ---
|
||||
pipe.step(flux_L, flux_R, dt)
|
||||
|
||||
# --- Phase 5: advance tanks with the SAME boundary fluxes * area ---
|
||||
fL_A = flux_L * pipe.area
|
||||
fR_A = flux_R * pipe.area
|
||||
# Left boundary flux is "rightward positive"; tank1 loses that mass
|
||||
tank1.apply_flux(mdot=fL_A[0], edot=fL_A[2], dt=dt, sign=-1)
|
||||
# Right boundary flux is "rightward positive"; tank2 gains that mass
|
||||
tank2.apply_flux(mdot=fR_A[0], edot=fR_A[2], dt=dt, sign=+1)
|
||||
|
||||
# --- Phase 6: advance time ---
|
||||
t += dt
|
||||
step += 1
|
||||
|
||||
# --- Phase 7: record history ---
|
||||
history['t'].append(t)
|
||||
history['P1'].append(tank1.P)
|
||||
history['T1'].append(tank1.T)
|
||||
history['P2'].append(tank2.P)
|
||||
history['T2'].append(tank2.T)
|
||||
history['W_hist'].append(pipe.W.copy())
|
||||
|
||||
if verbose and step % log_every == 0:
|
||||
_, u, _, _ = pipe.primitives()
|
||||
print(
|
||||
f"step={step:6d} t={t:.5f} dt={dt:.2e} "
|
||||
f"P1={tank1.P/1e6:7.4f}MPa P2={tank2.P/1e6:7.4f}MPa "
|
||||
f"max|u|={float(np.max(np.abs(u))):7.1f}m/s"
|
||||
)
|
||||
|
||||
if step == 0:
|
||||
raise RuntimeError("solver.run() exited without taking any step")
|
||||
|
||||
# Convert lists to arrays for downstream consumers
|
||||
history['t'] = np.asarray(history['t'])
|
||||
history['P1'] = np.asarray(history['P1'])
|
||||
history['T1'] = np.asarray(history['T1'])
|
||||
history['P2'] = np.asarray(history['P2'])
|
||||
history['T2'] = np.asarray(history['T2'])
|
||||
history['W_hist'] = np.stack(history['W_hist']) # shape (n_steps, 3, N)
|
||||
|
||||
return history
|
||||
Reference in new issue
Block a user