Spaces:
Running
Running
| """`ald-screen` command-line interface. | |
| Examples: | |
| ald-screen run examples/wf6_w.json | |
| ald-screen run examples/wf6_w.json --json | |
| ald-screen run examples/new_material_screen.json --csv out.csv --uma | |
| ald-screen recipes # list the known-good calibration recipes | |
| ald-screen demo # run the WF6 -> W headline case | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| import json | |
| import sys | |
| from pathlib import Path | |
| from densitygen.data import KNOWN_RECIPES | |
| from densitygen.design import design | |
| from densitygen.reporting import render_csv, render_report | |
| from densitygen.schemas import Candidate, ScreeningRequest | |
| from densitygen.screen import screen | |
| def _run(args: argparse.Namespace) -> int: | |
| payload = json.loads(Path(args.request).read_text()) | |
| if args.uma: | |
| payload["use_ml_potential"] = True | |
| request = ScreeningRequest.model_validate(payload) | |
| resp = screen(request) | |
| if args.csv: | |
| Path(args.csv).write_text(render_csv(resp)) | |
| print(f"wrote {args.csv}", file=sys.stderr) | |
| if args.json: | |
| print(resp.model_dump_json(indent=2)) | |
| else: | |
| print(render_report(resp)) | |
| return 0 | |
| def _recipes(_args: argparse.Namespace) -> int: | |
| print("Known-good ALD recipes (calibration / regression set):\n") | |
| for r in KNOWN_RECIPES: | |
| print(f" {r.precursor:<8} + {r.co_reactant:<6} -> {r.film:<6} {r.note}") | |
| return 0 | |
| def _demo(args: argparse.Namespace) -> int: | |
| # The headline test case the platform is built around: WF6 -> W, screened | |
| # against a couple of foils so the ranking behavior is visible. | |
| request = ScreeningRequest( | |
| film="W", | |
| co_reactant="B2H6", | |
| temperature_max_c=350, | |
| use_ml_potential=args.uma, | |
| candidates=[ | |
| Candidate(name="WF6"), | |
| Candidate(name="WCl6", formula="WCl6"), | |
| Candidate(name="TMA"), # foil: contains no W, must hard-fail | |
| ], | |
| ) | |
| print(render_report(screen(request))) | |
| return 0 | |
| def _viz(args: argparse.Namespace) -> int: | |
| """Bake the interactive viz bundle from a screening request.""" | |
| from densitygen.viz import response_to_payload, write_bundle | |
| payload_in = json.loads(Path(args.request).read_text()) | |
| if args.uma: | |
| payload_in["use_ml_potential"] = True | |
| request = ScreeningRequest.model_validate(payload_in) | |
| resp = screen(request) | |
| data = response_to_payload(resp, request=request, mode="screen") | |
| entry = write_bundle(data, args.out) | |
| print(f"wrote viz bundle to {Path(args.out).resolve()}", file=sys.stderr) | |
| print(f"open: {entry}", file=sys.stderr) | |
| print(f" (or run `ald-screen serve {args.request}` for live precursor suggestions)", | |
| file=sys.stderr) | |
| return 0 | |
| def _serve(args: argparse.Namespace) -> int: | |
| from densitygen.server import serve | |
| if args.request: | |
| payload_in = json.loads(Path(args.request).read_text()) | |
| request = ScreeningRequest.model_validate(payload_in) | |
| else: | |
| # A sensible default demo so `ald-screen serve` works with no args. | |
| request = ScreeningRequest( | |
| film="HfO2", co_reactant="H2O", temperature_max_c=300, | |
| forbidden_elements=["Cl"], | |
| candidates=[ | |
| Candidate(name="TEMAH"), Candidate(name="HfCl4"), | |
| Candidate(name="TDMAH", formula="Hf[N(CH3)2]4"), | |
| Candidate(name="Hf(OtBu)4", formula="Hf(OC4H9)4"), | |
| ], | |
| ) | |
| serve(request, host=args.host, port=args.port, bundle_dir=args.out) | |
| return 0 | |
| def _design(args: argparse.Namespace) -> int: | |
| resp = design( | |
| film=args.film, | |
| co_reactant=args.co_reactant, | |
| temperature_max_c=args.temperature_max_c, | |
| forbidden_elements=[e.strip() for e in (args.forbidden or "").split(",") if e.strip()], | |
| oxidation=args.oxidation, | |
| top_n=args.top_n, | |
| use_ml_potential=args.uma, | |
| ) | |
| if args.json: | |
| print(resp.model_dump_json(indent=2)) | |
| else: | |
| print(render_report(resp)) | |
| return 0 | |
| def _interconnects(args: argparse.Namespace) -> int: | |
| from densitygen.interconnects import ( | |
| HAVE_PRECURSORS, NEED_PRECURSORS, run_design_bucket, run_test_bucket) | |
| print("=" * 72) | |
| print("INTERCONNECT RESISTANCE — alternative metals to beat Cu/W at scaled nodes") | |
| print("=" * 72) | |
| print("\n### BUCKET A — materials WITH precursors: end-to-end screening tests ###") | |
| for t in HAVE_PRECURSORS: | |
| print(f"\n>>> {t.film}: {t.why}") | |
| print(render_report(run_test_bucket(t, use_ml_potential=args.uma))) | |
| print("\n\n### BUCKET B — materials WITHOUT precursors: inverse-design demos ###") | |
| for t in NEED_PRECURSORS: | |
| print(f"\n>>> {t.film}: {t.why}") | |
| print(f" ({t.note})") | |
| print(render_report(run_design_bucket(t, use_ml_potential=args.uma, top_n=args.top_n))) | |
| return 0 | |
| def main(argv: list[str] | None = None) -> int: | |
| p = argparse.ArgumentParser(prog="ald-screen", description=__doc__, | |
| formatter_class=argparse.RawDescriptionHelpFormatter) | |
| sub = p.add_subparsers(dest="cmd", required=True) | |
| pr = sub.add_parser("run", help="screen candidates from a JSON request file") | |
| pr.add_argument("request", help="path to a ScreeningRequest JSON file") | |
| pr.add_argument("--json", action="store_true", help="emit JSON instead of a text report") | |
| pr.add_argument("--csv", metavar="PATH", help="also write a CSV scorecard") | |
| pr.add_argument("--uma", action="store_true", help="use the hosted UMA model on Replicate") | |
| pr.set_defaults(func=_run) | |
| prc = sub.add_parser("recipes", help="list known-good calibration recipes") | |
| prc.set_defaults(func=_recipes) | |
| pd = sub.add_parser("demo", help="run the WF6 -> W headline case") | |
| pd.add_argument("--uma", action="store_true", help="use the hosted UMA model") | |
| pd.set_defaults(func=_demo) | |
| pv = sub.add_parser("viz", help="bake the interactive viz bundle from a request JSON") | |
| pv.add_argument("request", help="path to a ScreeningRequest JSON file") | |
| pv.add_argument("--out", default="densitygen_viz", help="output bundle directory") | |
| pv.add_argument("--uma", action="store_true", help="use the hosted UMA model") | |
| pv.set_defaults(func=_viz) | |
| ps = sub.add_parser("serve", help="serve the viz with a live /api/screen for precursor suggestions") | |
| ps.add_argument("request", nargs="?", default=None, | |
| help="optional ScreeningRequest JSON; omitted -> HfO2 demo") | |
| ps.add_argument("--host", default="127.0.0.1") | |
| ps.add_argument("--port", type=int, default=8765) | |
| ps.add_argument("--out", default="densitygen_viz", help="bundle directory to serve") | |
| ps.set_defaults(func=_serve) | |
| pg = sub.add_parser("design", help="PROPOSE novel precursors for a film (inverse design)") | |
| pg.add_argument("--film", required=True, help="target film, e.g. W, Mo, HfO2") | |
| pg.add_argument("--co-reactant", dest="co_reactant", default=None) | |
| pg.add_argument("--temperature-max-c", dest="temperature_max_c", type=float, default=None) | |
| pg.add_argument("--forbidden", default="", help="comma-separated elements to exclude") | |
| pg.add_argument("--oxidation", type=int, default=None, help="metal oxidation state override") | |
| pg.add_argument("--top-n", dest="top_n", type=int, default=12) | |
| pg.add_argument("--uma", action="store_true", help="confirm top survivors with real UMA energies") | |
| pg.add_argument("--json", action="store_true") | |
| pg.set_defaults(func=_design) | |
| pi = sub.add_parser("interconnects", | |
| help="interconnect-resistance demo: screen materials with " | |
| "precursors, design ones without") | |
| pi.add_argument("--uma", action="store_true", help="use real ML-potential energies") | |
| pi.add_argument("--top-n", dest="top_n", type=int, default=6) | |
| pi.set_defaults(func=_interconnects) | |
| args = p.parse_args(argv) | |
| return args.func(args) | |
| if __name__ == "__main__": | |
| raise SystemExit(main()) | |