STAR
Cal STAR — firmware, DAQ server, and engine design
Loading...
Searching...
No Matches
test_layerx_golden Namespace Reference

Functions

 _script ()
Any _get (Dict[str, Any] d, str path)
Dict[str, Any] baseline ()
Dict[str, Any] burn (baseline)
 test_embedded_config_round_trips (baseline)
 test_layerx_defaults_unchanged (baseline)
 test_inputs_unchanged (baseline, burn)
 test_he_pad_matches_baseline (baseline, burn, key)

Variables

 ROOT = Path(__file__).resolve().parents[1]
 BASELINE = Path(os.environ.get("LAYERX_GOLDEN_BASELINE") or (ROOT / "docs" / "layerx" / "baseline-2026-10-07.json"))
str CASE = "he_pad"
list pytestmark
dict TOLERANCES

Detailed Description

LE4 golden burn: Layer X's pad helium baseline must not move.

The reference is ``docs/layerx/baseline-2026-10-07.json``, written by
``scripts/layerx_baseline.py`` (case ``he_pad``: LE4 on the ``copv_study_he`` hot-fire drawing, on
the pad, erosion replay on, every other ``LayerXSettings`` field at its default). The overnight rule
is that a change moving the LE4 baseline by more than 1 % is reported; this test is what notices.

Slow: one helium burn with the replay loop is ~35-100 s depending on load, so it is skipped unless
``LAYERX_GOLDEN=1`` (the suite registers no ``slow`` marker). ``LAYERX_GOLDEN_BASELINE`` points it at
another baseline file.

What it holds fixed, and what it does not
-----------------------------------------
* **The design is the baseline's own.** The burn uses the config embedded in the baseline JSON,
  not the live ``configs/ethalox_6800N.yaml``, so editing the design does not read as a physics
  change. ``test_embedded_config_round_trips`` checks every field of it survives the schema
  unchanged (a validator that rewrites a stored value *is* an input change).
* **The drawing, the CEA table and the DAQ tables are the checkout's**, and
  ``test_inputs_unchanged`` compares their hashes with the baseline's first. When one of them has
  changed the figures are expected to move: re-run the script, review, and report the move.
* **Everything else is code**: lib/feedtwin, engine/layerx, the engine card, the replay.

Tolerances
----------
Measured: the burn is bitwise reproducible across processes on one machine (three runs, every
figure identical to the last digit), so on the baseline's own platform any non-zero move is a
change. The bands are wider than that on purpose:

* **0.5 % on the integral performance figures** (impulse, burn time, thrust, Pc, O/F): half the
  1 % reporting threshold, so a change that must be reported cannot pass, with room for platform
  drift (CI runs other numpy/CoolProp versions; that drift has not been measured here).
* **0.3 % on Isp**: Isp is a ratio of two integrals that move together; a 0.3 % move in delivered
  Isp is already a material engine change (0.7 s).
* **1 % on the bottle** (pressure and gas used at burnout): the bottle integrates the whole burn's
  gas demand and is a feed-side quantity the 1 % rule reads directly.
* **Absolute bands** where a relative one means nothing: tank pressures 3 psi (0.5 % of the
  ~618 psia peak), injector stiffness 0.003 (1.2 psi of the ~136 psi drop at a ~400 psia chamber,
  ~0.9 % of the value), the other tank's residual 0.02 kg (what an O/F move of ~0.5 % strands),
  the time of the lowest chug margin one 50 ms step.
* **2 % on throat growth and recession**: erosion feeds thrust weakly (a 2 % change in a 4 % area
  growth is ~0.08 % of area); tighter would flag erosion-model edits that cannot move a reported
  figure by 1 %.
* **Exact** on the discrete outcomes: which tank runs dry, converged, no failed steps, no step
  outside the engine card.

Checked against changes made in-process, not in the code: halving the regulator's supply-pressure
effect fails 8 of these (burn time +1.45 %, mean thrust -1.73 %, Pc -1.65 %, bottle +9.5 %, tank
peaks -24 psi) while total impulse moves only -0.30 %: the load is fixed, so impulse alone is a weak
witness to a feed-system change. Loosening the network solve from 1e-6 to 1e-4 passes all of them
(impulse -0.018 %, stiffness -0.15 % of its value). Doubling or shortening the step is a scheme change
and is held out by burning at the baseline's own ``dt`` (0.1 s: impulse +0.08 %; 0.02 s: -0.06 %,
lowest chug margin -0.58 % because its minimum is the first firing sample).

