Skip to content

Commit 3189728

Browse files
authored
Fix/reflected shock w5 (nasa#56)
* initial fix for w5 velocity error * added transport property output for shock probles * aligned shock values for failed equilibrium cases * updated shock problem sequencing * added cached transport solution for failed solves * resolved reflected-frozen header difference * fixed transport property values * quick cleanup and updated changelog * fixed v_sonic * updated reflected velocities; removed pandas dependancy * added conservation equations test for shock problem * updated changelog
1 parent 6d6cc56 commit 3189728

File tree

14 files changed

+1168
-525
lines changed

14 files changed

+1168
-525
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,15 @@ All notable user-visible changes to this project are documented here.
99
- Added SI-focused custom-reactant handling at the Python API layer: `Reactant.temperature` is specified in K and `Reactant.enthalpy` in J/kg (converted internally for core input) (`#53`).
1010
- Legacy input parsing now supports repeated `outp` dataset keywords (including multiline forms) by merging successive `outp` entries during dataset assembly (`#52`).
1111
- FAC rocket chamber-closure iteration logic in `RocketSolver_solve_fac` was updated toward CEA2 parity: Option-1 pressure correction direction now follows legacy semantics, the Option-1 convergence check is normalized to assigned injector pressure, the fixed 4-pass outer loop was replaced with tolerance-driven iteration plus a bounded safety guard, and FAC combustor-end reseeding now refreshes from the current infinity state each chamber iteration (`#54`).
12+
- Legacy SHCK branch sequencing and output selection were reworked toward CEA2 parity: explicit incident requests now report only the requested incident branch (with reflected state attached when requested), while reflected-only requests continue exploring the mixed equilibrium/frozen permutations.
13+
- Shock reports now include incident/reflected transport tables (`Visc`, `Cp`, `Conductivity`, `Prandtl Number`) when `outp tran` is enabled and transport data are available.
14+
- Python test/main-interface helper workflows no longer require `pandas`; CSV result output is now written with the standard library and the installation guidance/environment were updated accordingly.
1215

1316
### Fixed
1417
- Legacy CLI equilibrium/rocket/shock workflows now propagate `include_ions` into generated product mixtures so ionized products are retained when requested (`#52`).
18+
- Reflected-shock velocity outputs were corrected for reflected equilibrium/frozen workflows: the legacy CEA2 `u5`/`u5+v2` relations effectively used `rho5/rho2` where the reflected-shock mass balance requires `rho5/rho2 - 1`, so the old equations underpredicted reflected velocities and did not satisfy the reflected conservation equations. CEA now reports conservation-consistent `u5`, `u5+v2`, sonic speed, reflected ratios, and reflected-frozen header thermodynamic values.
19+
- Shock failure handling now stops at the last valid state instead of emitting partially invalid reflected results; failed downstream shock states are explicitly zeroed for consistent output.
20+
- Shock transport-property calculations now preserve and restore stable transport seeds across singular-recovery paths, fixing incorrect or missing transport values in difficult reflected-shock cases.
1521

1622
### Added
1723
- Added C and Python support for custom reactant data (including species not present in `thermo.lib`) in parity with the main interface workflow used by RP-1311 Example 5 (`#53`).
@@ -20,6 +26,8 @@ All notable user-visible changes to this project are documented here.
2026
- `cea_mixture_create_products_from_input_reactants_w_ions` (`#53`).
2127
- Added a shared bindc parser path for `cea_reactant_input -> ReactantInput` conversion to reduce duplicated C-binding logic (`#53`).
2228
- Added Python `cea.Reactant` and mixed-input `Mixture(...)` support in the Cython binding (`#53`).
29+
- Added shock regression coverage for transport output population across equilibrium/frozen branches and for singular-recovery reflected-shock cases.
30+
- Added a Python `pytest` reflected-shock conservation check that verifies incident/reflected mass, momentum, and energy balances and confirms the corrected `u5+v2` relation.
2331

2432
## [3.1.0] - 2026-03-02
2533

docs/source/installation.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ Build and install:
238238

239239
3. Prepare Python packages used by the optional binding/tests/docs::
240240

241-
pip install cython numpy setuptools pandas pytest
241+
pip install cython numpy setuptools pytest
242242

243243
4. Configure with the Intel preset (this is the corrected preset usage) and
244244
build::

environment.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ dependencies:
1010
- gxx
1111
- ninja
1212
- numpy
13-
- pandas
1413
- pytest
1514
- python
1615
- setuptools
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import numpy as np
2+
import pytest
3+
4+
5+
_CONSERVATION_RTOL = 5.0e-5
6+
7+
8+
def _example7_case(cea):
9+
reac_names = ["H2", "O2", "Ar"]
10+
moles = np.array([0.05, 0.05, 0.9])
11+
p0 = cea.units.mmhg_to_bar(10.0)
12+
T0 = 300.0
13+
14+
reac = cea.Mixture(reac_names)
15+
prod = cea.Mixture(reac_names, products_from_reactants=True)
16+
solver = cea.ShockSolver(prod, reactants=reac)
17+
soln = cea.ShockSolution(solver, reflected=True)
18+
weights = reac.moles_to_weights(moles)
19+
return solver, soln, weights, T0, p0
20+
21+
22+
def _scaled_tolerance(lhs, rhs):
23+
return _CONSERVATION_RTOL * max(abs(lhs), abs(rhs), 1.0)
24+
25+
26+
def _assert_conserved(lhs, rhs):
27+
residual = lhs - rhs
28+
assert abs(residual) <= _scaled_tolerance(lhs, rhs)
29+
30+
31+
@pytest.mark.parametrize(
32+
("mode", "u1", "kwargs"),
33+
[
34+
("equilibrium", 1100.0, {"reflected": True}),
35+
("equilibrium", 1400.0, {"reflected": True}),
36+
("frozen", 1100.0, {"reflected": True, "incident_frozen": True, "reflected_frozen": True}),
37+
("frozen", 1400.0, {"reflected": True, "incident_frozen": True, "reflected_frozen": True}),
38+
],
39+
)
40+
def test_shock_conservation_equations(cea_module, mode, u1, kwargs):
41+
cea = cea_module
42+
solver, soln, weights, T0, p0 = _example7_case(cea)
43+
44+
solver.solve(soln, weights, T0, p0, u1=u1, **kwargs)
45+
46+
assert soln.converged, f"{mode} shock solve failed to converge for u1={u1}"
47+
48+
w1 = soln.velocity[0]
49+
w2 = soln.velocity[1]
50+
ur = soln.velocity[2]
51+
v2 = soln.v2
52+
53+
rho1 = soln.density[0]
54+
rho2 = soln.density[1]
55+
rho5 = soln.density[2]
56+
57+
p1 = soln.P[0] * 1.0e5
58+
p2 = soln.P[1] * 1.0e5
59+
p5 = soln.P[2] * 1.0e5
60+
61+
h1 = soln.enthalpy[0]
62+
h2 = soln.enthalpy[1]
63+
h5 = soln.enthalpy[2]
64+
65+
# Incident shock conservation in the incident shock frame.
66+
incident_mass_lhs = rho1 * w1
67+
incident_mass_rhs = rho2 * w2
68+
_assert_conserved(incident_mass_lhs, incident_mass_rhs)
69+
70+
incident_momentum_lhs = p1 + rho1 * w1**2
71+
incident_momentum_rhs = p2 + rho2 * w2**2
72+
_assert_conserved(incident_momentum_lhs, incident_momentum_rhs)
73+
74+
incident_energy_lhs = h1 + w1**2 / 2000.0
75+
incident_energy_rhs = h2 + w2**2 / 2000.0
76+
_assert_conserved(incident_energy_lhs, incident_energy_rhs)
77+
78+
# Reflected shock conservation in the reflected-shock frame.
79+
reflected_upstream_speed = v2 + ur
80+
reflected_downstream_speed = ur
81+
82+
reflected_mass_lhs = rho2 * reflected_upstream_speed
83+
reflected_mass_rhs = rho5 * reflected_downstream_speed
84+
_assert_conserved(reflected_mass_lhs, reflected_mass_rhs)
85+
86+
reflected_momentum_lhs = p2 + rho2 * reflected_upstream_speed**2
87+
reflected_momentum_rhs = p5 + rho5 * reflected_downstream_speed**2
88+
_assert_conserved(reflected_momentum_lhs, reflected_momentum_rhs)
89+
90+
reflected_energy_lhs = h2 + reflected_upstream_speed**2 / 2000.0
91+
reflected_energy_rhs = h5 + reflected_downstream_speed**2 / 2000.0
92+
_assert_conserved(reflected_energy_lhs, reflected_energy_rhs)
93+
94+
assert soln.u5_p_v2 == pytest.approx(soln.v2 + soln.velocity[2], rel=1.0e-10, abs=1.0e-10)

0 commit comments

Comments
 (0)