28 """L1-04: two secant steps from the proposal left +0.4..0.9 % thrust depending on the start."""
29 from engine
import accel
31 cfg = load_config(str(ROOT /
"configs" /
"ethalox_6500N.yaml"))
32 runner = PintleEngineRunner(copy.deepcopy(cfg))
33 cg = cfg.chamber_geometry
34 wall = L1._layer1_total_wall_thickness_m(cfg)
38 r = accel.evaluate(runner.config, runner.cea_cache, P, P, 94070.0)
41 r = runner.evaluate(P, P, P_ambient=94070.0, silent=
True)
46 c = dict(layer1_derive_expansion_ratio=
True, layer1_derive_throat_from_thrust=
True,
47 layer1_derive_max_iters=getattr(L1,
"_LAYER1_DERIVE_MAX_ITERS_DEFAULT", 2),
48 layer1_derive_thrust_tol_rel=1e-3, derive_eps_min=4.0, derive_eps_max=14.0,
49 derive_At_min=5e-4, derive_At_max=6e-3, P_ambient=94070.0, target_thrust=6500.0,
50 TOTAL_WALL_THICKNESS_M=wall, max_nozzle_exit=0.2032)
52 for f0
in (0.8, 1.0, 1.3, 1.66):
53 x = np.array([cg.A_throat * f0, cg.Lstar, cg.expansion_ratio, cg.chamber_diameter + wall])
54 L1._layer1_apply_chamber_geometry_to_config(
55 runner.config, A_throat=x[0], Lstar=x[1], expansion_ratio=x[2],
56 D_chamber_outer=x[3], max_nozzle_exit=0.2032, wall_thickness_m=wall)
57 res = L1._layer1_solve_derived_geometry(x, runner.config, c, ev, ev())
58 errs.append(abs(res[
"F"] / 6500.0 - 1.0))
59 assert max(errs) <= 1.0e-3, errs
63 """L1-05: 24 doublets in a 127 mm bore is a 22.97 mm pitch against the 6500 N config's
64 22.5 mm limit; the 4.4e-4 squared miss hid under gate_eps and every seed said VALID."""
65 cfg = load_config(str(ROOT /
"configs" /
"ethalox_6500N.yaml"))
66 req = cfg.design_requirements.model_dump()
67 gates = L1._layer1_declared_limit_gates(cfg, req, {})
68 ok, msg = gates[
"element_pitch"]
69 pitch = math.sqrt(math.pi / 4.0 * 0.127 ** 2 / 24)
70 assert pitch == pytest.approx(0.02297, abs=1e-5)
71 assert not ok
and "22.97 mm > limit 22.50 mm" in msg
72 assert gates[
"engine_length"][0]
73 assert gates[
"free_jet"][0]
77 """L1-01: the replay retried at 1.03-1.72x tank pressure, took the first that solved,
78 and stamped the design VALID with the un-raised pressures written to the YAML."""
79 real_cfg = load_config(str(ROOT /
"configs" /
"impinging_smoke.yaml"))
80 state = {
"val":
False,
"nom":
None}
81 base = {
"F": 7000.0,
"Pc": 2.5e6,
"MR": 2.3,
"Cf": 1.55,
"Cf_actual": 1.55,
82 "mdot_O": 1.0,
"mdot_F": 0.45,
"stability_results": {}}
84 def evaluate(po, pf, **kw):
86 if state[
"nom"]
is None:
88 if po < 1.02 * state[
"nom"]:
89 raise ValueError(
"Supply < Demand at all Pc")
92 engine_mock = MagicMock()
93 engine_mock.evaluate.side_effect = evaluate
94 x13 = [0.002, 1.0, 8.0, 0.14, 12.0, 0.002, 45.0, 0.012, 0.0022, 45.0, 0.011, 550.0, 650.0]
96 def _dc(obj, memo=None):
97 return real_cfg
if obj
is real_cfg
else copy.deepcopy(obj)
99 def _rebuild(*a, **k):
103 with patch(
"engine.optimizer.layers.layer1_static_optimization.copy.deepcopy", side_effect=_dc), \
104 patch(
"engine.optimizer.layers.layer1_static_optimization.PintleEngineRunner",
105 return_value=engine_mock), \
106 patch(
"engine.optimizer.layers.layer1_static_optimization._layer1_rebuild_final_config",
107 side_effect=_rebuild), \
108 patch(
"engine.optimizer.layers.layer1_static_optimization.cma.CMAEvolutionStrategy")
as mcma, \
109 patch(
"engine.optimizer.layers.layer1_static_optimization.minimize")
as mmin, \
110 patch(
"engine.optimizer.layers.layer1_static_optimization.ProcessPoolExecutor"):
111 mcma.return_value.stop.return_value =
True
112 mcma.return_value.result.xbest = x13
113 mcma.return_value.result.fbest = 0.1
114 lb = MagicMock(); lb.x = np.array(x13); lb.fun = 0.05; lb.success =
True
115 mmin.return_value = lb
116 req = real_cfg.design_requirements.model_dump()
117 _, results = L1.run_layer1_optimization(
118 config_obj=real_cfg, runner=MagicMock(), requirements=req, target_burn_time=10.0,
119 tolerances={
"thrust": 0.1}, pressure_config={
"mode":
"optimizer_controlled"},
120 layer1_max_iterations=1, layer1_cma_restarts=1)
121 perf = results[
"performance"]
122 assert perf[
"layer1_validation_tank_pressure_scale"] == pytest.approx(1.03)
123 assert perf[
"pressure_candidate_valid"]
is False
124 assert any(
"own tank pressures" in r
for r
in perf[
"failure_reasons"])
128 """L1-07: blocks ran isotropic CMA (sigma = 0.2 x median span) over coordinates whose
129 ranges differ by 1e5 and searched the solved throat, eps and fuel pitch freely."""
133 def fake_core(objective_fn, x0, sigma0, bounds, budget, popsize, **kw):
134 calls.append(dict(kw, x0=np.asarray(x0), sigma0=sigma0, n=len(bounds)))
135 return np.asarray(x0, dtype=float), float(objective_fn(np.asarray(x0, dtype=float))), 1
137 lo = np.array([1e-4, 0.8, 4.0, 0.1, 6, 5e-4, 20, 3e-3, 5e-4, 20, 3e-3, 300, 300], float)
138 hi = np.array([4e-3, 1.5, 14.0, 0.2, 30, 4e-3, 80, 3e-2, 4e-3, 80, 3e-2, 600, 600], float)
140 fixed = {0: x0[0], 2: x0[2], 10: x0[10]}
141 hc = HybridOptimizerConfig(num_blocks=3, cycles=2, per_block_budget_fraction=0.5,
142 refresh_every_pass=
False, block_method=
"random")
143 with patch.object(L1,
"run_cma_core", side_effect=fake_core):
144 L1.run_hybrid_optimization(
lambda x: float(np.sum(((x - lo) / (hi - lo)) ** 2)),
145 list(zip(lo, hi)), x0, hc, total_budget=20000,
146 fixed_variables=fixed, integer_dims=[4, 6, 9], seed=3)
147 blocks = [c
for c
in calls
if c.get(
"true_objective_fn")
is not None]
150 assert c.get(
"cma_stds")
is not None
151 assert len(c[
"cma_stds"]) == c[
"n"]
153 assert sum(c[
"n"]
for c
in blocks[:3]) == 13 - len(fixed)
157 """L1-08: 'best > 100/150/300' read the ~1970-point mass floor as stuck on every 6500 N
158 run (7 sigma kicks) and never fired on blank runs."""
159 tracker = {
"function_evaluations": 2000,
"last_best_eval": 1600,
"best_objective": 2000.0,
160 "valley_escape_tier": 0,
"cooldown_until": 0}
161 L1.run_cma_core(
lambda x: 2000.0 + float(np.sum(x ** 2)), np.ones(3), 0.3,
162 [(-2.0, 2.0)] * 3, 64, 8, seed=1, valley_escape_tracker=tracker)
163 assert tracker[
"valley_escape_tier"] == 0
164 stuck = dict(tracker, best_objective=1.5e6)
165 L1.run_cma_core(
lambda x: 1.5e6 + float(np.sum(x ** 2)), np.ones(3), 0.3,
166 [(-2.0, 2.0)] * 3, 64, 8, seed=1, valley_escape_tracker=stuck)
167 assert stuck[
"valley_escape_tier"] == 1
171 """Run Layer-1 setup up to the worker pool and return (bounds, constants)."""
175 class _Stop(Exception):
178 def spy(config_dict, bounds_array, requirements_dict, constants_dict, debug_strict):
179 cap.update(b=np.asarray(bounds_array), c=constants_dict)
182 monkeypatch.setenv(
"ED_L1_WORKERS",
"1")
183 monkeypatch.setattr(L1,
"_init_worker", spy)
184 req = cfg.design_requirements.model_dump()
185 with pytest.raises(_Stop):
186 L1.run_layer1_optimization(
187 copy.deepcopy(cfg), PintleEngineRunner(copy.deepcopy(cfg)), req,
188 float(req.get(
"target_burn_time")
or 4.0), {
"thrust": 0.1},
189 {
"mode":
"optimizer_controlled",
190 "max_lox_pressure_psi": req[
"max_lox_tank_pressure_psi"],
191 "max_fuel_pressure_psi": req[
"max_fuel_tank_pressure_psi"]})
192 return cap[
"b"], cap[
"c"]
196 """DEF-09: the OD floor was 0.5 x cap -- a 72.9 mm bore under a 1 kN engine whose
197 D/Dt band (2.2-3.2) wants a 41-60 mm bore."""
199 cfg = load_config(str(ROOT /
"configs" /
"ethalox_6500N.yaml"))
200 dr = cfg.design_requirements
201 dr.target_thrust = 1000.0
202 dr.max_chamber_outer_diameter = 0.2032
203 dr.target_chamber_pressure_psi =
None
204 dr.frozen_parameters =
None
206 wall = float(c[
"TOTAL_WALL_THICKNESS_M"])
207 At_1kN = 1000.0 / (1.40 * 378.0 * PSI)
208 assert b[3][0] - wall <= 2.2 * math.sqrt(4.0 * At_1kN / math.pi)
212 """Dropping the derived DOFs can leave a block with one coordinate; CMA_stds on a 1-D
213 problem raised IndexError inside cma (seen on configs/ethalox_6500N.yaml, seeds 3 and 4)."""
215 lo, hi = np.zeros(4), np.array([1.0, 1.0, 100.0, 1e-3])
217 hc = HybridOptimizerConfig(num_blocks=2, cycles=1, per_block_budget_fraction=0.5,
218 refresh_every_pass=
False, block_method=
"random")
219 x, f, n = L1.run_hybrid_optimization(
220 lambda v: float(np.sum(((v - 0.3 * hi) / hi) ** 2)), list(zip(lo, hi)), x0, hc,
221 total_budget=600, fixed_variables={0: x0[0], 1: x0[1]}, seed=5)
222 assert np.isfinite(f)
226 """Seeds ended at n = 5, 5, 7, 13 with objectives 20 % apart: at fixed jet diameters each
227 count is a different injector, so an integer CMA step never crossed between them. Scored
228 along constant flow area and pitch circle, every count is reachable in one batch."""
232 x = np.asarray(x, dtype=float)
233 n, d, s = x[4], x[5], x[7]
234 area = n * d * d / A0 - 1.0
235 ring = n * s / (5 * 0.0071) - 1.0
236 return {
"value": float((n - 12.0) ** 2 + 1e4 * area ** 2 + 1e4 * ring ** 2),
237 "success":
True,
"x_solved": x.tolist()}
239 lo = np.array([1e-4, 0.8, 4, 0.1, 5, 5e-4, 20, 2e-3, 5e-4, 20, 2e-3, 300, 300], float)
240 hi = np.array([4e-3, 1.5, 14, 0.2, 20.499, 6e-3, 80, 3e-2, 6e-3, 80, 5e-2, 600, 600], float)
241 x = np.array([1.7e-3, 0.76, 5.0, 0.13, 5.0, 4.0e-3, 39, 0.0071, 3.6e-3, 52, 0.047, 600, 574])
242 with patch.object(L1,
"_eval_candidate", side_effect=fake_eval):
243 xb, fb, n_ev = L1._layer1_iso_area_count_scan(
244 x, fake_eval(x)[
"value"], list(zip(lo, hi)), L1._LocalSerialExecutor(), [4, 6, 9],
245 (4, (5, 8), (7, 10)))
246 assert int(round(xb[4])) == 12
and fb < 1e-6
247 assert xb[5] == pytest.approx(4.0e-3 * math.sqrt(5 / 12), rel=1e-12)
248 assert n_ev <= 2 * 15
251 with patch.object(L1,
"_eval_candidate", side_effect=fake_eval):
252 xb2, _, _ = L1._layer1_iso_area_count_scan(
253 x, fake_eval(x)[
"value"], list(zip(lo, hi)), L1._LocalSerialExecutor(), [4, 6, 9],
254 (4, (5, 8), (7, 10)))
255 assert int(round(xb2[4])) == 11