"""Synthetic well-mixed CO2 mole balance; Python 3.10+ stdlib only.""" import json,math,sys from pathlib import Path R=8.314462618; M=0.04401 LIMITS={"duration_hours":(.1,168),"time_step_seconds":(1,3600),"room_volume_m3":(1,1e7),"air_temperature_c":(-10,60),"pressure_pa":(50000,120000),"initial_co2_ppm":(0,10000),"outside_co2_ppm":(0,10000),"ventilation_m3_per_s":(0,1e5),"lights_on_hour":(0,24),"lights_off_hour":(0,24),"plant_uptake_lit_kg_per_h":(0,1e5),"respiration_dark_kg_per_h":(0,1e5),"injection_capacity_kg_per_h":(0,1e5),"injection_on_ppm":(0,10000),"injection_off_ppm":(0,10000)} def number(v,k,a,b): if isinstance(v,bool): raise ValueError(k+': boolean') try:v=float(v) except (TypeError,ValueError):raise ValueError(k+': nonnumeric') from None if not math.isfinite(v) or not a<=v<=b:raise ValueError(f'{k}: expected [{a}, {b}]') return v def validate(x): if set(x)!=set(LIMITS):raise ValueError('keys must match contract') c={k:number(x[k],k,*b) for k,b in LIMITS.items()} if c['lights_off_hour']<=c['lights_on_hour']:raise ValueError('lights schedule') if c['injection_on_ppm']>=c['injection_off_ppm']:raise ValueError('injection thresholds') n=c['duration_hours']*3600/c['time_step_seconds'] if abs(n-round(n))>1e-9:raise ValueError('integer steps required') return c def air_moles(c):return c['pressure_pa']*c['room_volume_m3']/(R*(c['air_temperature_c']+273.15)) def simulate(raw): c=validate(raw); total=air_moles(c); n=total*c['initial_co2_ppm']/1e6; out=c['outside_co2_ppm']/1e6; dt=c['time_step_seconds']; on=False sums={'uptake':0.,'respiration':0.,'injection':0.,'ventilation_net':0.}; residual=0.; low=high=c['initial_co2_ppm']; hourly=[] for i in range(round(c['duration_hours']*3600/dt)): ppm=n/total*1e6 if on and ppm>=c['injection_off_ppm']:on=False elif not on and ppm<=c['injection_on_ppm']:on=True h=i*dt/3600; lit=c['lights_on_hour']<=h%241 else Path(__file__).with_name('config.json');print(json.dumps(simulate(json.loads(p.read_text())),indent=2,allow_nan=False)) except (OSError,ValueError,json.JSONDecodeError) as e:raise SystemExit('Invalid input: '+str(e)) from None