Update simulation preset and UI template

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-02-23 08:16:17 -06:00
parent 6e4ffce49b
commit 30b8c55d3c
2 changed files with 64 additions and 10 deletions

View File

@@ -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 @@
<tbody id="sweep-table-body"></tbody>
</table>
</div>
<div style="padding:6px 14px 10px;font-size:11px;color:#64748b;display:flex;gap:16px;align-items:center">
<span>P_out shading:</span>
<span><span style="display:inline-block;width:12px;height:12px;background:#dcfce7;border:1px solid #86efac;vertical-align:middle;margin-right:4px"></span>Met target</span>
<span><span style="display:inline-block;width:12px;height:12px;background:#fef9c3;border:1px solid #d4b800;vertical-align:middle;margin-right:4px"></span>Near limit (&gt;90%)</span>
<span><span style="display:inline-block;width:12px;height:12px;background:#fed7aa;border:1px solid #c2590a;vertical-align:middle;margin-right:4px"></span>Over limit</span>
<span style="color:#94a3b8">Near/over applies to: B, Vs, Ip, Is, P_out</span>
</div>
</div>
</div><!-- /right-col -->
@@ -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 '<span class="cell-null">—</span>';
if (key === 'met_target') {
return val
? '<span class="cell-ok">YES</span>'
: '<span class="cell-bad">NO</span>';
}
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 = '<tr>' + _TABLE_COLS.map(c => `<th>${c.label}</th>`).join('') + '</tr>';
tbody.innerHTML = rows.map(r =>
'<tr>' + _TABLE_COLS.map(c => `<td>${fmtCell(c.key, r[c.key], r)}</td>`).join('') + '</tr>'
).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 `<td${cls}>${fmtCell(c.key, r[c.key], r)}</td>`;
});
return '<tr>' + cells.join('') + '</tr>';
}).join('');
}
function triggerCsvDownload() {