Function Documentation

◆ _get()

Any test_layerx_golden._get ( Dict[str, Any] d,
str path )
protected

Definition at line 113 of file test_layerx_golden.py.

Referenced by test_he_pad_matches_baseline().

◆ _script()

test_layerx_golden._script ( )
protected

Definition at line 106 of file test_layerx_golden.py.

Referenced by burn(), and test_embedded_config_round_trips().

◆ baseline()

Dict[str, Any] test_layerx_golden.baseline ( )

Definition at line 120 of file test_layerx_golden.py.

◆ burn()

Dict[str, Any] test_layerx_golden.burn ( baseline)

Definition at line 130 of file test_layerx_golden.py.

References _script().

◆ test_embedded_config_round_trips()

test_layerx_golden.test_embedded_config_round_trips ( baseline)
Every stored field of the baseline's design comes back out of the schema as it went in.

Definition at line 138 of file test_layerx_golden.py.

References _script().

◆ test_he_pad_matches_baseline()

test_layerx_golden.test_he_pad_matches_baseline ( baseline,
burn,
key )

Definition at line 189 of file test_layerx_golden.py.

References _get().

◆ test_inputs_unchanged()

test_layerx_golden.test_inputs_unchanged ( baseline,
burn )
The drawing, CEA table and DAQ tables are the ones the baseline burned. If this fails, the
figures below are expected to move: re-baseline and report the move, do not widen a band.

Definition at line 177 of file test_layerx_golden.py.

◆ test_layerx_defaults_unchanged()

test_layerx_golden.test_layerx_defaults_unchanged ( baseline)
The baseline is "today's defaults". A changed default is a changed answer for every run the
tab starts, so it is reported like a physics change; the burn below still uses the baseline's
own settings, so the figures isolate the code.

Definition at line 162 of file test_layerx_golden.py.

Variable Documentation

◆ BASELINE

test_layerx_golden.BASELINE = Path(os.environ.get("LAYERX_GOLDEN_BASELINE") or (ROOT / "docs" / "layerx" / "baseline-2026-10-07.json"))

Definition at line 67 of file test_layerx_golden.py.

◆ CASE

str test_layerx_golden.CASE = "he_pad"

Definition at line 68 of file test_layerx_golden.py.

◆ pytestmark

list test_layerx_golden.pytestmark
Initial value:
1= [
2 pytest.mark.skipif(os.environ.get("LAYERX_GOLDEN") != "1",
3 reason="slow LE4 golden burn: set LAYERX_GOLDEN=1 to run it"),
4]

Definition at line 70 of file test_layerx_golden.py.

◆ ROOT

test_layerx_golden.ROOT = Path(__file__).resolve().parents[1]

Definition at line 66 of file test_layerx_golden.py.

◆ TOLERANCES

dict test_layerx_golden.TOLERANCES
Initial value:
1= {
2 "total_impulse_Ns": ("rel", 5e-3),
3 "burn_time_s": ("rel", 5e-3),
4 "mean_thrust_N": ("rel", 5e-3),
5 "min_thrust_N": ("rel", 5e-3),
6 "max_thrust_N": ("rel", 5e-3),
7 "pc_mean_psia": ("rel", 5e-3),
8 "of_mean": ("rel", 5e-3),
9 "isp_mean_s": ("rel", 3e-3),
10 "propellant_used_kg": ("rel", 5e-3),
11 "copv_end_psia": ("rel", 1e-2),
12 "copv_used_kg": ("rel", 1e-2),
13 "ox_tank.peak_psia": ("abs", 3.0),
14 "fuel_tank.peak_psia": ("abs", 3.0),
15 "ox_tank.min_psia": ("abs", 3.0),
16 "fuel_tank.min_psia": ("abs", 3.0),
17 "ox_stiffness_min": ("abs", 3e-3),
18 "fuel_stiffness_min": ("abs", 3e-3),
19 "chug_margin_min": ("rel", 5e-3),
20 "chug_margin_min_t_s": ("abs", 0.051),
21 "residual_other_kg": ("abs", 0.02),
22 "throat_area_growth": ("rel", 2e-2),
23 "throat_recession_mm": ("rel", 2e-2),
24 "depleted_side": ("eq", 0.0),
25 "converged": ("eq", 0.0),
26 "failed_steps": ("eq", 0.0),
27 "card_outside_steps": ("eq", 0.0),
28}

Definition at line 76 of file test_layerx_golden.py.