Model SMD pad copper: exact net pad shapes stamped on their layers
Build PCM package / build (push) Successful in 6s
Build PCM package / build (push) Successful in 6s
Pads are the junctions where traces and thermal-relief spokes actually meet; without their copper a multi-track junction necks down to the accidental overlap of the rounded track ends, or is severed outright. gather_smd_pad_copper fetches the exact shape of every undrilled pad on the net (one API call per pad, layer from the padstack) and build_problem stamps them onto their own layer (INCLUDE_SMD_PADS). Selected SMD-pad contacts get their real copper as a side effect (previously electrode-shape intersected whatever lay underneath). Dead-end pads become floating islands that the existing connectivity restriction drops. Closes the documented SMD-pad-copper limitation (prompted by the padne comparison). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -563,6 +563,29 @@ def gather_barrels(board: Board, net_name: str,
|
||||
return barrels
|
||||
|
||||
|
||||
def gather_smd_pad_copper(board: Board, net_name: str
|
||||
) -> dict[str, list[Polygon]]:
|
||||
"""layer name -> exact copper shape(s) of every SMD (undrilled) pad
|
||||
on the net. Pads are junctions: traces and thermal-relief spokes
|
||||
meet ON the pad copper, and without it the junction necks down to
|
||||
the accidental overlap of the track ends - or is severed outright.
|
||||
Dead-end pads (component terminals) become floating islands that
|
||||
the solver's connectivity restriction drops. One API call per pad;
|
||||
pads whose copper layer cannot be determined are skipped."""
|
||||
shapes: dict[str, list[Polygon]] = {}
|
||||
for pad in board.get_pads():
|
||||
if pad.net is None or pad.net.name != net_name \
|
||||
or _pad_drill_nm(pad) > 0:
|
||||
continue
|
||||
layer = _pad_default_contact(pad) # SMD: its own copper layer
|
||||
if layer == "all":
|
||||
continue
|
||||
polys = _pad_polygons(board, pad, layer)
|
||||
if polys:
|
||||
shapes.setdefault(layer, []).extend(polys)
|
||||
return shapes
|
||||
|
||||
|
||||
def gather_tht_pad_copper(board: Board, net_name: str
|
||||
) -> dict[tuple[int, int], list[Polygon]]:
|
||||
"""(x, y) -> exact copper shape(s) of every drilled (THT) pad on the
|
||||
@@ -629,6 +652,19 @@ def build_problem(board: Board, net: str, layer_names: list[str],
|
||||
layer.polygons = list(layer.polygons) + extra
|
||||
print(f"{len(pad_shapes)} THT pad shape(s) stamped on every "
|
||||
f"included layer")
|
||||
# SMD pad copper too: pads are the junctions where traces/spokes
|
||||
# meet (also gives selected SMD-pad contacts their real copper)
|
||||
smd_shapes = (gather_smd_pad_copper(board, net)
|
||||
if config.INCLUDE_SMD_PADS else {})
|
||||
if smd_shapes:
|
||||
n = 0
|
||||
for layer in layers:
|
||||
polys = smd_shapes.get(layer.layer_name, [])
|
||||
if polys:
|
||||
layer.polygons = list(layer.polygons) + polys
|
||||
n += len(polys)
|
||||
if n:
|
||||
print(f"{n} SMD pad shape(s) stamped on their layers")
|
||||
included = {l.layer_name for l in layers}
|
||||
buildup_list = [
|
||||
SurfaceBuildup(layer_name=name, polygons=polys)
|
||||
|
||||
@@ -31,6 +31,11 @@ CAP_PLATING_UM = 15.0 # cap plating thickness (fab spec)
|
||||
CAP_MAX_DRILL_MM = 0.5 # fab caps only small vias: drills above this
|
||||
# stay open even with VIAS_CAPPED
|
||||
# (dialog-settable)
|
||||
INCLUDE_SMD_PADS = True # the net's SMD pad copper conducts too (exact
|
||||
# shapes on the pad's layer): pads are the
|
||||
# junctions where traces/spokes meet, and
|
||||
# selected pad contacts get their real copper.
|
||||
# Dead-end pads are dropped as floating islands
|
||||
INCLUDE_TH_PADS = True # plated through-hole pads stitch layers too;
|
||||
# their holes are modeled solder-filled (a
|
||||
# soldered component lead), so the solder core
|
||||
|
||||
Reference in New Issue
Block a user