import random import math from bots.base import BaseBot class ChillerBot(BaseBot): """Water-cooled chiller plant — supplies chilled water to CRAC/CRAH units.""" interval = 60 RATED_COOLING_KW = 300.0 # total cooling capacity DESIGN_FLOW_GPM = 600.0 # nominal chilled water flow CHW_SETPOINT = 7.0 # chilled water supply setpoint (°C) CW_RETURN_DESIGN = 35.0 # condenser water return design temp (°C) def __init__(self, site_id: str, chiller_id: str) -> None: super().__init__() self.site_id = site_id self.chiller_id = chiller_id self._fault = False self._run_hours = 8_000 + random.randint(0, 2_000) def get_topic(self) -> str: return f"bms/{self.site_id}/cooling/chiller/{self.chiller_id}" def set_scenario(self, name: str | None) -> None: super().set_scenario(name) self._fault = (name == "CHILLER_FAULT") def get_payload(self) -> dict: hour_inc = self.interval / 3_600 self._run_hours += hour_inc if self._fault: return { "state": "fault", "chw_supply_c": None, "chw_return_c": None, "chw_delta_c": None, "flow_gpm": 0, "cooling_load_kw": 0, "cooling_load_pct": 0, "cop": 0, "compressor_load_pct": 0, "condenser_pressure_bar": None, "evaporator_pressure_bar": None, "cw_supply_c": None, "cw_return_c": None, "run_hours": round(self._run_hours, 1), } # Normal operation load_pct = 55.0 + random.gauss(0, 5.0) load_pct = max(20.0, min(100.0, load_pct)) load_kw = self.RATED_COOLING_KW * load_pct / 100.0 # CHW temperatures — supply held near setpoint, return rises with load chw_supply = self.CHW_SETPOINT + random.gauss(0, 0.15) chw_delta = 5.0 + (load_pct / 100.0) * 3.0 + random.gauss(0, 0.2) chw_return = chw_supply + chw_delta # Condenser water cw_return = self.CW_RETURN_DESIGN - 3.0 + (load_pct / 100.0) * 4.0 + random.gauss(0, 0.5) cw_supply = cw_return - 5.0 + random.gauss(0, 0.3) # Flow — slight variation from design flow_gpm = self.DESIGN_FLOW_GPM * (0.92 + random.gauss(0, 0.01)) # Refrigerant pressures (R134a-like) evap_p = 3.0 + (chw_supply / 10.0) + random.gauss(0, 0.05) cond_p = 12.0 + (cw_return / 10.0) + random.gauss(0, 0.1) # COP comp_load = load_pct * 0.9 + random.gauss(0, 1.0) comp_power = (comp_load / 100.0) * (load_kw / 4.5) # ~4.5 COP design cop = load_kw / comp_power if comp_power > 0 else 0.0 return { "state": "online", "chw_supply_c": round(chw_supply, 2), "chw_return_c": round(chw_return, 2), "chw_delta_c": round(chw_delta, 2), "flow_gpm": round(flow_gpm, 0), "cooling_load_kw": round(load_kw, 1), "cooling_load_pct": round(load_pct, 1), "cop": round(cop, 2), "compressor_load_pct": round(comp_load, 1), "condenser_pressure_bar": round(cond_p, 2), "evaporator_pressure_bar": round(evap_p, 2), "cw_supply_c": round(cw_supply, 2), "cw_return_c": round(cw_return, 2), "run_hours": round(self._run_hours, 1), }