pandeydigant31's picture
Phase 1 + Phase 2: rename Advanced/Raw Sweep, add Guide tab + per-tab taglines
cfac7e4 verified
Raw
History Blame Contribute Delete
6.61 kB
"""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],
)