thermal_comfort.py 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. from abc import ABC, abstractmethod
  2. from typing import Optional
  3. import numpy as np
  4. from loguru import logger
  5. from app.models.domain.diagnosis import ThermalComfortDiagnosisRequest
  6. from app.schemas.diagnosis import FaultCategory
  7. from app.schemas.equipment import AHU, FCU, VAVBox, VRF
  8. from app.services.transfer import Season
  9. class DiagnotorBase(ABC):
  10. def __init__(self):
  11. self._result = FaultCategory()
  12. def controller_check(
  13. self,
  14. setting_value: float,
  15. feedback_value: float,
  16. is_categorical: bool = True,
  17. tolerance_pct: float = 0.0
  18. ) -> None:
  19. if is_categorical:
  20. if setting_value != feedback_value:
  21. self._result.controller_err = True
  22. else:
  23. if not setting_value * (1 - tolerance_pct) <= feedback_value <= setting_value * (1 + tolerance_pct):
  24. self._result.controller_err = True
  25. class ThermalComfortDiagnotor(DiagnotorBase):
  26. def __init__(self):
  27. super(ThermalComfortDiagnotor, self).__init__()
  28. class FCUDiagnotor(DiagnotorBase):
  29. def __init__(self, fcu: FCU, ahu: AHU, duration: float, target_temp: float, realtime_temp: float, season: Season):
  30. super(FCUDiagnotor, self).__init__()
  31. self._fcu = fcu
  32. self._ahu = ahu
  33. self._duration = duration
  34. self._target_temp = target_temp
  35. self._realtime_temp = realtime_temp
  36. self._season = season
  37. def low_in_heating(self) -> None:
  38. if self._fcu.air_valve_speed == 'off':
  39. if self._fcu.speed_limit == 'off':
  40. self._result.over_constrained = True
  41. else:
  42. self._result.control_logic_err = True
  43. else:
  44. if self._fcu.air_valve_speed == self._fcu.recommended_speed:
  45. self.controller_check(self._fcu.air_valve_speed_set, self._fcu.air_valve_speed)
  46. if not self._result.controller_err:
  47. if self._fcu.air_valve_speed_set == 'high':
  48. if self._duration > 120:
  49. if self._fcu.supply_air_temperature < self._target_temp + 1:
  50. self._result.insufficient_heating = True
  51. else:
  52. self._result.undefined_err = True
  53. else:
  54. self._result.no_problems_found = True
  55. else:
  56. self._result.control_logic_err = True
  57. else:
  58. if self._fcu.speed_limit == self._fcu.air_valve_speed:
  59. self._result.over_constrained = True
  60. else:
  61. self._result.obj_info_err = True
  62. def low_in_cooling(self) -> None:
  63. if self._fcu.recommended_speed == 'off':
  64. if self._fcu.air_valve_speed == self._fcu.recommended_speed:
  65. self.controller_check(self._fcu.air_valve_speed_set, self._fcu.air_valve_speed)
  66. if not self._result.controller_err:
  67. if self._duration > 120:
  68. self._result.undefined_err = True
  69. else:
  70. self._result.no_problems_found = True
  71. else:
  72. self._result.obj_info_err = True
  73. else:
  74. self._result.control_logic_err = True
  75. def high_in_heating(self) -> None:
  76. self.low_in_cooling()
  77. def high_in_cooling(self) -> None:
  78. self.low_in_heating()
  79. if self._result.insufficient_heating:
  80. self._result.insufficient_heating = False
  81. self._result.insufficient_cooling = True
  82. def run(self) -> None:
  83. if self._season == Season.heating or self._fcu.supply_air_temperature > self._target_temp:
  84. if self._realtime_temp < self._target_temp - 1:
  85. self.low_in_heating()
  86. elif self._realtime_temp > self._target_temp + 1:
  87. self.high_in_heating()
  88. elif self._season == Season.cooling or self._fcu.supply_air_temperature < self._target_temp:
  89. if self._realtime_temp < self._target_temp - 1:
  90. self.low_in_cooling()
  91. elif self._realtime_temp > self._target_temp + 1:
  92. self.high_in_cooling()
  93. class VAVDiagnotor(DiagnotorBase):
  94. def __init__(self, vav: VAVBox, ahu: AHU, known_err: FaultCategory, duration: float, season: Season):
  95. super(VAVDiagnotor, self).__init__()
  96. self._vav = vav
  97. self._ahu = ahu
  98. self._known_err = known_err
  99. self._duration = duration
  100. self._season = season
  101. def low_in_heating(self) -> None:
  102. self.controller_check(
  103. self._vav.supply_air_flow_set,
  104. self._vav.supply_air_flow,
  105. is_categorical=False,
  106. tolerance_pct=0.1
  107. )
  108. if self._vav.recommended_supply_air_flow >= self._vav.supply_air_flow_upper_limit:
  109. if self._vav.recommended_supply_air_flow == self._vav.supply_air_flow_set:
  110. if not self._result.controller_err:
  111. if self._duration > 120.0:
  112. if self._vav.supply_air_temperature < self._vav.virtual_target_temperature + 1 \
  113. or self._ahu.supply_air_temperature < self._vav.virtual_target_temperature + 2:
  114. self._result.insufficient_heating = True
  115. else:
  116. if self._vav.supply_air_flow_upper_limit < 1200.0:
  117. self._result.unreasonable_vav_flow_limit = True
  118. else:
  119. if not self._known_err.sensor_err:
  120. self._result.undefined_err = True
  121. else:
  122. self._result.no_problems_found = True
  123. else:
  124. self._result.obj_info_err = True
  125. else:
  126. if self._vav.recommended_supply_air_flow != self._vav.supply_air_flow_set:
  127. self._result.obj_info_err = True
  128. def low_in_cooling(self) -> None:
  129. if self._vav.recommended_supply_air_flow == self._vav.supply_air_flow_set:
  130. self.controller_check(
  131. self._vav.supply_air_flow_set,
  132. self._vav.supply_air_flow,
  133. is_categorical=False,
  134. tolerance_pct=0.1
  135. )
  136. if self._vav.supply_air_flow_set <= self._vav.supply_air_flow_lower_limit:
  137. if not self._result.controller_err:
  138. if self._duration >= 120.0:
  139. if self._vav.supply_air_flow_lower_limit > 400:
  140. self._result.unreasonable_vav_flow_limit = True
  141. else:
  142. if not self._known_err.sensor_err:
  143. if self._vav.supply_air_temperature < 18 or self._ahu.supply_air_temperature < 18:
  144. self._result.excessive_cooling = True
  145. else:
  146. self._result.obj_info_err = True
  147. else:
  148. self._result.no_problems_found = True
  149. else:
  150. self._result.no_problems_found = True
  151. else:
  152. self._result.obj_info_err = True
  153. def high_in_heating(self) -> None:
  154. self.low_in_cooling()
  155. if self._result.excessive_cooling:
  156. self._result.excessive_cooling = False
  157. self._result.excessive_heating = True
  158. def high_in_cooling(self) -> None:
  159. self.low_in_heating()
  160. if self._vav.recommended_supply_air_flow >= self._vav.supply_air_flow_upper_limit:
  161. if self._vav.recommended_supply_air_flow == self._vav.supply_air_flow_set:
  162. if not self._result.controller_err:
  163. if self._duration > 120.0:
  164. if self._vav.supply_air_temperature > self._vav.virtual_target_temperature + 1 \
  165. or self._ahu.supply_air_temperature > self._vav.virtual_target_temperature + 2:
  166. self._result.insufficient_cooling = True
  167. def run(self) -> None:
  168. if self._season == Season.heating or self._vav.supply_air_temperature > self._vav.virtual_target_temperature:
  169. if self._vav.virtual_realtime_temperature < self._vav.virtual_target_temperature - 1:
  170. self.low_in_heating()
  171. elif self._vav.virtual_realtime_temperature > self._vav.virtual_target_temperature + 1:
  172. self.high_in_heating()
  173. elif self._season == Season.cooling or self._vav.supply_air_temperature < self._vav.virtual_target_temperature:
  174. if self._vav.virtual_realtime_temperature < self._vav.virtual_target_temperature - 1:
  175. self.low_in_cooling()
  176. elif self._vav.virtual_realtime_temperature > self._vav.virtual_target_temperature + 1:
  177. self.high_in_cooling()
  178. class VRFDiagnotor(DiagnotorBase):
  179. def __init__(self, vrf: VRF):
  180. super(VRFDiagnotor, self).__init__()
  181. self._vrf = vrf
  182. def get_diagnosis_result(params: ThermalComfortDiagnosisRequest) -> FaultCategory:
  183. if params.fcu_list:
  184. for fcu in params.fcu_list:
  185. fcu_diagnotor = FCUDiagnotor(
  186. fcu,
  187. params.ahu,
  188. params.duration_minutes,
  189. params.target_temp,
  190. params.realtime_temp,
  191. params.season
  192. )