Initial import: KiCad zone resistance plugin
DC/AC resistance, power dissipation, and via/injection-area currents of copper zone fills. KiCad 10 IPC-API plugin (kicad-python/kipy): multi-layer via-coupled FDM solver, multi-part terminals via User.1/User.2 marker layers, pads as contacts, uniform-injection and equipotential contact models, per-foil skin effect, optional solder/copper buildup on mask openings. 54-case test suite incl. exact analytic references. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
import json
|
||||
import math
|
||||
|
||||
import numpy as np
|
||||
|
||||
from fill_resistance.geometry import (arc_points, linearize_ring,
|
||||
load_problem, problem_from_json,
|
||||
save_problem)
|
||||
from tests.util import make_multilayer, strip_problem
|
||||
|
||||
|
||||
def test_arc_points_quarter_circle():
|
||||
r = 10_000_000 # 10 mm in nm
|
||||
start = (r, 0)
|
||||
mid = (int(r / math.sqrt(2)), int(r / math.sqrt(2)))
|
||||
end = (0, r)
|
||||
tol = 50_000 # 50 um
|
||||
pts = arc_points(start, mid, end, tol)
|
||||
assert len(pts) >= 3
|
||||
radii = np.hypot(pts[:, 0].astype(float), pts[:, 1].astype(float))
|
||||
assert np.allclose(radii, r, rtol=1e-6)
|
||||
allpts = np.vstack([pts, [end]]).astype(float)
|
||||
for a, b in zip(allpts[:-1], allpts[1:]):
|
||||
half_chord = np.hypot(*(b - a)) / 2
|
||||
sagitta = r - math.sqrt(max(r**2 - half_chord**2, 0.0))
|
||||
assert sagitta <= tol * 1.01
|
||||
|
||||
|
||||
def test_arc_points_collinear_degrades_to_segment():
|
||||
pts = arc_points((0, 0), (5_000_000, 0), (10_000_000, 0), 1000)
|
||||
assert len(pts) == 1
|
||||
assert tuple(pts[0]) == (0, 0)
|
||||
|
||||
|
||||
def test_linearize_ring_mixed_nodes_and_closure():
|
||||
nodes = [
|
||||
("pt", (0, 0)),
|
||||
("pt", (10, 0)),
|
||||
("arc", ((10, 0), (17, 7), (10, 14))),
|
||||
("pt", (0, 14)),
|
||||
("pt", (0, 0)),
|
||||
]
|
||||
ring = linearize_ring(nodes, tol_nm=1)
|
||||
assert (ring[0] != ring[-1]).any()
|
||||
assert len(ring) > 4
|
||||
|
||||
|
||||
def test_problem_json_roundtrip_v2(tmp_path):
|
||||
p = make_multilayer(
|
||||
[[([(0, 0), (10, 0), (10, 1), (0, 1)], [])],
|
||||
[([(0, 0), (10, 0), (10, 1), (0, 1)], [])]],
|
||||
rect1_mm=(0, 0, 1, 1), rect2_mm=(9, 0, 10, 1),
|
||||
contact1="L0", contact2="L1", vias_mm=[(5.5, 0.5)])
|
||||
f = tmp_path / "dump.json"
|
||||
save_problem(p, f)
|
||||
q = load_problem(f)
|
||||
assert q.layer_names == ["L0", "L1"]
|
||||
assert q.electrodes1[0].contact == "L0"
|
||||
assert q.electrodes2[0].contact == "L1"
|
||||
assert len(q.vias) == 1 and q.vias[0].drill_nm == p.vias[0].drill_nm
|
||||
assert q.plating_nm == p.plating_nm
|
||||
assert np.array_equal(q.layers[0].polygons[0].outline,
|
||||
p.layers[0].polygons[0].outline)
|
||||
assert abs(q.sigma_s(0) - p.sigma_s(0)) < 1e-12 * p.sigma_s(0)
|
||||
|
||||
|
||||
def test_v1_schema_still_loads():
|
||||
p = strip_problem()
|
||||
v1 = {
|
||||
"schema_version": 1,
|
||||
"board_path": "old",
|
||||
"layer_name": "F.Cu",
|
||||
"net_name": "GND",
|
||||
"thickness_nm": 70000,
|
||||
"thickness_source": "stackup",
|
||||
"rho_ohm_m": 1.68e-8,
|
||||
"rect1": {"x0": 0, "y0": 0, "x1": 1000, "y1": 1000,
|
||||
"layer_name": "User.1"},
|
||||
"rect2": {"x0": 5000, "y0": 0, "x1": 6000, "y1": 1000,
|
||||
"layer_name": "User.1"},
|
||||
"polygons": [{"outline": p.layers[0].polygons[0].outline.tolist(),
|
||||
"holes": []}],
|
||||
}
|
||||
q = problem_from_json(json.loads(json.dumps(v1)))
|
||||
assert q.layer_names == ["F.Cu"]
|
||||
assert q.vias == []
|
||||
assert q.electrodes1[0].contact == "all"
|
||||
assert q.layers[0].thickness_nm == 70000
|
||||
Reference in New Issue
Block a user