"""Bounded browser adapter; original one-minute synthetic balance model.""" import json,math,time from pathlib import Path from co2_kernel import simulate CONTROLS = {'plant_uptake_lit_kg_per_h': {'type': 'number', 'min': 0, 'max': 0.08, 'default': 0.08, 'step': 0.01, 'unit': 'kg/h'}, 'respiration_dark_kg_per_h': {'type': 'number', 'min': 0, 'max': 0.04, 'default': 0.02, 'step': 0.005, 'unit': 'kg/h'}, 'injection_capacity_kg_per_h': {'type': 'number', 'min': 0, 'max': 0.6, 'default': 0.4, 'step': 0.05, 'unit': 'kg/h'}, 'ventilation_m3_per_s': {'type': 'number', 'min': 0.03, 'max': 0.1, 'default': 0.03, 'step': 0.01, 'unit': 'm³/s'}, 'outside_co2_ppm': {'type': 'number', 'min': 420, 'max': 600, 'default': 420, 'step': 20, 'unit': 'ppm'}} SPEC = {'state': 'co2_ppm', 'initial': 'initial_co2_ppm', 'unit': 'ppm', 'metrics': [('minimum_co2_ppm', 'ppm'), ('maximum_co2_ppm', 'ppm'), ('final_co2_ppm', 'ppm'), ('injection_kg', 'kg CO₂'), ('uptake_kg', 'kg CO₂'), ('absolute_mole_balance_residual_mol', 'mol')], 'totals': ['respiration_kg', 'injection_kg', 'uptake_kg', 'ventilation_net_kg'], 'total_unit': 'kg CO₂'} def run(name,p): if name != 'co2' or set(p)!=set(CONTROLS):raise ValueError('Unknown experiment or parameters') for key,c in CONTROLS.items(): v=p[key] if type(v) not in (int,float) or not math.isfinite(v) or not c['min']<=v<=c['max']:raise ValueError('Parameter out of range: '+key) ticks=(v-c['min'])/c['step'] if abs(ticks-round(ticks))>1e-8:raise ValueError('Parameter off step: '+key) start=time.perf_counter() config=json.loads(Path(__file__).with_name('config.json').read_text()) config.update(p);result=simulate(config) hours=[0.]+[r['hour'] for r in result['hourly']] values=[config[SPEC['initial']]]+[r[SPEC['state']] for r in result['hourly']] series=[dict(id='room_state',x=hours,y=values,x_unit='h',y_unit=SPEC['unit']),dict(id='daily_flows',x=list(range(1,len(SPEC['totals'])+1)),y=[result[k] for k in SPEC['totals']],x_unit='term',y_unit=SPEC['total_unit'])] if name=='thermal': from thermal_kernel import outside_temperature # The kernel's hourly outside samples are from the last step start. series.append(dict(id='outside_state',x=hours,y=[outside_temperature(config,0)]+[r['outside_temp_c'] for r in result['hourly']],x_unit='h',y_unit='°C')) return dict(metrics=[dict(id=k,value=result[k],unit=u) for k,u in SPEC['metrics']],series=series,stdout='',provenance=dict(data_type='synthetic',compute_ms=(time.perf_counter()-start)*1000)) if __name__=='__main__': import argparse parser=argparse.ArgumentParser(description=__doc__);parser.add_argument('--parameters',default=json.dumps({k:c['default'] for k,c in CONTROLS.items()})) print(json.dumps(run('co2',json.loads(parser.parse_args().parameters)),allow_nan=False))