"""Tab 3 -- Constraint-aware hardware optimizer via cryosim. Builds all Gradio components (left control panel + right results panel) and wires up the click handler. Must be called inside an active ``gr.TabItem`` context. """ from __future__ import annotations import traceback import gradio as gr import pandas as pd import cryosim from cryosim.calibration.params import PARAM_NAMES, get_nominal_values from murphy_unified.theme import metric_html, engine_banner # -- Helpers ----------------------------------------------------------------- def _compute_improvement(target: str, base: float, opt: float) -> tuple[float, bool]: """Return (signed_improvement_pct, is_better). For max_ targets (higher = better), improvement = (opt/base - 1)*100. For min_ targets (lower = better), improvement = (1 - opt/base)*100. Returns (0.0, False) when baseline is 0 (degenerate). """ if base == 0: return 0.0, False pct = (opt / base - 1) * 100.0 is_min = target.startswith("min_") signed = -pct if is_min else pct return signed, signed > 0 # -- Click handler ----------------------------------------------------------- def run_optimize( hardware: str, target: str, Pexit: float, speed: float, maxiter: float, c_metric: str, c_op: str, c_val: float, ): """Run constrained optimisation and return (metrics_html, table_df).""" try: # Build constraints dict constraints: dict[str, str] = {} if c_metric: constraints[c_metric] = f"{c_op}{c_val}" # Baseline prediction baseline = cryosim.predict(hardware=hardware, Pexit=Pexit, speed=speed) # Optimise opt = cryosim.optimize( hardware=hardware, target=target, speed=speed, Pexit=Pexit, constraints=constraints, maxiter=int(maxiter), ) # Parameter comparison table nominal = get_nominal_values(hardware) rows = [] for name, nom, opt_val in zip(PARAM_NAMES, nominal, opt.optimal_values): pct = (opt_val / nom - 1) * 100 if abs(nom) > 1e-12 else 0 rows.append({ "Parameter": name, "Nominal": f"{nom:.6f}", "Optimized": f"{opt_val:.6f}", "Change %": f"{pct:+.1f}%", }) table_df = pd.DataFrame(rows) # Metric cards status_accent = "green" if opt.constraints_satisfied else "red" # Baseline extraction: the baseline struct carries attrs per-target. _BASE_ATTRS = { "max_mdot": "mdot_kgpm", "min_kWh": "kWh_extend", "min_Tc_peak": "Tc_peak_K", "max_mass_eff": "mass_eff", } base_attr = _BASE_ATTRS.get(target, "mdot_kgpm") baseline_val = getattr(baseline, base_attr, 0) or 0 opt_val_scalar = opt.target_value improvement, is_better = _compute_improvement( target, float(baseline_val), float(opt_val_scalar) ) metrics = ( '