supply_air_temperature_set.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. import arrow
  2. import numpy as np
  3. from loguru import logger
  4. from app.api.errors.iot import MissingIOTDataError
  5. from app.controllers.equipment.ahu.thermal_mode import count_vav_box_weight
  6. from app.models.domain.devices import ThermalMode, ACATAHSupplyAirTempSetRequest
  7. from app.schemas.equipment import VAVBox
  8. from app.services.transfer import Season
  9. from app.utils.date import get_time_str, TIME_FMT
  10. class ACATAHSupplyAirTemperatureController:
  11. """
  12. Supply air temperature setting logic version 2 by WuXu.
  13. """
  14. def __init__(
  15. self,
  16. vav_boxes_list: list[VAVBox],
  17. current_set: float,
  18. return_air: float,
  19. thermal_mode: ThermalMode,
  20. is_off_to_on: bool,
  21. is_thermal_mode_switched: bool,
  22. season: Season,
  23. ):
  24. super(ACATAHSupplyAirTemperatureController, self).__init__()
  25. self.vav_boxes_list = vav_boxes_list
  26. self.current_set = current_set
  27. self.return_air = return_air
  28. self.thermal_mode = thermal_mode
  29. self.is_off_to_on = is_off_to_on
  30. self.is_thermal_mode_switched = is_thermal_mode_switched
  31. self.season = season
  32. def calculate_by_cold_vav(self, cold_ratio: float) -> float:
  33. if self.thermal_mode == ThermalMode.cooling:
  34. if cold_ratio < 0.3:
  35. new = self.current_set - 1.0
  36. elif cold_ratio < 0.45:
  37. new = self.current_set - 0.5
  38. elif cold_ratio <= 0.55:
  39. new = self.current_set
  40. elif cold_ratio <= 0.7:
  41. new = self.current_set + 1.0
  42. elif cold_ratio <= 1.0:
  43. new = self.current_set + 1.5
  44. else:
  45. new = self.current_set
  46. elif self.thermal_mode == ThermalMode.heating:
  47. if cold_ratio < 0.3:
  48. new = self.return_air
  49. elif cold_ratio < 0.45:
  50. new = self.current_set - 1.0
  51. elif cold_ratio <= 0.55:
  52. new = self.current_set
  53. elif cold_ratio <= 0.7:
  54. new = self.current_set + 0.5
  55. elif cold_ratio <= 1.0:
  56. new = self.current_set + 1.0
  57. else:
  58. new = self.current_set
  59. else:
  60. new = self.current_set
  61. return new
  62. def get_cold_ratio(self):
  63. cold, total = 0, 0
  64. for box in self.vav_boxes_list:
  65. temp = count_vav_box_weight(
  66. box.virtual_realtime_temperature,
  67. box.virtual_target_temperature,
  68. box.supply_air_flow_upper_limit,
  69. box.supply_air_flow_lower_limit,
  70. box.supply_air_flow_set,
  71. box.valve_opening,
  72. box.supply_air_temperature
  73. )
  74. cold += temp if temp < 0 else 0
  75. total += abs(temp)
  76. try:
  77. cold_ratio = abs(cold / total)
  78. except ZeroDivisionError:
  79. cold_ratio = np.NAN
  80. logger.debug(f"cold ratio: {cold_ratio}")
  81. return cold_ratio
  82. def get_normal_ratio(self):
  83. normal = 0
  84. for box in self.vav_boxes_list:
  85. if abs(box.virtual_realtime_temperature - box.virtual_target_temperature) <= 1:
  86. normal += 1
  87. try:
  88. ratio = normal / len(self.vav_boxes_list)
  89. except ZeroDivisionError:
  90. ratio = np.NAN
  91. return ratio
  92. def build(self) -> float:
  93. try:
  94. if not self.is_off_to_on:
  95. normal_ratio = self.get_normal_ratio()
  96. if normal_ratio < 0.9:
  97. cold_ratio = self.get_cold_ratio()
  98. temperature = self.calculate_by_cold_vav(cold_ratio)
  99. else:
  100. temperature = self.current_set
  101. else:
  102. if self.season == Season.heating:
  103. temperature = 27.0
  104. elif self.season == Season.cooling:
  105. temperature = 20.0
  106. else:
  107. temperature = 25.0
  108. if self.season == Season.heating:
  109. temperature = max(20.0, min(30.0, temperature))
  110. else:
  111. temperature = max(18.0, min(25.0, temperature))
  112. except TypeError:
  113. raise MissingIOTDataError
  114. return temperature
  115. class ACATAHSupplyAirTemperatureDefaultController:
  116. """
  117. Determine supply air temperature when missing data.
  118. """
  119. def __init__(self, is_clear_day: bool):
  120. super(ACATAHSupplyAirTemperatureDefaultController, self).__init__()
  121. self.is_clear_day = is_clear_day
  122. def build(self) -> float:
  123. now = get_time_str()
  124. now_time_str = arrow.get(now, TIME_FMT).time().strftime("%H%M%S")
  125. if "080000" <= now_time_str < "100000":
  126. is_morning = True
  127. else:
  128. is_morning = False
  129. if is_morning:
  130. temperature = 27.0
  131. else:
  132. if self.is_clear_day:
  133. temperature = 23.0
  134. else:
  135. temperature = 25.0
  136. return temperature
  137. def build_acatah_supply_air_temperature_set(params: ACATAHSupplyAirTempSetRequest) -> float:
  138. try:
  139. vav_list = list()
  140. for raw_vav in params.vav_list:
  141. vav = VAVBox(**raw_vav.dict())
  142. if not vav.supply_air_temperature:
  143. vav.supply_air_temperature = params.supply_air_temperature
  144. vav.virtual_target_temperature = raw_vav.virtual_temperature_target
  145. if vav.virtual_target_temperature and vav.virtual_realtime_temperature:
  146. vav_list.append(vav)
  147. if params.chill_water_valve_opening_set_list[-1] == 0.0:
  148. thermal_mode = ThermalMode.heating
  149. else:
  150. thermal_mode = ThermalMode.cooling
  151. is_off_to_on = False
  152. if params.equip_switch_set_list[-1] == 1.0:
  153. for item in params.equip_switch_set_list[::-1]:
  154. if item == 0.0:
  155. is_off_to_on = True
  156. break
  157. is_thermal_mode_switched = False
  158. if len(set([item for item in params.hot_water_valve_opening_set_list])) > 1:
  159. is_thermal_mode_switched = True
  160. if len(set([item for item in params.chill_water_valve_opening_set_list])) > 1:
  161. is_thermal_mode_switched = True
  162. controller = ACATAHSupplyAirTemperatureController(
  163. vav_list,
  164. params.supply_air_temperature_set,
  165. params.return_air_temperature,
  166. thermal_mode,
  167. is_off_to_on,
  168. is_thermal_mode_switched,
  169. Season(params.season),
  170. )
  171. supply_air_temperature_set = controller.build()
  172. except (KeyError, IndexError):
  173. controller = ACATAHSupplyAirTemperatureDefaultController(params.is_clear_day)
  174. supply_air_temperature_set = controller.build()
  175. return supply_air_temperature_set