Skip to content

Commit b2268d4

Browse files
committed
Refactor to eliminate code duplication (SSOT/DRY)
Python scripts: - Delete duplicate run_gsph_sod_tube.py (same as gsph_sod_shock_tube.py) - Update animate_sod_tube.py to use shamrock.phys.SodTube instead of custom riemann.py - Replace riemann.py with sedov.py (keep only SedovAnalytical, use shamphys.SodTube for Sod) C++ VTK dump utilities: - Create shared VTKDumpUtils.hpp in shammodels/common/io/ - Refactor both SPH and GSPH VTKDump.cpp to use shared utilities - Eliminates 5 duplicated helper functions (start_dump, vtk_dump_add_patch_id, etc.) Net reduction: ~528 lines of duplicate code removed
1 parent 06872f1 commit b2268d4

8 files changed

Lines changed: 358 additions & 886 deletions

File tree

exemples/common/analytical/riemann.py

Lines changed: 0 additions & 476 deletions
This file was deleted.
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Sedov-Taylor Blast Wave Analytical Solution
4+
5+
Computes the self-similar solution for the Sedov-Taylor blast wave problem.
6+
7+
References:
8+
- Sedov, L.I. (1959) "Similarity and Dimensional Methods in Mechanics"
9+
- Taylor, G.I. (1950) "The Formation of a Blast Wave by a Very Intense Explosion"
10+
11+
Note: For Sod shock tube, use shamrock.phys.SodTube instead.
12+
"""
13+
14+
import numpy as np
15+
16+
17+
class SedovAnalytical:
18+
"""
19+
Sedov-Taylor blast wave analytical solution.
20+
21+
Parameters:
22+
-----------
23+
gamma : float
24+
Adiabatic index
25+
E_blast : float
26+
Total blast energy
27+
rho_0 : float
28+
Background density
29+
ndim : int
30+
Dimensionality (1=planar, 2=cylindrical, 3=spherical)
31+
"""
32+
33+
def __init__(self, gamma=5.0 / 3.0, E_blast=1.0, rho_0=1.0, ndim=3):
34+
self.gamma = gamma
35+
self.E_blast = E_blast
36+
self.rho_0 = rho_0
37+
self.ndim = ndim
38+
39+
# Similarity exponent
40+
self.alpha = 2.0 / (ndim + 2)
41+
42+
# Sedov constant (depends on gamma and ndim)
43+
self.xi_0 = self._compute_xi0()
44+
45+
def _compute_xi0(self):
46+
"""Compute the Sedov constant xi_0."""
47+
gamma = self.gamma
48+
ndim = self.ndim
49+
50+
# Approximate values for common cases
51+
if ndim == 3: # Spherical
52+
if abs(gamma - 5.0 / 3.0) < 0.01:
53+
return 1.15167
54+
elif abs(gamma - 1.4) < 0.01:
55+
return 1.03275
56+
elif ndim == 2: # Cylindrical
57+
if abs(gamma - 1.4) < 0.01:
58+
return 1.033
59+
elif ndim == 1: # Planar
60+
if abs(gamma - 1.4) < 0.01:
61+
return 0.911
62+
63+
# General approximation
64+
return 1.0
65+
66+
def shock_radius(self, t):
67+
"""Compute shock radius at time t."""
68+
if t <= 0:
69+
return 0.0
70+
return self.xi_0 * (self.E_blast * t**2 / self.rho_0) ** (1.0 / (self.ndim + 2))
71+
72+
def shock_velocity(self, t):
73+
"""Compute shock velocity at time t."""
74+
if t <= 0:
75+
return 0.0
76+
R_s = self.shock_radius(t)
77+
return 2.0 / (self.ndim + 2) * R_s / t
78+
79+
def post_shock_density(self):
80+
"""Compute post-shock density."""
81+
return self.rho_0 * (self.gamma + 1) / (self.gamma - 1)
82+
83+
def solution_at_time(self, t, r_max=None, n_points=500):
84+
"""
85+
Compute the radial profile at time t.
86+
87+
Returns:
88+
--------
89+
r, rho, v, p : arrays
90+
Radius and primitive variables
91+
"""
92+
if t <= 1e-10:
93+
r = np.linspace(0, r_max or 0.01, n_points)
94+
return (
95+
r,
96+
np.ones(n_points) * self.rho_0,
97+
np.zeros(n_points),
98+
np.ones(n_points) * 1e-10,
99+
)
100+
101+
gamma = self.gamma
102+
ndim = self.ndim
103+
104+
R_s = self.shock_radius(t)
105+
v_s = self.shock_velocity(t)
106+
107+
if r_max is None:
108+
r_max = R_s * 1.5
109+
110+
# Similarity variable lambda = r/R_s
111+
lam = np.linspace(0, min(1.0, r_max / R_s), n_points)
112+
r = lam * R_s
113+
114+
# Post-shock values
115+
rho_s = self.post_shock_density()
116+
v_shock = 2.0 / (gamma + 1) * v_s
117+
p_s = 2.0 / (gamma + 1) * self.rho_0 * v_s**2
118+
119+
# Approximate profiles (self-similar structure)
120+
# Velocity: linear profile
121+
v = v_shock * lam
122+
123+
# Density: peaks near shock, low at center
124+
omega = (ndim + 2) * gamma / (2 + ndim * (gamma - 1))
125+
rho = rho_s * lam ** (omega - 1) * np.maximum(0.1, 1 - 0.8 * (1 - lam) ** 2)
126+
rho[0] = rho[1] if n_points > 1 else rho_s * 0.1
127+
128+
# Pressure: higher at center
129+
p = p_s * (0.5 + 0.5 * lam**2)
130+
131+
return r, rho, v, p

exemples/gsph/run_gsph_sod_tube.py

Lines changed: 0 additions & 152 deletions
This file was deleted.

exemples/gsph/scripts/animate_sedov_blast.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
# Import from shared analytical module
2525
exemples_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
2626
sys.path.insert(0, exemples_dir)
27-
from common.analytical.riemann import SedovAnalytical
27+
from common.analytical.sedov import SedovAnalytical
2828

2929
# Try to import animation tools
3030
try:
@@ -184,7 +184,7 @@ def compute_radial_profiles(data, n_bins=100):
184184
print()
185185

186186
# Create analytical solution object
187-
sedov_analytical = SedovAnalytical(gamma=gamma, E_blast=E_blast, rho_0=rho_0, ndim=3)
187+
sedov_analytical = SedovAnalytical(gamma=gamma, E_blast=E_blast, rho_0=rho_0)
188188

189189
# Determine frame skip for reasonable animation size
190190
n_frames = len(files)

exemples/gsph/scripts/animate_sod_tube.py

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,8 @@
2121
import matplotlib.pyplot as plt
2222
import numpy as np
2323

24-
# Import from shared analytical module
25-
exemples_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
26-
sys.path.insert(0, exemples_dir)
27-
from common.analytical.riemann import SodAnalytical
24+
# Use shamrock's built-in SodTube analytical solution
25+
import shamrock
2826

2927
# Try to import animation tools
3028
try:
@@ -135,8 +133,22 @@ def find_snapshots(data_dir):
135133
print(f" Right: rho = {rho_R}, P = {p_R}")
136134
print()
137135

138-
# Create analytical solution object
139-
sod_analytical = SodAnalytical(gamma=gamma, rho_L=rho_L, rho_R=rho_R, p_L=p_L, p_R=p_R, x0=0.0)
136+
# Create analytical solution object using shamrock's built-in SodTube
137+
# Note: SodTube uses rho_1/P_1 for left state and rho_5/P_5 for right state
138+
sod_analytical = shamrock.phys.SodTube(
139+
gamma=gamma, rho_1=rho_L, P_1=p_L, rho_5=rho_R, P_5=p_R
140+
)
141+
142+
143+
def get_analytical_solution(sod, t, x_array):
144+
"""Get analytical solution at multiple x positions."""
145+
rho = np.zeros(len(x_array))
146+
vel = np.zeros(len(x_array))
147+
pres = np.zeros(len(x_array))
148+
for i, x in enumerate(x_array):
149+
rho[i], vel[i], pres[i] = sod.get_value(t, x)
150+
ene = pres / ((gamma - 1) * np.maximum(rho, 1e-10))
151+
return rho, vel, pres, ene
140152

141153
# Determine frame skip for reasonable animation size
142154
n_frames = len(files)
@@ -203,9 +215,8 @@ def update(frame_num):
203215
ene_sim = data["ene"][sort_idx]
204216

205217
# Get analytical solution
206-
x_ana, rho_ana, vel_ana, pres_ana, ene_ana = sod_analytical.solution_at_time(
207-
time, x_min=x_sim.min(), x_max=x_sim.max(), n_points=500
208-
)
218+
x_ana = np.linspace(x_sim.min(), x_sim.max(), 500)
219+
rho_ana, vel_ana, pres_ana, ene_ana = get_analytical_solution(sod_analytical, time, x_ana)
209220

210221
# Clear axes
211222
ax1.clear()
@@ -309,9 +320,8 @@ def update(frame_num):
309320
pres_sim = data["pres"][sort_idx]
310321
ene_sim = data["ene"][sort_idx]
311322

312-
x_ana, rho_ana, vel_ana, pres_ana, ene_ana = sod_analytical.solution_at_time(
313-
time, x_min=x_sim.min(), x_max=x_sim.max(), n_points=500
314-
)
323+
x_ana = np.linspace(x_sim.min(), x_sim.max(), 500)
324+
rho_ana, vel_ana, pres_ana, ene_ana = get_analytical_solution(sod_analytical, time, x_ana)
315325

316326
fig2, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(14, 12))
317327
fig2.suptitle(

0 commit comments

Comments
 (0)