project
Adom Custom Electronic Load
Public Made by Adomby adom
Two-layer digitally controlled electronic-load prototype. Compact R11 is under active electrical, cooling and probe-access review; not a manufacturing release.
← Commit history
Record current-screen milestone, combined cooling study and physical-pad solver review
18 files changed
+2541−8
design/fields-physical-pad-selector-review.json+15design/hotspot-v77-evidence.json+63design/spec-sdt10a45p5-explicit-pads.json+445docs/build-clips.md+4docs/clips/current-screen-milestone-poster.jpgdocs/clips/current-screen-milestone.mp4docs/completion-plan.md+6−6docs/current-cooling-refinement.md+6docs/q1-land-pattern-variant.md+7kicad/lib/Load.pretty/TO-220-3_Horizontal_TabUp_Drain1p6.kicad_mod+788tools/hotspot_via_candidate_v80.py+247tools/hotspot_via_candidate_v81.py+283tools/hotspot_via_candidate_v82.py+313tools/hotspot_via_candidate_v83.py+315tools/powerdi5_model.py+25videos/build-storyboard.json+6videos/inventory.json+8−2videos/shotlog.json+10design/fields-physical-pad-selector-review.jsonadded+15@@ -0,0 +1,15 @@+{+ "status": "owner-review-open",+ "url": "https://wiki.adom.inc/adom/adom-fields/prs/10",+ "number": 10,+ "base": "e34bcddab94b3f14442bf511a8b6b35bcdb02237",+ "currentMainCompatibilityBase": "b53340e01ab53366b846f7062d01a014f0157ae9",+ "tests": {+ "stack": 35,+ "currentMainSelectorCompatibility": 19+ },+ "productionCheck": true,+ "releaseBuild": true,+ "sharedRuntimeReplaced": false,+ "limits": "Current-main compatibility only adds selector module; entire unmerged solver stack is not integrated with main."+}
design/hotspot-v77-evidence.jsonadded+63@@ -0,0 +1,63 @@+{+ "status": "isolated-not-landed",+ "candidate": "checkpoints/unclipped-hotspots-v1/candidate-v77.kicad_pcb",+ "boardSha256": "2846a4fb678f0bef014074ed78f82b6bcf894210ab2e9ca12eb02b8f8d7d2ae8",+ "nativeDRC": {+ "errors": 0,+ "warnings": 1,+ "opens": 0,+ "sha256": "781a74bbfeb558e6c2fbaa558b4e991f4d2508fe867a69ec2b100de3ec1087c4"+ },+ "solver": {+ "sha256": "202bb43774babdeb18f3e677090d559d01b9b62938bac8aa659326502f82f168",+ "sourceCommit": "e34bcddab94b3f14442bf511a8b6b35bcdb02237",+ "review": "https://wiki.adom.inc/adom/adom-fields/prs/9",+ "sharedRuntimeReplaced": false+ },+ "reports": [+ {+ "path": "checkpoints/unclipped-hotspots-v1/fields-v77-current-0p1/fields.json",+ "sha256": "fc241294a850ae7ce606d5bad069b412b944b1eb85a1b738fef23696f616fcb9",+ "inputIdentity": {+ "algorithm": "sha256",+ "board": "2846a4fb678f0bef014074ed78f82b6bcf894210ab2e9ca12eb02b8f8d7d2ae8",+ "solverVersion": "0.6.2",+ "spec": "9050e3a86363f4958a3792dc41f3fb6a1ffbf09dd74681b343ec3048180c32d0"+ },+ "nets": [+ {+ "net": "LOAD_POS",+ "peakJ": 56.0890007019043,+ "peakAt": [+ 127.14999999999999,+ 84.75+ ],+ "peakLayer": "F.Cu",+ "residual": 9.624989034310564e-08+ },+ {+ "net": "GND",+ "peakJ": 58.31138229370117,+ "peakAt": [+ 139.65,+ 89.85000000000001+ ],+ "peakLayer": "F.Cu",+ "residual": 9.9980831845474e-08+ },+ {+ "net": "SHUNT_P",+ "peakJ": 56.50494384765625,+ "peakAt": [+ 126.85,+ 83.14999999999999+ ],+ "peakLayer": "B.Cu",+ "residual": 9.528981022937705e-08+ }+ ],+ "chips": [],+ "issues": []+ }+ ]+}
design/spec-sdt10a45p5-explicit-pads.jsonadded+445@@ -0,0 +1,445 @@+{+ "board": "Adom electronic load R0, 64 x 72 mm, 2 layers; engineering prototype",+ "copperUm": 35,+ "clearance": 0.2,+ "edgeClearance": 0.2,+ "fixedRefs": [+ "MP1",+ "MP2",+ "MP3",+ "MP4",+ "J1",+ "J2",+ "J3",+ "J4",+ "J5",+ "J6",+ "J7"+ ],+ "wideNets": {+ "DUT_POS": [+ 2.0,+ 1.5,+ 1.2+ ],+ "DUT_FUSED": [+ 2.0,+ 1.5,+ 1.2+ ],+ "LOAD_POS": [+ 2.0,+ 1.5,+ 1.2+ ],+ "SHUNT_P": [+ 2.0,+ 1.5,+ 1.2+ ],+ "GND": [+ 2.0,+ 1.5,+ 1.2+ ]+ },+ "midNets": {+ "5V_AUX": [+ 0.5,+ 0.4+ ],+ "V_GATE": [+ 0.3,+ 0.25+ ]+ },+ "kelvin": {+ "U4.10": "RSH1.2",+ "U4.9": "RSH1.3"+ },+ "chains": [+ {+ "refs": [+ "R8",+ "R9",+ "R10"+ ],+ "direction": "down"+ },+ {+ "refs": [+ "R18",+ "R19"+ ],+ "direction": "down"+ },+ {+ "refs": [+ "R22",+ "R23"+ ],+ "direction": "down"+ },+ {+ "refs": [+ "R27",+ "R28"+ ],+ "direction": "down"+ },+ {+ "refs": [+ "R35",+ "R36",+ "R37"+ ],+ "direction": "down"+ }+ ],+ "loads": {+ "DUT_POS": {+ "amps": 3.000220385674931,+ "maxRiseC": 10,+ "terminals": [+ {+ "reference": "J1",+ "pad": "1",+ "amps": 3.000220385674931+ },+ {+ "reference": "F1",+ "pad": "1",+ "amps": -3+ },+ {+ "reference": "R24",+ "pad": "1",+ "amps": -0.00022038567493112948+ },+ {+ "reference": "TP1",+ "pad": "1",+ "amps": 0+ }+ ],+ "currentModel": "resistive-branches"+ },+ "DUT_FUSED": {+ "amps": 3,+ "maxRiseC": 10,+ "currentModel": "resistive-branches",+ "terminals": [+ {+ "reference": "F1",+ "pad": "2",+ "amps": 3+ },+ {+ "reference": "D1",+ "pad": "2",+ "at": [+ 111.138,+ 90.08+ ],+ "amps": -1.5+ },+ {+ "reference": "D1",+ "pad": "2",+ "at": [+ 111.138,+ 91.92+ ],+ "amps": -1.5+ }+ ]+ },+ "LOAD_POS": {+ "amps": 3.0000289156626505,+ "maxRiseC": 10,+ "terminals": [+ {+ "reference": "D1",+ "pad": "1",+ "amps": 3.0000289156626505+ },+ {+ "reference": "Q1",+ "pad": "2",+ "amps": -3+ },+ {+ "reference": "U4",+ "pad": "8",+ "amps": -2.891566265060241e-05+ }+ ]+ },+ "SHUNT_P": {+ "amps": 3,+ "maxRiseC": 10,+ "terminals": [+ {+ "reference": "Q1",+ "pad": "3",+ "amps": 3+ },+ {+ "reference": "RSH1",+ "pad": "1",+ "amps": -3+ }+ ]+ },+ "GND": {+ "amps": 3,+ "maxRiseC": 10,+ "terminals": [+ {+ "reference": "RSH1",+ "pad": "4",+ "amps": 3+ },+ {+ "reference": "J2",+ "pad": "1",+ "amps": -3+ }+ ]+ },+ "5V_AUX": {+ "amps": 0.3,+ "maxRiseC": 10+ }+ },+ "hot": {+ "D1": {+ "watts": 0.5744444444444444,+ "tabNet": "LOAD_POS"+ },+ "RSH1": {+ "watts": 0.030025925925925928,+ "tabNet": "SHUNT_P"+ }+ },+ "hotMaxRiseC": 35,+ "pours": [+ {+ "net": "GND",+ "layers": [+ "B.Cu"+ ],+ "priority": 0,+ "connection": "solid",+ "name": "Power return and signal reference",+ "polygon": [+ [+ 98.3,+ 30.3+ ],+ [+ 161.7,+ 30.3+ ],+ [+ 161.7,+ 101.7+ ],+ [+ 98.3,+ 101.7+ ]+ ]+ },+ {+ "net": "GND",+ "layers": [+ "F.Cu"+ ],+ "priority": 0,+ "connection": "solid",+ "name": "Top return and shield",+ "polygon": [+ [+ 98.3,+ 30.3+ ],+ [+ 161.7,+ 30.3+ ],+ [+ 161.7,+ 101.7+ ],+ [+ 98.3,+ 101.7+ ]+ ]+ },+ {+ "name": "D1 heat spread front",+ "net": "LOAD_POS",+ "layers": [+ "F.Cu"+ ],+ "polygon": [+ [+ 106.5,+ 87.5+ ],+ [+ 114.5,+ 87.5+ ],+ [+ 114.5,+ 95+ ],+ [+ 106.5,+ 95+ ]+ ],+ "priority": 3,+ "connection": "solid",+ "clearance": 0.2,+ "minThickness": 0.25+ },+ {+ "name": "D1 heat spread back",+ "net": "LOAD_POS",+ "layers": [+ "B.Cu"+ ],+ "polygon": [+ [+ 106.5,+ 87.5+ ],+ [+ 114.5,+ 87.5+ ],+ [+ 114.5,+ 95+ ],+ [+ 106.5,+ 95+ ]+ ],+ "priority": 3,+ "connection": "solid",+ "clearance": 0.2,+ "minThickness": 0.25+ },+ {+ "name": "Shunt positive spread front",+ "net": "SHUNT_P",+ "layers": [+ "F.Cu"+ ],+ "polygon": [+ [+ 134.5,+ 84.7+ ],+ [+ 138,+ 84.7+ ],+ [+ 138,+ 88+ ],+ [+ 134.5,+ 88+ ]+ ],+ "priority": 3,+ "connection": "solid",+ "clearance": 0.2,+ "minThickness": 0.25+ },+ {+ "name": "Shunt positive spread back",+ "net": "SHUNT_P",+ "layers": [+ "B.Cu"+ ],+ "polygon": [+ [+ 134.5,+ 84.7+ ],+ [+ 138,+ 84.7+ ],+ [+ 138,+ 88+ ],+ [+ 134.5,+ 88+ ]+ ],+ "priority": 3,+ "connection": "solid",+ "clearance": 0.2,+ "minThickness": 0.25+ }+ ],+ "notPoured": [+ "BOOST_SW",+ "SERVO_FB",+ "SHUNT_P_SENSE",+ "SHUNT_N_SENSE",+ "I_SET",+ "DAC_OUT",+ "SET_DIV"+ ],+ "fab": {+ "rules": "fab",+ "target": "fab"+ },+ "sourcing": {+ "profile": "fab"+ },+ "molecule": {+ "pin": "medium",+ "gridMm": 2,+ "edgeMarginMm": 2,+ "cornerPinsNet": "GND"+ },+ "requireEvidence": [+ "calcs",+ "simulation"+ ],+ "qualificationNote": "Simultaneous 5 W/24 V/3 A limits; 3 A conductor, diode and shunt screens retained conservatively although initial DUT is 5 V/1 A. 25 W is a future option, not R0 rating. Actual cooling, USB and contact ratings remain unverified.",+ "planes": {+ "GND": "B.Cu"+ },+ "netClearance": {+ "USB_VBUS": 0.27+ },+ "stackupMm": [+ 1.53+ ],+ "dcMaxIterations": 60000,+ "diagnosticNote": "Explicit physical-pad SDT10A45P5 study. Both anode branches assigned 1.5 A as a symmetric boundary assumption, not a solved internal package model. Actual PowerDI5 geometry; 0.47 V max at 10 A / 25 C used at 1.222 A nominal guard corner. Q1 heat excluded; 3 A conductor screen retained. Not an adopted or cold-qualified rating.",+ "branchAssumptions": {+ "DUT_POS": "24 V across a 100k/10k series divider, both -1 percent; full 3 A F1 path retained. ADC input leakage is not included.",+ "LOAD_POS": "INA226 VBUS nominal 830 kohm (typical, no max specified here) gives 28.916 uA at 24 V. Nominal-only boundary, not guaranteed maximum."+ },+ "branchCurrentModel": {+ "boardThicknessMm": 1.6,+ "barrelPlatingUm": 25,+ "maxBarrelDensityAperMm2": 60+ }+}
docs/build-clips.md+4@@ -164,3 +164,7 @@ The raw inventory is now 70 takes, including rejected and static footage; that i ## Copper cooling refinement — 10 October [Watch the 17-second 2× excerpt](clips/copper-cooling-refinement.mp4). The actual viewer moves from the diode temperature to bottom copper heat flow, then to assumed moving-air convection. Q1 heat is excluded and the still-air result remains over budget. Full decode, contact sheet and captioned poster reviewed. This later review is not a native construction recording. Inventory: 72 raw takes, 19 reviewed excerpts, 15 storyboard clips.++## Current-screen milestone — 10 October++[Watch the 7-second 2× excerpt](clips/current-screen-milestone.mp4). V77's improved shunt, drain and ground paths are shown in the actual Fields viewer. All three main-net maxima fall below 60 A/mm² at 0.1 mm resolution. This is an isolated study; cooling and full-board acceptance remain open. The model uses two copper layers despite the old viewer method panel's stale four-layer sentence. Raw take 73 decoded and visually reviewed; 20 reviewed excerpts now available.
docs/clips/current-screen-milestone-poster.jpgadded⋯ 1 unchanged line ⋯
docs/clips/current-screen-milestone.mp4added⋯ 1 unchanged line ⋯
docs/completion-plan.md+6−6@@ -1,12 +1,12 @@-## Latest continuation — source current and diode cooling+## Latest continuation — combined current and diode cooling candidate -AIFlow 0.1.82 remains published and installed; Fields PR9 is still under owner review. The shared Fields executable has not been replaced. The live board revision was rechecked on 10 October and remains unchanged.+AIFlow 0.1.82 remains published and installed. Shared Fields is now 0.7.1 from another thread; this work does not replace it. PR9 remains under owner review. Board19 remains live and unchanged. -V52 remains the best completed fine-grid three-net result: GND 58.31, LOAD_POS 56.11, SHUNT_P 64.25 A/mm² against the unchanged 60 screen. V58 worsened the source exit to 80.11 and was rejected. V63 added a parallel top-layer source connection but moved the peak onto its via (64.80); v67 tests a smaller, offset via and has zero native DRC errors, warnings and opens. Its fine-grid solve is pending.+V77 passes the isolated fine-grid main-net screen: LOAD_POS 56.09, GND 58.31, SHUNT_P 56.50 A/mm² against 60. The Q1 drain land variant preserves its 1.1 mm drill and 0.25 mm nominal minimum annular ring. V80's PowerDI5 diode study predicts D1 +34.5°C still / +26.0°C assumed moving convection, with Q1 heat excluded. V82 combines those layouts and has zero native DRC errors, warnings and opens. Its full six-net fine-grid solve is running; an explicit-physical-pad branch solve is also running with an isolated tested Fields source change. -V61 was rejected before save when native connectivity reassigned its conflicting LOAD_POS via to GND. Removing that via and preserving the ground repair produced DRC-clean v62. Its operating-heat diagnostic predicts D1 +39.3°C still air / +29.4°C moving air. Expanded bottom copper plus a calculated ground reconnection in DRC-clean v65 improves this to +38.5°C / +28.9°C, still above the 35°C budget. V68 explores more lateral spreading. These studies exclude Q1 heat and retain a conservative separate 3 A conductor screen; they do not establish full-board thermal acceptance.+Expected copper nets remain checked before/after refill and save/reload. Clearing stale zone fills before connectivity rebuild prevents false reassignment against old pours. No net check or manufacturing rule was relaxed. The explicit-pad selector has 35 passing source tests and a production build; it is not installed as the shared executable. -No candidate is landed. Keep the native UUID/net invariant, original fabrication rules, physical heatsink/airflow qualification and complete same-board validation. Current/thermal/finish gates remain open. 71 raw takes, 18 reviewed excerpts, 14 storyboard clips; the final narrated film is not assembled.+No candidate is landed. Current/thermal/finish gates remain open pending complete same-board evidence, component/schematic updates and package cooling. There are 73 raw takes; the newest 29.6-second current-screen milestone take decoded and its contact sheet was reviewed. The final narrated film is not assembled. ## Latest continuation — v30 fine mesh running @@ -46,7 +46,7 @@ The active objective is to finish this electronic-load design through AIFlow, th Native-saved board 19 SHA-256 `54f1aab0a34622d1c3675c13f4aeb7a678b0cee8b5a4d3bfd7484b10edb8dcf6` is adopted. Seven existing copper zones were preserved and two Kelvin-sense pour keepouts added, with zero native DRC errors/opens and 15 inherited library warnings. Step 9 completed with explicit Q1 and RSH1 generic via-count exceptions; these are not thermal passes. See via-planning-review.md and pour-review.md. Rebuild the routing/pour proofs invalidated by adoption, then advance outline and same-board current/thermal checks. -Installed Fields remains 0.6.2; physical two-layer/plated-pad PR 5 is still open. Its rectangular heat blocks contact the PCB face, not the MOSFET case. Do not substitute that model for the missing package/interface/sink/airflow analysis. The four-hole 40 mm sink pattern was rejected against routed copper. The stock Wakefield 680-125220 candidate aligns a factory TO-220 hole with the existing Q1 3.5 mm hole and clears the current fitted-body/probe envelope screen; see stock-cooling-candidate.md. Exact model/contact, fasteners, insulation, cradle support, mating access and airflow remain unresolved. Do not call this a qualified assembly.+Shared Fields is 0.7.1 as of this continuation; physical two-layer/plated-pad PR 5 is still open. Its rectangular heat blocks contact the PCB face, not the MOSFET case. Do not substitute that model for the missing package/interface/sink/airflow analysis. The four-hole 40 mm sink pattern was rejected against routed copper. The stock Wakefield 680-125220 candidate aligns a factory TO-220 hole with the existing Q1 3.5 mm hole and clears the current fitted-body/probe envelope screen; see stock-cooling-candidate.md. Exact model/contact, fasteners, insulation, cradle support, mating access and airflow remain unresolved. Do not call this a qualified assembly. ## Resume here
docs/current-cooling-refinement.md+6@@ -1,3 +1,9 @@+## Latest: combined candidate v82++V77 passes the three-net fine-grid current screen (56.09/58.31/56.50 A/mm²). V80 PowerDI5 cooling predicts D1 rise 34.5°C still, 26.0°C with assumed moving convection; Q1 heat remains excluded. V82 combines the two changes and the named Q1 footprint variant: native DRC 0 errors, 0 warnings, 0 opens. The combined 0.1 mm solve is running. Duplicate diode anode pads currently use the legacy split in feasibility solves; explicit physical-pad selection is being implemented and must be validated before acceptance.++Clearing stale filled-zone polygons before rebuilding candidate connectivity resolved false via-net reassignment. Expected net membership is still checked before/after fill and after save/reload; native DRC remains mandatory. No live board change.+ # Source current and diode cooling refinement AIFlow 0.1.82 remains published and installed; Fields PR9 is still under owner review. The shared Fields executable has not been replaced. The live board revision was rechecked on 10 October and remains unchanged.
docs/q1-land-pattern-variant.mdadded+7@@ -0,0 +1,7 @@+# Q1 drain land-pattern study++Candidate v82 uses `Load:TO-220-3_Horizontal_TabUp_Drain1p6`, derived from the installed KiCad TO-220 horizontal tab-up footprint. Only drain pad 2 width changes from 1.905 to 1.600 mm. Pad height remains 2.000 mm; the 1.100 mm drill, lead pitch, mechanical hole, body, courtyard and 3D placement remain unchanged. Nominal minimum annular ring is (1.600 − 1.100)/2 = 0.250 mm. This retains the project's preferred ring and does not tighten the lead-insertion hole.++The narrower drain land provides real separation from the neighboring high-current source exit. V77's 0.1 mm three-net study reports 56.09 LOAD_POS, 58.31 GND and 56.50 SHUNT_P A/mm², below the unchanged 60 screen. V82 combines this geometry with the PowerDI5 diode cooling study and passes native DRC with zero errors, warnings or opens. Its combined current/thermal solve is pending.++This is a project-specific assembly variant, not a new manufacturer-recommended footprint. Copper land reduction, solder fillet/process tolerance, mechanical lead stress and prototype assembly still need physical review. No fabrication release or live-board replacement has occurred. The complete native source footprint is retained under `references/q1-land-pattern/`; the variant is in `kicad/lib/Load.pretty/`.
kicad/lib/Load.pretty/TO-220-3_Horizontal_TabUp_Drain1p6.kicad_modadded+788@@ -0,0 +1,788 @@+(footprint "TO-220-3_Horizontal_TabUp_Drain1p6"+ (version 20260206)+ (generator "kicad-footprint-generator")+ (layer "F.Cu")+ (descr "TO-220-3, Horizontal, RM 2.54mm, see https://www.vishay.com/docs/66542/to-220-1.pdf")+ (tags "TO-220-3 Horizontal RM 2.54mm")+ (property "Reference" "REF**"+ (at 2.54 -1.95 0)+ (layer "F.SilkS")+ (effects+ (font+ (size 1 1)+ (thickness 0.15)+ )+ )+ )+ (property "Value" "TO-220-3_Horizontal_TabUp"+ (at 2.54 20.41 0)+ (layer "F.Fab")+ (effects+ (font+ (size 1 1)+ (thickness 0.15)+ )+ )+ )+ (property "KiLib_Generator" "package/TO_SOT/THT"+ (at 0 0 0)+ (layer "F.SilkS")+ (hide yes)+ (effects+ (font+ (size 1 1)+ (thickness 0.15)+ )+ )+ )+ (attr through_hole)+ (duplicate_pad_numbers_are_jumpers no)+ (fp_line+ (start -2.57 3.7)+ (end -2.57 13.17)+ (stroke+ (width 0.12)+ (type solid)+ )+ (layer "F.SilkS")+ )+ (fp_line+ (start -2.57 3.7)+ (end 7.65 3.7)+ (stroke+ (width 0.12)+ (type solid)+ )+ (layer "F.SilkS")+ )+ (fp_line+ (start -2.57 13.17)+ (end 7.65 13.17)+ (stroke+ (width 0.12)+ (type solid)+ )+ (layer "F.SilkS")+ )+ (fp_line+ (start -2.57 13.41)+ (end -2.57 13.77)+ (stroke+ (width 0.12)+ (type solid)+ )+ (layer "F.SilkS")+ )+ (fp_line+ (start -2.57 14.13)+ (end -2.57 14.49)+ (stroke+ (width 0.12)+ (type solid)+ )+ (layer "F.SilkS")+ )+ (fp_line+ (start -2.57 14.85)+ (end -2.57 15.21)+ (stroke+ (width 0.12)+ (type solid)+ )+ (layer "F.SilkS")+ )+ (fp_line+ (start -2.57 15.57)+ (end -2.57 15.93)+ (stroke+ (width 0.12)+ (type solid)+ )+ (layer "F.SilkS")+ )+ (fp_line+ (start -2.57 16.29)+ (end -2.57 16.65)+ (stroke+ (width 0.12)+ (type solid)+ )+ (layer "F.SilkS")+ )+ (fp_line+ (start -2.57 17.01)+ (end -2.57 17.37)+ (stroke+ (width 0.12)+ (type solid)+ )+ (layer "F.SilkS")+ )+ (fp_line+ (start -2.57 17.73)+ (end -2.57 18.09)+ (stroke+ (width 0.12)+ (type solid)+ )+ (layer "F.SilkS")+ )+ (fp_line+ (start -2.57 18.45)+ (end -2.57 18.81)+ (stroke+ (width 0.12)+ (type solid)+ )+ (layer "F.SilkS")+ )+ (fp_line+ (start -2.57 19.17)+ (end -2.57 19.53)+ (stroke+ (width 0.12)+ (type solid)+ )+ (layer "F.SilkS")+ )+ (fp_line+ (start -2.57 19.57)+ (end -2.21 19.57)+ (stroke+ (width 0.12)+ (type solid)+ )+ (layer "F.SilkS")+ )+ (fp_line+ (start -1.85 19.57)+ (end -1.49 19.57)+ (stroke+ (width 0.12)+ (type solid)+ )+ (layer "F.SilkS")+ )+ (fp_line+ (start -1.13 19.57)+ (end -0.77 19.57)+ (stroke+ (width 0.12)+ (type solid)+ )+ (layer "F.SilkS")+ )+ (fp_line+ (start -0.485 3.7)+ (end -0.485 1.26)+ (stroke+ (width 0.12)+ (type solid)+ )+ (layer "F.SilkS")+ )+ (fp_line+ (start -0.41 19.57)+ (end -0.05 19.57)+ (stroke+ (width 0.12)+ (type solid)+ )+ (layer "F.SilkS")+ )+ (fp_line+ (start 0.31 19.57)+ (end 0.67 19.57)+ (stroke+ (width 0.12)+ (type solid)+ )+ (layer "F.SilkS")+ )+ (fp_line+ (start 0.485 3.7)+ (end 0.485 1.26)+ (stroke+ (width 0.12)+ (type solid)+ )+ (layer "F.SilkS")+ )+ (fp_line+ (start 1.03 19.57)+ (end 1.39 19.57)+ (stroke+ (width 0.12)+ (type solid)+ )+ (layer "F.SilkS")+ )+ (fp_line+ (start 1.75 19.57)+ (end 2.11 19.57)+ (stroke+ (width 0.12)+ (type solid)+ )+ (layer "F.SilkS")+ )+ (fp_line+ (start 2.055 3.7)+ (end 2.055 1.26)+ (stroke+ (width 0.12)+ (type solid)+ )+ (layer "F.SilkS")+ )+ (fp_line+ (start 2.47 19.57)+ (end 2.83 19.57)+ (stroke+ (width 0.12)+ (type solid)+ )+ (layer "F.SilkS")+ )+ (fp_line+ (start 3.025 3.7)+ (end 3.025 1.26)+ (stroke+ (width 0.12)+ (type solid)+ )+ (layer "F.SilkS")+ )+ (fp_line+ (start 3.19 19.57)+ (end 3.55 19.57)+ (stroke+ (width 0.12)+ (type solid)+ )+ (layer "F.SilkS")+ )+ (fp_line+ (start 3.91 19.57)+ (end 4.27 19.57)+ (stroke+ (width 0.12)+ (type solid)+ )+ (layer "F.SilkS")+ )+ (fp_line+ (start 4.595 3.7)+ (end 4.595 1.26)+ (stroke+ (width 0.12)+ (type solid)+ )+ (layer "F.SilkS")+ )+ (fp_line+ (start 4.63 19.57)+ (end 4.99 19.57)+ (stroke+ (width 0.12)+ (type solid)+ )+ (layer "F.SilkS")+ )+ (fp_line+ (start 5.35 19.57)+ (end 5.71 19.57)+ (stroke+ (width 0.12)+ (type solid)+ )+ (layer "F.SilkS")+ )+ (fp_line+ (start 5.565 3.7)+ (end 5.565 1.26)+ (stroke+ (width 0.12)+ (type solid)+ )+ (layer "F.SilkS")+ )+ (fp_line+ (start 6.07 19.57)+ (end 6.43 19.57)+ (stroke+ (width 0.12)+ (type solid)+ )+ (layer "F.SilkS")+ )+ (fp_line+ (start 6.79 19.57)+ (end 7.15 19.57)+ (stroke+ (width 0.12)+ (type solid)+ )+ (layer "F.SilkS")+ )+ (fp_line+ (start 7.51 19.57)+ (end 7.65 19.57)+ (stroke+ (width 0.12)+ (type solid)+ )+ (layer "F.SilkS")+ )+ (fp_line+ (start 7.65 3.7)+ (end 7.65 13.17)+ (stroke+ (width 0.12)+ (type solid)+ )+ (layer "F.SilkS")+ )+ (fp_line+ (start 7.65 13.41)+ (end 7.65 13.77)+ (stroke+ (width 0.12)+ (type solid)+ )+ (layer "F.SilkS")+ )+ (fp_line+ (start 7.65 14.13)+ (end 7.65 14.49)+ (stroke+ (width 0.12)+ (type solid)+ )+ (layer "F.SilkS")+ )+ (fp_line+ (start 7.65 14.85)+ (end 7.65 15.21)+ (stroke+ (width 0.12)+ (type solid)+ )+ (layer "F.SilkS")+ )+ (fp_line+ (start 7.65 15.57)+ (end 7.65 15.93)+ (stroke+ (width 0.12)+ (type solid)+ )+ (layer "F.SilkS")+ )+ (fp_line+ (start 7.65 16.29)+ (end 7.65 16.65)+ (stroke+ (width 0.12)+ (type solid)+ )+ (layer "F.SilkS")+ )+ (fp_line+ (start 7.65 17.01)+ (end 7.65 17.37)+ (stroke+ (width 0.12)+ (type solid)+ )+ (layer "F.SilkS")+ )+ (fp_line+ (start 7.65 17.73)+ (end 7.65 18.09)+ (stroke+ (width 0.12)+ (type solid)+ )+ (layer "F.SilkS")+ )+ (fp_line+ (start 7.65 18.45)+ (end 7.65 18.81)+ (stroke+ (width 0.12)+ (type solid)+ )+ (layer "F.SilkS")+ )+ (fp_line+ (start 7.65 19.17)+ (end 7.65 19.53)+ (stroke+ (width 0.12)+ (type solid)+ )+ (layer "F.SilkS")+ )+ (fp_line+ (start -2.71 3.56)+ (end -0.63 3.56)+ (stroke+ (width 0.05)+ (type solid)+ )+ (layer "F.CrtYd")+ )+ (fp_line+ (start -2.71 19.71)+ (end -2.71 3.56)+ (stroke+ (width 0.05)+ (type solid)+ )+ (layer "F.CrtYd")+ )+ (fp_line+ (start -1.21 -1.25)+ (end 1.21 -1.25)+ (stroke+ (width 0.05)+ (type solid)+ )+ (layer "F.CrtYd")+ )+ (fp_line+ (start -1.21 1.25)+ (end -1.21 -1.25)+ (stroke+ (width 0.05)+ (type solid)+ )+ (layer "F.CrtYd")+ )+ (fp_line+ (start -0.63 1.25)+ (end -1.21 1.25)+ (stroke+ (width 0.05)+ (type solid)+ )+ (layer "F.CrtYd")+ )+ (fp_line+ (start -0.63 3.56)+ (end -0.63 1.25)+ (stroke+ (width 0.05)+ (type solid)+ )+ (layer "F.CrtYd")+ )+ (fp_line+ (start 0.63 1.25)+ (end 0.63 3.56)+ (stroke+ (width 0.05)+ (type solid)+ )+ (layer "F.CrtYd")+ )+ (fp_line+ (start 0.63 3.56)+ (end 1.91 3.56)+ (stroke+ (width 0.05)+ (type solid)+ )+ (layer "F.CrtYd")+ )+ (fp_line+ (start 1.21 -1.25)+ (end 1.21 1.25)+ (stroke+ (width 0.05)+ (type solid)+ )+ (layer "F.CrtYd")+ )+ (fp_line+ (start 1.21 1.25)+ (end 0.63 1.25)+ (stroke+ (width 0.05)+ (type solid)+ )+ (layer "F.CrtYd")+ )+ (fp_line+ (start 1.33 -1.25)+ (end 3.75 -1.25)+ (stroke+ (width 0.05)+ (type solid)+ )+ (layer "F.CrtYd")+ )+ (fp_line+ (start 1.33 1.25)+ (end 1.33 -1.25)+ (stroke+ (width 0.05)+ (type solid)+ )+ (layer "F.CrtYd")+ )+ (fp_line+ (start 1.91 1.25)+ (end 1.33 1.25)+ (stroke+ (width 0.05)+ (type solid)+ )+ (layer "F.CrtYd")+ )+ (fp_line+ (start 1.91 3.56)+ (end 1.91 1.25)+ (stroke+ (width 0.05)+ (type solid)+ )+ (layer "F.CrtYd")+ )+ (fp_line+ (start 3.17 1.25)+ (end 3.17 3.56)+ (stroke+ (width 0.05)+ (type solid)+ )+ (layer "F.CrtYd")+ )+ (fp_line+ (start 3.17 3.56)+ (end 4.45 3.56)+ (stroke+ (width 0.05)+ (type solid)+ )+ (layer "F.CrtYd")+ )+ (fp_line+ (start 3.75 -1.25)+ (end 3.75 1.25)+ (stroke+ (width 0.05)+ (type solid)+ )+ (layer "F.CrtYd")+ )+ (fp_line+ (start 3.75 1.25)+ (end 3.17 1.25)+ (stroke+ (width 0.05)+ (type solid)+ )+ (layer "F.CrtYd")+ )+ (fp_line+ (start 3.87 -1.25)+ (end 6.29 -1.25)+ (stroke+ (width 0.05)+ (type solid)+ )+ (layer "F.CrtYd")+ )+ (fp_line+ (start 3.87 1.25)+ (end 3.87 -1.25)+ (stroke+ (width 0.05)+ (type solid)+ )+ (layer "F.CrtYd")+ )+ (fp_line+ (start 4.45 1.25)+ (end 3.87 1.25)+ (stroke+ (width 0.05)+ (type solid)+ )+ (layer "F.CrtYd")+ )+ (fp_line+ (start 4.45 3.56)+ (end 4.45 1.25)+ (stroke+ (width 0.05)+ (type solid)+ )+ (layer "F.CrtYd")+ )+ (fp_line+ (start 5.71 1.25)+ (end 5.71 3.56)+ (stroke+ (width 0.05)+ (type solid)+ )+ (layer "F.CrtYd")+ )+ (fp_line+ (start 5.71 3.56)+ (end 7.79 3.56)+ (stroke+ (width 0.05)+ (type solid)+ )+ (layer "F.CrtYd")+ )+ (fp_line+ (start 6.29 -1.25)+ (end 6.29 1.25)+ (stroke+ (width 0.05)+ (type solid)+ )+ (layer "F.CrtYd")+ )+ (fp_line+ (start 6.29 1.25)+ (end 5.71 1.25)+ (stroke+ (width 0.05)+ (type solid)+ )+ (layer "F.CrtYd")+ )+ (fp_line+ (start 7.79 3.56)+ (end 7.79 19.71)+ (stroke+ (width 0.05)+ (type solid)+ )+ (layer "F.CrtYd")+ )+ (fp_line+ (start 7.79 19.71)+ (end -2.71 19.71)+ (stroke+ (width 0.05)+ (type solid)+ )+ (layer "F.CrtYd")+ )+ (fp_rect+ (start -2.46 3.81)+ (end 7.54 13.06)+ (stroke+ (width 0.1)+ (type solid)+ )+ (fill no)+ (layer "F.Fab")+ )+ (fp_rect+ (start -2.46 13.06)+ (end 7.54 19.46)+ (stroke+ (width 0.1)+ (type solid)+ )+ (fill no)+ (layer "F.Fab")+ )+ (fp_rect+ (start -0.375 0)+ (end 0.375 3.81)+ (stroke+ (width 0.1)+ (type solid)+ )+ (fill no)+ (layer "F.Fab")+ )+ (fp_rect+ (start 2.165 0)+ (end 2.915 3.81)+ (stroke+ (width 0.1)+ (type solid)+ )+ (fill no)+ (layer "F.Fab")+ )+ (fp_rect+ (start 4.705 0)+ (end 5.455 3.81)+ (stroke+ (width 0.1)+ (type solid)+ )+ (fill no)+ (layer "F.Fab")+ )+ (fp_circle+ (center 2.54 16.66)+ (end 4.39 16.66)+ (stroke+ (width 0.1)+ (type solid)+ )+ (fill no)+ (layer "F.Fab")+ )+ (fp_text user "${REFERENCE}"+ (at 2.54 9.23 0)+ (layer "F.Fab")+ (effects+ (font+ (size 1 1)+ (thickness 0.15)+ )+ )+ )+ (pad "" np_thru_hole circle+ (at 2.54 16.66)+ (size 3.5 3.5)+ (drill 3.5)+ (layers "*.Cu" "*.Mask")+ )+ (pad "1" thru_hole rect+ (at 0 0)+ (size 1.905 2)+ (drill 1.1)+ (layers "*.Cu" "*.Mask")+ (remove_unused_layers no)+ )+ (pad "2" thru_hole oval+ (at 2.54 0)+ (size 1.6 2)+ (drill 1.1)+ (layers "*.Cu" "*.Mask")+ (remove_unused_layers no)+ )+ (pad "3" thru_hole oval+ (at 5.08 0)+ (size 1.905 2)+ (drill 1.1)+ (layers "*.Cu" "*.Mask")+ (remove_unused_layers no)+ )+ (embedded_fonts no)+ (model "${KICAD10_3DMODEL_DIR}/Package_TO_SOT_THT.3dshapes/TO-220-3_Horizontal_TabUp.step"+ (offset+ (xyz 0 0 0)+ )+ (scale+ (xyz 1 1 1)+ )+ (rotate+ (xyz 0 0 0)+ )+ )+)
tools/hotspot_via_candidate_v80.pyadded+247@@ -0,0 +1,247 @@+"""Run using native desktop KiCad Python; modifies only an isolated board copy."""+import pcbnew,sys,json+from pathlib import Path+source=Path(sys.argv[1]);out=Path(sys.argv[2]);board=pcbnew.LoadBoard(str(source));nets=board.GetNetsByName()+changes=[]+for net,x,y in [('GND',133.0,100.8),('GND',138.0,100.8),('GND',139.0,93.0),('GND',136.5,93.0),('GND',139.4,90.4),('GND',139.4,91.4),('GND',139.4,92.4)]:+ via=pcbnew.PCB_VIA(board);via.SetPosition(pcbnew.VECTOR2I(pcbnew.FromMM(x),pcbnew.FromMM(y)));via.SetWidth(pcbnew.FromMM(.8));via.SetDrill(pcbnew.FromMM(.4));via.SetLayerPair(pcbnew.F_Cu,pcbnew.B_Cu);via.SetNet(nets[net]);board.Add(via);changes.append({'kind':'via','net':net,'at':[x,y],'sizeMm':.8,'drillMm':.4,'uuid':str(via.m_Uuid.AsString())})+for item in board.GetTracks():+ uid=str(item.m_Uuid.AsString())+ if uid=='32514162-b870-4c7a-95a2-b85b8db79257':+ item.SetWidth(pcbnew.FromMM(1.0));item.SetDrill(pcbnew.FromMM(.6));changes.append({'kind':'resize-via','uuid':uid,'net':'LOAD_POS','sizeMm':1.0,'drillMm':.6})+for item in list(board.GetTracks()):+ uid=str(item.m_Uuid.AsString())+ if uid in ['91eed079-74e4-4863-8dc1-f211a19b6ee0','ad353074-a567-44a9-a20a-744b8057c5a9']:+ board.Remove(item);changes.append({'kind':'remove-track','uuid':uid})+ if uid=='aa6c5964-74d4-410e-a69e-ffb2cd9c9999':+ item.SetWidth(pcbnew.FromMM(1.0));item.SetDrill(pcbnew.FromMM(.6));changes.append({'kind':'resize-via','uuid':uid,'sizeMm':1.0,'drillMm':.6})+def track(net,layer,a,b,width):+ t=pcbnew.PCB_TRACK(board);t.SetStart(pcbnew.VECTOR2I(pcbnew.FromMM(a[0]),pcbnew.FromMM(a[1])));t.SetEnd(pcbnew.VECTOR2I(pcbnew.FromMM(b[0]),pcbnew.FromMM(b[1])));t.SetWidth(pcbnew.FromMM(width));t.SetLayer(layer);t.SetNet(nets[net]);board.Add(t);changes.append({'kind':'track','net':net,'start':a,'end':b,'widthMm':width,'layer':board.GetLayerName(layer)})+for a,b in [((127.46,82),(127.46,83.3)),((127.46,83.3),(126.4,84.36)),((126.4,84.36),(126.4,86.5))]:track('LOAD_POS',pcbnew.F_Cu,a,b,1.5)+for a,b in [((139.4,90.4),(139.4,92)),((139.4,92),(138.4,93)),((138.4,93),(136.5,93))]:track('GND',pcbnew.B_Cu,a,b,1.5)+for item in board.GetTracks():+ if isinstance(item,pcbnew.PCB_VIA) and item.GetNetname()=='GND':+ p=item.GetPosition();xy=(round(pcbnew.ToMM(p.x),3),round(pcbnew.ToMM(p.y),3))+ if xy in [(139.4,86.8),(136.5,93.0),(139.4,90.4),(139.4,91.4),(139.4,92.4)]:item.SetWidth(pcbnew.FromMM(1));item.SetDrill(pcbnew.FromMM(.6));changes.append({'kind':'resize-via','at':xy,'sizeMm':1,'drillMm':.6})+for fp in board.GetFootprints():+ delta={'Q2':-.3,'U3':.3,'R7':.1}.get(fp.GetReference())+ if delta:+ pos=fp.GetPosition();pos.x+=pcbnew.FromMM(delta);fp.SetPosition(pos);changes.append({'kind':'move-footprint','ref':fp.GetReference(),'dxMm':delta})+for item in list(board.GetTracks()):+ if item.GetNetname()=='SHUNT_P_SENSE' and isinstance(item,pcbnew.PCB_VIA):+ pos=item.GetPosition()+ if abs(pcbnew.ToMM(pos.x)-123.6)<.001 and abs(pcbnew.ToMM(pos.y)-85.4)<.001:+ pos.x=pcbnew.FromMM(123.3);item.SetPosition(pos);changes.append({'kind':'move-sense-via','from':[123.6,85.4],'to':[123.3,85.4]})+ if isinstance(item,pcbnew.PCB_VIA):continue+ if item.GetNetname()=='SHUNT_P_SENSE':+ for getter,setter in [(item.GetStart,item.SetStart),(item.GetEnd,item.SetEnd)]:+ pos=getter()+ if abs(pcbnew.ToMM(pos.x)-123.6)<.001 and abs(pcbnew.ToMM(pos.y)-85.4)<.001:pos.x=pcbnew.FromMM(123.3);setter(pos)+ if item.GetNetname()=='SHUNT_N_SENSE' and item.GetLayer()==pcbnew.F_Cu:+ for getter,setter in [(item.GetStart,item.SetStart),(item.GetEnd,item.SetEnd)]:+ pos=getter();x,y=pcbnew.ToMM(pos.x),pcbnew.ToMM(pos.y)+ if 128.79<x<129.31 and 87.29<y<88.81:pos.x+=pcbnew.FromMM(.2);setter(pos)+for item in list(board.GetTracks()):+ uid=str(item.m_Uuid.AsString())+ if uid=='32514162-b870-4c7a-95a2-b85b8db79257':+ item.SetPosition(pcbnew.VECTOR2I(pcbnew.FromMM(126.4),pcbnew.FromMM(86.7)))+ if uid=='13d2f936-e4d3-40cb-9bf7-1b6b26ca4cf2':+ item.SetStart(pcbnew.VECTOR2I(pcbnew.FromMM(126.4),pcbnew.FromMM(86.7)));item.SetWidth(pcbnew.FromMM(1.5))+ if uid=='f120f8db-288f-4938-acbf-98ff5e4065df':+ item.SetStart(pcbnew.VECTOR2I(pcbnew.FromMM(133.5),pcbnew.FromMM(84.2)));item.SetEnd(pcbnew.VECTOR2I(pcbnew.FromMM(126.8),pcbnew.FromMM(84.2)));item.SetWidth(pcbnew.FromMM(1.9))+ if not isinstance(item,pcbnew.PCB_VIA) and item.GetNetname()=='LOAD_POS' and item.GetLayer()==pcbnew.F_Cu:+ for getter,setter in [(item.GetStart,item.SetStart),(item.GetEnd,item.SetEnd)]:+ p=getter()+ if abs(pcbnew.ToMM(p.x)-126.4)<.001 and abs(pcbnew.ToMM(p.y)-86.5)<.001:p.y=pcbnew.FromMM(86.7);setter(p)+for item in board.GetTracks():+ if isinstance(item,pcbnew.PCB_VIA) or item.GetLayer()!=pcbnew.B_Cu or item.GetNetname()!='SHUNT_P_SENSE':continue+ for getter,setter in [(item.GetStart,item.SetStart),(item.GetEnd,item.SetEnd)]:+ p=getter();x,y=pcbnew.ToMM(p.x),pcbnew.ToMM(p.y)+ if abs(y-85.4)<.001 and (abs(x-123.3)<.001 or abs(x-132)<.001):+ p.y=pcbnew.FromMM(85.6)+ if abs(x-132)<.001:p.x=pcbnew.FromMM(132.2)+ setter(p)+# Ease the drain right-angle turn; relocate its adjacent setpoint via with endpoints.+track('LOAD_POS',pcbnew.B_Cu,(126.35,89.0),(125.65,89.7),1.5)+# Preserve explicit branch junctions instead of overlapped dangling endpoints.+for item in list(board.GetTracks()):+ uid=str(item.m_Uuid.AsString())+ if uid=='32514162-b870-4c7a-95a2-b85b8db79257':+ item.SetWidth(pcbnew.FromMM(.8));item.SetDrill(pcbnew.FromMM(.4));item.SetPosition(pcbnew.VECTOR2I(pcbnew.FromMM(126.05),pcbnew.FromMM(86.7)))+ if uid in ['b486fb59-b64e-41ee-a1a7-5a72fcf0a780','8e5d5e2b-78f3-4ed2-af96-409d2ea9d57b']:+ a=item.GetStart();b=item.GetEnd();start=(pcbnew.ToMM(a.x),pcbnew.ToMM(a.y));end=(pcbnew.ToMM(b.x),pcbnew.ToMM(b.y))+ mid=(126.35,89.0) if uid=='b486fb59-b64e-41ee-a1a7-5a72fcf0a780' else (125.65,89.7)+ board.Remove(item);track('LOAD_POS',pcbnew.B_Cu,start,mid,1.5);track('LOAD_POS',pcbnew.B_Cu,mid,end,1.5)+via=pcbnew.PCB_VIA(board);via.SetPosition(pcbnew.VECTOR2I(pcbnew.FromMM(126.8),pcbnew.FromMM(86.7)));via.SetWidth(pcbnew.FromMM(.8));via.SetDrill(pcbnew.FromMM(.4));via.SetLayerPair(pcbnew.F_Cu,pcbnew.B_Cu);via.SetNet(nets['LOAD_POS']);board.Add(via)+# Do not enlarge holes through narrow in-plane paths. Keep the three dedicated shunt thermal vias.+for item in list(board.GetTracks()):+ uid=str(item.m_Uuid.AsString())+ if uid in ['34d3207a-b14a-40d8-be82-5b108c2b867b','9a71af6b-ea83-4f97-b7f6-3f69253c7314']:+ board.Remove(item);continue+ if isinstance(item,pcbnew.PCB_VIA):+ p=item.GetPosition();x,y=pcbnew.ToMM(p.x),pcbnew.ToMM(p.y)+ if uid=='aa6c5964-74d4-410e-a69e-ffb2cd9c9999' or (item.GetNetname()=='GND' and abs(x-139.4)<.001 and abs(y-86.8)<.001):+ item.SetWidth(pcbnew.FromMM(.8));item.SetDrill(pcbnew.FromMM(.4))+# Move the transfer hole off the center of the shunt bus; remove a nearly unused ground hole through the return neck.+for item in list(board.GetTracks()):+ if not isinstance(item,pcbnew.PCB_VIA):continue+ p=item.GetPosition();x,y=pcbnew.ToMM(p.x),pcbnew.ToMM(p.y)+ if item.GetNetname()=='GND' and abs(x-139.4)<.001 and abs(y-86.8)<.001:board.Remove(item)+ if str(item.m_Uuid.AsString())=='aa6c5964-74d4-410e-a69e-ffb2cd9c9999':item.SetPosition(pcbnew.VECTOR2I(pcbnew.FromMM(133.7),pcbnew.FromMM(83.6)))+track('SHUNT_P',pcbnew.F_Cu,(133.7,83.6),(133.8,84.2),1.2)+# Preserve the full plated Q1 source connection; remove its redundant neck-drilling spur.+for item in list(board.GetTracks()):+ uid=str(item.m_Uuid.AsString())+ if uid in ['cd48c727-15bf-4276-b4b8-ae4d936d829d','30d9e8bc-805a-4b1d-8a79-ad625ee59388']:+ board.Remove(item);continue+ if isinstance(item,pcbnew.PCB_VIA) and item.GetNetname()=='GND':+ p=item.GetPosition();x,y=pcbnew.ToMM(p.x),pcbnew.ToMM(p.y)+ if abs(x-139.4)<.001 and any(abs(y-t)<.001 for t in [90.4,91.4,92.4]):p.x=pcbnew.FromMM(139.8);item.SetPosition(p)+# Share vertical current with smaller holes and widen the plated-source exit.+for item in board.GetTracks():+ uid=str(item.m_Uuid.AsString())+ if uid in ['2f235494-7976-4048-be2d-b71507b2b45e','ec56cc46-09f5-4ff9-a1b6-61699db68686']:item.SetWidth(pcbnew.FromMM(1.75))+ if isinstance(item,pcbnew.PCB_VIA) and item.GetNetname()=='GND':+ p=item.GetPosition();x,y=pcbnew.ToMM(p.x),pcbnew.ToMM(p.y)+ if abs(x-139.8)<.001 and any(abs(y-t)<.001 for t in [90.4,91.4,92.4]):+ p.x=pcbnew.FromMM(139.4);item.SetPosition(p);item.SetWidth(pcbnew.FromMM(.6));item.SetDrill(pcbnew.FromMM(.3))+for item in board.GetTracks():+ if str(item.m_Uuid.AsString())=='f120f8db-288f-4938-acbf-98ff5e4065df':+ item.SetStart(pcbnew.VECTOR2I(pcbnew.FromMM(133.5),pcbnew.FromMM(84.25)));item.SetEnd(pcbnew.VECTOR2I(pcbnew.FromMM(126.8),pcbnew.FromMM(84.25)));item.SetWidth(pcbnew.FromMM(2.0))+# Fine-grid hotspot revision: spread the return transfer and soften the two power necks.+for item in board.GetTracks():+ if isinstance(item,pcbnew.PCB_VIA):+ p=item.GetPosition();x,y=pcbnew.ToMM(p.x),pcbnew.ToMM(p.y)+ if item.GetNetname()=='GND' and abs(x-139.4)<.001 and abs(y-90.4)<.001:+ p.x=pcbnew.FromMM(139.0);item.SetPosition(p)+ continue+ uid=str(item.m_Uuid.AsString())+ if uid in ['32cd5f76-8c9c-4680-a071-fe4f71064f23','cecc8caa-bb93-470d-a793-50ae2c93732f']:item.SetWidth(pcbnew.FromMM(1.6))+ if item.GetNetname()=='LOAD_POS' and item.GetLayer()==pcbnew.B_Cu:+ a=item.GetStart();b=item.GetEnd()+ if abs(pcbnew.ToMM(a.y)-89.7)<.001 and abs(pcbnew.ToMM(b.y)-89.7)<.001:item.SetWidth(pcbnew.FromMM(1.75))+via=pcbnew.PCB_VIA(board);via.SetPosition(pcbnew.VECTOR2I(pcbnew.FromMM(139.8),pcbnew.FromMM(90.4)));via.SetWidth(pcbnew.FromMM(.6));via.SetDrill(pcbnew.FromMM(.3));via.SetLayerPair(pcbnew.F_Cu,pcbnew.B_Cu);via.SetNet(nets['GND']);board.Add(via)+track('GND',pcbnew.B_Cu,(139.0,90.4),(139.8,90.4),1.2)+# Keep clearance beside the setpoint via; widen only the diode-spread junction.+for item in list(board.GetTracks()):+ if isinstance(item,pcbnew.PCB_VIA) or item.GetNetname()!='LOAD_POS' or item.GetLayer()!=pcbnew.B_Cu:continue+ a=item.GetStart();b=item.GetEnd();ax,ay=pcbnew.ToMM(a.x),pcbnew.ToMM(a.y);bx,by=pcbnew.ToMM(b.x),pcbnew.ToMM(b.y)+ if abs(ay-89.7)<.001 and abs(by-89.7)<.001:+ item.SetWidth(pcbnew.FromMM(1.5))+ if min(ax,bx)<120 and max(ax,bx)>125:+ board.Remove(item);track('LOAD_POS',pcbnew.B_Cu,(119.8,89.7),(123.0,89.7),1.75);track('LOAD_POS',pcbnew.B_Cu,(123.0,89.7),(125.65,89.7),1.5)+# Fill the inside shunt-bus bend with a diagonal copper path.+track('SHUNT_P',pcbnew.B_Cu,(133.0,84.7),(134.7,85.5),1.2)+# Spread the plated-source exit without moving the gate or clamp circuit.+track('SHUNT_P',pcbnew.B_Cu,(125.1,81.8),(125.35,82.7),1.9)+for item in board.GetTracks():+ if isinstance(item,pcbnew.PCB_VIA):continue+ if item.GetNetname()=='LOAD_POS' and item.GetLayer()==pcbnew.B_Cu:+ for getter,setter in [(item.GetStart,item.SetStart),(item.GetEnd,item.SetEnd)]:+ p=getter();x,y=pcbnew.ToMM(p.x),pcbnew.ToMM(p.y)+ if abs(x-126.35)<.001 and abs(y-89)<.001:p.y=pcbnew.FromMM(88.85);setter(p)+ if abs(x-125.65)<.001 and abs(y-89.7)<.001:p.x=pcbnew.FromMM(125.5);setter(p)+for x in [139.0,139.8]:+ via=pcbnew.PCB_VIA(board);via.SetPosition(pcbnew.VECTOR2I(pcbnew.FromMM(x),pcbnew.FromMM(89.9)));via.SetWidth(pcbnew.FromMM(.6));via.SetDrill(pcbnew.FromMM(.3));via.SetLayerPair(pcbnew.F_Cu,pcbnew.B_Cu);via.SetNet(nets['GND']);board.Add(via)+track('GND',pcbnew.B_Cu,(139,89.9),(139.8,89.9),.6)+track('GND',pcbnew.B_Cu,(139.4,89.9),(139.4,90.4),.6)+# Give the ground transfer more uninterrupted top copper; keep UART endpoints fixed.+for item in list(board.GetTracks()):+ if isinstance(item,pcbnew.PCB_VIA) or item.GetLayer()!=pcbnew.F_Cu:continue+ a=item.GetStart();b=item.GetEnd();ax,ay=pcbnew.ToMM(a.x),pcbnew.ToMM(a.y);bx,by=pcbnew.ToMM(b.x),pcbnew.ToMM(b.y)+ if item.GetNetname()=='UART_RX' and abs(ax-140.7)<.001 and abs(bx-140.7)<.001 and abs(ay-83.5)<.001 and abs(by-93.2)<.001:+ board.Remove(item)+ points=[(140.7,83.5),(140.7,88.0),(141.0,88.3),(141.0,91.8),(140.7,92.1),(140.7,93.2)]+ for p,q in zip(points,points[1:]):track('UART_RX',pcbnew.F_Cu,p,q,.25)+ if item.GetNetname()=='UART_TX' and abs(ax-137.9)<.001 and abs(bx-137.9)<.001 and abs(ay-83.7)<.001 and abs(by-98.1)<.001:+ board.Remove(item)+ points=[(137.9,83.7),(137.9,89.0),(137.4,89.5),(137.4,93.3),(137.9,93.8),(137.9,98.1)]+ for p,q in zip(points,points[1:]):track('UART_TX',pcbnew.F_Cu,p,q,.25)+# Move the setpoint layer transfer clear of a wider drain bend, preserving its net/endpoints.+for item in board.GetTracks():+ if item.GetNetname()=='I_SET':+ endpoints=[(item.GetPosition,item.SetPosition)] if isinstance(item,pcbnew.PCB_VIA) else [(item.GetStart,item.SetStart),(item.GetEnd,item.SetEnd)]+ for getter,setter in endpoints:+ p=getter()+ if abs(pcbnew.ToMM(p.x)-125)<.001 and abs(pcbnew.ToMM(p.y)-88.4)<.001:p.y=pcbnew.FromMM(88);setter(p)+ if isinstance(item,pcbnew.PCB_VIA) or item.GetNetname()!='LOAD_POS' or item.GetLayer()!=pcbnew.B_Cu:continue+ for getter,setter in [(item.GetStart,item.SetStart),(item.GetEnd,item.SetEnd)]:+ p=getter();x,y=pcbnew.ToMM(p.x),pcbnew.ToMM(p.y)+ if abs(x-126.35)<.001 and abs(y-88.85)<.001:p.y=pcbnew.FromMM(89);setter(p)+ if abs(x-125.5)<.001 and abs(y-89.7)<.001:p.x=pcbnew.FromMM(125.65);setter(p)+ a=item.GetStart();b=item.GetEnd();ax,ay=pcbnew.ToMM(a.x),pcbnew.ToMM(a.y);bx,by=pcbnew.ToMM(b.x),pcbnew.ToMM(b.y)+ if abs(ay-89.7)<.001 and abs(by-89.7)<.001:item.SetWidth(pcbnew.FromMM(1.75))+ if abs(ax-bx)>.1 and abs(ay-by)>.1 and min(ax,bx)>125 and max(ax,bx)<127 and min(ay,by)>88.8 and max(ay,by)<89.8:item.SetWidth(pcbnew.FromMM(1.75))+# This local gate-drive segment carries the servo/gate current, not DUT current.+# Keep 0.5 mm copper and create clearance to the small setpoint via.+for item in board.GetTracks():+ if str(item.m_Uuid.AsString())=='a4e6790e-b900-47a1-bf1e-b5fb98adc8c7':item.SetWidth(pcbnew.FromMM(.5))+# Separate thermal-spread experiment, not a replacement of the accepted zone plan.+for z in list(board.Zones()):+ if not z.GetZoneName().startswith('D1 relocated heat spread'):continue+ layer=z.GetLayer();name=z.GetZoneName();board.Remove(z)+ z=pcbnew.ZONE(board);z.SetLayer(layer);z.SetNet(nets['LOAD_POS']);z.SetZoneName(name);z.SetAssignedPriority(3);z.SetLocalClearance(pcbnew.FromMM(.2));z.SetMinThickness(pcbnew.FromMM(.25));z.SetPadConnection(pcbnew.ZONE_CONNECTION_FULL);z.Outline().NewOutline()+ points=[(114,82),(132,82),(132,99),(104,99),(104,89.4),(114,89.4)] if layer==pcbnew.F_Cu else [(118.8,70),(132,70),(132,99),(104,99),(104,89.4),(114,89.4),(114,84),(118.8,84)]+ for x,y in points:z.Outline().Append(pcbnew.FromMM(x),pcbnew.FromMM(y))+ board.Add(z)+# Shorter board-to-board heat paths beside the diode cathode, outside its solder pad.+for x in [115.6,119.2]:+ for y in ([90.2,90.9] if x==115.6 else [90.2,91.4]):+ via=pcbnew.PCB_VIA(board);via.SetPosition(pcbnew.VECTOR2I(pcbnew.FromMM(x),pcbnew.FromMM(y)));via.SetWidth(pcbnew.FromMM(.6));via.SetDrill(pcbnew.FromMM(.3));via.SetLayerPair(pcbnew.F_Cu,pcbnew.B_Cu);via.SetNet(nets['LOAD_POS']);board.Add(via)+# Clearance-aware ground connection to the existing lower return plane.+track('GND',pcbnew.F_Cu,(112.728,88.696),(112.7,88.8),.4)+track('GND',pcbnew.F_Cu,(112.7,88.8),(112.7,99.5),.4)+# Restore the displaced signal-ground region; native checks remain mandatory.+track('GND',pcbnew.B_Cu,(117.1, 77.2),(116.0, 77.2),.25)+track('GND',pcbnew.B_Cu,(116.0, 77.2),(115.4, 77.8),.25)+track('GND',pcbnew.B_Cu,(115.4, 77.8),(114.9, 77.8),.25)+track('GND',pcbnew.B_Cu,(114.9, 77.8),(113.8, 78.9),.25)+track('GND',pcbnew.B_Cu,(113.8, 78.9),(106.4, 78.9),.25)+via=pcbnew.PCB_VIA(board);via.SetPosition(pcbnew.VECTOR2I(pcbnew.FromMM(106.4),pcbnew.FromMM(78.9)));via.SetWidth(pcbnew.FromMM(.6));via.SetDrill(pcbnew.FromMM(.3));via.SetLayerPair(pcbnew.F_Cu,pcbnew.B_Cu);via.SetNet(nets['GND']);board.Add(via)+track('GND',pcbnew.F_Cu,(106.4, 78.9),(105.9, 79.4),.25)+track('GND',pcbnew.F_Cu,(105.9, 79.4),(105.9, 79.5),.25)+track('GND',pcbnew.F_Cu,(105.9, 79.5),(105.8, 79.6),.25)+track('GND',pcbnew.F_Cu,(105.8, 79.6),(105.8, 80.1),.25)+track('GND',pcbnew.F_Cu,(105.8, 80.1),(105.3, 80.6),.25)+via=pcbnew.PCB_VIA(board);via.SetPosition(pcbnew.VECTOR2I(pcbnew.FromMM(105.3),pcbnew.FromMM(80.6)));via.SetWidth(pcbnew.FromMM(.6));via.SetDrill(pcbnew.FromMM(.3));via.SetLayerPair(pcbnew.F_Cu,pcbnew.B_Cu);via.SetNet(nets['GND']);board.Add(via)+# Isolated SDT10A45P5-7 feasibility layout; schematic/BOM are not changed here.+old=next(fp for fp in board.GetFootprints() if fp.GetReference()=='D1')+new=pcbnew.FootprintLoad('C:/Users/john/AppData/Local/Programs/KiCad/10.0/share/kicad/footprints/Diode_SMD.pretty','D_PowerDI-5')+assert new is not None+new.SetReference('D1');new.SetValue('SDT10A45P5-7');new.SetFPID(pcbnew.LIB_ID('Diode_SMD','D_PowerDI-5'))+board.Add(new);new.SetOrientationDegrees(180);new.SetPosition(old.GetPosition())+for a,b in [(new.Reference(),old.Reference()),(new.Value(),old.Value())]:+ a.SetPosition(b.GetPosition());a.SetTextAngle(b.GetTextAngle());a.SetTextSize(b.GetTextSize());a.SetTextThickness(b.GetTextThickness());a.SetVisible(b.IsVisible())+for pad in new.Pads():pad.SetNet(nets['LOAD_POS' if pad.GetNumber()=='1' else 'DUT_FUSED'])+board.Remove(old)+# Keep unfilled vias outside the new exposed cathode solder land.+for item in list(board.GetTracks()):+ if item.GetNetname()=='LOAD_POS' and isinstance(item,pcbnew.PCB_VIA):+ p=item.GetPosition();x,y=pcbnew.ToMM(p.x),pcbnew.ToMM(p.y)+ if 112.39<x<117.85 and 89.02<y<92.98:board.Remove(item);continue+ if item.GetNetname()=='GND' and not isinstance(item,pcbnew.PCB_VIA) and item.GetLayer()==pcbnew.F_Cu:+ for getter,setter in [(item.GetStart,item.SetStart),(item.GetEnd,item.SetEnd)]:+ p=getter();x,y=pcbnew.ToMM(p.x),pcbnew.ToMM(p.y)+ if abs(x-112.7)<.001:+ p.x=pcbnew.FromMM(112.25)+ if abs(y-88.8)<.001:p.y=pcbnew.FromMM(89.174)+ setter(p)+# Both anodes explicitly join the existing fused-input junction.+for path in [[(110.6,91),(110.6,90.618),(111.138,90.08)],[(110.6,91),(110.6,91.382),(111.138,91.92)]]:+ for a,b in zip(path,path[1:]):track('DUT_FUSED',pcbnew.F_Cu,a,b,1.0)+changes.append({'kind':'feasibility-replace-footprint','reference':'D1','candidateMPN':'SDT10A45P5-7','footprint':'Diode_SMD:D_PowerDI-5','adopted':False})+for x,y in [[115.2, 93.2], [114.2, 93.2], [116.2, 93.2], [116.6, 88.8], [118.0, 91.0], [113.2, 93.2], [115.0, 94.0], [118.0, 90.0]]:+ via=pcbnew.PCB_VIA(board);via.SetPosition(pcbnew.VECTOR2I(pcbnew.FromMM(x),pcbnew.FromMM(y)));via.SetWidth(pcbnew.FromMM(.6));via.SetDrill(pcbnew.FromMM(.3));via.SetLayerPair(pcbnew.F_Cu,pcbnew.B_Cu);via.SetNet(nets['LOAD_POS']);board.Add(via)+expected_nets={str(item.m_Uuid.AsString()):item.GetNetname() for item in board.GetTracks()}+for zone in board.Zones():+ zone.UnFill()+ zone.SetNeedRefill(True)+board.BuildConnectivity()+actual_nets={str(item.m_Uuid.AsString()):item.GetNetname() for item in board.GetTracks()}+assert actual_nets==expected_nets, {'unexpectedNetChanges':[(k,v,actual_nets.get(k)) for k,v in expected_nets.items() if actual_nets.get(k)!=v]}+for zone in board.Zones():zone.SetNeedRefill(True)+pcbnew.ZONE_FILLER(board).Fill(board.Zones());assert {str(item.m_Uuid.AsString()):item.GetNetname() for item in board.GetTracks()}==expected_nets, 'Zone fill changed copper nets'+pcbnew.SaveBoard(str(out),board)+readback=pcbnew.LoadBoard(str(out))+assert {str(item.m_Uuid.AsString()):item.GetNetname() for item in readback.GetTracks()}==expected_nets, 'Save/readback changed copper nets' +out.with_suffix('.changes.json').write_text(json.dumps({'source':str(source),'candidate':str(out),'changes':changes},indent=2));print(json.dumps({'saved':str(out),'changes':changes}))
tools/hotspot_via_candidate_v81.pyadded+283@@ -0,0 +1,283 @@+"""Run using native desktop KiCad Python; modifies only an isolated board copy."""+import pcbnew,sys,json+from pathlib import Path+source=Path(sys.argv[1]);out=Path(sys.argv[2]);board=pcbnew.LoadBoard(str(source));nets=board.GetNetsByName()+changes=[]+for net,x,y in [('GND',133.0,100.8),('GND',138.0,100.8),('GND',139.0,93.0),('GND',136.5,93.0),('GND',139.4,90.4),('GND',139.4,91.4),('GND',139.4,92.4)]:+ via=pcbnew.PCB_VIA(board);via.SetPosition(pcbnew.VECTOR2I(pcbnew.FromMM(x),pcbnew.FromMM(y)));via.SetWidth(pcbnew.FromMM(.8));via.SetDrill(pcbnew.FromMM(.4));via.SetLayerPair(pcbnew.F_Cu,pcbnew.B_Cu);via.SetNet(nets[net]);board.Add(via);changes.append({'kind':'via','net':net,'at':[x,y],'sizeMm':.8,'drillMm':.4,'uuid':str(via.m_Uuid.AsString())})+for item in board.GetTracks():+ uid=str(item.m_Uuid.AsString())+ if uid=='32514162-b870-4c7a-95a2-b85b8db79257':+ item.SetWidth(pcbnew.FromMM(1.0));item.SetDrill(pcbnew.FromMM(.6));changes.append({'kind':'resize-via','uuid':uid,'net':'LOAD_POS','sizeMm':1.0,'drillMm':.6})+for item in list(board.GetTracks()):+ uid=str(item.m_Uuid.AsString())+ if uid in ['91eed079-74e4-4863-8dc1-f211a19b6ee0','ad353074-a567-44a9-a20a-744b8057c5a9']:+ board.Remove(item);changes.append({'kind':'remove-track','uuid':uid})+ if uid=='aa6c5964-74d4-410e-a69e-ffb2cd9c9999':+ item.SetWidth(pcbnew.FromMM(1.0));item.SetDrill(pcbnew.FromMM(.6));changes.append({'kind':'resize-via','uuid':uid,'sizeMm':1.0,'drillMm':.6})+def track(net,layer,a,b,width):+ t=pcbnew.PCB_TRACK(board);t.SetStart(pcbnew.VECTOR2I(pcbnew.FromMM(a[0]),pcbnew.FromMM(a[1])));t.SetEnd(pcbnew.VECTOR2I(pcbnew.FromMM(b[0]),pcbnew.FromMM(b[1])));t.SetWidth(pcbnew.FromMM(width));t.SetLayer(layer);t.SetNet(nets[net]);board.Add(t);changes.append({'kind':'track','net':net,'start':a,'end':b,'widthMm':width,'layer':board.GetLayerName(layer)})+for a,b in [((127.46,82),(127.46,83.3)),((127.46,83.3),(126.4,84.36)),((126.4,84.36),(126.4,86.5))]:track('LOAD_POS',pcbnew.F_Cu,a,b,1.5)+for a,b in [((139.4,90.4),(139.4,92)),((139.4,92),(138.4,93)),((138.4,93),(136.5,93))]:track('GND',pcbnew.B_Cu,a,b,1.5)+for item in board.GetTracks():+ if isinstance(item,pcbnew.PCB_VIA) and item.GetNetname()=='GND':+ p=item.GetPosition();xy=(round(pcbnew.ToMM(p.x),3),round(pcbnew.ToMM(p.y),3))+ if xy in [(139.4,86.8),(136.5,93.0),(139.4,90.4),(139.4,91.4),(139.4,92.4)]:item.SetWidth(pcbnew.FromMM(1));item.SetDrill(pcbnew.FromMM(.6));changes.append({'kind':'resize-via','at':xy,'sizeMm':1,'drillMm':.6})+for fp in board.GetFootprints():+ delta={'Q2':-.3,'U3':.3,'R7':.1}.get(fp.GetReference())+ if delta:+ pos=fp.GetPosition();pos.x+=pcbnew.FromMM(delta);fp.SetPosition(pos);changes.append({'kind':'move-footprint','ref':fp.GetReference(),'dxMm':delta})+for item in list(board.GetTracks()):+ if item.GetNetname()=='SHUNT_P_SENSE' and isinstance(item,pcbnew.PCB_VIA):+ pos=item.GetPosition()+ if abs(pcbnew.ToMM(pos.x)-123.6)<.001 and abs(pcbnew.ToMM(pos.y)-85.4)<.001:+ pos.x=pcbnew.FromMM(123.3);item.SetPosition(pos);changes.append({'kind':'move-sense-via','from':[123.6,85.4],'to':[123.3,85.4]})+ if isinstance(item,pcbnew.PCB_VIA):continue+ if item.GetNetname()=='SHUNT_P_SENSE':+ for getter,setter in [(item.GetStart,item.SetStart),(item.GetEnd,item.SetEnd)]:+ pos=getter()+ if abs(pcbnew.ToMM(pos.x)-123.6)<.001 and abs(pcbnew.ToMM(pos.y)-85.4)<.001:pos.x=pcbnew.FromMM(123.3);setter(pos)+ if item.GetNetname()=='SHUNT_N_SENSE' and item.GetLayer()==pcbnew.F_Cu:+ for getter,setter in [(item.GetStart,item.SetStart),(item.GetEnd,item.SetEnd)]:+ pos=getter();x,y=pcbnew.ToMM(pos.x),pcbnew.ToMM(pos.y)+ if 128.79<x<129.31 and 87.29<y<88.81:pos.x+=pcbnew.FromMM(.2);setter(pos)+for item in list(board.GetTracks()):+ uid=str(item.m_Uuid.AsString())+ if uid=='32514162-b870-4c7a-95a2-b85b8db79257':+ item.SetPosition(pcbnew.VECTOR2I(pcbnew.FromMM(126.4),pcbnew.FromMM(86.7)))+ if uid=='13d2f936-e4d3-40cb-9bf7-1b6b26ca4cf2':+ item.SetStart(pcbnew.VECTOR2I(pcbnew.FromMM(126.4),pcbnew.FromMM(86.7)));item.SetWidth(pcbnew.FromMM(1.5))+ if uid=='f120f8db-288f-4938-acbf-98ff5e4065df':+ item.SetStart(pcbnew.VECTOR2I(pcbnew.FromMM(133.5),pcbnew.FromMM(84.2)));item.SetEnd(pcbnew.VECTOR2I(pcbnew.FromMM(126.8),pcbnew.FromMM(84.2)));item.SetWidth(pcbnew.FromMM(1.9))+ if not isinstance(item,pcbnew.PCB_VIA) and item.GetNetname()=='LOAD_POS' and item.GetLayer()==pcbnew.F_Cu:+ for getter,setter in [(item.GetStart,item.SetStart),(item.GetEnd,item.SetEnd)]:+ p=getter()+ if abs(pcbnew.ToMM(p.x)-126.4)<.001 and abs(pcbnew.ToMM(p.y)-86.5)<.001:p.y=pcbnew.FromMM(86.7);setter(p)+for item in board.GetTracks():+ if isinstance(item,pcbnew.PCB_VIA) or item.GetLayer()!=pcbnew.B_Cu or item.GetNetname()!='SHUNT_P_SENSE':continue+ for getter,setter in [(item.GetStart,item.SetStart),(item.GetEnd,item.SetEnd)]:+ p=getter();x,y=pcbnew.ToMM(p.x),pcbnew.ToMM(p.y)+ if abs(y-85.4)<.001 and (abs(x-123.3)<.001 or abs(x-132)<.001):+ p.y=pcbnew.FromMM(85.6)+ if abs(x-132)<.001:p.x=pcbnew.FromMM(132.2)+ setter(p)+# Ease the drain right-angle turn; relocate its adjacent setpoint via with endpoints.+track('LOAD_POS',pcbnew.B_Cu,(126.35,89.0),(125.65,89.7),1.5)+# Preserve explicit branch junctions instead of overlapped dangling endpoints.+for item in list(board.GetTracks()):+ uid=str(item.m_Uuid.AsString())+ if uid=='32514162-b870-4c7a-95a2-b85b8db79257':+ item.SetWidth(pcbnew.FromMM(.8));item.SetDrill(pcbnew.FromMM(.4));item.SetPosition(pcbnew.VECTOR2I(pcbnew.FromMM(126.05),pcbnew.FromMM(86.7)))+ if uid in ['b486fb59-b64e-41ee-a1a7-5a72fcf0a780','8e5d5e2b-78f3-4ed2-af96-409d2ea9d57b']:+ a=item.GetStart();b=item.GetEnd();start=(pcbnew.ToMM(a.x),pcbnew.ToMM(a.y));end=(pcbnew.ToMM(b.x),pcbnew.ToMM(b.y))+ mid=(126.35,89.0) if uid=='b486fb59-b64e-41ee-a1a7-5a72fcf0a780' else (125.65,89.7)+ board.Remove(item);track('LOAD_POS',pcbnew.B_Cu,start,mid,1.5);track('LOAD_POS',pcbnew.B_Cu,mid,end,1.5)+via=pcbnew.PCB_VIA(board);via.SetPosition(pcbnew.VECTOR2I(pcbnew.FromMM(126.8),pcbnew.FromMM(86.7)));via.SetWidth(pcbnew.FromMM(.8));via.SetDrill(pcbnew.FromMM(.4));via.SetLayerPair(pcbnew.F_Cu,pcbnew.B_Cu);via.SetNet(nets['LOAD_POS']);board.Add(via)+# Do not enlarge holes through narrow in-plane paths. Keep the three dedicated shunt thermal vias.+for item in list(board.GetTracks()):+ uid=str(item.m_Uuid.AsString())+ if uid in ['34d3207a-b14a-40d8-be82-5b108c2b867b','9a71af6b-ea83-4f97-b7f6-3f69253c7314']:+ board.Remove(item);continue+ if isinstance(item,pcbnew.PCB_VIA):+ p=item.GetPosition();x,y=pcbnew.ToMM(p.x),pcbnew.ToMM(p.y)+ if uid=='aa6c5964-74d4-410e-a69e-ffb2cd9c9999' or (item.GetNetname()=='GND' and abs(x-139.4)<.001 and abs(y-86.8)<.001):+ item.SetWidth(pcbnew.FromMM(.8));item.SetDrill(pcbnew.FromMM(.4))+# Move the transfer hole off the center of the shunt bus; remove a nearly unused ground hole through the return neck.+for item in list(board.GetTracks()):+ if not isinstance(item,pcbnew.PCB_VIA):continue+ p=item.GetPosition();x,y=pcbnew.ToMM(p.x),pcbnew.ToMM(p.y)+ if item.GetNetname()=='GND' and abs(x-139.4)<.001 and abs(y-86.8)<.001:board.Remove(item)+ if str(item.m_Uuid.AsString())=='aa6c5964-74d4-410e-a69e-ffb2cd9c9999':item.SetPosition(pcbnew.VECTOR2I(pcbnew.FromMM(133.7),pcbnew.FromMM(83.6)))+track('SHUNT_P',pcbnew.F_Cu,(133.7,83.6),(133.8,84.2),1.2)+# Preserve the full plated Q1 source connection; remove its redundant neck-drilling spur.+for item in list(board.GetTracks()):+ uid=str(item.m_Uuid.AsString())+ if uid in ['cd48c727-15bf-4276-b4b8-ae4d936d829d','30d9e8bc-805a-4b1d-8a79-ad625ee59388']:+ board.Remove(item);continue+ if isinstance(item,pcbnew.PCB_VIA) and item.GetNetname()=='GND':+ p=item.GetPosition();x,y=pcbnew.ToMM(p.x),pcbnew.ToMM(p.y)+ if abs(x-139.4)<.001 and any(abs(y-t)<.001 for t in [90.4,91.4,92.4]):p.x=pcbnew.FromMM(139.8);item.SetPosition(p)+# Share vertical current with smaller holes and widen the plated-source exit.+for item in board.GetTracks():+ uid=str(item.m_Uuid.AsString())+ if uid in ['2f235494-7976-4048-be2d-b71507b2b45e','ec56cc46-09f5-4ff9-a1b6-61699db68686']:item.SetWidth(pcbnew.FromMM(1.75))+ if isinstance(item,pcbnew.PCB_VIA) and item.GetNetname()=='GND':+ p=item.GetPosition();x,y=pcbnew.ToMM(p.x),pcbnew.ToMM(p.y)+ if abs(x-139.8)<.001 and any(abs(y-t)<.001 for t in [90.4,91.4,92.4]):+ p.x=pcbnew.FromMM(139.4);item.SetPosition(p);item.SetWidth(pcbnew.FromMM(.6));item.SetDrill(pcbnew.FromMM(.3))+for item in board.GetTracks():+ if str(item.m_Uuid.AsString())=='f120f8db-288f-4938-acbf-98ff5e4065df':+ item.SetStart(pcbnew.VECTOR2I(pcbnew.FromMM(133.5),pcbnew.FromMM(84.25)));item.SetEnd(pcbnew.VECTOR2I(pcbnew.FromMM(126.8),pcbnew.FromMM(84.25)));item.SetWidth(pcbnew.FromMM(2.0))+# Fine-grid hotspot revision: spread the return transfer and soften the two power necks.+for item in board.GetTracks():+ if isinstance(item,pcbnew.PCB_VIA):+ p=item.GetPosition();x,y=pcbnew.ToMM(p.x),pcbnew.ToMM(p.y)+ if item.GetNetname()=='GND' and abs(x-139.4)<.001 and abs(y-90.4)<.001:+ p.x=pcbnew.FromMM(139.0);item.SetPosition(p)+ continue+ uid=str(item.m_Uuid.AsString())+ if uid in ['32cd5f76-8c9c-4680-a071-fe4f71064f23','cecc8caa-bb93-470d-a793-50ae2c93732f']:item.SetWidth(pcbnew.FromMM(1.6))+ if item.GetNetname()=='LOAD_POS' and item.GetLayer()==pcbnew.B_Cu:+ a=item.GetStart();b=item.GetEnd()+ if abs(pcbnew.ToMM(a.y)-89.7)<.001 and abs(pcbnew.ToMM(b.y)-89.7)<.001:item.SetWidth(pcbnew.FromMM(1.75))+via=pcbnew.PCB_VIA(board);via.SetPosition(pcbnew.VECTOR2I(pcbnew.FromMM(139.8),pcbnew.FromMM(90.4)));via.SetWidth(pcbnew.FromMM(.6));via.SetDrill(pcbnew.FromMM(.3));via.SetLayerPair(pcbnew.F_Cu,pcbnew.B_Cu);via.SetNet(nets['GND']);board.Add(via)+track('GND',pcbnew.B_Cu,(139.0,90.4),(139.8,90.4),1.2)+# Keep clearance beside the setpoint via; widen only the diode-spread junction.+for item in list(board.GetTracks()):+ if isinstance(item,pcbnew.PCB_VIA) or item.GetNetname()!='LOAD_POS' or item.GetLayer()!=pcbnew.B_Cu:continue+ a=item.GetStart();b=item.GetEnd();ax,ay=pcbnew.ToMM(a.x),pcbnew.ToMM(a.y);bx,by=pcbnew.ToMM(b.x),pcbnew.ToMM(b.y)+ if abs(ay-89.7)<.001 and abs(by-89.7)<.001:+ item.SetWidth(pcbnew.FromMM(1.5))+ if min(ax,bx)<120 and max(ax,bx)>125:+ board.Remove(item);track('LOAD_POS',pcbnew.B_Cu,(119.8,89.7),(123.0,89.7),1.75);track('LOAD_POS',pcbnew.B_Cu,(123.0,89.7),(125.65,89.7),1.5)+# Fill the inside shunt-bus bend with a diagonal copper path.+track('SHUNT_P',pcbnew.B_Cu,(133.0,84.7),(134.7,85.5),1.2)+# Spread the plated-source exit without moving the gate or clamp circuit.+track('SHUNT_P',pcbnew.B_Cu,(125.1,81.8),(125.35,82.7),1.9)+for item in board.GetTracks():+ if isinstance(item,pcbnew.PCB_VIA):continue+ if item.GetNetname()=='LOAD_POS' and item.GetLayer()==pcbnew.B_Cu:+ for getter,setter in [(item.GetStart,item.SetStart),(item.GetEnd,item.SetEnd)]:+ p=getter();x,y=pcbnew.ToMM(p.x),pcbnew.ToMM(p.y)+ if abs(x-126.35)<.001 and abs(y-89)<.001:p.y=pcbnew.FromMM(88.85);setter(p)+ if abs(x-125.65)<.001 and abs(y-89.7)<.001:p.x=pcbnew.FromMM(125.5);setter(p)+for x in [139.0,139.8]:+ via=pcbnew.PCB_VIA(board);via.SetPosition(pcbnew.VECTOR2I(pcbnew.FromMM(x),pcbnew.FromMM(89.9)));via.SetWidth(pcbnew.FromMM(.6));via.SetDrill(pcbnew.FromMM(.3));via.SetLayerPair(pcbnew.F_Cu,pcbnew.B_Cu);via.SetNet(nets['GND']);board.Add(via)+track('GND',pcbnew.B_Cu,(139,89.9),(139.8,89.9),.6)+track('GND',pcbnew.B_Cu,(139.4,89.9),(139.4,90.4),.6)+# Give the ground transfer more uninterrupted top copper; keep UART endpoints fixed.+for item in list(board.GetTracks()):+ if isinstance(item,pcbnew.PCB_VIA) or item.GetLayer()!=pcbnew.F_Cu:continue+ a=item.GetStart();b=item.GetEnd();ax,ay=pcbnew.ToMM(a.x),pcbnew.ToMM(a.y);bx,by=pcbnew.ToMM(b.x),pcbnew.ToMM(b.y)+ if item.GetNetname()=='UART_RX' and abs(ax-140.7)<.001 and abs(bx-140.7)<.001 and abs(ay-83.5)<.001 and abs(by-93.2)<.001:+ board.Remove(item)+ points=[(140.7,83.5),(140.7,88.0),(141.0,88.3),(141.0,91.8),(140.7,92.1),(140.7,93.2)]+ for p,q in zip(points,points[1:]):track('UART_RX',pcbnew.F_Cu,p,q,.25)+ if item.GetNetname()=='UART_TX' and abs(ax-137.9)<.001 and abs(bx-137.9)<.001 and abs(ay-83.7)<.001 and abs(by-98.1)<.001:+ board.Remove(item)+ points=[(137.9,83.7),(137.9,89.0),(137.4,89.5),(137.4,93.3),(137.9,93.8),(137.9,98.1)]+ for p,q in zip(points,points[1:]):track('UART_TX',pcbnew.F_Cu,p,q,.25)+# Move the setpoint layer transfer clear of a wider drain bend, preserving its net/endpoints.+for item in board.GetTracks():+ if item.GetNetname()=='I_SET':+ endpoints=[(item.GetPosition,item.SetPosition)] if isinstance(item,pcbnew.PCB_VIA) else [(item.GetStart,item.SetStart),(item.GetEnd,item.SetEnd)]+ for getter,setter in endpoints:+ p=getter()+ if abs(pcbnew.ToMM(p.x)-125)<.001 and abs(pcbnew.ToMM(p.y)-88.4)<.001:p.y=pcbnew.FromMM(88);setter(p)+ if isinstance(item,pcbnew.PCB_VIA) or item.GetNetname()!='LOAD_POS' or item.GetLayer()!=pcbnew.B_Cu:continue+ for getter,setter in [(item.GetStart,item.SetStart),(item.GetEnd,item.SetEnd)]:+ p=getter();x,y=pcbnew.ToMM(p.x),pcbnew.ToMM(p.y)+ if abs(x-126.35)<.001 and abs(y-88.85)<.001:p.y=pcbnew.FromMM(89);setter(p)+ if abs(x-125.5)<.001 and abs(y-89.7)<.001:p.x=pcbnew.FromMM(125.65);setter(p)+ a=item.GetStart();b=item.GetEnd();ax,ay=pcbnew.ToMM(a.x),pcbnew.ToMM(a.y);bx,by=pcbnew.ToMM(b.x),pcbnew.ToMM(b.y)+ if abs(ay-89.7)<.001 and abs(by-89.7)<.001:item.SetWidth(pcbnew.FromMM(1.75))+ if abs(ax-bx)>.1 and abs(ay-by)>.1 and min(ax,bx)>125 and max(ax,bx)<127 and min(ay,by)>88.8 and max(ay,by)<89.8:item.SetWidth(pcbnew.FromMM(1.75))+# This local gate-drive segment carries the servo/gate current, not DUT current.+# Keep 0.5 mm copper and create clearance to the small setpoint via.+for item in board.GetTracks():+ if str(item.m_Uuid.AsString())=='a4e6790e-b900-47a1-bf1e-b5fb98adc8c7':item.SetWidth(pcbnew.FromMM(.5))+# Separate thermal-spread experiment, not a replacement of the accepted zone plan.+for z in list(board.Zones()):+ if not z.GetZoneName().startswith('D1 relocated heat spread'):continue+ layer=z.GetLayer();name=z.GetZoneName();board.Remove(z)+ z=pcbnew.ZONE(board);z.SetLayer(layer);z.SetNet(nets['LOAD_POS']);z.SetZoneName(name);z.SetAssignedPriority(3);z.SetLocalClearance(pcbnew.FromMM(.2));z.SetMinThickness(pcbnew.FromMM(.25));z.SetPadConnection(pcbnew.ZONE_CONNECTION_FULL);z.Outline().NewOutline()+ points=[(114,82),(132,82),(132,99),(104,99),(104,89.4),(114,89.4)] if layer==pcbnew.F_Cu else [(118.8,70),(132,70),(132,99),(104,99),(104,89.4),(114,89.4),(114,84),(118.8,84)]+ for x,y in points:z.Outline().Append(pcbnew.FromMM(x),pcbnew.FromMM(y))+ board.Add(z)+# Shorter board-to-board heat paths beside the diode cathode, outside its solder pad.+for x in [115.6,119.2]:+ for y in ([90.2,90.9] if x==115.6 else [90.2,91.4]):+ via=pcbnew.PCB_VIA(board);via.SetPosition(pcbnew.VECTOR2I(pcbnew.FromMM(x),pcbnew.FromMM(y)));via.SetWidth(pcbnew.FromMM(.6));via.SetDrill(pcbnew.FromMM(.3));via.SetLayerPair(pcbnew.F_Cu,pcbnew.B_Cu);via.SetNet(nets['LOAD_POS']);board.Add(via)+# Clearance-aware ground connection to the existing lower return plane.+track('GND',pcbnew.F_Cu,(112.728,88.696),(112.7,88.8),.4)+track('GND',pcbnew.F_Cu,(112.7,88.8),(112.7,99.5),.4)+# Restore the displaced signal-ground region; native checks remain mandatory.+track('GND',pcbnew.B_Cu,(117.1, 77.2),(116.0, 77.2),.25)+track('GND',pcbnew.B_Cu,(116.0, 77.2),(115.4, 77.8),.25)+track('GND',pcbnew.B_Cu,(115.4, 77.8),(114.9, 77.8),.25)+track('GND',pcbnew.B_Cu,(114.9, 77.8),(113.8, 78.9),.25)+track('GND',pcbnew.B_Cu,(113.8, 78.9),(106.4, 78.9),.25)+via=pcbnew.PCB_VIA(board);via.SetPosition(pcbnew.VECTOR2I(pcbnew.FromMM(106.4),pcbnew.FromMM(78.9)));via.SetWidth(pcbnew.FromMM(.6));via.SetDrill(pcbnew.FromMM(.3));via.SetLayerPair(pcbnew.F_Cu,pcbnew.B_Cu);via.SetNet(nets['GND']);board.Add(via)+track('GND',pcbnew.F_Cu,(106.4, 78.9),(105.9, 79.4),.25)+track('GND',pcbnew.F_Cu,(105.9, 79.4),(105.9, 79.5),.25)+track('GND',pcbnew.F_Cu,(105.9, 79.5),(105.8, 79.6),.25)+track('GND',pcbnew.F_Cu,(105.8, 79.6),(105.8, 80.1),.25)+track('GND',pcbnew.F_Cu,(105.8, 80.1),(105.3, 80.6),.25)+via=pcbnew.PCB_VIA(board);via.SetPosition(pcbnew.VECTOR2I(pcbnew.FromMM(105.3),pcbnew.FromMM(80.6)));via.SetWidth(pcbnew.FromMM(.6));via.SetDrill(pcbnew.FromMM(.3));via.SetLayerPair(pcbnew.F_Cu,pcbnew.B_Cu);via.SetNet(nets['GND']);board.Add(via)+# Isolated SDT10A45P5-7 feasibility layout; schematic/BOM are not changed here.+old=next(fp for fp in board.GetFootprints() if fp.GetReference()=='D1')+new=pcbnew.FootprintLoad('C:/Users/john/AppData/Local/Programs/KiCad/10.0/share/kicad/footprints/Diode_SMD.pretty','D_PowerDI-5')+assert new is not None+new.SetReference('D1');new.SetValue('SDT10A45P5-7');new.SetFPID(pcbnew.LIB_ID('Diode_SMD','D_PowerDI-5'))+board.Add(new);new.SetOrientationDegrees(180);new.SetPosition(old.GetPosition())+for a,b in [(new.Reference(),old.Reference()),(new.Value(),old.Value())]:+ a.SetPosition(b.GetPosition());a.SetTextAngle(b.GetTextAngle());a.SetTextSize(b.GetTextSize());a.SetTextThickness(b.GetTextThickness());a.SetVisible(b.IsVisible())+for pad in new.Pads():pad.SetNet(nets['LOAD_POS' if pad.GetNumber()=='1' else 'DUT_FUSED'])+board.Remove(old)+# Keep unfilled vias outside the new exposed cathode solder land.+for item in list(board.GetTracks()):+ if item.GetNetname()=='LOAD_POS' and isinstance(item,pcbnew.PCB_VIA):+ p=item.GetPosition();x,y=pcbnew.ToMM(p.x),pcbnew.ToMM(p.y)+ if 112.39<x<117.85 and 89.02<y<92.98:board.Remove(item);continue+ if item.GetNetname()=='GND' and not isinstance(item,pcbnew.PCB_VIA) and item.GetLayer()==pcbnew.F_Cu:+ for getter,setter in [(item.GetStart,item.SetStart),(item.GetEnd,item.SetEnd)]:+ p=getter();x,y=pcbnew.ToMM(p.x),pcbnew.ToMM(p.y)+ if abs(x-112.7)<.001:+ p.x=pcbnew.FromMM(112.25)+ if abs(y-88.8)<.001:p.y=pcbnew.FromMM(89.174)+ setter(p)+# Both anodes explicitly join the existing fused-input junction.+for path in [[(110.6,91),(110.6,90.618),(111.138,90.08)],[(110.6,91),(110.6,91.382),(111.138,91.92)]]:+ for a,b in zip(path,path[1:]):track('DUT_FUSED',pcbnew.F_Cu,a,b,1.0)+changes.append({'kind':'feasibility-replace-footprint','reference':'D1','candidateMPN':'SDT10A45P5-7','footprint':'Diode_SMD:D_PowerDI-5','adopted':False})+for x,y in [[115.2, 93.2], [114.2, 93.2], [116.2, 93.2], [116.6, 88.8], [118.0, 91.0], [113.2, 93.2], [115.0, 94.0], [118.0, 90.0]]:+ via=pcbnew.PCB_VIA(board);via.SetPosition(pcbnew.VECTOR2I(pcbnew.FromMM(x),pcbnew.FromMM(y)));via.SetWidth(pcbnew.FromMM(.6));via.SetDrill(pcbnew.FromMM(.3));via.SetLayerPair(pcbnew.F_Cu,pcbnew.B_Cu);via.SetNet(nets['LOAD_POS']);board.Add(via)+# Relocate gate transfer and use a broad source exit before the horizontal bus.+for item in list(board.GetTracks()):+ uid=str(item.m_Uuid.AsString())+ if uid in ['2f235494-7976-4048-be2d-b71507b2b45e','ec56cc46-09f5-4ff9-a1b6-61699db68686','18b5314d-bef3-4f18-ac3d-13f344b6d6c0','eadf962f-0989-4ddd-9e5f-0a34d0ef01ec','85b410dd-d11b-4c9c-8b1a-8aaedff6e92f']:board.Remove(item);continue+ if item.GetNetname()=='SHUNT_P' and not isinstance(item,pcbnew.PCB_VIA):+ p=item.GetStart()+ if abs(pcbnew.ToMM(p.x)-125.1)<.001 and abs(pcbnew.ToMM(p.y)-81.8)<.001:board.Remove(item);continue+ if item.GetNetname()=='GATE':+ endpoints=[(item.GetPosition,item.SetPosition)] if isinstance(item,pcbnew.PCB_VIA) else [(item.GetStart,item.SetStart),(item.GetEnd,item.SetEnd)]+ for getter,setter in endpoints:+ p=getter()+ if abs(pcbnew.ToMM(p.x)-124.5)<.001 and abs(pcbnew.ToMM(p.y)-84.2)<.001:p.x=pcbnew.FromMM(123.4);p.y=pcbnew.FromMM(82.7);setter(p)+ if isinstance(item,pcbnew.PCB_VIA) and uid=='3d518e9c-a66d-4cd2-8d4a-af76fb9a2e46':item.SetWidth(pcbnew.FromMM(.6));item.SetDrill(pcbnew.FromMM(.3))+track('SHUNT_P',pcbnew.B_Cu,(124.92,82),(124.92,83.7),1.9)+track('SHUNT_P',pcbnew.B_Cu,(124.92,83.7),(125.62,84.4),1.9)+track('SHUNT_P',pcbnew.B_Cu,(125.62,84.4),(126.8,84.4),2.3)+track('GATE',pcbnew.B_Cu,(123.4,81.3),(123.4,82.7),.25)+# These two local paths carry gate/base control current, not DUT current.+for item in board.GetTracks():+ if isinstance(item,pcbnew.PCB_VIA):continue+ if item.GetLayer()==pcbnew.F_Cu and item.GetNetname() in ['GATE','CLAMP_BASE']:+ a=item.GetStart();b=item.GetEnd()+ if max(pcbnew.ToMM(a.x),pcbnew.ToMM(b.x))<=125 and min(pcbnew.ToMM(a.y),pcbnew.ToMM(b.y))>=81 and max(pcbnew.ToMM(a.y),pcbnew.ToMM(b.y))<=85:item.SetWidth(pcbnew.FromMM(.25))+# Preserve the original pad-exit spreading copper as well as the wider lower bus.+track('SHUNT_P',pcbnew.B_Cu,(125.1,81.8),(125.35,82.7),1.9)+# Isolated land-pattern study: preserve the 1.1 mm drill and preferred 0.25 mm ring.+# A project footprint variant and assembly review are required before adoption.+for fp in board.GetFootprints():+ if fp.GetReference()=='Q1':+ for pad in fp.Pads():+ if pad.GetNumber()=='2':pad.SetSize(pcbnew.VECTOR2I(pcbnew.FromMM(1.6),pcbnew.FromMM(2.0)))+for item in list(board.GetTracks()):+ if isinstance(item,pcbnew.PCB_VIA) or item.GetNetname()!='SHUNT_P' or item.GetLayer()!=pcbnew.B_Cu:continue+ p=item.GetStart()+ if abs(pcbnew.ToMM(p.x)-125.1)<.001 and abs(pcbnew.ToMM(p.y)-81.8)<.001:board.Remove(item)+track('SHUNT_P',pcbnew.B_Cu,(125.1,82.0),(125.45,82.7),2.0)+expected_nets={str(item.m_Uuid.AsString()):item.GetNetname() for item in board.GetTracks()}+for zone in board.Zones():+ zone.UnFill()+ zone.SetNeedRefill(True)+board.BuildConnectivity()+actual_nets={str(item.m_Uuid.AsString()):item.GetNetname() for item in board.GetTracks()}+assert actual_nets==expected_nets, {'unexpectedNetChanges':[(k,v,actual_nets.get(k)) for k,v in expected_nets.items() if actual_nets.get(k)!=v]}+for zone in board.Zones():zone.SetNeedRefill(True)+pcbnew.ZONE_FILLER(board).Fill(board.Zones());assert {str(item.m_Uuid.AsString()):item.GetNetname() for item in board.GetTracks()}==expected_nets, 'Zone fill changed copper nets'+pcbnew.SaveBoard(str(out),board)+readback=pcbnew.LoadBoard(str(out))+assert {str(item.m_Uuid.AsString()):item.GetNetname() for item in readback.GetTracks()}==expected_nets, 'Save/readback changed copper nets' +out.with_suffix('.changes.json').write_text(json.dumps({'source':str(source),'candidate':str(out),'changes':changes},indent=2));print(json.dumps({'saved':str(out),'changes':changes}))
tools/hotspot_via_candidate_v82.pyadded+313@@ -0,0 +1,313 @@+"""Run using native desktop KiCad Python; modifies only an isolated board copy."""+import pcbnew,sys,json+from pathlib import Path+source=Path(sys.argv[1]);out=Path(sys.argv[2]);board=pcbnew.LoadBoard(str(source));nets=board.GetNetsByName()+changes=[]+for net,x,y in [('GND',133.0,100.8),('GND',138.0,100.8),('GND',139.0,93.0),('GND',136.5,93.0),('GND',139.4,90.4),('GND',139.4,91.4),('GND',139.4,92.4)]:+ via=pcbnew.PCB_VIA(board);via.SetPosition(pcbnew.VECTOR2I(pcbnew.FromMM(x),pcbnew.FromMM(y)));via.SetWidth(pcbnew.FromMM(.8));via.SetDrill(pcbnew.FromMM(.4));via.SetLayerPair(pcbnew.F_Cu,pcbnew.B_Cu);via.SetNet(nets[net]);board.Add(via);changes.append({'kind':'via','net':net,'at':[x,y],'sizeMm':.8,'drillMm':.4,'uuid':str(via.m_Uuid.AsString())})+for item in board.GetTracks():+ uid=str(item.m_Uuid.AsString())+ if uid=='32514162-b870-4c7a-95a2-b85b8db79257':+ item.SetWidth(pcbnew.FromMM(1.0));item.SetDrill(pcbnew.FromMM(.6));changes.append({'kind':'resize-via','uuid':uid,'net':'LOAD_POS','sizeMm':1.0,'drillMm':.6})+for item in list(board.GetTracks()):+ uid=str(item.m_Uuid.AsString())+ if uid in ['91eed079-74e4-4863-8dc1-f211a19b6ee0','ad353074-a567-44a9-a20a-744b8057c5a9']:+ board.Remove(item);changes.append({'kind':'remove-track','uuid':uid})+ if uid=='aa6c5964-74d4-410e-a69e-ffb2cd9c9999':+ item.SetWidth(pcbnew.FromMM(1.0));item.SetDrill(pcbnew.FromMM(.6));changes.append({'kind':'resize-via','uuid':uid,'sizeMm':1.0,'drillMm':.6})+def track(net,layer,a,b,width):+ t=pcbnew.PCB_TRACK(board);t.SetStart(pcbnew.VECTOR2I(pcbnew.FromMM(a[0]),pcbnew.FromMM(a[1])));t.SetEnd(pcbnew.VECTOR2I(pcbnew.FromMM(b[0]),pcbnew.FromMM(b[1])));t.SetWidth(pcbnew.FromMM(width));t.SetLayer(layer);t.SetNet(nets[net]);board.Add(t);changes.append({'kind':'track','net':net,'start':a,'end':b,'widthMm':width,'layer':board.GetLayerName(layer)})+for a,b in [((127.46,82),(127.46,83.3)),((127.46,83.3),(126.4,84.36)),((126.4,84.36),(126.4,86.5))]:track('LOAD_POS',pcbnew.F_Cu,a,b,1.5)+for a,b in [((139.4,90.4),(139.4,92)),((139.4,92),(138.4,93)),((138.4,93),(136.5,93))]:track('GND',pcbnew.B_Cu,a,b,1.5)+for item in board.GetTracks():+ if isinstance(item,pcbnew.PCB_VIA) and item.GetNetname()=='GND':+ p=item.GetPosition();xy=(round(pcbnew.ToMM(p.x),3),round(pcbnew.ToMM(p.y),3))+ if xy in [(139.4,86.8),(136.5,93.0),(139.4,90.4),(139.4,91.4),(139.4,92.4)]:item.SetWidth(pcbnew.FromMM(1));item.SetDrill(pcbnew.FromMM(.6));changes.append({'kind':'resize-via','at':xy,'sizeMm':1,'drillMm':.6})+for fp in board.GetFootprints():+ delta={'Q2':-.3,'U3':.3,'R7':.1}.get(fp.GetReference())+ if delta:+ pos=fp.GetPosition();pos.x+=pcbnew.FromMM(delta);fp.SetPosition(pos);changes.append({'kind':'move-footprint','ref':fp.GetReference(),'dxMm':delta})+for item in list(board.GetTracks()):+ if item.GetNetname()=='SHUNT_P_SENSE' and isinstance(item,pcbnew.PCB_VIA):+ pos=item.GetPosition()+ if abs(pcbnew.ToMM(pos.x)-123.6)<.001 and abs(pcbnew.ToMM(pos.y)-85.4)<.001:+ pos.x=pcbnew.FromMM(123.3);item.SetPosition(pos);changes.append({'kind':'move-sense-via','from':[123.6,85.4],'to':[123.3,85.4]})+ if isinstance(item,pcbnew.PCB_VIA):continue+ if item.GetNetname()=='SHUNT_P_SENSE':+ for getter,setter in [(item.GetStart,item.SetStart),(item.GetEnd,item.SetEnd)]:+ pos=getter()+ if abs(pcbnew.ToMM(pos.x)-123.6)<.001 and abs(pcbnew.ToMM(pos.y)-85.4)<.001:pos.x=pcbnew.FromMM(123.3);setter(pos)+ if item.GetNetname()=='SHUNT_N_SENSE' and item.GetLayer()==pcbnew.F_Cu:+ for getter,setter in [(item.GetStart,item.SetStart),(item.GetEnd,item.SetEnd)]:+ pos=getter();x,y=pcbnew.ToMM(pos.x),pcbnew.ToMM(pos.y)+ if 128.79<x<129.31 and 87.29<y<88.81:pos.x+=pcbnew.FromMM(.2);setter(pos)+for item in list(board.GetTracks()):+ uid=str(item.m_Uuid.AsString())+ if uid=='32514162-b870-4c7a-95a2-b85b8db79257':+ item.SetPosition(pcbnew.VECTOR2I(pcbnew.FromMM(126.4),pcbnew.FromMM(86.7)))+ if uid=='13d2f936-e4d3-40cb-9bf7-1b6b26ca4cf2':+ item.SetStart(pcbnew.VECTOR2I(pcbnew.FromMM(126.4),pcbnew.FromMM(86.7)));item.SetWidth(pcbnew.FromMM(1.5))+ if uid=='f120f8db-288f-4938-acbf-98ff5e4065df':+ item.SetStart(pcbnew.VECTOR2I(pcbnew.FromMM(133.5),pcbnew.FromMM(84.2)));item.SetEnd(pcbnew.VECTOR2I(pcbnew.FromMM(126.8),pcbnew.FromMM(84.2)));item.SetWidth(pcbnew.FromMM(1.9))+ if not isinstance(item,pcbnew.PCB_VIA) and item.GetNetname()=='LOAD_POS' and item.GetLayer()==pcbnew.F_Cu:+ for getter,setter in [(item.GetStart,item.SetStart),(item.GetEnd,item.SetEnd)]:+ p=getter()+ if abs(pcbnew.ToMM(p.x)-126.4)<.001 and abs(pcbnew.ToMM(p.y)-86.5)<.001:p.y=pcbnew.FromMM(86.7);setter(p)+for item in board.GetTracks():+ if isinstance(item,pcbnew.PCB_VIA) or item.GetLayer()!=pcbnew.B_Cu or item.GetNetname()!='SHUNT_P_SENSE':continue+ for getter,setter in [(item.GetStart,item.SetStart),(item.GetEnd,item.SetEnd)]:+ p=getter();x,y=pcbnew.ToMM(p.x),pcbnew.ToMM(p.y)+ if abs(y-85.4)<.001 and (abs(x-123.3)<.001 or abs(x-132)<.001):+ p.y=pcbnew.FromMM(85.6)+ if abs(x-132)<.001:p.x=pcbnew.FromMM(132.2)+ setter(p)+# Ease the drain right-angle turn; relocate its adjacent setpoint via with endpoints.+track('LOAD_POS',pcbnew.B_Cu,(126.35,89.0),(125.65,89.7),1.5)+# Preserve explicit branch junctions instead of overlapped dangling endpoints.+for item in list(board.GetTracks()):+ uid=str(item.m_Uuid.AsString())+ if uid=='32514162-b870-4c7a-95a2-b85b8db79257':+ item.SetWidth(pcbnew.FromMM(.8));item.SetDrill(pcbnew.FromMM(.4));item.SetPosition(pcbnew.VECTOR2I(pcbnew.FromMM(126.05),pcbnew.FromMM(86.7)))+ if uid in ['b486fb59-b64e-41ee-a1a7-5a72fcf0a780','8e5d5e2b-78f3-4ed2-af96-409d2ea9d57b']:+ a=item.GetStart();b=item.GetEnd();start=(pcbnew.ToMM(a.x),pcbnew.ToMM(a.y));end=(pcbnew.ToMM(b.x),pcbnew.ToMM(b.y))+ mid=(126.35,89.0) if uid=='b486fb59-b64e-41ee-a1a7-5a72fcf0a780' else (125.65,89.7)+ board.Remove(item);track('LOAD_POS',pcbnew.B_Cu,start,mid,1.5);track('LOAD_POS',pcbnew.B_Cu,mid,end,1.5)+via=pcbnew.PCB_VIA(board);via.SetPosition(pcbnew.VECTOR2I(pcbnew.FromMM(126.8),pcbnew.FromMM(86.7)));via.SetWidth(pcbnew.FromMM(.8));via.SetDrill(pcbnew.FromMM(.4));via.SetLayerPair(pcbnew.F_Cu,pcbnew.B_Cu);via.SetNet(nets['LOAD_POS']);board.Add(via)+# Do not enlarge holes through narrow in-plane paths. Keep the three dedicated shunt thermal vias.+for item in list(board.GetTracks()):+ uid=str(item.m_Uuid.AsString())+ if uid in ['34d3207a-b14a-40d8-be82-5b108c2b867b','9a71af6b-ea83-4f97-b7f6-3f69253c7314']:+ board.Remove(item);continue+ if isinstance(item,pcbnew.PCB_VIA):+ p=item.GetPosition();x,y=pcbnew.ToMM(p.x),pcbnew.ToMM(p.y)+ if uid=='aa6c5964-74d4-410e-a69e-ffb2cd9c9999' or (item.GetNetname()=='GND' and abs(x-139.4)<.001 and abs(y-86.8)<.001):+ item.SetWidth(pcbnew.FromMM(.8));item.SetDrill(pcbnew.FromMM(.4))+# Move the transfer hole off the center of the shunt bus; remove a nearly unused ground hole through the return neck.+for item in list(board.GetTracks()):+ if not isinstance(item,pcbnew.PCB_VIA):continue+ p=item.GetPosition();x,y=pcbnew.ToMM(p.x),pcbnew.ToMM(p.y)+ if item.GetNetname()=='GND' and abs(x-139.4)<.001 and abs(y-86.8)<.001:board.Remove(item)+ if str(item.m_Uuid.AsString())=='aa6c5964-74d4-410e-a69e-ffb2cd9c9999':item.SetPosition(pcbnew.VECTOR2I(pcbnew.FromMM(133.7),pcbnew.FromMM(83.6)))+track('SHUNT_P',pcbnew.F_Cu,(133.7,83.6),(133.8,84.2),1.2)+# Preserve the full plated Q1 source connection; remove its redundant neck-drilling spur.+for item in list(board.GetTracks()):+ uid=str(item.m_Uuid.AsString())+ if uid in ['cd48c727-15bf-4276-b4b8-ae4d936d829d','30d9e8bc-805a-4b1d-8a79-ad625ee59388']:+ board.Remove(item);continue+ if isinstance(item,pcbnew.PCB_VIA) and item.GetNetname()=='GND':+ p=item.GetPosition();x,y=pcbnew.ToMM(p.x),pcbnew.ToMM(p.y)+ if abs(x-139.4)<.001 and any(abs(y-t)<.001 for t in [90.4,91.4,92.4]):p.x=pcbnew.FromMM(139.8);item.SetPosition(p)+# Share vertical current with smaller holes and widen the plated-source exit.+for item in board.GetTracks():+ uid=str(item.m_Uuid.AsString())+ if uid in ['2f235494-7976-4048-be2d-b71507b2b45e','ec56cc46-09f5-4ff9-a1b6-61699db68686']:item.SetWidth(pcbnew.FromMM(1.75))+ if isinstance(item,pcbnew.PCB_VIA) and item.GetNetname()=='GND':+ p=item.GetPosition();x,y=pcbnew.ToMM(p.x),pcbnew.ToMM(p.y)+ if abs(x-139.8)<.001 and any(abs(y-t)<.001 for t in [90.4,91.4,92.4]):+ p.x=pcbnew.FromMM(139.4);item.SetPosition(p);item.SetWidth(pcbnew.FromMM(.6));item.SetDrill(pcbnew.FromMM(.3))+for item in board.GetTracks():+ if str(item.m_Uuid.AsString())=='f120f8db-288f-4938-acbf-98ff5e4065df':+ item.SetStart(pcbnew.VECTOR2I(pcbnew.FromMM(133.5),pcbnew.FromMM(84.25)));item.SetEnd(pcbnew.VECTOR2I(pcbnew.FromMM(126.8),pcbnew.FromMM(84.25)));item.SetWidth(pcbnew.FromMM(2.0))+# Fine-grid hotspot revision: spread the return transfer and soften the two power necks.+for item in board.GetTracks():+ if isinstance(item,pcbnew.PCB_VIA):+ p=item.GetPosition();x,y=pcbnew.ToMM(p.x),pcbnew.ToMM(p.y)+ if item.GetNetname()=='GND' and abs(x-139.4)<.001 and abs(y-90.4)<.001:+ p.x=pcbnew.FromMM(139.0);item.SetPosition(p)+ continue+ uid=str(item.m_Uuid.AsString())+ if uid in ['32cd5f76-8c9c-4680-a071-fe4f71064f23','cecc8caa-bb93-470d-a793-50ae2c93732f']:item.SetWidth(pcbnew.FromMM(1.6))+ if item.GetNetname()=='LOAD_POS' and item.GetLayer()==pcbnew.B_Cu:+ a=item.GetStart();b=item.GetEnd()+ if abs(pcbnew.ToMM(a.y)-89.7)<.001 and abs(pcbnew.ToMM(b.y)-89.7)<.001:item.SetWidth(pcbnew.FromMM(1.75))+via=pcbnew.PCB_VIA(board);via.SetPosition(pcbnew.VECTOR2I(pcbnew.FromMM(139.8),pcbnew.FromMM(90.4)));via.SetWidth(pcbnew.FromMM(.6));via.SetDrill(pcbnew.FromMM(.3));via.SetLayerPair(pcbnew.F_Cu,pcbnew.B_Cu);via.SetNet(nets['GND']);board.Add(via)+track('GND',pcbnew.B_Cu,(139.0,90.4),(139.8,90.4),1.2)+# Keep clearance beside the setpoint via; widen only the diode-spread junction.+for item in list(board.GetTracks()):+ if isinstance(item,pcbnew.PCB_VIA) or item.GetNetname()!='LOAD_POS' or item.GetLayer()!=pcbnew.B_Cu:continue+ a=item.GetStart();b=item.GetEnd();ax,ay=pcbnew.ToMM(a.x),pcbnew.ToMM(a.y);bx,by=pcbnew.ToMM(b.x),pcbnew.ToMM(b.y)+ if abs(ay-89.7)<.001 and abs(by-89.7)<.001:+ item.SetWidth(pcbnew.FromMM(1.5))+ if min(ax,bx)<120 and max(ax,bx)>125:+ board.Remove(item);track('LOAD_POS',pcbnew.B_Cu,(119.8,89.7),(123.0,89.7),1.75);track('LOAD_POS',pcbnew.B_Cu,(123.0,89.7),(125.65,89.7),1.5)+# Fill the inside shunt-bus bend with a diagonal copper path.+track('SHUNT_P',pcbnew.B_Cu,(133.0,84.7),(134.7,85.5),1.2)+# Spread the plated-source exit without moving the gate or clamp circuit.+track('SHUNT_P',pcbnew.B_Cu,(125.1,81.8),(125.35,82.7),1.9)+for item in board.GetTracks():+ if isinstance(item,pcbnew.PCB_VIA):continue+ if item.GetNetname()=='LOAD_POS' and item.GetLayer()==pcbnew.B_Cu:+ for getter,setter in [(item.GetStart,item.SetStart),(item.GetEnd,item.SetEnd)]:+ p=getter();x,y=pcbnew.ToMM(p.x),pcbnew.ToMM(p.y)+ if abs(x-126.35)<.001 and abs(y-89)<.001:p.y=pcbnew.FromMM(88.85);setter(p)+ if abs(x-125.65)<.001 and abs(y-89.7)<.001:p.x=pcbnew.FromMM(125.5);setter(p)+for x in [139.0,139.8]:+ via=pcbnew.PCB_VIA(board);via.SetPosition(pcbnew.VECTOR2I(pcbnew.FromMM(x),pcbnew.FromMM(89.9)));via.SetWidth(pcbnew.FromMM(.6));via.SetDrill(pcbnew.FromMM(.3));via.SetLayerPair(pcbnew.F_Cu,pcbnew.B_Cu);via.SetNet(nets['GND']);board.Add(via)+track('GND',pcbnew.B_Cu,(139,89.9),(139.8,89.9),.6)+track('GND',pcbnew.B_Cu,(139.4,89.9),(139.4,90.4),.6)+# Give the ground transfer more uninterrupted top copper; keep UART endpoints fixed.+for item in list(board.GetTracks()):+ if isinstance(item,pcbnew.PCB_VIA) or item.GetLayer()!=pcbnew.F_Cu:continue+ a=item.GetStart();b=item.GetEnd();ax,ay=pcbnew.ToMM(a.x),pcbnew.ToMM(a.y);bx,by=pcbnew.ToMM(b.x),pcbnew.ToMM(b.y)+ if item.GetNetname()=='UART_RX' and abs(ax-140.7)<.001 and abs(bx-140.7)<.001 and abs(ay-83.5)<.001 and abs(by-93.2)<.001:+ board.Remove(item)+ points=[(140.7,83.5),(140.7,88.0),(141.0,88.3),(141.0,91.8),(140.7,92.1),(140.7,93.2)]+ for p,q in zip(points,points[1:]):track('UART_RX',pcbnew.F_Cu,p,q,.25)+ if item.GetNetname()=='UART_TX' and abs(ax-137.9)<.001 and abs(bx-137.9)<.001 and abs(ay-83.7)<.001 and abs(by-98.1)<.001:+ board.Remove(item)+ points=[(137.9,83.7),(137.9,89.0),(137.4,89.5),(137.4,93.3),(137.9,93.8),(137.9,98.1)]+ for p,q in zip(points,points[1:]):track('UART_TX',pcbnew.F_Cu,p,q,.25)+# Move the setpoint layer transfer clear of a wider drain bend, preserving its net/endpoints.+for item in board.GetTracks():+ if item.GetNetname()=='I_SET':+ endpoints=[(item.GetPosition,item.SetPosition)] if isinstance(item,pcbnew.PCB_VIA) else [(item.GetStart,item.SetStart),(item.GetEnd,item.SetEnd)]+ for getter,setter in endpoints:+ p=getter()+ if abs(pcbnew.ToMM(p.x)-125)<.001 and abs(pcbnew.ToMM(p.y)-88.4)<.001:p.y=pcbnew.FromMM(88);setter(p)+ if isinstance(item,pcbnew.PCB_VIA) or item.GetNetname()!='LOAD_POS' or item.GetLayer()!=pcbnew.B_Cu:continue+ for getter,setter in [(item.GetStart,item.SetStart),(item.GetEnd,item.SetEnd)]:+ p=getter();x,y=pcbnew.ToMM(p.x),pcbnew.ToMM(p.y)+ if abs(x-126.35)<.001 and abs(y-88.85)<.001:p.y=pcbnew.FromMM(89);setter(p)+ if abs(x-125.5)<.001 and abs(y-89.7)<.001:p.x=pcbnew.FromMM(125.65);setter(p)+ a=item.GetStart();b=item.GetEnd();ax,ay=pcbnew.ToMM(a.x),pcbnew.ToMM(a.y);bx,by=pcbnew.ToMM(b.x),pcbnew.ToMM(b.y)+ if abs(ay-89.7)<.001 and abs(by-89.7)<.001:item.SetWidth(pcbnew.FromMM(1.75))+ if abs(ax-bx)>.1 and abs(ay-by)>.1 and min(ax,bx)>125 and max(ax,bx)<127 and min(ay,by)>88.8 and max(ay,by)<89.8:item.SetWidth(pcbnew.FromMM(1.75))+# This local gate-drive segment carries the servo/gate current, not DUT current.+# Keep 0.5 mm copper and create clearance to the small setpoint via.+for item in board.GetTracks():+ if str(item.m_Uuid.AsString())=='a4e6790e-b900-47a1-bf1e-b5fb98adc8c7':item.SetWidth(pcbnew.FromMM(.5))+# Separate thermal-spread experiment, not a replacement of the accepted zone plan.+for z in list(board.Zones()):+ if not z.GetZoneName().startswith('D1 relocated heat spread'):continue+ layer=z.GetLayer();name=z.GetZoneName();board.Remove(z)+ z=pcbnew.ZONE(board);z.SetLayer(layer);z.SetNet(nets['LOAD_POS']);z.SetZoneName(name);z.SetAssignedPriority(3);z.SetLocalClearance(pcbnew.FromMM(.2));z.SetMinThickness(pcbnew.FromMM(.25));z.SetPadConnection(pcbnew.ZONE_CONNECTION_FULL);z.Outline().NewOutline()+ points=[(114,82),(132,82),(132,99),(104,99),(104,89.4),(114,89.4)] if layer==pcbnew.F_Cu else [(118.8,70),(132,70),(132,99),(104,99),(104,89.4),(114,89.4),(114,84),(118.8,84)]+ for x,y in points:z.Outline().Append(pcbnew.FromMM(x),pcbnew.FromMM(y))+ board.Add(z)+# Shorter board-to-board heat paths beside the diode cathode, outside its solder pad.+for x in [115.6,119.2]:+ for y in ([90.2,90.9] if x==115.6 else [90.2,91.4]):+ via=pcbnew.PCB_VIA(board);via.SetPosition(pcbnew.VECTOR2I(pcbnew.FromMM(x),pcbnew.FromMM(y)));via.SetWidth(pcbnew.FromMM(.6));via.SetDrill(pcbnew.FromMM(.3));via.SetLayerPair(pcbnew.F_Cu,pcbnew.B_Cu);via.SetNet(nets['LOAD_POS']);board.Add(via)+# Clearance-aware ground connection to the existing lower return plane.+track('GND',pcbnew.F_Cu,(112.728,88.696),(112.7,88.8),.4)+track('GND',pcbnew.F_Cu,(112.7,88.8),(112.7,99.5),.4)+# Restore the displaced signal-ground region; native checks remain mandatory.+track('GND',pcbnew.B_Cu,(117.1, 77.2),(116.0, 77.2),.25)+track('GND',pcbnew.B_Cu,(116.0, 77.2),(115.4, 77.8),.25)+track('GND',pcbnew.B_Cu,(115.4, 77.8),(114.9, 77.8),.25)+track('GND',pcbnew.B_Cu,(114.9, 77.8),(113.8, 78.9),.25)+track('GND',pcbnew.B_Cu,(113.8, 78.9),(106.4, 78.9),.25)+via=pcbnew.PCB_VIA(board);via.SetPosition(pcbnew.VECTOR2I(pcbnew.FromMM(106.4),pcbnew.FromMM(78.9)));via.SetWidth(pcbnew.FromMM(.6));via.SetDrill(pcbnew.FromMM(.3));via.SetLayerPair(pcbnew.F_Cu,pcbnew.B_Cu);via.SetNet(nets['GND']);board.Add(via)+track('GND',pcbnew.F_Cu,(106.4, 78.9),(105.9, 79.4),.25)+track('GND',pcbnew.F_Cu,(105.9, 79.4),(105.9, 79.5),.25)+track('GND',pcbnew.F_Cu,(105.9, 79.5),(105.8, 79.6),.25)+track('GND',pcbnew.F_Cu,(105.8, 79.6),(105.8, 80.1),.25)+track('GND',pcbnew.F_Cu,(105.8, 80.1),(105.3, 80.6),.25)+via=pcbnew.PCB_VIA(board);via.SetPosition(pcbnew.VECTOR2I(pcbnew.FromMM(105.3),pcbnew.FromMM(80.6)));via.SetWidth(pcbnew.FromMM(.6));via.SetDrill(pcbnew.FromMM(.3));via.SetLayerPair(pcbnew.F_Cu,pcbnew.B_Cu);via.SetNet(nets['GND']);board.Add(via)+# Isolated SDT10A45P5-7 feasibility layout; schematic/BOM are not changed here.+old=next(fp for fp in board.GetFootprints() if fp.GetReference()=='D1')+new=pcbnew.FootprintLoad('C:/Users/john/AppData/Local/Programs/KiCad/10.0/share/kicad/footprints/Diode_SMD.pretty','D_PowerDI-5')+assert new is not None+new.SetReference('D1');new.SetValue('SDT10A45P5-7');new.SetFPID(pcbnew.LIB_ID('Diode_SMD','D_PowerDI-5'))+board.Add(new);new.SetOrientationDegrees(180);new.SetPosition(old.GetPosition())+for a,b in [(new.Reference(),old.Reference()),(new.Value(),old.Value())]:+ a.SetPosition(b.GetPosition());a.SetTextAngle(b.GetTextAngle());a.SetTextSize(b.GetTextSize());a.SetTextThickness(b.GetTextThickness());a.SetVisible(b.IsVisible())+for pad in new.Pads():pad.SetNet(nets['LOAD_POS' if pad.GetNumber()=='1' else 'DUT_FUSED'])+board.Remove(old)+# Keep unfilled vias outside the new exposed cathode solder land.+for item in list(board.GetTracks()):+ if item.GetNetname()=='LOAD_POS' and isinstance(item,pcbnew.PCB_VIA):+ p=item.GetPosition();x,y=pcbnew.ToMM(p.x),pcbnew.ToMM(p.y)+ if 112.39<x<117.85 and 89.02<y<92.98:board.Remove(item);continue+ if item.GetNetname()=='GND' and not isinstance(item,pcbnew.PCB_VIA) and item.GetLayer()==pcbnew.F_Cu:+ for getter,setter in [(item.GetStart,item.SetStart),(item.GetEnd,item.SetEnd)]:+ p=getter();x,y=pcbnew.ToMM(p.x),pcbnew.ToMM(p.y)+ if abs(x-112.7)<.001:+ p.x=pcbnew.FromMM(112.25)+ if abs(y-88.8)<.001:p.y=pcbnew.FromMM(89.174)+ setter(p)+# Both anodes explicitly join the existing fused-input junction.+for path in [[(110.6,91),(110.6,90.618),(111.138,90.08)],[(110.6,91),(110.6,91.382),(111.138,91.92)]]:+ for a,b in zip(path,path[1:]):track('DUT_FUSED',pcbnew.F_Cu,a,b,1.0)+changes.append({'kind':'feasibility-replace-footprint','reference':'D1','candidateMPN':'SDT10A45P5-7','footprint':'Diode_SMD:D_PowerDI-5','adopted':False})+for x,y in [[115.2, 93.2], [114.2, 93.2], [116.2, 93.2], [116.6, 88.8], [118.0, 91.0], [113.2, 93.2], [115.0, 94.0], [118.0, 90.0]]:+ via=pcbnew.PCB_VIA(board);via.SetPosition(pcbnew.VECTOR2I(pcbnew.FromMM(x),pcbnew.FromMM(y)));via.SetWidth(pcbnew.FromMM(.6));via.SetDrill(pcbnew.FromMM(.3));via.SetLayerPair(pcbnew.F_Cu,pcbnew.B_Cu);via.SetNet(nets['LOAD_POS']);board.Add(via)+# Widen the source bus on its lower edge; retain the neighboring gate/drain clearances.+for item in board.GetTracks():+ if isinstance(item,pcbnew.PCB_VIA):continue+ if str(item.m_Uuid.AsString())=='f120f8db-288f-4938-acbf-98ff5e4065df':+ item.SetStart(pcbnew.VECTOR2I(pcbnew.FromMM(133.5),pcbnew.FromMM(84.4)));item.SetEnd(pcbnew.VECTOR2I(pcbnew.FromMM(126.8),pcbnew.FromMM(84.4)));item.SetWidth(pcbnew.FromMM(2.3))+ if item.GetNetname()=='SHUNT_P_SENSE' and item.GetLayer()==pcbnew.B_Cu:+ for getter,setter in [(item.GetStart,item.SetStart),(item.GetEnd,item.SetEnd)]:+ p=getter()+ if abs(pcbnew.ToMM(p.y)-85.6)<.001:p.y=pcbnew.FromMM(85.9);setter(p)+track('SHUNT_P_SENSE',pcbnew.B_Cu,(123.3,85.4),(123.3,85.9),.25)+# Move the drain layer transfer down to preserve space for the widened shunt bus and Kelvin trace.+for item in board.GetTracks():+ if item.GetNetname()!='LOAD_POS':continue+ endpoints=[(item.GetPosition,item.SetPosition)] if isinstance(item,pcbnew.PCB_VIA) else [(item.GetStart,item.SetStart),(item.GetEnd,item.SetEnd)]+ for getter,setter in endpoints:+ p=getter();x,y=pcbnew.ToMM(p.x),pcbnew.ToMM(p.y)+ if 126.0<x<126.9 and (abs(y-86.7)<.001 or abs(y-87.0)<.001):p.y=pcbnew.FromMM(87.2);setter(p)+# Keep the setpoint route below the moved power transfer, with its original op-amp pad fixed.+for item in list(board.GetTracks()):+ if item.GetNetname()!='I_SET':continue+ if not isinstance(item,pcbnew.PCB_VIA) and item.GetLayer()==pcbnew.F_Cu:+ a=item.GetStart();b=item.GetEnd()+ if min(pcbnew.ToMM(a.x),pcbnew.ToMM(b.x))>=125 and min(pcbnew.ToMM(a.y),pcbnew.ToMM(b.y))>=87.9:board.Remove(item);continue+ endpoints=[(item.GetPosition,item.SetPosition)] if isinstance(item,pcbnew.PCB_VIA) else [(item.GetStart,item.SetStart),(item.GetEnd,item.SetEnd)]+ for getter,setter in endpoints:+ p=getter()+ if abs(pcbnew.ToMM(p.x)-125)<.001 and abs(pcbnew.ToMM(p.y)-88)<.001:p.y=pcbnew.FromMM(88.3);setter(p)+track('I_SET',pcbnew.F_Cu,(127.8625,87.95),(127.8625,88.3),.25)+track('I_SET',pcbnew.F_Cu,(127.8625,88.3),(125,88.3),.25)+# Relocate gate transfer and use a broad source exit before the horizontal bus.+for item in list(board.GetTracks()):+ uid=str(item.m_Uuid.AsString())+ if uid in ['2f235494-7976-4048-be2d-b71507b2b45e','ec56cc46-09f5-4ff9-a1b6-61699db68686','18b5314d-bef3-4f18-ac3d-13f344b6d6c0','eadf962f-0989-4ddd-9e5f-0a34d0ef01ec','85b410dd-d11b-4c9c-8b1a-8aaedff6e92f']:board.Remove(item);continue+ if item.GetNetname()=='SHUNT_P' and not isinstance(item,pcbnew.PCB_VIA):+ p=item.GetStart()+ if abs(pcbnew.ToMM(p.x)-125.1)<.001 and abs(pcbnew.ToMM(p.y)-81.8)<.001:board.Remove(item);continue+ if item.GetNetname()=='GATE':+ endpoints=[(item.GetPosition,item.SetPosition)] if isinstance(item,pcbnew.PCB_VIA) else [(item.GetStart,item.SetStart),(item.GetEnd,item.SetEnd)]+ for getter,setter in endpoints:+ p=getter()+ if abs(pcbnew.ToMM(p.x)-124.5)<.001 and abs(pcbnew.ToMM(p.y)-84.2)<.001:p.x=pcbnew.FromMM(123.4);p.y=pcbnew.FromMM(82.7);setter(p)+ if isinstance(item,pcbnew.PCB_VIA) and uid=='3d518e9c-a66d-4cd2-8d4a-af76fb9a2e46':item.SetWidth(pcbnew.FromMM(.6));item.SetDrill(pcbnew.FromMM(.3))+track('SHUNT_P',pcbnew.B_Cu,(124.92,82),(124.92,83.7),1.9)+track('SHUNT_P',pcbnew.B_Cu,(124.92,83.7),(125.62,84.4),1.9)+track('SHUNT_P',pcbnew.B_Cu,(125.62,84.4),(126.8,84.4),2.3)+track('GATE',pcbnew.B_Cu,(123.4,81.3),(123.4,82.7),.25)+# These two local paths carry gate/base control current, not DUT current.+for item in board.GetTracks():+ if isinstance(item,pcbnew.PCB_VIA):continue+ if item.GetLayer()==pcbnew.F_Cu and item.GetNetname() in ['GATE','CLAMP_BASE']:+ a=item.GetStart();b=item.GetEnd()+ if max(pcbnew.ToMM(a.x),pcbnew.ToMM(b.x))<=125 and min(pcbnew.ToMM(a.y),pcbnew.ToMM(b.y))>=81 and max(pcbnew.ToMM(a.y),pcbnew.ToMM(b.y))<=85:item.SetWidth(pcbnew.FromMM(.25))+# Preserve the original pad-exit spreading copper as well as the wider lower bus.+track('SHUNT_P',pcbnew.B_Cu,(125.1,81.8),(125.35,82.7),1.9)+# Isolated land-pattern study: preserve the 1.1 mm drill and preferred 0.25 mm ring.+# A project footprint variant and assembly review are required before adoption.+for fp in board.GetFootprints():+ if fp.GetReference()=='Q1':+ fp.SetFPID(pcbnew.LIB_ID('Load','TO-220-3_Horizontal_TabUp_Drain1p6'))+ for pad in fp.Pads():+ if pad.GetNumber()=='2':pad.SetSize(pcbnew.VECTOR2I(pcbnew.FromMM(1.6),pcbnew.FromMM(2.0)))+for item in list(board.GetTracks()):+ if isinstance(item,pcbnew.PCB_VIA) or item.GetNetname()!='SHUNT_P' or item.GetLayer()!=pcbnew.B_Cu:continue+ p=item.GetStart()+ if abs(pcbnew.ToMM(p.x)-125.1)<.001 and abs(pcbnew.ToMM(p.y)-81.8)<.001:board.Remove(item)+track('SHUNT_P',pcbnew.B_Cu,(125.1,82.0),(125.45,82.7),2.0)+expected_nets={str(item.m_Uuid.AsString()):item.GetNetname() for item in board.GetTracks()}+for zone in board.Zones():+ zone.UnFill()+ zone.SetNeedRefill(True)+board.BuildConnectivity()+actual_nets={str(item.m_Uuid.AsString()):item.GetNetname() for item in board.GetTracks()}+assert actual_nets==expected_nets, {'unexpectedNetChanges':[(k,v,actual_nets.get(k)) for k,v in expected_nets.items() if actual_nets.get(k)!=v]}+for zone in board.Zones():zone.SetNeedRefill(True)+pcbnew.ZONE_FILLER(board).Fill(board.Zones());assert {str(item.m_Uuid.AsString()):item.GetNetname() for item in board.GetTracks()}==expected_nets, 'Zone fill changed copper nets'+pcbnew.SaveBoard(str(out),board)+readback=pcbnew.LoadBoard(str(out))+assert {str(item.m_Uuid.AsString()):item.GetNetname() for item in readback.GetTracks()}==expected_nets, 'Save/readback changed copper nets' +out.with_suffix('.changes.json').write_text(json.dumps({'source':str(source),'candidate':str(out),'changes':changes},indent=2));print(json.dumps({'saved':str(out),'changes':changes}))
tools/hotspot_via_candidate_v83.pyadded+315@@ -0,0 +1,315 @@+"""Run using native desktop KiCad Python; modifies only an isolated board copy."""+import pcbnew,sys,json+from pathlib import Path+source=Path(sys.argv[1]);out=Path(sys.argv[2]);board=pcbnew.LoadBoard(str(source));nets=board.GetNetsByName()+changes=[]+for net,x,y in [('GND',133.0,100.8),('GND',138.0,100.8),('GND',139.0,93.0),('GND',136.5,93.0),('GND',139.4,90.4),('GND',139.4,91.4),('GND',139.4,92.4)]:+ via=pcbnew.PCB_VIA(board);via.SetPosition(pcbnew.VECTOR2I(pcbnew.FromMM(x),pcbnew.FromMM(y)));via.SetWidth(pcbnew.FromMM(.8));via.SetDrill(pcbnew.FromMM(.4));via.SetLayerPair(pcbnew.F_Cu,pcbnew.B_Cu);via.SetNet(nets[net]);board.Add(via);changes.append({'kind':'via','net':net,'at':[x,y],'sizeMm':.8,'drillMm':.4,'uuid':str(via.m_Uuid.AsString())})+for item in board.GetTracks():+ uid=str(item.m_Uuid.AsString())+ if uid=='32514162-b870-4c7a-95a2-b85b8db79257':+ item.SetWidth(pcbnew.FromMM(1.0));item.SetDrill(pcbnew.FromMM(.6));changes.append({'kind':'resize-via','uuid':uid,'net':'LOAD_POS','sizeMm':1.0,'drillMm':.6})+for item in list(board.GetTracks()):+ uid=str(item.m_Uuid.AsString())+ if uid in ['91eed079-74e4-4863-8dc1-f211a19b6ee0','ad353074-a567-44a9-a20a-744b8057c5a9']:+ board.Remove(item);changes.append({'kind':'remove-track','uuid':uid})+ if uid=='aa6c5964-74d4-410e-a69e-ffb2cd9c9999':+ item.SetWidth(pcbnew.FromMM(1.0));item.SetDrill(pcbnew.FromMM(.6));changes.append({'kind':'resize-via','uuid':uid,'sizeMm':1.0,'drillMm':.6})+def track(net,layer,a,b,width):+ t=pcbnew.PCB_TRACK(board);t.SetStart(pcbnew.VECTOR2I(pcbnew.FromMM(a[0]),pcbnew.FromMM(a[1])));t.SetEnd(pcbnew.VECTOR2I(pcbnew.FromMM(b[0]),pcbnew.FromMM(b[1])));t.SetWidth(pcbnew.FromMM(width));t.SetLayer(layer);t.SetNet(nets[net]);board.Add(t);changes.append({'kind':'track','net':net,'start':a,'end':b,'widthMm':width,'layer':board.GetLayerName(layer)})+for a,b in [((127.46,82),(127.46,83.3)),((127.46,83.3),(126.4,84.36)),((126.4,84.36),(126.4,86.5))]:track('LOAD_POS',pcbnew.F_Cu,a,b,1.5)+for a,b in [((139.4,90.4),(139.4,92)),((139.4,92),(138.4,93)),((138.4,93),(136.5,93))]:track('GND',pcbnew.B_Cu,a,b,1.5)+for item in board.GetTracks():+ if isinstance(item,pcbnew.PCB_VIA) and item.GetNetname()=='GND':+ p=item.GetPosition();xy=(round(pcbnew.ToMM(p.x),3),round(pcbnew.ToMM(p.y),3))+ if xy in [(139.4,86.8),(136.5,93.0),(139.4,90.4),(139.4,91.4),(139.4,92.4)]:item.SetWidth(pcbnew.FromMM(1));item.SetDrill(pcbnew.FromMM(.6));changes.append({'kind':'resize-via','at':xy,'sizeMm':1,'drillMm':.6})+for fp in board.GetFootprints():+ delta={'Q2':-.3,'U3':.3,'R7':.1}.get(fp.GetReference())+ if delta:+ pos=fp.GetPosition();pos.x+=pcbnew.FromMM(delta);fp.SetPosition(pos);changes.append({'kind':'move-footprint','ref':fp.GetReference(),'dxMm':delta})+for item in list(board.GetTracks()):+ if item.GetNetname()=='SHUNT_P_SENSE' and isinstance(item,pcbnew.PCB_VIA):+ pos=item.GetPosition()+ if abs(pcbnew.ToMM(pos.x)-123.6)<.001 and abs(pcbnew.ToMM(pos.y)-85.4)<.001:+ pos.x=pcbnew.FromMM(123.3);item.SetPosition(pos);changes.append({'kind':'move-sense-via','from':[123.6,85.4],'to':[123.3,85.4]})+ if isinstance(item,pcbnew.PCB_VIA):continue+ if item.GetNetname()=='SHUNT_P_SENSE':+ for getter,setter in [(item.GetStart,item.SetStart),(item.GetEnd,item.SetEnd)]:+ pos=getter()+ if abs(pcbnew.ToMM(pos.x)-123.6)<.001 and abs(pcbnew.ToMM(pos.y)-85.4)<.001:pos.x=pcbnew.FromMM(123.3);setter(pos)+ if item.GetNetname()=='SHUNT_N_SENSE' and item.GetLayer()==pcbnew.F_Cu:+ for getter,setter in [(item.GetStart,item.SetStart),(item.GetEnd,item.SetEnd)]:+ pos=getter();x,y=pcbnew.ToMM(pos.x),pcbnew.ToMM(pos.y)+ if 128.79<x<129.31 and 87.29<y<88.81:pos.x+=pcbnew.FromMM(.2);setter(pos)+for item in list(board.GetTracks()):+ uid=str(item.m_Uuid.AsString())+ if uid=='32514162-b870-4c7a-95a2-b85b8db79257':+ item.SetPosition(pcbnew.VECTOR2I(pcbnew.FromMM(126.4),pcbnew.FromMM(86.7)))+ if uid=='13d2f936-e4d3-40cb-9bf7-1b6b26ca4cf2':+ item.SetStart(pcbnew.VECTOR2I(pcbnew.FromMM(126.4),pcbnew.FromMM(86.7)));item.SetWidth(pcbnew.FromMM(1.5))+ if uid=='f120f8db-288f-4938-acbf-98ff5e4065df':+ item.SetStart(pcbnew.VECTOR2I(pcbnew.FromMM(133.5),pcbnew.FromMM(84.2)));item.SetEnd(pcbnew.VECTOR2I(pcbnew.FromMM(126.8),pcbnew.FromMM(84.2)));item.SetWidth(pcbnew.FromMM(1.9))+ if not isinstance(item,pcbnew.PCB_VIA) and item.GetNetname()=='LOAD_POS' and item.GetLayer()==pcbnew.F_Cu:+ for getter,setter in [(item.GetStart,item.SetStart),(item.GetEnd,item.SetEnd)]:+ p=getter()+ if abs(pcbnew.ToMM(p.x)-126.4)<.001 and abs(pcbnew.ToMM(p.y)-86.5)<.001:p.y=pcbnew.FromMM(86.7);setter(p)+for item in board.GetTracks():+ if isinstance(item,pcbnew.PCB_VIA) or item.GetLayer()!=pcbnew.B_Cu or item.GetNetname()!='SHUNT_P_SENSE':continue+ for getter,setter in [(item.GetStart,item.SetStart),(item.GetEnd,item.SetEnd)]:+ p=getter();x,y=pcbnew.ToMM(p.x),pcbnew.ToMM(p.y)+ if abs(y-85.4)<.001 and (abs(x-123.3)<.001 or abs(x-132)<.001):+ p.y=pcbnew.FromMM(85.6)+ if abs(x-132)<.001:p.x=pcbnew.FromMM(132.2)+ setter(p)+# Ease the drain right-angle turn; relocate its adjacent setpoint via with endpoints.+track('LOAD_POS',pcbnew.B_Cu,(126.35,89.0),(125.65,89.7),1.5)+# Preserve explicit branch junctions instead of overlapped dangling endpoints.+for item in list(board.GetTracks()):+ uid=str(item.m_Uuid.AsString())+ if uid=='32514162-b870-4c7a-95a2-b85b8db79257':+ item.SetWidth(pcbnew.FromMM(.8));item.SetDrill(pcbnew.FromMM(.4));item.SetPosition(pcbnew.VECTOR2I(pcbnew.FromMM(126.05),pcbnew.FromMM(86.7)))+ if uid in ['b486fb59-b64e-41ee-a1a7-5a72fcf0a780','8e5d5e2b-78f3-4ed2-af96-409d2ea9d57b']:+ a=item.GetStart();b=item.GetEnd();start=(pcbnew.ToMM(a.x),pcbnew.ToMM(a.y));end=(pcbnew.ToMM(b.x),pcbnew.ToMM(b.y))+ mid=(126.35,89.0) if uid=='b486fb59-b64e-41ee-a1a7-5a72fcf0a780' else (125.65,89.7)+ board.Remove(item);track('LOAD_POS',pcbnew.B_Cu,start,mid,1.5);track('LOAD_POS',pcbnew.B_Cu,mid,end,1.5)+via=pcbnew.PCB_VIA(board);via.SetPosition(pcbnew.VECTOR2I(pcbnew.FromMM(126.8),pcbnew.FromMM(86.7)));via.SetWidth(pcbnew.FromMM(.8));via.SetDrill(pcbnew.FromMM(.4));via.SetLayerPair(pcbnew.F_Cu,pcbnew.B_Cu);via.SetNet(nets['LOAD_POS']);board.Add(via)+# Do not enlarge holes through narrow in-plane paths. Keep the three dedicated shunt thermal vias.+for item in list(board.GetTracks()):+ uid=str(item.m_Uuid.AsString())+ if uid in ['34d3207a-b14a-40d8-be82-5b108c2b867b','9a71af6b-ea83-4f97-b7f6-3f69253c7314']:+ board.Remove(item);continue+ if isinstance(item,pcbnew.PCB_VIA):+ p=item.GetPosition();x,y=pcbnew.ToMM(p.x),pcbnew.ToMM(p.y)+ if uid=='aa6c5964-74d4-410e-a69e-ffb2cd9c9999' or (item.GetNetname()=='GND' and abs(x-139.4)<.001 and abs(y-86.8)<.001):+ item.SetWidth(pcbnew.FromMM(.8));item.SetDrill(pcbnew.FromMM(.4))+# Move the transfer hole off the center of the shunt bus; remove a nearly unused ground hole through the return neck.+for item in list(board.GetTracks()):+ if not isinstance(item,pcbnew.PCB_VIA):continue+ p=item.GetPosition();x,y=pcbnew.ToMM(p.x),pcbnew.ToMM(p.y)+ if item.GetNetname()=='GND' and abs(x-139.4)<.001 and abs(y-86.8)<.001:board.Remove(item)+ if str(item.m_Uuid.AsString())=='aa6c5964-74d4-410e-a69e-ffb2cd9c9999':item.SetPosition(pcbnew.VECTOR2I(pcbnew.FromMM(133.7),pcbnew.FromMM(83.6)))+track('SHUNT_P',pcbnew.F_Cu,(133.7,83.6),(133.8,84.2),1.2)+# Preserve the full plated Q1 source connection; remove its redundant neck-drilling spur.+for item in list(board.GetTracks()):+ uid=str(item.m_Uuid.AsString())+ if uid in ['cd48c727-15bf-4276-b4b8-ae4d936d829d','30d9e8bc-805a-4b1d-8a79-ad625ee59388']:+ board.Remove(item);continue+ if isinstance(item,pcbnew.PCB_VIA) and item.GetNetname()=='GND':+ p=item.GetPosition();x,y=pcbnew.ToMM(p.x),pcbnew.ToMM(p.y)+ if abs(x-139.4)<.001 and any(abs(y-t)<.001 for t in [90.4,91.4,92.4]):p.x=pcbnew.FromMM(139.8);item.SetPosition(p)+# Share vertical current with smaller holes and widen the plated-source exit.+for item in board.GetTracks():+ uid=str(item.m_Uuid.AsString())+ if uid in ['2f235494-7976-4048-be2d-b71507b2b45e','ec56cc46-09f5-4ff9-a1b6-61699db68686']:item.SetWidth(pcbnew.FromMM(1.75))+ if isinstance(item,pcbnew.PCB_VIA) and item.GetNetname()=='GND':+ p=item.GetPosition();x,y=pcbnew.ToMM(p.x),pcbnew.ToMM(p.y)+ if abs(x-139.8)<.001 and any(abs(y-t)<.001 for t in [90.4,91.4,92.4]):+ p.x=pcbnew.FromMM(139.4);item.SetPosition(p);item.SetWidth(pcbnew.FromMM(.6));item.SetDrill(pcbnew.FromMM(.3))+for item in board.GetTracks():+ if str(item.m_Uuid.AsString())=='f120f8db-288f-4938-acbf-98ff5e4065df':+ item.SetStart(pcbnew.VECTOR2I(pcbnew.FromMM(133.5),pcbnew.FromMM(84.25)));item.SetEnd(pcbnew.VECTOR2I(pcbnew.FromMM(126.8),pcbnew.FromMM(84.25)));item.SetWidth(pcbnew.FromMM(2.0))+# Fine-grid hotspot revision: spread the return transfer and soften the two power necks.+for item in board.GetTracks():+ if isinstance(item,pcbnew.PCB_VIA):+ p=item.GetPosition();x,y=pcbnew.ToMM(p.x),pcbnew.ToMM(p.y)+ if item.GetNetname()=='GND' and abs(x-139.4)<.001 and abs(y-90.4)<.001:+ p.x=pcbnew.FromMM(139.0);item.SetPosition(p)+ continue+ uid=str(item.m_Uuid.AsString())+ if uid in ['32cd5f76-8c9c-4680-a071-fe4f71064f23','cecc8caa-bb93-470d-a793-50ae2c93732f']:item.SetWidth(pcbnew.FromMM(1.6))+ if item.GetNetname()=='LOAD_POS' and item.GetLayer()==pcbnew.B_Cu:+ a=item.GetStart();b=item.GetEnd()+ if abs(pcbnew.ToMM(a.y)-89.7)<.001 and abs(pcbnew.ToMM(b.y)-89.7)<.001:item.SetWidth(pcbnew.FromMM(1.75))+via=pcbnew.PCB_VIA(board);via.SetPosition(pcbnew.VECTOR2I(pcbnew.FromMM(139.8),pcbnew.FromMM(90.4)));via.SetWidth(pcbnew.FromMM(.6));via.SetDrill(pcbnew.FromMM(.3));via.SetLayerPair(pcbnew.F_Cu,pcbnew.B_Cu);via.SetNet(nets['GND']);board.Add(via)+track('GND',pcbnew.B_Cu,(139.0,90.4),(139.8,90.4),1.2)+# Keep clearance beside the setpoint via; widen only the diode-spread junction.+for item in list(board.GetTracks()):+ if isinstance(item,pcbnew.PCB_VIA) or item.GetNetname()!='LOAD_POS' or item.GetLayer()!=pcbnew.B_Cu:continue+ a=item.GetStart();b=item.GetEnd();ax,ay=pcbnew.ToMM(a.x),pcbnew.ToMM(a.y);bx,by=pcbnew.ToMM(b.x),pcbnew.ToMM(b.y)+ if abs(ay-89.7)<.001 and abs(by-89.7)<.001:+ item.SetWidth(pcbnew.FromMM(1.5))+ if min(ax,bx)<120 and max(ax,bx)>125:+ board.Remove(item);track('LOAD_POS',pcbnew.B_Cu,(119.8,89.7),(123.0,89.7),1.75);track('LOAD_POS',pcbnew.B_Cu,(123.0,89.7),(125.65,89.7),1.5)+# Fill the inside shunt-bus bend with a diagonal copper path.+track('SHUNT_P',pcbnew.B_Cu,(133.0,84.7),(134.7,85.5),1.2)+# Spread the plated-source exit without moving the gate or clamp circuit.+track('SHUNT_P',pcbnew.B_Cu,(125.1,81.8),(125.35,82.7),1.9)+for item in board.GetTracks():+ if isinstance(item,pcbnew.PCB_VIA):continue+ if item.GetNetname()=='LOAD_POS' and item.GetLayer()==pcbnew.B_Cu:+ for getter,setter in [(item.GetStart,item.SetStart),(item.GetEnd,item.SetEnd)]:+ p=getter();x,y=pcbnew.ToMM(p.x),pcbnew.ToMM(p.y)+ if abs(x-126.35)<.001 and abs(y-89)<.001:p.y=pcbnew.FromMM(88.85);setter(p)+ if abs(x-125.65)<.001 and abs(y-89.7)<.001:p.x=pcbnew.FromMM(125.5);setter(p)+for x in [139.0,139.8]:+ via=pcbnew.PCB_VIA(board);via.SetPosition(pcbnew.VECTOR2I(pcbnew.FromMM(x),pcbnew.FromMM(89.9)));via.SetWidth(pcbnew.FromMM(.6));via.SetDrill(pcbnew.FromMM(.3));via.SetLayerPair(pcbnew.F_Cu,pcbnew.B_Cu);via.SetNet(nets['GND']);board.Add(via)+track('GND',pcbnew.B_Cu,(139,89.9),(139.8,89.9),.6)+track('GND',pcbnew.B_Cu,(139.4,89.9),(139.4,90.4),.6)+# Give the ground transfer more uninterrupted top copper; keep UART endpoints fixed.+for item in list(board.GetTracks()):+ if isinstance(item,pcbnew.PCB_VIA) or item.GetLayer()!=pcbnew.F_Cu:continue+ a=item.GetStart();b=item.GetEnd();ax,ay=pcbnew.ToMM(a.x),pcbnew.ToMM(a.y);bx,by=pcbnew.ToMM(b.x),pcbnew.ToMM(b.y)+ if item.GetNetname()=='UART_RX' and abs(ax-140.7)<.001 and abs(bx-140.7)<.001 and abs(ay-83.5)<.001 and abs(by-93.2)<.001:+ board.Remove(item)+ points=[(140.7,83.5),(140.7,88.0),(141.0,88.3),(141.0,91.8),(140.7,92.1),(140.7,93.2)]+ for p,q in zip(points,points[1:]):track('UART_RX',pcbnew.F_Cu,p,q,.25)+ if item.GetNetname()=='UART_TX' and abs(ax-137.9)<.001 and abs(bx-137.9)<.001 and abs(ay-83.7)<.001 and abs(by-98.1)<.001:+ board.Remove(item)+ points=[(137.9,83.7),(137.9,89.0),(137.4,89.5),(137.4,93.3),(137.9,93.8),(137.9,98.1)]+ for p,q in zip(points,points[1:]):track('UART_TX',pcbnew.F_Cu,p,q,.25)+# Move the setpoint layer transfer clear of a wider drain bend, preserving its net/endpoints.+for item in board.GetTracks():+ if item.GetNetname()=='I_SET':+ endpoints=[(item.GetPosition,item.SetPosition)] if isinstance(item,pcbnew.PCB_VIA) else [(item.GetStart,item.SetStart),(item.GetEnd,item.SetEnd)]+ for getter,setter in endpoints:+ p=getter()+ if abs(pcbnew.ToMM(p.x)-125)<.001 and abs(pcbnew.ToMM(p.y)-88.4)<.001:p.y=pcbnew.FromMM(88);setter(p)+ if isinstance(item,pcbnew.PCB_VIA) or item.GetNetname()!='LOAD_POS' or item.GetLayer()!=pcbnew.B_Cu:continue+ for getter,setter in [(item.GetStart,item.SetStart),(item.GetEnd,item.SetEnd)]:+ p=getter();x,y=pcbnew.ToMM(p.x),pcbnew.ToMM(p.y)+ if abs(x-126.35)<.001 and abs(y-88.85)<.001:p.y=pcbnew.FromMM(89);setter(p)+ if abs(x-125.5)<.001 and abs(y-89.7)<.001:p.x=pcbnew.FromMM(125.65);setter(p)+ a=item.GetStart();b=item.GetEnd();ax,ay=pcbnew.ToMM(a.x),pcbnew.ToMM(a.y);bx,by=pcbnew.ToMM(b.x),pcbnew.ToMM(b.y)+ if abs(ay-89.7)<.001 and abs(by-89.7)<.001:item.SetWidth(pcbnew.FromMM(1.75))+ if abs(ax-bx)>.1 and abs(ay-by)>.1 and min(ax,bx)>125 and max(ax,bx)<127 and min(ay,by)>88.8 and max(ay,by)<89.8:item.SetWidth(pcbnew.FromMM(1.75))+# This local gate-drive segment carries the servo/gate current, not DUT current.+# Keep 0.5 mm copper and create clearance to the small setpoint via.+for item in board.GetTracks():+ if str(item.m_Uuid.AsString())=='a4e6790e-b900-47a1-bf1e-b5fb98adc8c7':item.SetWidth(pcbnew.FromMM(.5))+# Separate thermal-spread experiment, not a replacement of the accepted zone plan.+for z in list(board.Zones()):+ if not z.GetZoneName().startswith('D1 relocated heat spread'):continue+ layer=z.GetLayer();name=z.GetZoneName();board.Remove(z)+ z=pcbnew.ZONE(board);z.SetLayer(layer);z.SetNet(nets['LOAD_POS']);z.SetZoneName(name);z.SetAssignedPriority(3);z.SetLocalClearance(pcbnew.FromMM(.2));z.SetMinThickness(pcbnew.FromMM(.25));z.SetPadConnection(pcbnew.ZONE_CONNECTION_FULL);z.Outline().NewOutline()+ points=[(114,82),(132,82),(132,99),(104,99),(104,89.4),(114,89.4)] if layer==pcbnew.F_Cu else [(118.8,70),(132,70),(132,99),(104,99),(104,89.4),(114,89.4),(114,84),(118.8,84)]+ for x,y in points:z.Outline().Append(pcbnew.FromMM(x),pcbnew.FromMM(y))+ board.Add(z)+# Shorter board-to-board heat paths beside the diode cathode, outside its solder pad.+for x in [115.6,119.2]:+ for y in ([90.2,90.9] if x==115.6 else [90.2,91.4]):+ via=pcbnew.PCB_VIA(board);via.SetPosition(pcbnew.VECTOR2I(pcbnew.FromMM(x),pcbnew.FromMM(y)));via.SetWidth(pcbnew.FromMM(.6));via.SetDrill(pcbnew.FromMM(.3));via.SetLayerPair(pcbnew.F_Cu,pcbnew.B_Cu);via.SetNet(nets['LOAD_POS']);board.Add(via)+# Clearance-aware ground connection to the existing lower return plane.+track('GND',pcbnew.F_Cu,(112.728,88.696),(112.7,88.8),.4)+track('GND',pcbnew.F_Cu,(112.7,88.8),(112.7,99.5),.4)+# Restore the displaced signal-ground region; native checks remain mandatory.+track('GND',pcbnew.B_Cu,(117.1, 77.2),(116.0, 77.2),.25)+track('GND',pcbnew.B_Cu,(116.0, 77.2),(115.4, 77.8),.25)+track('GND',pcbnew.B_Cu,(115.4, 77.8),(114.9, 77.8),.25)+track('GND',pcbnew.B_Cu,(114.9, 77.8),(113.8, 78.9),.25)+track('GND',pcbnew.B_Cu,(113.8, 78.9),(106.4, 78.9),.25)+via=pcbnew.PCB_VIA(board);via.SetPosition(pcbnew.VECTOR2I(pcbnew.FromMM(106.4),pcbnew.FromMM(78.9)));via.SetWidth(pcbnew.FromMM(.6));via.SetDrill(pcbnew.FromMM(.3));via.SetLayerPair(pcbnew.F_Cu,pcbnew.B_Cu);via.SetNet(nets['GND']);board.Add(via)+track('GND',pcbnew.F_Cu,(106.4, 78.9),(105.9, 79.4),.25)+track('GND',pcbnew.F_Cu,(105.9, 79.4),(105.9, 79.5),.25)+track('GND',pcbnew.F_Cu,(105.9, 79.5),(105.8, 79.6),.25)+track('GND',pcbnew.F_Cu,(105.8, 79.6),(105.8, 80.1),.25)+track('GND',pcbnew.F_Cu,(105.8, 80.1),(105.3, 80.6),.25)+via=pcbnew.PCB_VIA(board);via.SetPosition(pcbnew.VECTOR2I(pcbnew.FromMM(105.3),pcbnew.FromMM(80.6)));via.SetWidth(pcbnew.FromMM(.6));via.SetDrill(pcbnew.FromMM(.3));via.SetLayerPair(pcbnew.F_Cu,pcbnew.B_Cu);via.SetNet(nets['GND']);board.Add(via)+# Isolated SDT10A45P5-7 feasibility layout; schematic/BOM are not changed here.+old=next(fp for fp in board.GetFootprints() if fp.GetReference()=='D1')+new=pcbnew.FootprintLoad('C:/Users/john/AppData/Local/Programs/KiCad/10.0/share/kicad/footprints/Diode_SMD.pretty','D_PowerDI-5')+assert new is not None+new.SetReference('D1');new.SetValue('SDT10A45P5-7');new.SetFPID(pcbnew.LIB_ID('Diode_SMD','D_PowerDI-5'))+board.Add(new);new.SetOrientationDegrees(180);new.SetPosition(old.GetPosition())+for a,b in [(new.Reference(),old.Reference()),(new.Value(),old.Value())]:+ a.SetPosition(b.GetPosition());a.SetTextAngle(b.GetTextAngle());a.SetTextSize(b.GetTextSize());a.SetTextThickness(b.GetTextThickness());a.SetVisible(b.IsVisible())+for pad in new.Pads():pad.SetNet(nets['LOAD_POS' if pad.GetNumber()=='1' else 'DUT_FUSED'])+board.Remove(old)+# Keep unfilled vias outside the new exposed cathode solder land.+for item in list(board.GetTracks()):+ if item.GetNetname()=='LOAD_POS' and isinstance(item,pcbnew.PCB_VIA):+ p=item.GetPosition();x,y=pcbnew.ToMM(p.x),pcbnew.ToMM(p.y)+ if 112.39<x<117.85 and 89.02<y<92.98:board.Remove(item);continue+ if item.GetNetname()=='GND' and not isinstance(item,pcbnew.PCB_VIA) and item.GetLayer()==pcbnew.F_Cu:+ for getter,setter in [(item.GetStart,item.SetStart),(item.GetEnd,item.SetEnd)]:+ p=getter();x,y=pcbnew.ToMM(p.x),pcbnew.ToMM(p.y)+ if abs(x-112.7)<.001:+ p.x=pcbnew.FromMM(112.25)+ if abs(y-88.8)<.001:p.y=pcbnew.FromMM(89.174)+ setter(p)+# Both anodes explicitly join the existing fused-input junction.+for path in [[(110.6,91),(110.6,90.618),(111.138,90.08)],[(110.6,91),(110.6,91.382),(111.138,91.92)]]:+ for a,b in zip(path,path[1:]):track('DUT_FUSED',pcbnew.F_Cu,a,b,1.0)+changes.append({'kind':'feasibility-replace-footprint','reference':'D1','candidateMPN':'SDT10A45P5-7','footprint':'Diode_SMD:D_PowerDI-5','adopted':False})+for x,y in [[115.2, 93.2], [114.2, 93.2], [116.2, 93.2], [116.6, 88.8], [118.0, 91.0], [113.2, 93.2], [115.0, 94.0], [118.0, 90.0]]:+ via=pcbnew.PCB_VIA(board);via.SetPosition(pcbnew.VECTOR2I(pcbnew.FromMM(x),pcbnew.FromMM(y)));via.SetWidth(pcbnew.FromMM(.6));via.SetDrill(pcbnew.FromMM(.3));via.SetLayerPair(pcbnew.F_Cu,pcbnew.B_Cu);via.SetNet(nets['LOAD_POS']);board.Add(via)+# Widen the source bus on its lower edge; retain the neighboring gate/drain clearances.+for item in board.GetTracks():+ if isinstance(item,pcbnew.PCB_VIA):continue+ if str(item.m_Uuid.AsString())=='f120f8db-288f-4938-acbf-98ff5e4065df':+ item.SetStart(pcbnew.VECTOR2I(pcbnew.FromMM(133.5),pcbnew.FromMM(84.4)));item.SetEnd(pcbnew.VECTOR2I(pcbnew.FromMM(126.8),pcbnew.FromMM(84.4)));item.SetWidth(pcbnew.FromMM(2.3))+ if item.GetNetname()=='SHUNT_P_SENSE' and item.GetLayer()==pcbnew.B_Cu:+ for getter,setter in [(item.GetStart,item.SetStart),(item.GetEnd,item.SetEnd)]:+ p=getter()+ if abs(pcbnew.ToMM(p.y)-85.6)<.001:p.y=pcbnew.FromMM(85.9);setter(p)+track('SHUNT_P_SENSE',pcbnew.B_Cu,(123.3,85.4),(123.3,85.9),.25)+# Move the drain layer transfer down to preserve space for the widened shunt bus and Kelvin trace.+for item in board.GetTracks():+ if item.GetNetname()!='LOAD_POS':continue+ endpoints=[(item.GetPosition,item.SetPosition)] if isinstance(item,pcbnew.PCB_VIA) else [(item.GetStart,item.SetStart),(item.GetEnd,item.SetEnd)]+ for getter,setter in endpoints:+ p=getter();x,y=pcbnew.ToMM(p.x),pcbnew.ToMM(p.y)+ if 126.0<x<126.9 and (abs(y-86.7)<.001 or abs(y-87.0)<.001):p.y=pcbnew.FromMM(87.2);setter(p)+# Keep the setpoint route below the moved power transfer, with its original op-amp pad fixed.+for item in list(board.GetTracks()):+ if item.GetNetname()!='I_SET':continue+ if not isinstance(item,pcbnew.PCB_VIA) and item.GetLayer()==pcbnew.F_Cu:+ a=item.GetStart();b=item.GetEnd()+ if min(pcbnew.ToMM(a.x),pcbnew.ToMM(b.x))>=125 and min(pcbnew.ToMM(a.y),pcbnew.ToMM(b.y))>=87.9:board.Remove(item);continue+ endpoints=[(item.GetPosition,item.SetPosition)] if isinstance(item,pcbnew.PCB_VIA) else [(item.GetStart,item.SetStart),(item.GetEnd,item.SetEnd)]+ for getter,setter in endpoints:+ p=getter()+ if abs(pcbnew.ToMM(p.x)-125)<.001 and abs(pcbnew.ToMM(p.y)-88)<.001:p.y=pcbnew.FromMM(88.3);setter(p)+track('I_SET',pcbnew.F_Cu,(127.8625,87.95),(127.8625,88.3),.25)+track('I_SET',pcbnew.F_Cu,(127.8625,88.3),(125,88.3),.25)+# Relocate gate transfer and use a broad source exit before the horizontal bus.+for item in list(board.GetTracks()):+ uid=str(item.m_Uuid.AsString())+ if uid in ['2f235494-7976-4048-be2d-b71507b2b45e','ec56cc46-09f5-4ff9-a1b6-61699db68686','18b5314d-bef3-4f18-ac3d-13f344b6d6c0','eadf962f-0989-4ddd-9e5f-0a34d0ef01ec','85b410dd-d11b-4c9c-8b1a-8aaedff6e92f']:board.Remove(item);continue+ if item.GetNetname()=='SHUNT_P' and not isinstance(item,pcbnew.PCB_VIA):+ p=item.GetStart()+ if abs(pcbnew.ToMM(p.x)-125.1)<.001 and abs(pcbnew.ToMM(p.y)-81.8)<.001:board.Remove(item);continue+ if item.GetNetname()=='GATE':+ endpoints=[(item.GetPosition,item.SetPosition)] if isinstance(item,pcbnew.PCB_VIA) else [(item.GetStart,item.SetStart),(item.GetEnd,item.SetEnd)]+ for getter,setter in endpoints:+ p=getter()+ if abs(pcbnew.ToMM(p.x)-124.5)<.001 and abs(pcbnew.ToMM(p.y)-84.2)<.001:p.x=pcbnew.FromMM(123.4);p.y=pcbnew.FromMM(82.7);setter(p)+ if isinstance(item,pcbnew.PCB_VIA) and uid=='3d518e9c-a66d-4cd2-8d4a-af76fb9a2e46':item.SetWidth(pcbnew.FromMM(.6));item.SetDrill(pcbnew.FromMM(.3))+track('SHUNT_P',pcbnew.B_Cu,(124.92,82),(124.92,83.7),1.9)+track('SHUNT_P',pcbnew.B_Cu,(124.92,83.7),(125.62,84.4),1.9)+track('SHUNT_P',pcbnew.B_Cu,(125.62,84.4),(126.8,84.4),2.3)+track('GATE',pcbnew.B_Cu,(123.4,81.3),(123.4,82.7),.25)+# These two local paths carry gate/base control current, not DUT current.+for item in board.GetTracks():+ if isinstance(item,pcbnew.PCB_VIA):continue+ if item.GetLayer()==pcbnew.F_Cu and item.GetNetname() in ['GATE','CLAMP_BASE']:+ a=item.GetStart();b=item.GetEnd()+ if max(pcbnew.ToMM(a.x),pcbnew.ToMM(b.x))<=125 and min(pcbnew.ToMM(a.y),pcbnew.ToMM(b.y))>=81 and max(pcbnew.ToMM(a.y),pcbnew.ToMM(b.y))<=85:item.SetWidth(pcbnew.FromMM(.25))+# Preserve the original pad-exit spreading copper as well as the wider lower bus.+track('SHUNT_P',pcbnew.B_Cu,(125.1,81.8),(125.35,82.7),1.9)+# Isolated land-pattern study: preserve the 1.1 mm drill and preferred 0.25 mm ring.+# A project footprint variant and assembly review are required before adoption.+for fp in board.GetFootprints():+ if fp.GetReference()=='Q1':+ fp.SetFPID(pcbnew.LIB_ID('Load','TO-220-3_Horizontal_TabUp_Drain1p6'))+ for pad in fp.Pads():+ if pad.GetNumber()=='2':pad.SetSize(pcbnew.VECTOR2I(pcbnew.FromMM(1.6),pcbnew.FromMM(2.0)))+for item in list(board.GetTracks()):+ if isinstance(item,pcbnew.PCB_VIA) or item.GetNetname()!='SHUNT_P' or item.GetLayer()!=pcbnew.B_Cu:continue+ p=item.GetStart()+ if abs(pcbnew.ToMM(p.x)-125.1)<.001 and abs(pcbnew.ToMM(p.y)-81.8)<.001:board.Remove(item)+track('SHUNT_P',pcbnew.B_Cu,(125.1,82.0),(125.45,82.7),2.0)+for x,y in [[115.6, 93.6], [114.6, 93.6], [116.6, 93.6], [113.6, 93.6], [118.0, 89.4], [118.4, 90.6], [118.4, 91.4], [115.4, 94.4], [114.6, 94.4], [117.2, 88.2], [117.6, 93.6], [116.4, 94.4], [113.6, 94.4], [118.8, 89.8], [117.4, 94.4], [118.4, 93.6]]:+ via=pcbnew.PCB_VIA(board);via.SetPosition(pcbnew.VECTOR2I(pcbnew.FromMM(x),pcbnew.FromMM(y)));via.SetWidth(pcbnew.FromMM(.6));via.SetDrill(pcbnew.FromMM(.3));via.SetLayerPair(pcbnew.F_Cu,pcbnew.B_Cu);via.SetNet(nets['LOAD_POS']);board.Add(via)+expected_nets={str(item.m_Uuid.AsString()):item.GetNetname() for item in board.GetTracks()}+for zone in board.Zones():+ zone.UnFill()+ zone.SetNeedRefill(True)+board.BuildConnectivity()+actual_nets={str(item.m_Uuid.AsString()):item.GetNetname() for item in board.GetTracks()}+assert actual_nets==expected_nets, {'unexpectedNetChanges':[(k,v,actual_nets.get(k)) for k,v in expected_nets.items() if actual_nets.get(k)!=v]}+for zone in board.Zones():zone.SetNeedRefill(True)+pcbnew.ZONE_FILLER(board).Fill(board.Zones());assert {str(item.m_Uuid.AsString()):item.GetNetname() for item in board.GetTracks()}==expected_nets, 'Zone fill changed copper nets'+pcbnew.SaveBoard(str(out),board)+readback=pcbnew.LoadBoard(str(out))+assert {str(item.m_Uuid.AsString()):item.GetNetname() for item in readback.GetTracks()}==expected_nets, 'Save/readback changed copper nets' +out.with_suffix('.changes.json').write_text(json.dumps({'source':str(source),'candidate':str(out),'changes':changes},indent=2));print(json.dumps({'saved':str(out),'changes':changes}))
tools/powerdi5_model.pyadded+25@@ -0,0 +1,25 @@+"""Nominal PowerDI5 reconstruction from Diodes DS39577 rev3-2 p4; not vendor CAD."""+from pathlib import Path+import json,hashlib+import cadquery as cq+root=Path(__file__).resolve().parents[1];out=root/'references/powerdi5';out.mkdir(exist_ok=True)+# Footprint axes: cathode to -X, two anodes to +X; Z above seating plane.+a=cq.Assembly(name='SDT10A45P5_Nominal_Drawing_Reconstruction')+black=cq.Color(.08,.085,.09);metal=cq.Color(.72,.74,.76)+# The drawing gives typical external bounds, but not a complete mold surface.+body=cq.Workplane('XY').box(5.37,3.966,1.075,centered=(True,True,False)).translate((0,0,.025))+a.add(body,name='MoldedBody_NominalEnvelope',color=black)+for y,name in [(-.92,'Anode_2_Lower'),(.92,'Anode_2_Upper')]:+ lead=cq.Workplane('XY').box(.85,.89,.381,centered=(False,True,False)).translate((2.405,y,0))+ a.add(lead,name=name,color=metal)+plate=cq.Workplane('XY').box(3.549,3.054,.381,centered=(False,True,False)).translate((-2.685,0,0))+tab=cq.Workplane('XY').box(.57,1.78,.381,centered=(False,True,False)).translate((-3.255,0,0))+cathode=plate.union(tab);a.add(cathode,name='Cathode_1_ExposedPlate',color=metal)+# Exposed metal occupies the same mold envelope; cut it out rather than overlap solids.+cut=body.cut(cathode)+for y in [-.92,.92]:cut=cut.cut(cq.Workplane('XY').box(.85,.89,.381,centered=(False,True,False)).translate((2.405,y,0)))+a.objects['MoldedBody_NominalEnvelope'].obj=cut+step=out/'SDT10A45P5-nominal.step';a.save(str(step))+bb=a.toCompound().BoundingBox()+metadata={'source':'https://www.diodes.com/datasheet/download/SDT10A45P5.pdf','drawing':'DS39577 Rev 3-2, March 2019, page 4','provenance':'Original nominal package reconstruction; not manufacturer CAD','units':'mm','axes':'KiCad footprint X/Y; Z above seating plane; cathode -X','boundsMm':[bb.xmin,bb.ymin,bb.zmin,bb.xmax,bb.ymax,bb.zmax],'nominalDimensions':{'E':6.51,'E1':5.37,'D':3.966,'A':1.10,'A1Assumed':.025,'A2':.381,'e':1.84,'b1':.89,'b2':1.78,'E2':3.549,'D2':3.054,'L':.85,'L1':.57},'limitations':['Mold taper and small edge radii omitted; body is conservative nominal envelope','No internal die/bond geometry','No as-built dimensional or marking validation','Two external anodes share logical pin 2 and must both be connected'],'stepSha256':hashlib.sha256(step.read_bytes()).hexdigest()}+(out/'model-provenance.json').write_text(json.dumps(metadata,indent=2)+'\n');print(json.dumps(metadata))
videos/build-storyboard.json+6@@ -90,6 +90,12 @@ "title": "Copper spreading and ground continuity", "description": "Actual v65 diagnostic review at 2x: inspect bottom copper and compare assumed convection. Q1 excluded; still-air diode budget fails. Later review, not construction.", "raw_path": "/home/adom/project/adom-custom-electronic-load/docs/clips/copper-cooling-refinement.mp4"+ },+ {+ "id": "current-screen-milestone",+ "title": "Current-path refinement clears the screen",+ "description": "Seven seconds at 2x, actual v77 viewer review. Three main-net maxima below 60; isolated candidate, cooling/full-board acceptance still open.",+ "raw_path": "/home/adom/project/adom-custom-electronic-load/docs/clips/current-screen-milestone.mp4" } ] }
videos/inventory.json+8−2@@ -1,6 +1,6 @@ {- "rawTakeCount": 72,- "reviewedExcerptCount": 19,+ "rawTakeCount": 73,+ "reviewedExcerptCount": 20, "countingRule": "Unique raw window recording filenames in this project; excludes derivative action/speed exports and reference projects. Raw count includes rejected takes.", "takes": [ {@@ -434,6 +434,12 @@ "durationSeconds": 45.066667, "topic": "Expanded bottom copper and diode cooling review, candidate v65", "review": "Full decode and contact sheet reviewed; 17-second 2x excerpt with Q1 exclusion and assumed-airflow captions. Later diagnostic review, not native construction or accepted cooling."+ },+ {+ "file": "videos/raw/window-10291968-20261010-144037.mp4",+ "durationSeconds": 29.6,+ "topic": "Fine-grid current-screen milestone, isolated v77; shunt, drain and return",+ "review": "Full decode and contact sheet reviewed. Seven-second 2x excerpt labels isolated study and unresolved cooling/full-board acceptance. Method panel has a stale four-layer string; actual board/report are two layers. No fabrication qualification." } ] }
videos/shotlog.json+10@@ -267,6 +267,16 @@ "status": "Full decode, contact sheet and captioned poster reviewed; raw auto-stopped at 45 s; final film not approved", "excerpt": "../docs/clips/copper-cooling-refinement.mp4", "playbackSpeed": 2+ },+ {+ "id": "current-screen-milestone-73",+ "file": "raw/window-10291968-20261010-144037.mp4",+ "recordingId": "rec-f90677fb9ba1426eab0c6c432e7f123a",+ "duration_s": 29.6,+ "topic": "Fine-grid current-screen milestone on isolated v77",+ "status": "Full decode and contact sheet reviewed; cooling remains open",+ "excerpt": "../docs/clips/current-screen-milestone.mp4",+ "playbackSpeed": 2 } ], "failed_captures": [