Multiple Thevenin supplies and prescribed-current loads on one net,
solved in absolute volts with the Tellegen power balance verified per
run; a source-sink pair table (effective copper resistance per
supply x load pair plus an exactly-summing proportional-sharing loss
attribution), in summary.txt and as its own figure. Bonded terminals
short a package's contacts into one lug so the per-pin split becomes
a solve outcome. Geometry dumps carry the terminal set (schema v8).
The dialog gained a Classic/PDN mode selector and a full PDN editor:
per-role supply/load tables built from the marker rectangles (or a
config's terminal set, which never pins mode or net), with Component
hints, per-terminal Layer scopes, Active checkboxes, comments, a
per-net row filter, resizable tables and a scrolling, screen-sized
dialog. Numbers accept SI suffixes (50m, 4.7k) everywhere.
fill_res_config.json fully specifies a run (classic or PDN) with
validation, comments, named side-by-side configs (the one called
default auto-loads), Load/Save buttons with an editable file name,
and saves that never drop anything drawn on the board.
347 tests, green on Python 3.13 and on the 3.9 macOS wheel stack.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
On OK the dialog closed and nothing appeared until the figures did,
which on a real board is minutes of looking like the plugin did
nothing. Put a small always-on-top window up for that stretch: the
stage now running, elapsed seconds, and Cancel.
Qt only repaints while the event loop runs and the solve owns the
thread, so the window pumps events itself - from inside the CG/AMG
iteration callback, which is where the time actually goes. That is
also where Cancel is noticed. The state is module-level because the
tick happens several frames deep in scipy/pyamg, and threading a
handle through those signatures for a progress bar is not worth it;
it stays inert until start(), so the standalone runner and the tests
are unaffected.
The window covers the figure work too, not just the solve: laying out
labels and writing four PNGs at full DPI is seconds on a real board -
10-15 of them on a large one - and closing before that left the same
silent gap one step later.
- adaptive: barrel links could attach to coarse leaves (up to 1 mm),
making the whole leaf equipotential and deleting the local spreading
resistance - via-field results read up to ~13% low. Barrel attachment
cells are now pinned into the keep-fine set before the quadtree is
built; the guard ring grades around them.
- skin/dialog: the decimal-comma rewrite turned '1,500' into 1.5, a
silent 1000x error in frequency, test current or cell size. Ambiguous
comma patterns (thousands separators, multiple commas, mixed with a
dot) now raise with a message; a real decimal comma ('1,5') still
parses.
- dialog: Selection.adaptive default now matches the documented
on-by-default; the adaptive accuracy claim is aligned to the measured
0.03% quoted in README and config.
- adaptive: skip layers with zero quadtree leaves in the connectivity
restriction and mesh-boundary loops (IndexError on boards where a
selected layer has no copper)
- board_io: accept ZT_TEARDROP zones as conducting copper (KiCad types
teardrop fills ZT_TEARDROP, never ZT_COPPER, so they were dropped)
- geometry: copper_bbox uses the exact stroke bbox (centerline extrema
+ half width) instead of a 100 um chord tessellation that could
undershoot arc/cap extrema past the raster guard margin
- solver/adaptive: reference |J| to the conduction-equivalent thickness
sigma*rho in every branch (the uniform branch used geometric t, so AC
plots changed scale ~rs_ratio depending on unrelated per-cell maps)
- solver/adaptive/raster: chain cells no longer show phantom sheet-face
currents; store dl per chain link and overlay the true 1D density
|dV|/(rho*dl) (exact at any frequency: AC scaling of link conductance
and cross-section cancels)
- test_quadtree: compare edge lists pair-for-pair (the independent
column sort destroyed endpoint association)
Two display fixes for the adaptive grid, from field feedback:
- Equipotential contours showed leaf-sized staircase corners on plane
interiors: the potential is now expanded piecewise-LINEARLY from each
leaf's reconstructed gradient instead of constant-per-leaf, and the
default ADAPTIVE_MAX_CELL_UM drops 2 mm -> 1 mm (interior leaves
beyond that buy almost nothing).
- The raster map now overlays the adaptive mesh: boundaries of coarse
leaves draw in darker copper (fine regions stay plain = fully
resolved), with a legend entry. Uniform-grid runs are unchanged.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two-point fluxes across coarse-fine faces miss the tangential potential
gradient (offset leaf centers), biasing R ~0.5-2% low. After the first
solve, per-leaf gradients are reconstructed by least squares over face
neighbors and the known tangential term g*delta*Gt moves to the
right-hand side of a re-solve (ADAPTIVE_CORRECTION_PASSES, default 1).
The matrix is unchanged, so the new PreparedSolver reuses the LU
factorization / AMG hierarchy across passes; the corrected currents
satisfy KCL exactly, so the power-balance identity, via currents and
part fluxes all use them consistently (edge power = dV * I_corr).
Measured: strip worst case -1.74% -> -0.028% (1 pass); feature-dense
plate end-to-end -1.1% -> -0.011% at 7.2 s vs 25.9 s uniform (5.58M ->
823k unknowns). Tests tightened accordingly plus a passes-knob test.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
config.ADAPTIVE_CELLS (dialog checkbox "adaptive cells", off by
default; standalone --adaptive) routes run_solve through
fill_resistance/adaptive.py: per-layer balanced leaf grids where every
non-uniform fine cell (electrodes, 1D chain cells, buildup, via-mouth
thickness map) is pinned at the fine size, leaf faces via the
series-half-cell rule, chain links and barrels re-attached by node id,
connectivity restriction and both contact models on the leaf graph via
solver cores extracted for reuse (_equipotential_core, _uniform_core,
_conductance_params, _barrel_links). All fields (V, |J|, power density)
are computed per leaf and expanded to the fine grid, so plots, summary
and dumps are unchanged.
Element sizes: minimum = the grid cell size itself (auto / dialog /
CELL_UM_OVERRIDE); maximum = ADAPTIVE_MAX_CELL_UM (2 mm default);
ADAPTIVE_GUARD sets the clearance a block needs to grow.
Measured end-to-end (feature-dense 120x120 plate, h=50um): 25.9 s ->
5.4 s, 5.58M -> 823k unknowns, R -1.1%. Accuracy documented honestly:
coarse-fine interfaces carry a first-order tangential flux error
biasing R low by ~0.5-2% depending on geometry (worst on narrow
strips); the earlier assumption that linear fields solve exactly on the
leaf graph was wrong - offset centers across size transitions leave an
unpaired residue. Gradient-corrected interface fluxes remain as phase 4
if tighter accuracy per leaf is needed.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>