open-fdd 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. open_fdd/__init__.py +39 -0
  2. open_fdd/air_handling_unit/__init__.py +0 -0
  3. open_fdd/air_handling_unit/faults/__init__.py +0 -0
  4. open_fdd/air_handling_unit/faults/fault_condition.py +49 -0
  5. open_fdd/air_handling_unit/faults/fault_condition_eight.py +67 -0
  6. open_fdd/air_handling_unit/faults/fault_condition_eleven.py +68 -0
  7. open_fdd/air_handling_unit/faults/fault_condition_fifteen.py +90 -0
  8. open_fdd/air_handling_unit/faults/fault_condition_five.py +68 -0
  9. open_fdd/air_handling_unit/faults/fault_condition_four.py +93 -0
  10. open_fdd/air_handling_unit/faults/fault_condition_fourteen.py +80 -0
  11. open_fdd/air_handling_unit/faults/fault_condition_nine.py +68 -0
  12. open_fdd/air_handling_unit/faults/fault_condition_one.py +60 -0
  13. open_fdd/air_handling_unit/faults/fault_condition_seven.py +55 -0
  14. open_fdd/air_handling_unit/faults/fault_condition_six.py +120 -0
  15. open_fdd/air_handling_unit/faults/fault_condition_ten.py +62 -0
  16. open_fdd/air_handling_unit/faults/fault_condition_thirteen.py +66 -0
  17. open_fdd/air_handling_unit/faults/fault_condition_three.py +58 -0
  18. open_fdd/air_handling_unit/faults/fault_condition_twelve.py +71 -0
  19. open_fdd/air_handling_unit/faults/fault_condition_two.py +61 -0
  20. open_fdd/air_handling_unit/faults/helper_utils.py +191 -0
  21. open_fdd/air_handling_unit/faults/shared_utils.py +75 -0
  22. open_fdd/air_handling_unit/reports/__init__.py +0 -0
  23. open_fdd/air_handling_unit/reports/report_fc1.py +115 -0
  24. open_fdd/air_handling_unit/reports/report_fc10.py +126 -0
  25. open_fdd/air_handling_unit/reports/report_fc11.py +128 -0
  26. open_fdd/air_handling_unit/reports/report_fc12.py +126 -0
  27. open_fdd/air_handling_unit/reports/report_fc13.py +126 -0
  28. open_fdd/air_handling_unit/reports/report_fc14.py +124 -0
  29. open_fdd/air_handling_unit/reports/report_fc15.py +124 -0
  30. open_fdd/air_handling_unit/reports/report_fc2.py +119 -0
  31. open_fdd/air_handling_unit/reports/report_fc3.py +119 -0
  32. open_fdd/air_handling_unit/reports/report_fc4.py +148 -0
  33. open_fdd/air_handling_unit/reports/report_fc5.py +132 -0
  34. open_fdd/air_handling_unit/reports/report_fc6.py +156 -0
  35. open_fdd/air_handling_unit/reports/report_fc7.py +124 -0
  36. open_fdd/air_handling_unit/reports/report_fc8.py +118 -0
  37. open_fdd/air_handling_unit/reports/report_fc9.py +120 -0
  38. open_fdd/tests/__init__.py +0 -0
  39. open_fdd/tests/ahu/__init__.py +0 -0
  40. open_fdd/tests/ahu/test_ahu_fc1.py +159 -0
  41. open_fdd/tests/ahu/test_ahu_fc10.py +132 -0
  42. open_fdd/tests/ahu/test_ahu_fc11.py +136 -0
  43. open_fdd/tests/ahu/test_ahu_fc12.py +167 -0
  44. open_fdd/tests/ahu/test_ahu_fc13.py +163 -0
  45. open_fdd/tests/ahu/test_ahu_fc14.py +197 -0
  46. open_fdd/tests/ahu/test_ahu_fc15.py +183 -0
  47. open_fdd/tests/ahu/test_ahu_fc2.py +132 -0
  48. open_fdd/tests/ahu/test_ahu_fc3.py +131 -0
  49. open_fdd/tests/ahu/test_ahu_fc4.py +200 -0
  50. open_fdd/tests/ahu/test_ahu_fc5.py +180 -0
  51. open_fdd/tests/ahu/test_ahu_fc6.py +246 -0
  52. open_fdd/tests/ahu/test_ahu_fc7.py +71 -0
  53. open_fdd/tests/ahu/test_ahu_fc8.py +131 -0
  54. open_fdd/tests/ahu/test_ahu_fc9.py +136 -0
  55. open_fdd-0.1.0.dist-info/LICENSE +21 -0
  56. open_fdd-0.1.0.dist-info/METADATA +65 -0
  57. open_fdd-0.1.0.dist-info/RECORD +59 -0
  58. open_fdd-0.1.0.dist-info/WHEEL +5 -0
  59. open_fdd-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,180 @@
