master
John Lauer Publish thermal bottleneck planner, accepted candidate, analysis and native recording scripts 0049da5 28d ago
import sys,json,math,xml.etree.ElementTree as ET
from pathlib import Path
from shapely.geometry import LineString
P=Path(__file__).resolve().parent;sys.path.insert(0,str(P.parent/'thermal-expansion'));from analyze import copper,regions
from thermal_model import mesh,solve
B=P.parent/'thermal-expansion/recording/bq25792-expanded-thermal.brd';F=P/'candidate-01/filled.brd'
a=ET.parse(B).find('drawing/board');b=ET.parse(F).find('drawing/board')
unchanged=['elements','libraries','designrules','classes'];result={'preserved':{tag:ET.tostring(a.find(tag))==ET.tostring(b.find(tag)) for tag in unchanged}}
for key,xpath in [('vias','signals/signal/via'),('pads','signals/signal/contactref')]:
 def get(x):return sorted((s.get('name'),tuple(sorted(i.attrib.items()))) for s in x.findall('signals/signal') for i in s.findall('via' if key=='vias' else 'contactref'))
 result['preserved'][key]=get(a)==get(b)
exceptions=[]
for s in b.findall('signals/signal'):
 for w in s.findall('wire'):
  dx=float(w.get('x2'))-float(w.get('x1'));dy=float(w.get('y2'))-float(w.get('y1'))
  angle=math.degrees(math.atan2(dy,dx))%45;err=min(angle,45-angle)
  if err>.02:exceptions.append({'net':s.get('name'),'angleError':err})
result['offAngleSegments']=exceptions
result['coverage']={label:{str(l):sum(g.area for g in copper(f,l).values())/(50.4*26.4)*100 for l in [1,2,15,16]} for label,f in [('before',B),('after',F)]}
ref=copper(F,2)['PGND'];result['routeReferenceScreening']=[]
for r in json.loads((P/'candidate-01/plan.json').read_text()):
 line=LineString(r['points']);before=sum(math.hypot(float(w['x2'])-float(w['x1']),float(w['y2'])-float(w['y1'])) for w in r['removed'])
 result['routeReferenceScreening'].append({'net':r['net'],'layer':r['layer'],'oldLengthMm':before,'newLengthMm':line.length,'lengthOverLayer2PGNDPercent':line.intersection(ref).length/line.length*100 if r['layer']==15 else None})
result['meshSensitivity']={}
for n,r in regions(F).items():
 q=solve(mesh(r['geometry'],r['sources'],.075),5,.8)[0];result['meshSensitivity'][n]=q
(P/'candidate-01/audit.json').write_text(json.dumps(result,indent=2));print(json.dumps(result,indent=2))