45 """What the UI hands a user who starts a new design: the canonical config for the injector
46 type, with the propellant preset switched in."""
48 base = yaml.safe_load((ROOT /
"configs" /
"canonical" / f
"{injector}.yaml").read_text())
49 return switch_config(base, injector_type=
None, propellant_preset=propellant)
54 if src.startswith(
"blank:"):
55 _, inj, prop = src.split(
":")
58 cfg = yaml.safe_load((ROOT / src).read_text()
if not Path(src).is_absolute()
else Path(src).read_text())
59 cfg = copy.deepcopy(cfg)
60 for k, v
in (case.get(
"set")
or {}).items():
62 p = out_dir / f
"{case['name']}_input.yaml"
63 p.write_text(yaml.safe_dump(cfg, sort_keys=
False))
67def run_case(case: dict, out_dir: Path, workers: int, timeout: int) -> dict:
72 except Exception
as e:
73 return {
"name": name,
"status":
"build_failed",
"error": repr(e)}
74 env = dict(os.environ, ED_L1_WORKERS=str(workers),
75 PYTHONPATH=str(ROOT.parent /
"lib" /
"stardesign") + os.pathsep + os.environ.get(
"PYTHONPATH",
""),
76 PYTHONWARNINGS=
"ignore")
77 out_json = out_dir / f
"{name}.json"
78 log = out_dir / f
"{name}.log"
80 with open(log,
"w")
as fh:
81 proc = subprocess.run(
82 [sys.executable,
"-W",
"ignore", str(ROOT /
"scripts" /
"layer1_run.py"),
83 "--config", str(cfg_path),
"--out", str(out_json),
"--label", name],
84 cwd=str(ROOT), env=env, stdout=fh, stderr=subprocess.STDOUT, timeout=timeout)
85 status =
"ok" if proc.returncode == 0
else f
"exit_{proc.returncode}"
86 except subprocess.TimeoutExpired:
88 res = {
"name": name,
"status": status,
"seconds": round(
time.time() - t0, 1),
"case": case}
91 res[
"result"] = json.loads(out_json.read_text())
92 except Exception
as e:
93 res[
"result_error"] = repr(e)
95 res[
"log_tail"] = log.read_text()[-3000:]
if log.exists()
else ""
100 ap = argparse.ArgumentParser()
101 ap.add_argument(
"cases")
102 ap.add_argument(
"--out", required=
True)
103 ap.add_argument(
"--parallel", type=int, default=3)
104 ap.add_argument(
"--workers", type=int, default=3)
105 ap.add_argument(
"--timeout", type=int, default=1800)
107 cases = json.loads(Path(a.cases).read_text())
109 out.mkdir(parents=
True, exist_ok=
True)
111 summary_path = out /
"summary.json"
112 with ThreadPoolExecutor(max_workers=a.parallel)
as ex:
113 futs = {ex.submit(run_case, c, out, a.workers, a.timeout): c
for c
in cases}
114 for f
in as_completed(futs):
117 rr = r.get(
"result")
or {}
118 print(f
"[{len(results)}/{len(cases)}] {r['name']}: {r['status']} {r.get('seconds')}s "
119 f
"F={rr.get('F')} O/F={rr.get('MR')} Pc={rr.get('Pc_psi')} Isp={rr.get('Isp')} "
120 f
"fail={rr.get('failure_reasons')}", flush=
True)
121 summary_path.write_text(json.dumps(results, indent=1, default=str))
122 print(f
"wrote {summary_path}")