"""Bounded browser adapter; original one-minute synthetic balance model.""" import json,math,time from pathlib import Path from thermal_kernel import simulate CONTROLS = {'led_power_w': {'type': 'number', 'min': 0, 'max': 20000, 'default': 12000, 'step': 500, 'unit': 'W'}, 'cooling_capacity_w': {'type': 'number', 'min': 0, 'max': 20000, 'default': 14000, 'step': 500, 'unit': 'W'}, 'outside_mean_temp_c': {'type': 'number', 'min': 10, 'max': 35, 'default': 21, 'step': 1, 'unit': '°C'}, 'ventilation_m3_per_s': {'type': 'number', 'min': 0, 'max': 0.2, 'default': 0.08, 'step': 0.01, 'unit': 'm³/s'}, 'cooling_setpoint_c': {'type': 'number', 'min': 20, 'max': 28, 'default': 24, 'step': 0.5, 'unit': '°C'}} SPEC = {'state': 'indoor_temp_c', 'initial': 'initial_indoor_temp_c', 'unit': '°C', 'metrics': [('minimum_indoor_temp_c', '°C'), ('maximum_indoor_temp_c', '°C'), ('final_indoor_temp_c', '°C'), ('led_heat_kwh', 'kWh thermal'), ('cooling_thermal_energy_kwh', 'kWh thermal'), ('absolute_energy_balance_residual_j', 'J')], 'totals': ['led_heat_kwh', 'cooling_thermal_energy_kwh'], 'total_unit': 'kWh thermal'} def run(name,p): if name != 'thermal' 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('thermal',json.loads(parser.parse_args().parameters)),allow_nan=False))