1
+ import pandas as pd
2
+ import pytest
3
+ from open_fdd.air_handling_unit.faults.fault_condition_five import FaultConditionFive
4
+ from open_fdd.air_handling_unit.faults.helper_utils import HelperUtils
5
+
6
+ """
7
+ To see print statements in pytest run with:
8
+ $ py -3.12 -m pytest open_fdd/tests/ahu/test_ahu_fc5.py -rP -s
9
+
10
+ SAT too low; should be higher than MAT in HTG MODE
11
+ """
12
+
13
+ # Constants
14
+ TEST_MIX_DEGF_ERR_THRES = 2.0
15
+ TEST_SUPPLY_DEGF_ERR_THRES = 2.0
16
+ TEST_DELTA_T_SUPPLY_FAN = 5.0
17
+ TEST_MIX_TEMP_COL = "mix_air_temp"
18
+ TEST_SUPPLY_TEMP_COL = "supply_air_temp"
19
+ TEST_HEATING_COIL_SIG_COL = "heating_sig_col"
20
+ TEST_SUPPLY_VFD_SPEED_COL = "supply_vfd_speed"
21
+ ROLLING_WINDOW_SIZE = 5
22
+
23
+ # Initialize FaultConditionFive with a dictionary
24
+ fault_condition_params = {
25
+ "MIX_DEGF_ERR_THRES": TEST_MIX_DEGF_ERR_THRES,
26
+ "SUPPLY_DEGF_ERR_THRES": TEST_SUPPLY_DEGF_ERR_THRES,
27
+ "DELTA_T_SUPPLY_FAN": TEST_DELTA_T_SUPPLY_FAN,
28
+ "MAT_COL": TEST_MIX_TEMP_COL,
29
+ "SAT_COL": TEST_SUPPLY_TEMP_COL,
30
+ "HEATING_SIG_COL": TEST_HEATING_COIL_SIG_COL,
31
+ "SUPPLY_VFD_SPEED_COL": TEST_SUPPLY_VFD_SPEED_COL,
32
+ "TROUBLESHOOT_MODE": False, # default value
33
+ "ROLLING_WINDOW_SIZE": ROLLING_WINDOW_SIZE, # rolling sum window size
34
+ }
35
+
36
+ fc5 = FaultConditionFive(fault_condition_params)
37
+
38
+
39
+ class TestNoFaultInHtg(object):
40
+
41
+ def no_fault_df_in_htg(self) -> pd.DataFrame:
42
+ data = {
43
+ TEST_MIX_TEMP_COL: [40.0, 42.0, 43.0, 41.0, 39.0, 40.0],
44
+ TEST_SUPPLY_TEMP_COL: [80.0, 82.0, 81.0, 83.0, 84.0, 80.0],
45
+ TEST_HEATING_COIL_SIG_COL: [0.55, 0.56, 0.54, 0.55, 0.57, 0.55],
46
+ TEST_SUPPLY_VFD_SPEED_COL: [0.55, 0.56, 0.54, 0.55, 0.57, 0.55],
47
+ }
48
+ return pd.DataFrame(data)
49
+
50
+ def test_no_fault_in_htg(self):
51
+ results = fc5.apply(self.no_fault_df_in_htg())
52
+ actual = results["fc5_flag"].sum()
53
+ expected = 0
54
+ message = (
55
+ f"FC5 no_fault_df_in_htg actual is {actual} and expected is {expected}"
56
+ )
57
+ assert actual == expected, message
58
+
59
+
60
+ class TestFaultInHtg(object):
61
+
62
+ def fault_df_in_htg(self) -> pd.DataFrame:
63
+ data = {
64
+ TEST_MIX_TEMP_COL: [80.0, 81.0, 79.0, 80.5, 82.0, 80.0],
65
+ TEST_SUPPLY_TEMP_COL: [
66
+ 81.0,
67
+ 81.0,
68
+ 79.0,
69
+ 80.5,
70
+ 82.0,
71
+ 80.0,
72
+ ], # sim not temp rise
73
+ TEST_HEATING_COIL_SIG_COL: [0.55, 0.56, 0.54, 0.55, 0.57, 0.55],
74
+ TEST_SUPPLY_VFD_SPEED_COL: [0.55, 0.56, 0.54, 0.55, 0.57, 0.55],
75
+ }
76
+ return pd.DataFrame(data)
77
+
78
+ def test_fault_in_htg(self):
79
+ results = fc5.apply(self.fault_df_in_htg())
80
+ actual = results["fc5_flag"].sum()
81
+ expected = 2
82
+ message = f"FC5 fault_df_in_htg actual is {actual} and expected is {expected}"
83
+ assert actual == expected, message
84
+
85
+
86
+ class TestNoFaultNoHtg(object):
87
+
88
+ def no_fault_df_no_htg(self) -> pd.DataFrame:
89
+ data = {
90
+ TEST_MIX_TEMP_COL: [40.0, 42.0, 43.0, 41.0, 39.0, 40.0],
91
+ TEST_SUPPLY_TEMP_COL: [80.0, 82.0, 81.0, 83.0, 84.0, 80.0],
92
+ TEST_HEATING_COIL_SIG_COL: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
93
+ TEST_SUPPLY_VFD_SPEED_COL: [0.55, 0.56, 0.54, 0.55, 0.57, 0.55],
94
+ }
95
+ return pd.DataFrame(data)
96
+
97
+ def test_no_fault_no_htg(self):
98
+ results = fc5.apply(self.no_fault_df_no_htg())
99
+ actual = results["fc5_flag"].sum()
100
+ expected = 0
101
+ message = (
102
+ f"FC5 no_fault_df_no_htg actual is {actual} and expected is {expected}"
103
+ )
104
+ assert actual == expected, message
105
+
106
+
107
+ class TestFaultNoHtg(object):
108
+
109
+ def fault_df_no_htg(self) -> pd.DataFrame:
110
+ data = {
111
+ TEST_MIX_TEMP_COL: [80.0, 81.0, 79.0, 80.5, 82.0, 80.0],
112
+ TEST_SUPPLY_TEMP_COL: [40.0, 41.0, 42.0, 43.0, 40.5, 40.0],
113
+ TEST_HEATING_COIL_SIG_COL: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
114
+ TEST_SUPPLY_VFD_SPEED_COL: [0.55, 0.56, 0.54, 0.55, 0.57, 0.55],
115
+ }
116
+ return pd.DataFrame(data)
117
+
118
+ def test_fault_no_htg(self):
119
+ results = fc5.apply(self.fault_df_no_htg())
120
+ actual = results["fc5_flag"].sum()
121
+ expected = 0
122
+ message = f"FC5 fault_df_no_htg actual is {actual} and expected is {expected}"
123
+ assert actual == expected, message
124
+
125
+
126
+ class TestFaultOnInt(object):
127
+
128
+ def fault_df_on_output_int(self) -> pd.DataFrame:
129
+ data = {
130
+ TEST_MIX_TEMP_COL: [80.0, 81.0, 79.0, 80.5, 82.0, 80.0],
131
+ TEST_SUPPLY_TEMP_COL: [40.0, 41.0, 42.0, 43.0, 40.5, 40.0],
132
+ TEST_HEATING_COIL_SIG_COL: [0.55, 0.56, 0.54, 0.55, 0.57, 0.55],
133
+ TEST_SUPPLY_VFD_SPEED_COL: [55, 56, 54, 55, 57, 55],
134
+ }
135
+ return pd.DataFrame(data)
136
+
137
+ def test_fault_on_int(self):
138
+ with pytest.raises(
139
+ TypeError,
140
+ match=HelperUtils().float_int_check_err(TEST_SUPPLY_VFD_SPEED_COL),
141
+ ):
142
+ fc5.apply(self.fault_df_on_output_int())
143
+
144
+
145
+ class TestFaultOnFloatGreaterThanOne(object):
146
+
147
+ def fault_df_on_output_greater_than_one(self) -> pd.DataFrame:
148
+ data = {
149
+ TEST_MIX_TEMP_COL: [80.0, 81.0, 79.0, 80.5, 82.0, 80.0],
150
+ TEST_SUPPLY_TEMP_COL: [40.0, 41.0, 42.0, 43.0, 40.5, 40.0],
151
+ TEST_HEATING_COIL_SIG_COL: [0.55, 0.56, 0.54, 0.55, 0.57, 0.55],
152
+ TEST_SUPPLY_VFD_SPEED_COL: [1.1, 1.2, 1.1, 1.3, 1.1, 1.2],
153
+ }
154
+ return pd.DataFrame(data)
155
+
156
+ def test_fault_on_float_greater_than_one(self):
157
+ with pytest.raises(
158
+ TypeError,
159
+ match=HelperUtils().float_max_check_err(TEST_SUPPLY_VFD_SPEED_COL),
160
+ ):
161
+ fc5.apply(self.fault_df_on_output_greater_than_one())
162
+
163
+
164
+ class TestFaultOnMixedTypes(object):
165
+
166
+ def fault_df_on_mixed_types(self) -> pd.DataFrame:
167
+ data = {
168
+ TEST_MIX_TEMP_COL: [80.0, 81.0, 79.0, 80.5, 82.0, 80.0],
169
+ TEST_SUPPLY_TEMP_COL: [40.0, 41.0, 42.0, 43.0, 40.5, 40.0],
170
+ TEST_HEATING_COIL_SIG_COL: [0.55, 0.56, 0.54, 0.55, 0.57, 0.55],
171
+ TEST_SUPPLY_VFD_SPEED_COL: [1.1, 0.55, 1.2, 1.3, 0.55, 1.1],
172
+ }
173
+ return pd.DataFrame(data)
174
+
175
+ def test_fault_on_mixed_types(self):
176
+ with pytest.raises(
177
+ TypeError,
178
+ match=HelperUtils().float_max_check_err(TEST_SUPPLY_VFD_SPEED_COL),
179
+ ):
180
+ fc5.apply(self.fault_df_on_mixed_types())
@@ -0,0 +1,246 @@
1
+ import pandas as pd
2
+ import pytest
3
+ from open_fdd.air_handling_unit.faults.fault_condition_six import FaultConditionSix
4
+ from open_fdd.air_handling_unit.faults.helper_utils import HelperUtils
5
+
6
+ """
7
+ To see print statements in pytest run with:
8
+ $ py -3.12 -m pytest tests/ahu/test_ahu_fc6.py -rP -s
9
+
10
+ OA FRACTION TOO LOW OR TOO HIGH; SHOULD BE EQUAL TO %OAmin
11
+ """
12
+
13
+ # Constants
14
+ TEST_AIRFLOW_ERR_THRES = 0.3
15
+ TEST_AHU_MIN_CFM_DESIGN = 3000
16
+ TEST_OAT_DEGF_ERR_THRES = 5.0
17
+ TEST_RAT_DEGF_ERR_THRES = 2.0
18
+ TEST_DELTA_TEMP_MIN = 10.0
19
+ TEST_AHU_MIN_OA_DPR = 0.2
20
+ TEST_VAV_TOTAL_AIR_FLOW_COL = "vav_total_air_flow"
21
+ TEST_MIX_TEMP_COL = "mix_air_temp"
22
+ TEST_OUT_TEMP_COL = "out_air_temp"
23
+ TEST_RETURN_TEMP_COL = "return_air_temp"
24
+ TEST_SUPPLY_VFD_SPEED_COL = "fan_vfd_speed_col"
25
+ TEST_MIX_AIR_DAMPER_COL = "economizer_sig_col"
26
+ TEST_HEATING_COIL_SIG_COL = "heating_sig_col"
27
+ TEST_COOLING_COIL_SIG_COL = "cooling_sig_col"
28
+ ROLLING_WINDOW_SIZE = 5
29
+
30
+ # Initialize FaultConditionSix with a dictionary
31
+ fault_condition_params = {
32
+ "AIRFLOW_ERR_THRES": TEST_AIRFLOW_ERR_THRES,
33
+ "AHU_MIN_OA_CFM_DESIGN": TEST_AHU_MIN_CFM_DESIGN,
34
+ "OUTDOOR_DEGF_ERR_THRES": TEST_OAT_DEGF_ERR_THRES,
35
+ "RETURN_DEGF_ERR_THRES": TEST_RAT_DEGF_ERR_THRES,
36
+ "OAT_RAT_DELTA_MIN": TEST_DELTA_TEMP_MIN,
37
+ "AHU_MIN_OA_DPR": TEST_AHU_MIN_OA_DPR,
38
+ "SUPPLY_FAN_AIR_VOLUME_COL": TEST_VAV_TOTAL_AIR_FLOW_COL,
39
+ "MAT_COL": TEST_MIX_TEMP_COL,
40
+ "OAT_COL": TEST_OUT_TEMP_COL,
41
+ "RAT_COL": TEST_RETURN_TEMP_COL,
42
+ "SUPPLY_VFD_SPEED_COL": TEST_SUPPLY_VFD_SPEED_COL,
43
+ "ECONOMIZER_SIG_COL": TEST_MIX_AIR_DAMPER_COL,
44
+ "HEATING_SIG_COL": TEST_HEATING_COIL_SIG_COL,
45
+ "COOLING_SIG_COL": TEST_COOLING_COIL_SIG_COL,
46
+ "TROUBLESHOOT_MODE": False, # default value
47
+ "ROLLING_WINDOW_SIZE": ROLLING_WINDOW_SIZE, # rolling sum window size
48
+ }
49
+
50
+ fc6 = FaultConditionSix(fault_condition_params)
51
+
52
+
53
+ class TestNoFaultNoHtg(object):
54
+
55
+ def no_fault_df_no_htg(self) -> pd.DataFrame:
56
+ data = {
57
+ TEST_VAV_TOTAL_AIR_FLOW_COL: [10000, 10050, 10025, 10075, 10030, 10020],
58
+ TEST_MIX_TEMP_COL: [55, 56, 55.5, 56.5, 55.2, 55.8],
59
+ TEST_OUT_TEMP_COL: [10, 10.5, 10.2, 10.8, 10.3, 10.1],
60
+ TEST_RETURN_TEMP_COL: [72, 72.5, 72.2, 72.8, 72.3, 72.1],
61
+ TEST_SUPPLY_VFD_SPEED_COL: [0.66, 0.67, 0.65, 0.66, 0.68, 0.67],
62
+ TEST_MIX_AIR_DAMPER_COL: [TEST_AHU_MIN_OA_DPR] * 6,
63
+ TEST_HEATING_COIL_SIG_COL: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
64
+ TEST_COOLING_COIL_SIG_COL: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
65
+ }
66
+ return pd.DataFrame(data)
67
+
68
+ def test_no_fault_no_htg(self):
69
+ results = fc6.apply(self.no_fault_df_no_htg())
70
+ actual = results["fc6_flag"].sum()
71
+ expected = 0
72
+ message = (
73
+ f"FC6 no_fault_df_no_htg actual is {actual} and expected is {expected}"
74
+ )
75
+ assert actual == expected, message
76
+
77
+
78
+ class TestFaultInHtg(object):
79
+
80
+ def fault_df_in_htg(self) -> pd.DataFrame:
81
+ data = {
82
+ TEST_VAV_TOTAL_AIR_FLOW_COL: [10000, 10050, 10025, 10075, 10030, 10020],
83
+ TEST_MIX_TEMP_COL: [30, 29.5, 30.5, 29.8, 30.2, 29.6],
84
+ TEST_OUT_TEMP_COL: [10, 10.5, 10.2, 10.8, 10.3, 10.1],
85
+ TEST_RETURN_TEMP_COL: [72, 72.5, 72.2, 72.8, 72.3, 72.1],
86
+ TEST_SUPPLY_VFD_SPEED_COL: [0.66, 0.67, 0.65, 0.66, 0.68, 0.67],
87
+ TEST_MIX_AIR_DAMPER_COL: [TEST_AHU_MIN_OA_DPR] * 6,
88
+ TEST_HEATING_COIL_SIG_COL: [0.66, 0.67, 0.65, 0.66, 0.68, 0.67],
89
+ TEST_COOLING_COIL_SIG_COL: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
90
+ }
91
+ return pd.DataFrame(data)
92
+
93
+ def test_fault_in_htg(self):
94
+ results = fc6.apply(self.fault_df_in_htg())
95
+ actual = results["fc6_flag"].sum()
96
+ expected = 2
97
+ message = f"FC6 fault_df_in_htg actual is {actual} and expected is {expected}"
98
+ assert actual == expected, message
99
+
100
+
101
+ class TestNoFaultInEconMode(object):
102
+
103
+ def no_fault_df_in_econ(self) -> pd.DataFrame:
104
+ data = {
105
+ TEST_VAV_TOTAL_AIR_FLOW_COL: [10000, 10050, 10025, 10075, 10030, 10020],
106
+ TEST_MIX_TEMP_COL: [90, 91, 89.5, 91.2, 90.5, 91.1],
107
+ TEST_OUT_TEMP_COL: [110, 110.5, 110.2, 110.8, 110.3, 110.1],
108
+ TEST_RETURN_TEMP_COL: [72, 72.5, 72.2, 72.8, 72.3, 72.1],
109
+ TEST_SUPPLY_VFD_SPEED_COL: [0.66, 0.67, 0.65, 0.66, 0.68, 0.67],
110
+ TEST_MIX_AIR_DAMPER_COL: [0.99, 0.98, 0.97, 0.99, 0.98, 0.99],
111
+ TEST_HEATING_COIL_SIG_COL: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
112
+ TEST_COOLING_COIL_SIG_COL: [0.50, 0.51, 0.49, 0.52, 0.50, 0.51],
113
+ }
114
+ return pd.DataFrame(data)
115
+
116
+ def test_no_fault_in_econ_mode(self):
117
+ results = fc6.apply(self.no_fault_df_in_econ())
118
+ actual = results["fc6_flag"].sum()
119
+ expected = 0
120
+ message = (
121
+ f"FC6 no_fault_df_in_econ actual is {actual} and expected is {expected}"
122
+ )
123
+ assert actual == expected, message
124
+
125
+
126
+ class TestNoFaultInEconPlusMechClg(object):
127
+
128
+ def no_fault_df_in_econ_plus_mech(self) -> pd.DataFrame:
129
+ data = {
130
+ TEST_VAV_TOTAL_AIR_FLOW_COL: [10000, 10050, 10025, 10075, 10030, 10020],
131
+ TEST_MIX_TEMP_COL: [55, 56, 55.5, 56.5, 55.2, 55.8],
132
+ TEST_OUT_TEMP_COL: [10, 10.5, 10.2, 10.8, 10.3, 10.1],
133
+ TEST_RETURN_TEMP_COL: [72, 72.5, 72.2, 72.8, 72.3, 72.1],
134
+ TEST_SUPPLY_VFD_SPEED_COL: [0.66, 0.67, 0.65, 0.66, 0.68, 0.67],
135
+ TEST_MIX_AIR_DAMPER_COL: [0.66, 0.67, 0.65, 0.66, 0.68, 0.67],
136
+ TEST_HEATING_COIL_SIG_COL: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
137
+ TEST_COOLING_COIL_SIG_COL: [0.5, 0.51, 0.49, 0.52, 0.50, 0.51],
138
+ }
139
+ return pd.DataFrame(data)
140
+
141
+ def test_no_fault_in_econ_plus_mech_mode(self):
142
+ results = fc6.apply(self.no_fault_df_in_econ_plus_mech())
143
+ actual = results["fc6_flag"].sum()
144
+ expected = 0
145
+ message = f"FC6 no_fault_df_in_econ_plus_mech actual is {actual} and expected is {expected}"
146
+ assert actual == expected, message
147
+
148
+
149
+ class TestFaultInMechClg(object):
150
+
151
+ def fault_df_in_mech_clg(self) -> pd.DataFrame:
152
+ data = {
153
+ TEST_VAV_TOTAL_AIR_FLOW_COL: [10000, 10050, 10025, 10075, 10030, 10020],
154
+ TEST_MIX_TEMP_COL: [30, 30.5, 30, 30.5, 30.8, 30.2],
155
+ TEST_OUT_TEMP_COL: [5.2, 5.5, 5.2, 5.8, 5.3, 5.1],
156
+ TEST_RETURN_TEMP_COL: [72, 72.5, 72.2, 72.8, 72.3, 72.1],
157
+ TEST_SUPPLY_VFD_SPEED_COL: [0.66, 0.67, 0.65, 0.66, 0.68, 0.67],
158
+ TEST_MIX_AIR_DAMPER_COL: [TEST_AHU_MIN_OA_DPR] * 6,
159
+ TEST_HEATING_COIL_SIG_COL: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
160
+ TEST_COOLING_COIL_SIG_COL: [0.50, 0.51, 0.49, 0.52, 0.50, 0.51],
161
+ }
162
+ return pd.DataFrame(data)
163
+
164
+ def test_fault_in_mech_mode(self):
165
+ results = fc6.apply(self.fault_df_in_mech_clg())
166
+ actual = results["fc6_flag"].sum()
167
+ expected = 2
168
+ message = (
169
+ f"FC6 fault_df_in_mech_clg actual is {actual} and expected is {expected}"
170
+ )
171
+ assert actual == expected, message
172
+
173
+
174
+ class TestNoFaultInMechClg(object):
175
+
176
+ def no_fault_df_in_mech_clg(self) -> pd.DataFrame:
177
+ data = {
178
+ TEST_VAV_TOTAL_AIR_FLOW_COL: [10000, 10050, 10025, 10075, 10030, 10020],
179
+ TEST_MIX_TEMP_COL: [60, 60.5, 60, 60.5, 60.8, 60.2],
180
+ TEST_OUT_TEMP_COL: [5.2, 5.5, 5.2, 5.8, 5.3, 5.1],
181
+ TEST_RETURN_TEMP_COL: [72, 72.5, 72.2, 72.8, 72.3, 72.1],
182
+ TEST_SUPPLY_VFD_SPEED_COL: [0.66, 0.67, 0.65, 0.66, 0.68, 0.67],
183
+ TEST_MIX_AIR_DAMPER_COL: [TEST_AHU_MIN_OA_DPR] * 6,
184
+ TEST_HEATING_COIL_SIG_COL: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
185
+ TEST_COOLING_COIL_SIG_COL: [0.50, 0.51, 0.49, 0.52, 0.50, 0.51],
186
+ }
187
+ return pd.DataFrame(data)
188
+
189
+ def test_no_fault_in_mech_mode(self):
190
+ results = fc6.apply(self.no_fault_df_in_mech_clg())
191
+ actual = results["fc6_flag"].sum()
192
+ expected = 0
193
+ message = (
194
+ f"FC6 no_fault_df_in_mech_clg actual is {actual} and expected is {expected}"
195
+ )
196
+ assert actual == expected, message
197
+
198
+
199
+ class TestFaultOnInt(object):
200
+
201
+ def fault_df_on_output_int(self) -> pd.DataFrame:
202
+ data = {
203
+ TEST_VAV_TOTAL_AIR_FLOW_COL: [10000, 10050, 10025, 10075, 10030, 10020],
204
+ TEST_MIX_TEMP_COL: [80, 80.5, 81, 80.8, 81.2, 80.3],
205
+ TEST_OUT_TEMP_COL: [110, 110.5, 110.2, 110.8, 110.3, 110.1],
206
+ TEST_RETURN_TEMP_COL: [72, 72.5, 72.2, 72.8, 72.3, 72.1],
207
+ TEST_SUPPLY_VFD_SPEED_COL: [66, 67, 65, 66, 68, 67],
208
+ TEST_MIX_AIR_DAMPER_COL: [TEST_AHU_MIN_OA_DPR] * 6,
209
+ TEST_HEATING_COIL_SIG_COL: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
210
+ TEST_COOLING_COIL_SIG_COL: [0.50, 0.51, 0.49, 0.52, 0.50, 0.51],
211
+ }
212
+ return pd.DataFrame(data)
213
+
214
+ def test_fault_on_int(self):
215
+ with pytest.raises(
216
+ TypeError,
217
+ match=HelperUtils().float_int_check_err(TEST_SUPPLY_VFD_SPEED_COL),
218
+ ):
219
+ fc6.apply(self.fault_df_on_output_int())
220
+
221
+
222
+ class TestFaultOnFloatGreaterThanOne(object):
223
+
224
+ def fault_df_on_output_greater_than_one(self) -> pd.DataFrame:
225
+ data = {
226
+ TEST_VAV_TOTAL_AIR_FLOW_COL: [10000, 10050, 10025, 10075, 10030, 10020],
227
+ TEST_MIX_TEMP_COL: [80, 80.5, 81, 80.8, 81.2, 80.3],
228
+ TEST_OUT_TEMP_COL: [110, 110.5, 110.2, 110.8, 110.3, 110.1],
229
+ TEST_RETURN_TEMP_COL: [72, 72.5, 72.2, 72.8, 72.3, 72.1],
230
+ TEST_SUPPLY_VFD_SPEED_COL: [1.1, 1.2, 1.3, 1.1, 1.2, 1.1],
231
+ TEST_MIX_AIR_DAMPER_COL: [TEST_AHU_MIN_OA_DPR] * 6,
232
+ TEST_HEATING_COIL_SIG_COL: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
233
+ TEST_COOLING_COIL_SIG_COL: [0.50, 0.51, 0.49, 0.52, 0.50, 0.51],
234
+ }
235
+ return pd.DataFrame(data)
236
+
237
+ def test_fault_on_float_greater_than_one(self):
238
+ with pytest.raises(
239
+ TypeError,
240
+ match=HelperUtils().float_max_check_err(TEST_SUPPLY_VFD_SPEED_COL),
241
+ ):
242
+ fc6.apply(self.fault_df_on_output_greater_than_one())
243
+
244
+
245
+ if __name__ == "__main__":
246
+ pytest.main()
@@ -0,0 +1,71 @@
1
+ import pandas as pd
2
+ import pytest
3
+ from open_fdd.air_handling_unit.faults.fault_condition_seven import FaultConditionSeven
4
+ from open_fdd.air_handling_unit.faults.helper_utils import HelperUtils
5
+
6
+ """
7
+ To see print statements in pytest run with:
8
+ $ py -3.12 -m pytest tests/ahu/test_ahu_fc7.py -rP -s
9
+
10
+ Supply air temperature too low in full heating.
11
+ """
12
+
13
+ # Constants
14
+ TEST_SUPPLY_DEGF_ERR_THRES = 2.0
15
+ TEST_SAT_COL = "supply_air_temp"
16
+ TEST_SAT_SETPOINT_COL = "sat_setpoint"
17
+ TEST_HEATING_SIG_COL = "heating_sig"
18
+ TEST_SUPPLY_VFD_SPEED_COL = "supply_vfd_speed"
19
+ ROLLING_WINDOW_SIZE = 5
20
+
21
+ # Initialize FaultConditionSeven with a dictionary
22
+ fault_condition_params = {
23
+ "SUPPLY_DEGF_ERR_THRES": TEST_SUPPLY_DEGF_ERR_THRES,
24
+ "SAT_COL": TEST_SAT_COL,
25
+ "SAT_SETPOINT_COL": TEST_SAT_SETPOINT_COL,
26
+ "HEATING_SIG_COL": TEST_HEATING_SIG_COL,
27
+ "SUPPLY_VFD_SPEED_COL": TEST_SUPPLY_VFD_SPEED_COL,
28
+ "TROUBLESHOOT_MODE": False, # default value
29
+ "ROLLING_WINDOW_SIZE": ROLLING_WINDOW_SIZE, # rolling sum window size
30
+ }
31
+
32
+ fc7 = FaultConditionSeven(fault_condition_params)
33
+
34
+
35
+ class TestFaultConditionSeven:
36
+
37
+ def fault_df(self) -> pd.DataFrame:
38
+ data = {
39
+ TEST_SAT_COL: [65, 64, 63, 62, 61, 60],
40
+ TEST_SAT_SETPOINT_COL: [70, 70, 70, 70, 70, 70],
41
+ TEST_HEATING_SIG_COL: [0.95, 0.96, 0.97, 0.98, 0.99, 1.0],
42
+ TEST_SUPPLY_VFD_SPEED_COL: [0.5, 0.5, 0.5, 0.5, 0.5, 0.5],
43
+ }
44
+ return pd.DataFrame(data)
45
+
46
+ def no_fault_df(self) -> pd.DataFrame:
47
+ data = {
48
+ TEST_SAT_COL: [71, 72, 73, 74, 75, 76],
49
+ TEST_SAT_SETPOINT_COL: [70, 70, 70, 70, 70, 70],
50
+ TEST_HEATING_SIG_COL: [0.95, 0.96, 0.97, 0.98, 0.99, 1.0],
51
+ TEST_SUPPLY_VFD_SPEED_COL: [0.5, 0.5, 0.5, 0.5, 0.5, 0.5],
52
+ }
53
+ return pd.DataFrame(data)
54
+
55
+ def test_fault_condition_seven(self):
56
+ results = fc7.apply(self.fault_df())
57
+ actual = results["fc7_flag"].sum()
58
+ expected = 2
59
+ message = f"FC7 fault_df actual is {actual} and expected is {expected}"
60
+ assert actual == expected, message
61
+
62
+ def test_no_fault_condition_seven(self):
63
+ results = fc7.apply(self.no_fault_df())
64
+ actual = results["fc7_flag"].sum()
65
+ expected = 0
66
+ message = f"FC7 no_fault_df actual is {actual} and expected is {expected}"
67
+ assert actual == expected, message
68
+
69
+
70
+ if __name__ == "__main__":
71
+ pytest.main()
@@ -0,0 +1,131 @@
1
+ import pandas as pd
2
+ import pytest
3
+ from open_fdd.air_handling_unit.faults.fault_condition_eight import FaultConditionEight
4
+ from open_fdd.air_handling_unit.faults.helper_utils import HelperUtils
5
+
6
+ """
7
+ To see print statements in pytest run with:
8
+ $ py -3.12 -m pytest tests/ahu/test_ahu_fc8.py -rP -s
9
+
10
+ Supply air temperature approx equal mix temp in full econ.
11
+ """
12
+
13
+ # Constants
14
+ TEST_DELTA_T_SUPPLY_FAN = 2.0
15
+ TEST_MIX_DEGF_ERR_THRES = 2.0
16
+ TEST_SUPPLY_DEGF_ERR_THRES = 2.0
17
+ TEST_AHU_MIN_OA_DPR = 0.2
18
+ TEST_MAT_COL = "mix_air_temp"
19
+ TEST_SAT_COL = "supply_air_temp"
20
+ TEST_ECONOMIZER_SIG_COL = "economizer_sig"
21
+ TEST_COOLING_SIG_COL = "cooling_sig"
22
+ ROLLING_WINDOW_SIZE = 5
23
+
24
+ # Initialize FaultConditionEight with a dictionary
25
+ fault_condition_params = {
26
+ "DELTA_T_SUPPLY_FAN": TEST_DELTA_T_SUPPLY_FAN,
27
+ "MIX_DEGF_ERR_THRES": TEST_MIX_DEGF_ERR_THRES,
28
+ "SUPPLY_DEGF_ERR_THRES": TEST_SUPPLY_DEGF_ERR_THRES,
29
+ "AHU_MIN_OA_DPR": TEST_AHU_MIN_OA_DPR,
30
+ "MAT_COL": TEST_MAT_COL,
31
+ "SAT_COL": TEST_SAT_COL,
32
+ "ECONOMIZER_SIG_COL": TEST_ECONOMIZER_SIG_COL,
33
+ "COOLING_SIG_COL": TEST_COOLING_SIG_COL,
34
+ "TROUBLESHOOT_MODE": False,
35
+ "ROLLING_WINDOW_SIZE": ROLLING_WINDOW_SIZE,
36
+ }
37
+
38
+ fc8 = FaultConditionEight(fault_condition_params)
39
+
40
+
41
+ class TestFaultConditionEight:
42
+
43
+ def fault_df(self) -> pd.DataFrame:
44
+ data = {
45
+ TEST_MAT_COL: [50, 50.5, 50.5, 50.8, 50.2, 50.6],
46
+ TEST_SAT_COL: [40, 39.5, 39.8, 39.6, 39.2, 39.4],
47
+ TEST_ECONOMIZER_SIG_COL: [0.5, 0.6, 0.5, 0.7, 0.5, 0.6],
48
+ TEST_COOLING_SIG_COL: [0.05, 0.04, 0.03, 0.02, 0.01, 0.0],
49
+ }
50
+ return pd.DataFrame(data)
51
+
52
+ def no_fault_df(self) -> pd.DataFrame:
53
+ data = {
54
+ TEST_MAT_COL: [50, 50.5, 50.5, 50.8, 50.2, 50.6],
55
+ TEST_SAT_COL: [51, 51.5, 50.9, 51.8, 51.2, 51.6],
56
+ TEST_ECONOMIZER_SIG_COL: [0.5, 0.6, 0.5, 0.7, 0.5, 0.6],
57
+ TEST_COOLING_SIG_COL: [0.05, 0.04, 0.03, 0.02, 0.01, 0.0],
58
+ }
59
+ return pd.DataFrame(data)
60
+
61
+ def test_fault_condition_eight(self):
62
+ results = fc8.apply(self.fault_df())
63
+ actual = results["fc8_flag"].sum()
64
+ expected = 2
65
+ message = f"FC8 fault_df actual is {actual} and expected is {expected}"
66
+ assert actual == expected, message
67
+
68
+ def test_no_fault_condition_eight(self):
69
+ results = fc8.apply(self.no_fault_df())
70
+ actual = results["fc8_flag"].sum()
71
+ expected = 0
72
+ message = f"FC8 no_fault_df actual is {actual} and expected is {expected}"
73
+ assert actual == expected, message
74
+
75
+
76
+ class TestFaultOnInt:
77
+
78
+ def fault_df_on_output_int(self) -> pd.DataFrame:
79
+ data = {
80
+ TEST_MAT_COL: [50, 50.5, 50.5, 50.8, 50.2, 50.6],
81
+ TEST_SAT_COL: [40, 39.5, 39.8, 39.6, 39.2, 39.4],
82
+ TEST_ECONOMIZER_SIG_COL: [0.5, 0.6, 0.5, 0.7, 0.5, 0.6],
83
+ TEST_COOLING_SIG_COL: [55, 56, 54, 55, 57, 55], # Incorrect type
84
+ }
85
+ return pd.DataFrame(data)
86
+
87
+ def test_fault_on_int(self):
88
+ with pytest.raises(
89
+ TypeError, match=HelperUtils().float_int_check_err(TEST_COOLING_SIG_COL)
90
+ ):
91
+ fc8.apply(self.fault_df_on_output_int())
92
+
93
+
94
+ class TestFaultOnFloatGreaterThanOne:
95
+
96
+ def fault_df_on_output_greater_than_one(self) -> pd.DataFrame:
97
+ data = {
98
+ TEST_MAT_COL: [50, 50.5, 50.5, 50.8, 50.2, 50.6],
99
+ TEST_SAT_COL: [40, 39.5, 39.8, 39.6, 39.2, 39.4],
100
+ TEST_ECONOMIZER_SIG_COL: [0.5, 0.6, 0.5, 0.7, 0.5, 0.6],
101
+ TEST_COOLING_SIG_COL: [1.1, 1.2, 1.1, 1.3, 1.1, 1.2], # Values > 1.0
102
+ }
103
+ return pd.DataFrame(data)
104
+
105
+ def test_fault_on_float_greater_than_one(self):
106
+ with pytest.raises(
107
+ TypeError, match=HelperUtils().float_max_check_err(TEST_COOLING_SIG_COL)
108
+ ):
109
+ fc8.apply(self.fault_df_on_output_greater_than_one())
110
+
111
+
112
+ class TestFaultOnMixedTypes:
113
+
114
+ def fault_df_on_mixed_types(self) -> pd.DataFrame:
115
+ data = {
116
+ TEST_MAT_COL: [50, 50.5, 50.5, 50.8, 50.2, 50.6],
117
+ TEST_SAT_COL: [40, 39.5, 39.8, 39.6, 39.2, 39.4],
118
+ TEST_ECONOMIZER_SIG_COL: [0.5, 0.6, 0.5, 0.7, 0.5, 0.6],
119
+ TEST_COOLING_SIG_COL: [1.1, 0.55, 1.2, 1.3, 0.55, 1.1], # Mixed types
120
+ }
121
+ return pd.DataFrame(data)
122
+
123
+ def test_fault_on_mixed_types(self):
124
+ with pytest.raises(
125
+ TypeError, match=HelperUtils().float_max_check_err(TEST_COOLING_SIG_COL)
126
+ ):
127
+ fc8.apply(self.fault_df_on_mixed_types())
128
+
129
+
130
+ if __name__ == "__main__":
131
+ pytest.main()