Solder coat only on the solder side of a THT joint
Build PCM package / build (push) Successful in 6s
Build PCM package / build (push) Successful in 6s
The pad face is wetted where the joint is: the protrusion side, opposite the component (already read from the owning footprint). The component-side pad face stays bare - contact_solder_buildups and tht_joint_buildups no longer coat both outer layers. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -89,10 +89,11 @@ SWIG API. Requires KiCad **10.0.1+**.
|
||||
**single-layer runs too** (drill mouths perforate a lone plane).
|
||||
THT-pad copper and drills remain outside the model, but every
|
||||
**populated THT pad** of the net carries its full **soldered
|
||||
joint**: a solder-filled barrel (SAC305 core in parallel with the
|
||||
plating), the average-thickness solder coat over a pad-diameter disc
|
||||
on the outer layers, and the protruding-lead cone on the side
|
||||
opposite its footprint (see barrel contacts below). Whether a hole
|
||||
joint** on its SOLDER side (opposite the component; the
|
||||
component-side pad face stays bare): a solder-filled barrel (SAC305
|
||||
core in parallel with the plating), the average-thickness solder
|
||||
coat over a pad-diameter disc, and the protruding-lead cone (see
|
||||
barrel contacts below). Whether a hole
|
||||
is a via or a THT pad, the owning footprint's side, and its **Do not
|
||||
populate** flag are all read from KiCad — DNP pads stay plating-only
|
||||
with no joint. At f > 0 the thickness scaling is applied
|
||||
@@ -130,12 +131,14 @@ SWIG API. Requires KiCad **10.0.1+**.
|
||||
of the result (both contact models; verified against
|
||||
R = ρ/(π·t)·acosh(d/2a) for two circular contacts on a sheet). A
|
||||
soldered **THT joint** additionally assumes the **hole is filled with
|
||||
solder** (core in parallel with the plating) and the **pad face
|
||||
carries an average-thickness solder coat** (`SOLDER_THICKNESS_UM`,
|
||||
50 µm) over the modeled copper under the pad shape. The **clipped
|
||||
lead protrudes** `THT_LEAD_PROTRUSION_MM` (1.5 mm, 0 = off) on the
|
||||
side opposite the component (taken from the owning footprint;
|
||||
assumed `B.Cu` if it cannot be found) and a **solder cone** wraps
|
||||
solder** (core in parallel with the plating) and the **pad face on
|
||||
the solder side carries an average-thickness solder coat**
|
||||
(`SOLDER_THICKNESS_UM`, 50 µm) over the modeled copper under the pad
|
||||
shape — the solder side is the side opposite the component (taken
|
||||
from the owning footprint; assumed `B.Cu` if it cannot be found),
|
||||
and the component-side pad face stays bare. There the **clipped
|
||||
lead protrudes** `THT_LEAD_PROTRUSION_MM` (1.5 mm, 0 = off)
|
||||
and a **solder cone** wraps
|
||||
it: full protrusion height at the drill wall, tapering linearly to
|
||||
zero at the pad edge, applied as extra conduction-equivalent copper
|
||||
per cell. The tall solder column at the wall pulls the joint
|
||||
|
||||
+27
-22
@@ -105,8 +105,10 @@ class Electrode:
|
||||
lead/wire soldered into the hole), so the contact cells are the
|
||||
copper ring at the drill wall, not the whole pad face. `solder`
|
||||
additionally models a soldered THT joint: the hole is filled with
|
||||
solder and the pad face carries an average-thickness solder coat
|
||||
(Problem.solder_thickness_nm over `polygons`)."""
|
||||
solder and the pad face on the SOLDER side (protrusion_side,
|
||||
opposite the component) carries an average-thickness solder coat
|
||||
(Problem.solder_thickness_nm over `polygons`) plus the
|
||||
protruding-lead cone."""
|
||||
rect: Rect # bounding box (labels/summary)
|
||||
contact: str = "all"
|
||||
polygons: list[Polygon] | None = None
|
||||
@@ -217,21 +219,23 @@ class Problem:
|
||||
|
||||
|
||||
def contact_solder_buildups(problem: Problem) -> list[str]:
|
||||
"""Soldered THT-joint contacts: the pad face is covered in solder of
|
||||
average thickness solder_thickness_nm. Adds one SurfaceBuildup per
|
||||
outer layer for every `solder` electrode's pad shape (the buildup
|
||||
machinery intersects with actual copper at raster time). Returns the
|
||||
affected layer names. Called once when the problem is built."""
|
||||
"""Soldered THT-joint contacts: the pad face on the SOLDER side (the
|
||||
protrusion side, opposite the component - the component-side face
|
||||
stays bare) is covered in solder of average thickness
|
||||
solder_thickness_nm. Adds one SurfaceBuildup there for every
|
||||
`solder` electrode's pad shape (the buildup machinery intersects
|
||||
with actual copper at raster time). Returns the affected layer
|
||||
names. Called once when the problem is built."""
|
||||
included = {l.layer_name for l in problem.layers}
|
||||
outer = [n for n in ("F.Cu", "B.Cu") if n in included]
|
||||
touched = []
|
||||
for e in problem.electrodes1 + problem.electrodes2:
|
||||
if not e.solder or not e.polygons:
|
||||
if not e.solder or not e.polygons \
|
||||
or e.protrusion_side not in included:
|
||||
continue
|
||||
for name in outer:
|
||||
problem.buildups.append(
|
||||
SurfaceBuildup(layer_name=name, polygons=list(e.polygons)))
|
||||
touched.append(name)
|
||||
SurfaceBuildup(layer_name=e.protrusion_side,
|
||||
polygons=list(e.polygons)))
|
||||
touched.append(e.protrusion_side)
|
||||
return sorted(set(touched))
|
||||
|
||||
|
||||
@@ -245,25 +249,26 @@ def _disc_polygon(x_nm: float, y_nm: float, r_nm: float,
|
||||
|
||||
def tht_joint_buildups(problem: Problem) -> list[str]:
|
||||
"""Solder coat of the net's populated STITCHING through-hole pads
|
||||
(ViaLink kind 'pad' with solder_filled): one pad-diameter disc per
|
||||
outer layer - the exact pad shape is unknown for non-contact pads,
|
||||
and the coat intersects the modeled copper at raster time anyway.
|
||||
Contact pads are skipped: contact_solder_buildups already coats
|
||||
them with the exact pad shape. Returns the affected layer names."""
|
||||
(ViaLink kind 'pad' with solder_filled): one pad-diameter disc on
|
||||
the pad's SOLDER side (the protrusion side, opposite the component;
|
||||
the component-side face stays bare). The exact pad shape is unknown
|
||||
for non-contact pads, and the coat intersects the modeled copper at
|
||||
raster time anyway. Contact pads are skipped:
|
||||
contact_solder_buildups already coats them with the exact pad
|
||||
shape. Returns the affected layer names."""
|
||||
included = {l.layer_name for l in problem.layers}
|
||||
outer = [n for n in ("F.Cu", "B.Cu") if n in included]
|
||||
contacts = {e.center for e in problem.electrodes1 + problem.electrodes2
|
||||
if e.drill_nm > 0 and e.center is not None}
|
||||
touched = []
|
||||
for v in problem.vias:
|
||||
if v.kind != "pad" or not v.solder_filled \
|
||||
or v.pad_nm <= v.drill_nm or (v.x, v.y) in contacts:
|
||||
or v.pad_nm <= v.drill_nm or (v.x, v.y) in contacts \
|
||||
or v.protrusion_side not in included:
|
||||
continue
|
||||
disc = _disc_polygon(v.x, v.y, v.pad_nm / 2.0)
|
||||
for name in outer:
|
||||
problem.buildups.append(
|
||||
SurfaceBuildup(layer_name=name, polygons=[disc]))
|
||||
touched.append(name)
|
||||
SurfaceBuildup(layer_name=v.protrusion_side, polygons=[disc]))
|
||||
touched.append(v.protrusion_side)
|
||||
return sorted(set(touched))
|
||||
|
||||
|
||||
|
||||
@@ -125,15 +125,21 @@ def test_solder_filled_barrel_resistance():
|
||||
|
||||
def test_contact_solder_coat():
|
||||
"""A soldered THT contact adds an average-thickness solder buildup
|
||||
over the pad face on the outer layers, lowering the spreading
|
||||
resistance vs the bare barrel contact."""
|
||||
over the pad face on its SOLDER side only (opposite the component),
|
||||
lowering the spreading resistance vs the bare barrel contact."""
|
||||
def prob():
|
||||
p = make_problem([(PLATE20, [])],
|
||||
rect1_mm=(0, 0, 1, 20), rect2_mm=(19, 0, 20, 20))
|
||||
p.electrodes1 = [_barrel(10, 10, drill_mm=1.0, pad_mm=2.4,
|
||||
solder=True, polygons=[_disc(10, 10, 1.2)])]
|
||||
p.electrodes1[0].protrusion_side = "F.Cu"
|
||||
return p
|
||||
|
||||
# solder side not among the included layers -> no coat there
|
||||
q = prob()
|
||||
q.electrodes1[0].protrusion_side = "B.Cu"
|
||||
assert contact_solder_buildups(q) == []
|
||||
|
||||
p = prob()
|
||||
assert contact_solder_buildups(p) == ["F.Cu"]
|
||||
assert len(p.buildups) == 1 and p.buildups[0].layer_name == "F.Cu"
|
||||
|
||||
Reference in New Issue
Block a user