| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091 |
- """P4-M5: L0 pre-screening promoted to shared core (single implementation).
- Run: python scripts/test_p4_m5_l0.py (exit 0 = PASS)
- Verifies:
- 1. The single implementation lives at src/afmcore/l0/prescreening.py.
- 2. The web re-export references the same classes (no drift).
- 3. Core behavior is unchanged: geometric/electrical/thermal/manufacturing
- checks, evaluate(), filter_feasible(), empty-input gate.
- 4. Consumers (feasibility_search) still work through the re-export.
- """
- import os
- import sys
- _ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
- _BACKEND = os.path.join(_ROOT, "web", "backend")
- sys.path.insert(0, _BACKEND)
- sys.path.insert(0, _ROOT)
- from src.afmcore.l0.prescreening import ( # noqa: E402
- L0PreScreeningEngine as SrcEngine,
- FeasibilityReport, ConstraintResult,
- )
- from app.services.l0_prescreening import L0PreScreeningEngine as WebEngine # noqa: E402
- # 1) single definition, no drift
- assert SrcEngine is WebEngine, "re-export must point at the same class"
- assert SrcEngine.__module__ == "src.afmcore.l0.prescreening"
- print("[1] single implementation in shared core OK (%s)" % SrcEngine.__module__)
- eng = SrcEngine()
- # 2) geometric: valid vs invalid diameter ratio
- r = eng.evaluate({"outer_diameter_mm": 150, "inner_diameter_mm": 80})
- assert r.feasible
- r_bad = eng.evaluate({"outer_diameter_mm": 150, "inner_diameter_mm": 10})
- assert not r_bad.feasible # ratio 0.067 < 0.2
- print("[2] geometric diameter-ratio gate OK")
- # 3) electrical: current density limit
- r = eng.evaluate({"current_a": 10, "conductor_area_mm2": 2.0}) # 5 A/mm2
- assert r.feasible
- r2 = eng.evaluate({"current_a": 100, "conductor_area_mm2": 2.0}) # 50 A/mm2
- assert not r2.feasible
- print("[3] electrical current-density gate OK")
- # 4) thermal: winding temp limit
- r = eng.evaluate({"winding_temp_c": 120}) # below 150
- assert r.feasible
- r2 = eng.evaluate({"winding_temp_c": 160}) # above 150
- assert not r2.feasible
- print("[4] thermal winding-temp gate OK")
- # 5) manufacturing: PCB line width
- r = eng.evaluate({"pcb_line_width_mm": 0.5})
- assert r.feasible
- r2 = eng.evaluate({"pcb_line_width_mm": 0.05})
- assert not r2.feasible
- print("[5] manufacturing PCB line-width gate OK")
- # 6) empty input -> infeasible with UNKNOWN risk item (C3 gate)
- r = eng.evaluate({})
- assert not r.feasible
- assert any("[UNKNOWN]" in x for x in r.risk_items)
- print("[6] empty-input gate OK")
- # 7) filter_feasible splits sets
- sets = [
- {"outer_diameter_mm": 150, "inner_diameter_mm": 80},
- {"outer_diameter_mm": 150, "inner_diameter_mm": 10},
- ]
- feas, infeas = eng.filter_feasible(sets)
- assert len(feas) == 1 and len(infeas) == 1
- print("[7] filter_feasible split OK")
- # 8) consumers still work through the re-export
- from app.services.feasibility_search import ( # noqa: E402
- FeasibilityFirstSearch, ParameterRange,
- )
- search = FeasibilityFirstSearch(
- parameters=[ParameterRange(name="outer_diameter_mm", min_value=100, max_value=300),
- ParameterRange(name="inner_diameter_mm", min_value=50, max_value=150)],
- l0_engine=WebEngine(),
- total_budget=20, batch_size=4, initial_samples=6,
- objective_metric="tavg_nm", objective_direction="maximize", seed=1,
- )
- ini = search.generate_initial_batch()
- assert len(ini) > 0
- print("[8] feasibility_search L0 integration OK (%d initial pts)" % len(ini))
- print("\nALL P4-M5 L0 PROMOTION TESTS PASSED")
|