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.
- open_fdd/__init__.py +39 -0
- open_fdd/air_handling_unit/__init__.py +0 -0
- open_fdd/air_handling_unit/faults/__init__.py +0 -0
- open_fdd/air_handling_unit/faults/fault_condition.py +49 -0
- open_fdd/air_handling_unit/faults/fault_condition_eight.py +67 -0
- open_fdd/air_handling_unit/faults/fault_condition_eleven.py +68 -0
- open_fdd/air_handling_unit/faults/fault_condition_fifteen.py +90 -0
- open_fdd/air_handling_unit/faults/fault_condition_five.py +68 -0
- open_fdd/air_handling_unit/faults/fault_condition_four.py +93 -0
- open_fdd/air_handling_unit/faults/fault_condition_fourteen.py +80 -0
- open_fdd/air_handling_unit/faults/fault_condition_nine.py +68 -0
- open_fdd/air_handling_unit/faults/fault_condition_one.py +60 -0
- open_fdd/air_handling_unit/faults/fault_condition_seven.py +55 -0
- open_fdd/air_handling_unit/faults/fault_condition_six.py +120 -0
- open_fdd/air_handling_unit/faults/fault_condition_ten.py +62 -0
- open_fdd/air_handling_unit/faults/fault_condition_thirteen.py +66 -0
- open_fdd/air_handling_unit/faults/fault_condition_three.py +58 -0
- open_fdd/air_handling_unit/faults/fault_condition_twelve.py +71 -0
- open_fdd/air_handling_unit/faults/fault_condition_two.py +61 -0
- open_fdd/air_handling_unit/faults/helper_utils.py +191 -0
- open_fdd/air_handling_unit/faults/shared_utils.py +75 -0
- open_fdd/air_handling_unit/reports/__init__.py +0 -0
- open_fdd/air_handling_unit/reports/report_fc1.py +115 -0
- open_fdd/air_handling_unit/reports/report_fc10.py +126 -0
- open_fdd/air_handling_unit/reports/report_fc11.py +128 -0
- open_fdd/air_handling_unit/reports/report_fc12.py +126 -0
- open_fdd/air_handling_unit/reports/report_fc13.py +126 -0
- open_fdd/air_handling_unit/reports/report_fc14.py +124 -0
- open_fdd/air_handling_unit/reports/report_fc15.py +124 -0
- open_fdd/air_handling_unit/reports/report_fc2.py +119 -0
- open_fdd/air_handling_unit/reports/report_fc3.py +119 -0
- open_fdd/air_handling_unit/reports/report_fc4.py +148 -0
- open_fdd/air_handling_unit/reports/report_fc5.py +132 -0
- open_fdd/air_handling_unit/reports/report_fc6.py +156 -0
- open_fdd/air_handling_unit/reports/report_fc7.py +124 -0
- open_fdd/air_handling_unit/reports/report_fc8.py +118 -0
- open_fdd/air_handling_unit/reports/report_fc9.py +120 -0
- open_fdd/tests/__init__.py +0 -0
- open_fdd/tests/ahu/__init__.py +0 -0
- open_fdd/tests/ahu/test_ahu_fc1.py +159 -0
- open_fdd/tests/ahu/test_ahu_fc10.py +132 -0
- open_fdd/tests/ahu/test_ahu_fc11.py +136 -0
- open_fdd/tests/ahu/test_ahu_fc12.py +167 -0
- open_fdd/tests/ahu/test_ahu_fc13.py +163 -0
- open_fdd/tests/ahu/test_ahu_fc14.py +197 -0
- open_fdd/tests/ahu/test_ahu_fc15.py +183 -0
- open_fdd/tests/ahu/test_ahu_fc2.py +132 -0
- open_fdd/tests/ahu/test_ahu_fc3.py +131 -0
- open_fdd/tests/ahu/test_ahu_fc4.py +200 -0
- open_fdd/tests/ahu/test_ahu_fc5.py +180 -0
- open_fdd/tests/ahu/test_ahu_fc6.py +246 -0
- open_fdd/tests/ahu/test_ahu_fc7.py +71 -0
- open_fdd/tests/ahu/test_ahu_fc8.py +131 -0
- open_fdd/tests/ahu/test_ahu_fc9.py +136 -0
- open_fdd-0.1.0.dist-info/LICENSE +21 -0
- open_fdd-0.1.0.dist-info/METADATA +65 -0
- open_fdd-0.1.0.dist-info/RECORD +59 -0
- open_fdd-0.1.0.dist-info/WHEEL +5 -0
- 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()
|