From 30b8c55d3ce8dcc9333fe8403808e5390fb5f96f Mon Sep 17 00:00:00 2001 From: Brent Perteet Date: Mon, 23 Feb 2026 08:16:17 -0600 Subject: [PATCH] Update simulation preset and UI template MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- presets/sim.json | 2 +- templates/index.html | 72 ++++++++++++++++++++++++++++++++++++++------ 2 files changed, 64 insertions(+), 10 deletions(-) diff --git a/presets/sim.json b/presets/sim.json index b81391b..6ba4224 100644 --- a/presets/sim.json +++ b/presets/sim.json @@ -1,6 +1,6 @@ { "points": { - "frequencies": "256\n870\n3140\n8900\n12000\n16000\n22000\n33000\n45000", + "frequencies": "256\n512\n1024\n3140\n8900\n12000\n16000\n22000\n33000\n45000", "loads": "10\n50\n100\n200\n600\n2000", "target_power_W": 25, "power_tol_pct": 2, diff --git a/templates/index.html b/templates/index.html index 112428b..dc67f01 100644 --- a/templates/index.html +++ b/templates/index.html @@ -358,6 +358,10 @@ .sweep-table .cell-ok { color: #166534; font-weight: 600; } .sweep-table .cell-bad { color: #b91c1c; } .sweep-table .cell-null { color: #94a3b8; } + /* Constraint proximity highlights — override row stripes */ + .sweep-table td.cell-limit-met { background: #dcfce7 !important; } /* green — met target */ + .sweep-table td.cell-limit-near { background: #fef9c3 !important; } /* yellow — near limit */ + .sweep-table td.cell-limit-over { background: #fed7aa !important; } /* orange — over limit */ /* ---- Spinner ---- */ .spinner { @@ -616,6 +620,13 @@ +
+ P_out shading: + Met target + Near limit (>90%) + Over limit + Near/over applies to: B, Vs, Ip, Is, P_out +
@@ -1003,6 +1014,14 @@ async function runSweep() { } sweepData = data; + // Stash constraint limits so updateTable can shade near-limit cells + sweepData._constraints = { + B_max_T: parseFloat(document.getElementById('con-B').value) || Infinity, + Vs_max: parseFloat(document.getElementById('con-Vs').value) || Infinity, + Ip_max: parseFloat(document.getElementById('con-Ip').value) || Infinity, + Is_max: parseFloat(document.getElementById('con-Is').value) || Infinity, + P_out_max_W: parseFloat(document.getElementById('con-Pout').value) || Infinity, + }; setMsg(msg, 'ok', `Done — ${data.rows.length} operating points computed.`); // Populate frequency dropdown @@ -1187,7 +1206,6 @@ const _TABLE_COLS = [ { key: 'freq_hz', label: 'freq (Hz)' }, { key: 'Z_load_R', label: 'R (Ω)' }, { key: 'Z_load_X', label: 'X (Ω)' }, - { key: 'met_target', label: 'met' }, { key: 'primary_tap', label: 'P tap' }, { key: 'secondary_tap', label: 'S tap' }, { key: 'Vp_rms', label: 'Vp (V)' }, @@ -1214,11 +1232,6 @@ function fmtCell(key, val, row) { if (key === '_T_rise') { val = row ? row._T_rise : null; } if (key === '_T_copper') { val = row ? row._T_copper : null; } if (val === null || val === undefined) return '—'; - if (key === 'met_target') { - return val - ? 'YES' - : 'NO'; - } if (key === 'efficiency' && typeof val === 'number') { return (val * 100).toFixed(2) + '%'; } @@ -1228,6 +1241,35 @@ function fmtCell(key, val, row) { return val; } +/** + * Return an object mapping column key → 'over' | 'near' | null for a row, + * based on how close each value is to its constraint limit. + * + * 'over' = value exceeds the limit + * 'near' = value is within NEAR_THRESHOLD of the limit (ratio > 0.9) + * null = fine + */ +const _LIMIT_NEAR_THRESHOLD = 0.90; // flag cell when value/limit > this + +function limitingCells(row, constraints) { + if (!constraints) return {}; + const result = {}; + + function check(colKey, value, limit) { + if (value == null || !isFinite(limit) || limit <= 0) return; + const ratio = value / limit; + if (ratio > 1.0) result[colKey] = 'over'; + else if (ratio > _LIMIT_NEAR_THRESHOLD) result[colKey] = 'near'; + } + + check('B_peak_T', row.B_peak_T, constraints.B_max_T); + check('Vs_rms', row.Vs_rms, constraints.Vs_max); + check('Ip_rms', row.Ip_rms, constraints.Ip_max); + check('Is_rms', row.Is_rms, constraints.Is_max); + check('P_out_W', row.P_out_W, constraints.P_out_max_W); + return result; +} + function updateTable(rows, T_rise, T_copper) { document.getElementById('table-card').style.display = 'block'; @@ -1239,12 +1281,24 @@ function updateTable(rows, T_rise, T_copper) { const thead = document.getElementById('sweep-table-head'); const tbody = document.getElementById('sweep-table-body'); + const constraints = sweepData && sweepData._constraints; thead.innerHTML = '' + _TABLE_COLS.map(c => `${c.label}`).join('') + ''; - tbody.innerHTML = rows.map(r => - '' + _TABLE_COLS.map(c => `${fmtCell(c.key, r[c.key], r)}`).join('') + '' - ).join(''); + tbody.innerHTML = rows.map(r => { + const limits = limitingCells(r, constraints); + const cells = _TABLE_COLS.map(c => { + const lvl = limits[c.key]; + // P_out_W gets green when the optimizer met its target (and isn't near/over a limit) + const metGreen = c.key === 'P_out_W' && r.met_target && !lvl; + const cls = lvl === 'over' ? ' class="cell-limit-over"' + : lvl === 'near' ? ' class="cell-limit-near"' + : metGreen ? ' class="cell-limit-met"' + : ''; + return `${fmtCell(c.key, r[c.key], r)}`; + }); + return '' + cells.join('') + ''; + }).join(''); } function triggerCsvDownload() {