densitygen-ald / src /densitygen /reporting.py
yushg's picture
Upload folder using huggingface_hub
28e66f9 verified
Raw
History Blame Contribute Delete
3.34 kB
"""Render screening results for humans (text report) and machines (CSV)."""
from __future__ import annotations
import csv
import io
from densitygen.schemas import ScreeningResponse
_BAR = "█"
def _bar(score: float, width: int = 10) -> str:
filled = round(score * width)
return _BAR * filled + "·" * (width - filled)
def render_report(resp: ScreeningResponse) -> str:
"""A compact, terminal-friendly ranked scorecard."""
lines: list[str] = []
lines.append(f"ALD precursor screening — target film: {resp.film}"
+ (f" (+ {resp.co_reactant})" if resp.co_reactant else ""))
lines.append(f"compute backend: {resp.model_provenance.compute_backend}"
+ (f" [{resp.model_provenance.model_name}]" if resp.model_provenance.model_name else ""))
lines.append("=" * 72)
for i, c in enumerate(resp.ranked_candidates, 1):
tag = " ★ known recipe" if c.is_known_recipe else ""
if c.origin == "proposed":
tag += " ⚗ proposed (novel)"
lines.append(f"\n#{i} {c.name}{tag}")
meta = f" formula={c.formula or '?'} MW={c.molecular_weight or '?'} delivers={c.film_element or '?'}"
if c.ml_energy_ev is not None:
backend = c.ml_calls[0].model if c.ml_calls else resp.model_provenance.compute_backend
meta += f" ML E={c.ml_energy_ev:.3f} eV ({backend})"
lines.append(meta)
lines.append(f" OVERALL {_bar(c.overall_score)} {c.overall_score:.2f}")
for comp in c.components:
conf = {"measured": "✓", "estimated": "~", "unknown": "?"}.get(comp.confidence, "~")
lines.append(f" {comp.name:<19} {_bar(comp.score)} {comp.score:.2f} {conf} {comp.evidence}")
for w in c.warnings:
lines.append(f" ⚠ {w}")
lines.append(f" → {c.recommended_next_step}")
if resp.warnings:
lines.append("\nRun warnings:")
lines.extend(f" - {w}" for w in resp.warnings)
if resp.billing:
lines.append(
f"\nReplicate estimate: ${resp.billing.estimated_cost_usd:.4f} "
f"({resp.billing.predict_seconds:.2f}s @ "
f"${resp.billing.rate_usd_per_second:.6f}/s on {resp.billing.hardware})"
)
lines.append("\nlegend: ✓ measured/literature ~ estimated ? unknown")
return "\n".join(lines)
def render_csv(resp: ScreeningResponse) -> str:
"""Flat CSV: one row per candidate, one column per score component."""
comp_names = ["delivery", "thermal_window", "surface_reactivity",
"self_limiting", "clean_ligand", "byproduct", "integration"]
buf = io.StringIO()
w = csv.writer(buf)
w.writerow(["rank", "name", "formula", "molecular_weight", "film_element",
"overall_score", *comp_names, "is_known_recipe", "warnings",
"recommended_next_step"])
for i, c in enumerate(resp.ranked_candidates, 1):
by = {comp.name: comp.score for comp in c.components}
w.writerow([i, c.name, c.formula or "", c.molecular_weight or "",
c.film_element or "", c.overall_score,
*[by.get(n, "") for n in comp_names],
c.is_known_recipe, " | ".join(c.warnings),
c.recommended_next_step])
return buf.getvalue()