BMS/simulators/bots/chiller.py
2026-03-19 11:32:17 +00:00

93 lines
3.7 KiB
Python

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),
}