1"""The injector face has finite real estate, and the optimizer must respect it.
3Until these constraints existed, WHERE the doublet ring pair sat radially was an exactly
4flat direction in the Layer-1 objective: ``_layer1_derive_fuel_spacing`` solves the fuel
5pitch so the standoff hits its target, and it does so by fixing the ring GAP, so ``dr`` is
6independent of ``s_O`` and the pair slides radially at zero cost. ``s_O`` fell onto its own
9Measured on configs/ethalox_8kN_FINAL.yaml before the fix:
11 D_pitch_O 26.90 mm (28 holes at a 3.018 mm pitch -- a 1.44 mm web)
13 impingement 41.79 mm circle, inside a 127.00 mm bore and NARROWER THAN THE
14 49.57 mm THROAT -- 10.8 % of the chamber area was fed directly
15 centre 24.83 mm clear, against 27.15 mm needed for a 3/8-18 NPT igniter boss
17None of that violated a single constraint, because none of those constraints existed.
24 _impinging_ring_geometry_squared,
25 _impinging_hard_geometry_blocks_eval,
31 spacing_O_m=0.0030182972100858507,
32 spacing_F_m=0.009875029755263455,
33 d_jet_O_m=0.0015821116699998301,
34 d_jet_F_m=0.001375132476863662,
35 D_chamber_inner_m=0.127,
39IGNITER_BOSS_M = 0.0280
43 return n * s / math.pi
51 """Documents the defect: the FINAL face cannot take a 3/8 NPT boss."""
52 half_major = 0.5 * FINAL[
"d_jet_O_m"] / math.cos(math.radians(FINAL[
"angle_O_deg"]))
53 inner_edge = 0.5 *
_dp(FINAL[
"n_elements"], FINAL[
"spacing_O_m"]) - half_major
54 assert 2 * inner_edge < IGNITER_BOSS_M,
"fixture stale: FINAL now clears the boss"
58 free = _impinging_ring_geometry_squared(**FINAL, center_clear_dia_m=0.0)
59 held = _impinging_ring_geometry_squared(**FINAL, center_clear_dia_m=IGNITER_BOSS_M)
60 assert held > free,
"reserving the centre circle must cost the shipped layout something"
64 """Same design, LOX ring opened to a 60 mm pitch circle: the term must go to zero."""
66 moved[
"spacing_O_m"] = math.pi * 0.060 / FINAL[
"n_elements"]
67 moved[
"spacing_F_m"] = moved[
"spacing_O_m"] + (
68 FINAL[
"spacing_F_m"] - FINAL[
"spacing_O_m"])
69 base = _impinging_ring_geometry_squared(**moved, center_clear_dia_m=0.0)
70 held = _impinging_ring_geometry_squared(**moved, center_clear_dia_m=IGNITER_BOSS_M)
71 assert held == pytest.approx(base),
"a ring well clear of the boss must pay nothing"
76 d_jet_O=FINAL[
"d_jet_O_m"], d_jet_F=FINAL[
"d_jet_F_m"],
77 sp_O=FINAL[
"spacing_O_m"], sp_F=FINAL[
"spacing_F_m"],
78 D_chamber_inner=FINAL[
"D_chamber_inner_m"],
79 D_throat_check=0.0496, A_chamber_check=0.012668, A_throat_check=0.00193,
80 n_elements=FINAL[
"n_elements"],
81 angle_O_deg=FINAL[
"angle_O_deg"], angle_F_deg=FINAL[
"angle_F_deg"],
83 assert _impinging_hard_geometry_blocks_eval(**kw)
is False,
"FINAL was accepted before"
84 assert _impinging_hard_geometry_blocks_eval(**kw, center_clear_dia_m=IGNITER_BOSS_M)
is True
92 """A hole inclined theta from the axis prints as an ellipse of major axis d/cos(theta).
94 At 69 deg that is 2.79x the drill diameter. Using d would under-state the radial reach
95 of every orifice, which is exactly the number the clearance terms are made of.
97 d, bore = 0.001375, 0.127
98 n, sp = 28.0, math.pi * 0.030 / 28.0
99 kw = dict(n_elements=n, spacing_O_m=math.pi * 0.090 / n, spacing_F_m=sp,
100 d_jet_O_m=0.0015821, d_jet_F_m=d, D_chamber_inner_m=bore,
101 angle_O_deg=40.0, ring_order_fuel_outboard=
False)
103 edge_round = 0.5 * 0.030 - 0.5 * d
104 edge_ellipse = 0.5 * 0.030 - 0.5 * d / math.cos(math.radians(69.0))
105 clear = 2 * 0.5 * (edge_round + edge_ellipse)
106 assert edge_ellipse < 0.5 * clear < edge_round
109 def _clearance_only(theta_F_deg):
110 with_c = _impinging_ring_geometry_squared(
111 **kw, angle_F_deg=theta_F_deg, center_clear_dia_m=clear)
112 without = _impinging_ring_geometry_squared(
113 **kw, angle_F_deg=theta_F_deg, center_clear_dia_m=0.0)
114 return with_c - without
116 steep, shallow = _clearance_only(69.0), _clearance_only(5.0)
117 assert shallow == pytest.approx(0.0, abs=1e-15), (
118 "a nearly-axial hole prints ~round, so its edge clears the circle and it pays nothing"
120 assert steep > 0.0, (
121 "a 69 deg hole reaches 2.79x further radially than its drill diameter; the clearance "
131 """spacing >= d_jet alone permits a web of exactly zero."""
132 web = FINAL[
"spacing_O_m"] - FINAL[
"d_jet_O_m"]
133 assert web == pytest.approx(0.001436, abs=1e-5),
"fixture stale"
134 assert _impinging_ring_geometry_squared(**FINAL, min_web_m=0.001) == pytest.approx(
135 _impinging_ring_geometry_squared(**FINAL, min_web_m=0.0)),
"1.0 mm floor is met"
136 assert _impinging_ring_geometry_squared(**FINAL, min_web_m=0.002) > \
137 _impinging_ring_geometry_squared(**FINAL, min_web_m=0.0),
"2.0 mm floor is not"
142 d_jet_O=FINAL[
"d_jet_O_m"], d_jet_F=FINAL[
"d_jet_F_m"],
143 sp_O=FINAL[
"spacing_O_m"], sp_F=FINAL[
"spacing_F_m"],
144 D_chamber_inner=FINAL[
"D_chamber_inner_m"],
145 D_throat_check=0.0496, A_chamber_check=0.012668, A_throat_check=0.00193,
146 n_elements=FINAL[
"n_elements"],
147 angle_O_deg=FINAL[
"angle_O_deg"], angle_F_deg=FINAL[
"angle_F_deg"],
149 assert _impinging_hard_geometry_blocks_eval(**kw, min_web_m=0.001)
is False
150 assert _impinging_hard_geometry_blocks_eval(**kw, min_web_m=0.002)
is True
159 crowd[
"spacing_F_m"] = math.pi * 0.120 / FINAL[
"n_elements"]
160 assert _impinging_ring_geometry_squared(**crowd, wall_clearance_m=0.008) > \
161 _impinging_ring_geometry_squared(**crowd, wall_clearance_m=0.0)
163 assert _impinging_ring_geometry_squared(**FINAL, wall_clearance_m=0.008) == pytest.approx(
164 _impinging_ring_geometry_squared(**FINAL, wall_clearance_m=0.0))
172 """CLAUDE.md: new physics is opt-in and defaults to the previous behaviour, exactly."""
173 base = _impinging_ring_geometry_squared(**FINAL)
174 assert _impinging_ring_geometry_squared(
175 **FINAL, center_clear_dia_m=0.0, min_web_m=0.0, wall_clearance_m=0.0
176 ) == pytest.approx(base)
178 d_jet_O=FINAL[
"d_jet_O_m"], d_jet_F=FINAL[
"d_jet_F_m"],
179 sp_O=FINAL[
"spacing_O_m"], sp_F=FINAL[
"spacing_F_m"],
180 D_chamber_inner=FINAL[
"D_chamber_inner_m"],
181 D_throat_check=0.0496, A_chamber_check=0.012668, A_throat_check=0.00193,
182 n_elements=FINAL[
"n_elements"],
184 assert _impinging_hard_geometry_blocks_eval(**kw)
is False
192 """theta is from the AXIS, so the drill meets the face at (90 - theta).
194 The shipped FINAL put the fuel jet at 69 deg -- 21 deg of incidence. A twist drill
195 entering a flat that shallow walks off the spot.
198 d_jet_O=FINAL[
"d_jet_O_m"], d_jet_F=FINAL[
"d_jet_F_m"],
199 sp_O=FINAL[
"spacing_O_m"], sp_F=FINAL[
"spacing_F_m"],
200 D_chamber_inner=FINAL[
"D_chamber_inner_m"],
201 D_throat_check=0.0496, A_chamber_check=0.012668, A_throat_check=0.00193,
202 n_elements=FINAL[
"n_elements"],
203 angle_O_deg=FINAL[
"angle_O_deg"], angle_F_deg=FINAL[
"angle_F_deg"],
205 assert _impinging_hard_geometry_blocks_eval(**kw)
is False,
"inert when unset"
206 assert _impinging_hard_geometry_blocks_eval(**kw, min_face_incidence_deg=20.0)
is False, \
207 "21 deg of incidence clears a 20 deg floor"
208 assert _impinging_hard_geometry_blocks_eval(**kw, min_face_incidence_deg=40.0)
is True, \
209 "21 deg of incidence must not clear a 40 deg floor"
213 """40 / 49 deg -> 50 / 41 deg of incidence, both above a 40 deg floor."""
214 assert _impinging_hard_geometry_blocks_eval(
215 d_jet_O=0.0016380, d_jet_F=0.0014330,
216 sp_O=0.0090639037305711, sp_F=0.0119080988215582,
217 D_chamber_inner=0.127, D_throat_check=0.0488,
218 A_chamber_check=0.012668, A_throat_check=0.00187,
219 n_elements=27.0, angle_O_deg=40.0, angle_F_deg=49.0,
220 center_clear_dia_m=0.0381, min_web_m=0.002, wall_clearance_m=0.008,
221 min_face_incidence_deg=40.0,
230 _resultant_tilt_breakeven_deg,
231 _resolve_tilt_allowance_deg,
234SHIP = dict(n_elements=27.0, spacing_O_m=0.0090639037305711,
235 spacing_F_m=0.0119080988215582, angle_O_deg=40.0, angle_F_deg=49.0,
236 D_chamber_inner_m=0.127, L_chamber_m=0.15340)
240 be = _resultant_tilt_breakeven_deg(**SHIP)
241 r_imp = 0.5 * 0.08821
242 assert math.degrees(math.atan2(0.0635 - r_imp, SHIP[
"L_chamber_m"])) == pytest.approx(be, abs=0.05)
244 assert (0.0635 - r_imp) / math.tan(math.radians(be)) == pytest.approx(SHIP[
"L_chamber_m"], rel=2e-3)
248 """A ring pair further out has less room, so it must be allowed less tilt."""
249 near = _resultant_tilt_breakeven_deg(**SHIP)
251 out[
"spacing_O_m"] *= 1.25
252 out[
"spacing_F_m"] *= 1.25
253 far = _resultant_tilt_breakeven_deg(**out)
254 assert far < near,
"a ring closer to the liner must earn a smaller allowance"
255 a_near = _resolve_tilt_allowance_deg(from_reach=
True, constant_deg=6.0,
256 breakeven_deg=near, margin=1.5)
257 a_far = _resolve_tilt_allowance_deg(from_reach=
True, constant_deg=6.0,
258 breakeven_deg=far, margin=1.5)
259 assert a_far < a_near < near,
"the margin must cut the allowance below break-even"
263 """margin = 1.5 => the fan reaches the liner at 1.5 chamber lengths, not 1.0."""
264 be = _resultant_tilt_breakeven_deg(**SHIP)
265 allowed = _resolve_tilt_allowance_deg(from_reach=
True, constant_deg=0.0,
266 breakeven_deg=be, margin=1.5)
267 r_imp = 0.5 * 0.08821
268 reach = (0.0635 - r_imp) / math.tan(math.radians(allowed))
269 assert reach == pytest.approx(1.5 * SHIP[
"L_chamber_m"], rel=5e-3)
273 """CLAUDE.md: defaults to the previous behaviour, exactly."""
274 assert _resolve_tilt_allowance_deg(
275 from_reach=
False, constant_deg=6.0, breakeven_deg=7.21, margin=1.5) == 6.0
277 for bad
in (float(
"nan"), 0.0, -3.0):
278 assert _resolve_tilt_allowance_deg(
279 from_reach=
True, constant_deg=6.0, breakeven_deg=bad, margin=1.5) == 6.0
286EQUAL_AREA = 1.0 / math.sqrt(2.0)
291 dpo, dpf = n * kw[
"spacing_O_m"] / math.pi, n * kw[
"spacing_F_m"] / math.pi
292 tan_sum = math.tan(math.radians(kw[
"angle_O_deg"])) + math.tan(math.radians(kw[
"angle_F_deg"]))
293 L = 0.5 * abs(dpo - dpf) / tan_sum
294 th_in = kw[
"angle_O_deg"]
if dpo <= dpf
else kw[
"angle_F_deg"]
295 return 0.5 *
min(dpo, dpf) + L * math.tan(math.radians(th_in))
299 """FINAL put every element on a circle at 0.33 of the bore radius."""
300 assert _r_imp(FINAL) / (0.5 * FINAL[
"D_chamber_inner_m"]) == pytest.approx(0.329, abs=0.005)
304 base = _impinging_ring_geometry_squared(**FINAL)
305 assert _impinging_ring_geometry_squared(**FINAL, spray_radius_frac=0.0) == pytest.approx(base)
309 """0.33 of the radius is 10.8 % of the area -- it must cost something against 0.707."""
310 free = _impinging_ring_geometry_squared(**FINAL)
311 held = _impinging_ring_geometry_squared(**FINAL, spray_radius_frac=EQUAL_AREA)
316 """A ring pair at the equal-area radius must pay exactly nothing."""
319 target_r = EQUAL_AREA * 0.5 * FINAL[
"D_chamber_inner_m"]
320 shift = target_r -
_r_imp(FINAL)
321 d_spacing = 2.0 * shift * math.pi / FINAL[
"n_elements"]
322 kw[
"spacing_O_m"] = FINAL[
"spacing_O_m"] + d_spacing
323 kw[
"spacing_F_m"] = FINAL[
"spacing_F_m"] + d_spacing
324 assert _r_imp(kw) / (0.5 * kw[
"D_chamber_inner_m"]) == pytest.approx(EQUAL_AREA, abs=1e-6)
325 base = _impinging_ring_geometry_squared(**kw)
326 held = _impinging_ring_geometry_squared(**kw, spray_radius_frac=EQUAL_AREA)
327 assert held == pytest.approx(base),
"on target must be free"
331 """Just outside the band costs; just inside does not."""
332 def at(frac_target, tol):
333 return _impinging_ring_geometry_squared(
334 **FINAL, spray_radius_frac=frac_target, spray_radius_tol=tol)
335 base = _impinging_ring_geometry_squared(**FINAL)
336 actual =
_r_imp(FINAL) / (0.5 * FINAL[
"D_chamber_inner_m"])
337 assert at(actual + 0.05, 0.08) == pytest.approx(base),
"inside the band is free"
338 assert at(actual + 0.20, 0.08) > base,
"outside the band is not"
342 """The soft term cost 1.5 points against an objective of ~2690, so every seed bought a
343 spray circle outside its own declared band. Hard, like ring fit."""
345 d_jet_O=FINAL[
"d_jet_O_m"], d_jet_F=FINAL[
"d_jet_F_m"],
346 sp_O=FINAL[
"spacing_O_m"], sp_F=FINAL[
"spacing_F_m"],
347 D_chamber_inner=FINAL[
"D_chamber_inner_m"],
348 D_throat_check=0.0496, A_chamber_check=0.012668, A_throat_check=0.00193,
349 n_elements=FINAL[
"n_elements"],
350 angle_O_deg=FINAL[
"angle_O_deg"], angle_F_deg=FINAL[
"angle_F_deg"],
352 assert _impinging_hard_geometry_blocks_eval(**kw)
is False,
"inert when unset"
354 assert _impinging_hard_geometry_blocks_eval(
355 **kw, spray_radius_frac=EQUAL_AREA, spray_radius_tol=0.08)
is True
357 assert _impinging_hard_geometry_blocks_eval(
358 **kw, spray_radius_frac=EQUAL_AREA, spray_radius_tol=0.40)
is False
362 """A limit only one path applies is not a limit.
364 The serial loop's _impinging_hard_geometry_blocks_eval never ran in the parallel CMA
365 workers, where essentially every candidate is scored. Two of three seeds converged
366 outside their own declared spray-radius band and reported ALL GATES PASS.
370 src = inspect.getsource(L1)
371 worker = src[src.index(
"def _compute_objective_value"):]
372 worker = worker[:worker.index(
"\ndef ", 10)]
373 assert "_impinging_face_infeasibility_terms(" in worker, (
374 "_compute_objective_value does not apply the face limits; the parallel workers "
375 "would score a violating candidate as feasible"
377 for key
in (
"layer1_injector_spray_radius_frac",
"layer1_injector_center_clear_dia_m",
378 "layer1_injector_wall_clearance_m",
"layer1_injector_min_web_m"):
379 assert key
in worker, f
"worker path never reads {key}"
383 """A binary block puts the whole violating region on one flat 1e6 plateau.
385 Measured: with `infeasibility_score += 1.0`, 2 of 3 seeds never found the feasible set
386 at all (converged O/F 2.09 and 25.6 against a 1.65 target). The term must carry a
387 gradient pointing back toward the band.
390 _impinging_face_infeasibility_terms,
393 n_elements=FINAL[
"n_elements"], spacing_O_m=FINAL[
"spacing_O_m"],
394 spacing_F_m=FINAL[
"spacing_F_m"], d_jet_O_m=FINAL[
"d_jet_O_m"],
395 d_jet_F_m=FINAL[
"d_jet_F_m"], D_chamber_inner_m=FINAL[
"D_chamber_inner_m"],
396 angle_O_deg=FINAL[
"angle_O_deg"], angle_F_deg=FINAL[
"angle_F_deg"],
398 assert _impinging_face_infeasibility_terms(**base) == 0.0,
"inert when nothing is declared"
404 for mult
in (1.0, 1.4, 1.8, 2.2):
406 kw[
"spacing_O_m"] = FINAL[
"spacing_O_m"] * mult
407 kw[
"spacing_F_m"] = FINAL[
"spacing_F_m"] + (kw[
"spacing_O_m"] - FINAL[
"spacing_O_m"])
408 scores.append(_impinging_face_infeasibility_terms(
409 **kw, spray_radius_frac=EQUAL_AREA, spray_radius_tol=0.08))
410 assert scores[0] > 0.0,
"the shipped bug must register as infeasible"
411 for a, b
in zip(scores, scores[1:]):
412 assert b < a, f
"no gradient: {scores} -- CMA cannot descend a flat plateau"
416 """Pushing the rings out to hit the spray target must not smuggle them past the wall.
418 At a fixed ring GAP, sliding the pair out far enough to reach the equal-area radius puts
419 the fuel ring at 136 mm on a 127 mm bore. The spray term is then satisfied and the wall
420 term is not, and the total must rise -- the sum is a constraint set, not a score to game.
423 _impinging_face_infeasibility_terms,
426 n_elements=FINAL[
"n_elements"], d_jet_O_m=FINAL[
"d_jet_O_m"],
427 d_jet_F_m=FINAL[
"d_jet_F_m"], D_chamber_inner_m=FINAL[
"D_chamber_inner_m"],
428 angle_O_deg=FINAL[
"angle_O_deg"], angle_F_deg=FINAL[
"angle_F_deg"],
430 kw[
"spacing_O_m"] = FINAL[
"spacing_O_m"] * 2.8
431 kw[
"spacing_F_m"] = FINAL[
"spacing_F_m"] + (kw[
"spacing_O_m"] - FINAL[
"spacing_O_m"])
432 assert _r_imp(dict(kw, spacing_O_m=kw[
"spacing_O_m"], spacing_F_m=kw[
"spacing_F_m"])) \
433 / (0.5 * kw[
"D_chamber_inner_m"]) == pytest.approx(EQUAL_AREA, abs=0.01)
434 spray_only = _impinging_face_infeasibility_terms(
435 **kw, spray_radius_frac=EQUAL_AREA, spray_radius_tol=0.08)
436 with_wall = _impinging_face_infeasibility_terms(
437 **kw, spray_radius_frac=EQUAL_AREA, spray_radius_tol=0.08, wall_clearance_m=0.008)
441 assert spray_only == pytest.approx(0.002730, rel=1e-3), (
442 "a trace across the bore must count even when no wall land is declared")
445 assert with_wall == pytest.approx(0.01328, rel=1e-3), (
446 "the fuel ring is 14.6 mm outside where the wall land allows, and that must be what "
447 "the total reports once the spray term is satisfied")
448 assert with_wall > spray_only
453 _impinging_face_infeasibility_terms, _layer1_centre_clear_m,
457 c = _yaml.safe_load(open(
"configs/ethalox_8kN_SHIP.yaml"))
458 g = c[
"injector"][
"geometry"]
459 rq = c[
"design_requirements"]
460 assert _impinging_face_infeasibility_terms(
461 n_elements=float(g[
"oxidizer"][
"n_elements"]),
462 spacing_O_m=g[
"oxidizer"][
"spacing"], spacing_F_m=g[
"fuel"][
"spacing"],
463 d_jet_O_m=g[
"oxidizer"][
"d_jet"], d_jet_F_m=g[
"fuel"][
"d_jet"],
464 D_chamber_inner_m=c[
"chamber_geometry"][
"chamber_diameter"],
465 angle_O_deg=g[
"oxidizer"][
"impingement_angle"],
466 angle_F_deg=g[
"fuel"][
"impingement_angle"],
469 center_clear_dia_m=_layer1_centre_clear_m(load_config(
"configs/ethalox_8kN_SHIP.yaml"), rq),
470 min_web_m=rq[
"layer1_injector_min_web_m"],
471 wall_clearance_m=rq[
"layer1_injector_wall_clearance_m"],
472 spray_radius_frac=rq[
"layer1_injector_spray_radius_frac"],
473 spray_radius_tol=rq[
"layer1_injector_spray_radius_tol"],
474 min_face_incidence_deg=rq[
"layer1_injector_min_face_incidence_deg"],
475 ) == 0.0,
"the shipped design must violate none of its own declared face limits"
T & at(const key_type &key)
test_shipped_design_has_no_room_for_the_igniter()
test_unset_keys_change_nothing()
test_radial_extent_uses_the_ellipse_not_the_drill_diameter()
test_breakeven_is_where_the_fan_arrives_at_the_throat_plane()
test_derived_mode_is_opt_in_and_falls_back_safely()
test_spray_radius_is_hard_not_merely_priced()
test_web_floor_catches_a_land_thinner_than_asked()
test_wall_clearance_costs_a_ring_crowding_the_bore()
test_face_infeasibility_zero_for_the_shipped_design()
test_derived_allowance_tracks_the_impingement_radius()
test_face_terms_compete_rather_than_cancel()
test_spray_radius_penalises_a_core_jet()
test_both_optimizer_paths_enforce_the_same_face_limits()
test_face_incidence_passes_the_replacement_design()
test_centre_clearance_hard_blocks_the_shipped_design()
test_spray_radius_band_has_width()
test_face_infeasibility_is_graded_not_binary()
test_centre_clearance_is_free_once_the_ring_moves_out()
test_centre_clearance_penalises_the_shipped_design()
test_face_incidence_blocks_a_jet_the_drill_cannot_start()
test_spray_radius_is_free_inside_the_band()
test_the_shipped_bug_is_a_third_of_the_way_out()
test_margin_means_chamber_lengths()
test_web_floor_hard_blocks()
test_spray_radius_term_is_inert_unless_asked()