"""Structure-viewer payloads (section 13 'interactive 3D scientific universe'). Visualization is kept separate from evidence: the payload carries the evidence state as explicit text labels; colours encode elements only.""" from __future__ import annotations from typing import Optional from ase.data import atomic_numbers, covalent_radii from ase.data.colors import jmol_colors from core.representation import MatterStructure from core.schemas import MATURITY_LABELS def _hex(sym: str) -> str: r, g, b = jmol_colors[atomic_numbers[sym]] return "#%02x%02x%02x" % (int(r * 255), int(g * 255), int(b * 255)) def structure_payload(app, candidate_id: str, run_id: Optional[str] = None) -> dict: c = app.repo.get_candidate(candidate_id) ms = MatterStructure.from_candidate(c) trajectory = None forces = None if run_id: run = app.repo.get_run(run_id) from evidence.replication import _final_structure fin = _final_structure(app, run) if fin is not None and fin.n_atoms == ms.n_atoms: trajectory = [ms.positions.tolist(), fin.positions.tolist()] f = run.observables.get("forces") if f: forces = f["value"] g = ms.graph() claims = app.repo.list_claims(candidate_id) return { "candidate_id": candidate_id, "formula": ms.formula(), "atoms": [{"symbol": s, "position": p.tolist(), "color": _hex(s), "radius": float(covalent_radii[atomic_numbers[s]])} for s, p in zip(ms.symbols, ms.positions)], "bonds": [{"a": e["source"], "b": e["target"], "type": e.get("type"), "confidence": e.get("confidence")} for e in g["edges"]], "cell": None if ms.cell is None else ms.cell.tolist(), "periodicity": list(ms.pbc), "overlays": {"graph": {"method": g["method"], "note": g.get("note")}, "symmetry": ms.symmetry(), "fields": ms.fields, "forces": forces, "uncertainty": [u.model_dump() for u in c.uncertainty]}, "trajectory": trajectory, "lineage": {"parents": c.parent_ids, "events": c.lineage, "generation_method": c.generation_method}, "evidence_state": {"maturity": c.maturity, "label": MATURITY_LABELS.get(c.maturity), "validation_status": c.validation_status, "claims": [{"claim_id": cl.claim_id, "maturity": cl.maturity_level, "strength": cl.claim_strength} for cl in claims], "disclaimer": "Rendering style does not imply validation. Evidence state is stated in text only."}, }