Spaces:
Sleeping
Sleeping
| """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 = ( | |
| '<div class="metric-row">' | |
| + metric_html("BASELINE", f"{baseline_val:.4f}") | |
| + metric_html("OPTIMIZED", f"{opt_val_scalar:.4f}", "", "green") | |
| + metric_html( | |
| "IMPROVEMENT", | |
| f"{improvement:+.1f}", | |
| "%", | |
| "green" if improvement > 0 else "amber", | |
| ) | |
| + metric_html( | |
| "CONSTRAINTS", | |
| "OK" if opt.constraints_satisfied else "VIOLATED", | |
| "", | |
| status_accent, | |
| ) | |
| + "</div>" | |
| + '<div class="metric-row">' | |
| + metric_html("EVALUATIONS", f"{opt.n_evals}") | |
| + metric_html("CONVERGED", "YES" if opt.converged else "NO") | |
| + "</div>" | |
| ) | |
| return metrics, table_df | |
| except Exception: | |
| err = traceback.format_exc() | |
| err_html = ( | |
| f'<div style="color:#c94a4a;font-family:JetBrains Mono,monospace;' | |
| f'font-size:12px;white-space:pre-wrap;">' | |
| f"Optimizer failed:\n{err}</div>" | |
| ) | |
| return err_html, pd.DataFrame() | |
| # -- Builder ----------------------------------------------------------------- | |
| def build_optimizer_tab(): | |
| """Create all Gradio components for the Optimizer tab and wire events. | |
| Must be called inside an active ``gr.TabItem(...)`` context manager. | |
| """ | |
| with gr.Row(): | |
| # -- Left panel -- controls ------------------------------------------- | |
| with gr.Column(scale=1, min_width=280): | |
| gr.HTML(engine_banner("ENGINE: EULER (optimizer default)")) | |
| hw_dd = gr.Dropdown( | |
| choices=["old_icv", "new_icv"], | |
| value="old_icv", | |
| label="Hardware Config", | |
| ) | |
| tgt_dd = gr.Dropdown( | |
| choices=["max_mdot", "max_mass_eff", "min_kWh", "min_Tc_peak"], | |
| value="max_mdot", | |
| label="Objective", | |
| ) | |
| pexit_sl = gr.Slider( | |
| minimum=50, maximum=900, value=500, step=10, | |
| label="Exit Pressure [barg]", | |
| ) | |
| speed_sl = gr.Slider( | |
| minimum=0.1, maximum=1.0, value=0.65, step=0.05, | |
| label="Speed Fraction", | |
| ) | |
| maxiter_sl = gr.Slider( | |
| minimum=3, maximum=50, value=10, step=1, | |
| label="Max Iterations", | |
| ) | |
| gr.Markdown("**Constraint (optional):**") | |
| c_metric_dd = gr.Dropdown( | |
| choices=["", "Tc_peak_K", "mass_eff", "kWh_extend", "pc_peak_barg"], | |
| value="", | |
| label="Metric", | |
| ) | |
| c_op_dd = gr.Dropdown( | |
| choices=["<", ">", "<=", ">="], | |
| value="<", | |
| label="Operator", | |
| ) | |
| c_val_num = gr.Number(value=200, label="Threshold") | |
| run_btn = gr.Button("OPTIMIZE", variant="primary") | |
| # -- Right panel -- results ------------------------------------------- | |
| with gr.Column(scale=3): | |
| metrics_out = gr.HTML(label="Metrics") | |
| table_out = gr.Dataframe(label="Parameter Changes") | |
| # -- Wire click event ----------------------------------------------------- | |
| run_btn.click( | |
| fn=run_optimize, | |
| inputs=[ | |
| hw_dd, tgt_dd, pexit_sl, speed_sl, maxiter_sl, | |
| c_metric_dd, c_op_dd, c_val_num, | |
| ], | |
| outputs=[metrics_out, table_out], | |
| ) | |