Fill Resistance
Fill Resistance is our KiCad plugin (package id th.co.b4l.fill-resistance) for computing
the DC resistance of a net’s copper, zone fills, traces and pads, between two sets of contacts,
on a single layer or coupled across several. It grew out of needing a real number for what a
copper pour on a high-current board actually costs in milliohms, and where it heats up, which
KiCad has no built-in way to answer.
- Source: git.b4l.co.th/B4L/kicad-zone-resistance
- Background: blog post Fill Resistance: Seeing Where the Copper Actually Gets Hot
- Community: KiCad forum thread Resistance Plugin
Requirements
Section titled “Requirements”The plugin is built on KiCad’s IPC API (kicad-python / kipy), not the deprecated SWIG API,
so it needs KiCad 10.0.1 or newer with the API server enabled under Preferences, Plugins.
| Platform | Interpreter KiCad uses | Status |
|---|---|---|
| Windows | C:\Program Files\KiCad\10.0\bin\pythonw.exe |
Development and test platform |
| macOS | /Applications/KiCad/KiCad.app/Contents/Frameworks/Python.framework/Versions/Current/bin/python3 |
Audited but untested; reports welcome |
| Linux | first python3 on PATH (Python 3.9+ with the venv module) |
Audited but untested; reports welcome |
On ARM64 (aarch64) Linux there are no pyamg wheels, so requirements.txt skips it there and
the solver falls back to Jacobi-CG: same results, just slower.
Installation
Section titled “Installation”Install the PCM addon zip from the releases page (Plugin and Content Manager, Install from
File), or run from source (tools/deploy.py on Linux/macOS, deploy.ps1 on Windows; both
support a --copy/-Mode Copy production mode alongside the default dev symlink/junction).
Either way the plugin lands under Documents/KiCad/10.0/plugins (Windows/macOS) or
~/.local/share/kicad/10.0/plugins (Linux).
On first load, KiCad builds a private venv from requirements.txt (numpy, scipy,
matplotlib, PySide6), cached at:
- Windows:
%LOCALAPPDATA%\kicad\10.0\python-environments\th.co.b4l.fill-resistance - macOS:
~/Library/Caches/kicad/10.0/python-environments/th.co.b4l.fill-resistance - Linux:
~/.cache/kicad/10.0/python-environments/th.co.b4l.fill-resistance
The Omega toolbar button appears once that finishes. If the venv ever gets into a bad state, right-click the plugin’s row under Preferences, PCB Editor, Action Plugins and choose “Recreate Plugin Environment” (context menu only, there is no button); the manual equivalent is deleting the venv directory above and restarting KiCad.
Mark the V+ / V- injection terminals with rectangles on User.1 / User.2, or select pads and
vias directly (a selected pad/via group fills whichever side has no rectangles). The legacy path
is selecting exactly two contacts. Then hit the Omega toolbar button and pick net, layers, and
test current, with an optional cell size override. A typical real-board run finishes in about
8 seconds on our test setup; large multi-layer pours at fine cell sizes can take minutes.
Results save under <board dir>/fill_res_results/<timestamp>/:
| File | Contents |
|---|---|
1_raster_map.png |
Mesh overview, adaptive quadtree drawn on the copper |
2_potential.png |
Per-layer potential map with equipotential contours |
3_current_density.png |
Per-layer current density map |
4_power_density.png |
Per-layer power density (dissipation) map |
summary.txt |
Resistance, per-via currents, total dissipation at the test current |
geometry_dump.json |
Full solved geometry for downstream tooling |
Experimental: per-layer current-density heatmaps can be pushed back into the open board as
reference images on User.9..User.12 (the OVERLAY_LAYERS setting). Usability there is
still rough.
Model and limits
Section titled “Model and limits”Fill Resistance solves DC resistance only. It is not an AC impedance simulation: there is no inductance and no proximity effect in the model. The only AC effect is an optional skin-effect correction at a user-set frequency, using the exact 1D foil solution. Because the resistance-driven current distribution is the minimum-dissipation one, this correction is a rigorous lower bound on the real AC resistance, not an estimate of it. Rule of thumb for 70 um foil: skin effect is negligible below roughly 300 kHz and adds about 11% at 1 MHz.
A real probe or bus bar sits somewhere between an ideal bonded connection and a conductor merely
pressed on top, so the plugin solves both bounding cases (the CONTACT_MODEL setting):
equipotential (Dirichlet) and uniform injection. Requipotential ≤ Rreal
≤ Runiform.
Against real boards measured with a UT3513+ micro-ohm meter, computed values have come in within plus or minus 20% of measured, a gap attributed to test-setup imperfections (probe placement, probe contact resistance versus the plugin’s idealized contacts) and manufacturing tolerance on actual copper and plating thickness. Treat relative comparisons between layout variants as more trustworthy than any single absolute number.
Numerics
Section titled “Numerics”- Each copper layer is a finite-difference sheet solved with a standard 5-point stencil, coupled
to other layers through the net’s own via and through-hole-pad barrels (
VIA_PLATING_UM = 18by default, configurable). - The mesh is an adaptive 2:1-balanced quadtree by default (
ADAPTIVE_CELLS): fine at copper boundaries, electrodes, traces and via mouths, coarsening to cells as large asADAPTIVE_MAX_CELL_UM(1 mm default) in plane interiors, roughly 8 million fine cells where a uniform grid needs about 2 million. A deferred-correction pass (ADAPTIVE_CORRECTION_PASSES) removes the coarse-fine flux bias, bringing the adaptive result within 0.03% of the uniform-grid answer. - Traces narrower than
TRACK_1D_FACTOR(3) grid cells skip meshing entirely and are modeled as exact 1D resistor chains along their true centerline arc length, so thin traces carry no discretization error. - Holes get real geometric treatment: vias below
CAP_MAX_DRILL_MM(0.5 mm) are capped, larger ones open; populated THT pads model the full solder joint (lead, solder and barrel plating in parallel, a one-sided solder coat, the solder cone from aTHT_LEAD_PROTRUSION_MMprotruding lead); unpopulated pads model plating only; slotted and oval holes keep their true stadium shape. - Below 500,000 unknowns the sparse system solves directly; above that it uses an
AMG-preconditioned conjugate gradient solver (
pyamg), falling back to Jacobi-CG wherepyamgis unavailable. - The test suite pins the solver to exact analytic references (strip and annulus resistances, the acosh spreading resistance of two circular contacts on a sheet, skin-effect limits, power-balance identities) plus convergence and regression checks.
License and authorship
Section titled “License and authorship”GPL-3.0-or-later. Fill Resistance was developed with Anthropic’s Claude (Claude Code, model
Claude Fable 5), feature by feature, under human direction and review; most commits carry a
Co-Authored-By: Claude trailer. Full disclosure in the repo
README.