Skip to content

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.

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.

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.

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.

  • 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 = 18 by 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 as ADAPTIVE_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 a THT_LEAD_PROTRUSION_MM protruding 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 where pyamg is 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.

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.