import hashlib,json,re
from pathlib import Path
r=Path(__file__).parent;cfg={"weak":20e-6,"nominal":40e-6,"strong":80e-6};cases=[]
for n,i in cfg.items():
 log=(r/f"sense-{n}.log").read_text();t=float(re.search(r"t_sense\s+=\s+([0-9.eE+-]+)",log).group(1));expected=100e-15*0.1/i
 rows=[]
 for line in (r/f"sense-{n}.csv").read_text().splitlines():
  p=line.split(); rows.append({"time_s":float(p[0]),"bl_v":float(p[3]),"blb_v":float(p[5]),"delta_v":float(p[-1])})
 stride=max(1,len(rows)//160);wave=rows[::stride];cases.append({"id":n,"cell_current_a":i,"bitline_capacitance_f":100e-15,"sense_delta_v":0.1,"sense_time_s":t,"analytical_time_s":expected,"relative_error_percent":abs(t-expected)/expected*100,"waveform":wave})
def sha(n):return hashlib.sha256((r/n).read_bytes()).hexdigest()
p={"schema":"semiagora.bitline-development.v1","experiment_id":"SRAM_BITLINE_SENSE_001","title":"Differential SRAM bitline development","model":{"precharge_v":1.2,"bitline_capacitance_f":100e-15,"sense_delta_v":0.1,"identity":"t=C_BL*DeltaV/I_cell"},"tools":{"ngspice":"42"},"verification":{"cases":3,"all_converged":True,"delay_monotonic":cases[0]["sense_time_s"]>cases[1]["sense_time_s"]>cases[2]["sense_time_s"],"max_analytical_error_percent":max(x["relative_error_percent"] for x in cases)},"cases":cases,"provenance":{"netlists":{n:sha(f"sense-{n}.spice") for n in cfg}},"limitations":["Linear current-source and lumped-capacitance bitline-development model","No 6T cell transistor nonlinearity, column parasitics, mismatch, noise, or latch sense amplifier","100 mV is a chosen enable threshold, not a characterized production specification","Browser implementation will replay executed ngspice evidence"]}
(r/"bitline-sense-web-v1.json").write_text(json.dumps(p,indent=2),encoding="utf-8");print(json.dumps(p["verification"],indent=2))
