supply_air_temperature_set.py 6.6 KB

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