open-fdd 0.1.7__py3-none-any.whl → 0.1.9__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 (52) hide show
  1. open_fdd/air_handling_unit/faults/__init__.py +29 -2283
  2. open_fdd/air_handling_unit/faults/fault_condition_eight.py +135 -0
  3. open_fdd/air_handling_unit/faults/fault_condition_eleven.py +108 -0
  4. open_fdd/air_handling_unit/faults/fault_condition_fifteen.py +189 -0
  5. open_fdd/air_handling_unit/faults/fault_condition_five.py +126 -0
  6. open_fdd/air_handling_unit/faults/fault_condition_four.py +128 -0
  7. open_fdd/air_handling_unit/faults/fault_condition_fourteen.py +177 -0
  8. open_fdd/air_handling_unit/faults/fault_condition_nine.py +140 -0
  9. open_fdd/air_handling_unit/faults/fault_condition_one.py +113 -0
  10. open_fdd/air_handling_unit/faults/fault_condition_seven.py +106 -0
  11. open_fdd/air_handling_unit/faults/fault_condition_six.py +228 -0
  12. open_fdd/air_handling_unit/faults/fault_condition_sixteen.py +196 -0
  13. open_fdd/air_handling_unit/faults/fault_condition_ten.py +119 -0
  14. open_fdd/air_handling_unit/faults/fault_condition_thirteen.py +139 -0
  15. open_fdd/air_handling_unit/faults/fault_condition_three.py +112 -0
  16. open_fdd/air_handling_unit/faults/fault_condition_twelve.py +164 -0
  17. open_fdd/air_handling_unit/faults/fault_condition_two.py +112 -0
  18. open_fdd/air_handling_unit/faults/helper_utils.py +29 -19
  19. open_fdd/air_handling_unit/reports/__init__.py +6 -4
  20. open_fdd/air_handling_unit/reports/fault_report.py +3 -2
  21. open_fdd/chiller_plant/__init__.py +0 -0
  22. open_fdd/chiller_plant/faults/__init__.py +7 -0
  23. open_fdd/chiller_plant/faults/fault_condition_one.py +113 -0
  24. open_fdd/chiller_plant/faults/fault_condition_two.py +100 -0
  25. open_fdd/tests/ahu/test_ahu_fc1.py +2 -2
  26. open_fdd/tests/ahu/test_ahu_fc10.py +1 -0
  27. open_fdd/tests/ahu/test_ahu_fc11.py +24 -33
  28. open_fdd/tests/ahu/test_ahu_fc12.py +56 -18
  29. open_fdd/tests/ahu/test_ahu_fc13.py +15 -8
  30. open_fdd/tests/ahu/test_ahu_fc14.py +11 -3
  31. open_fdd/tests/ahu/test_ahu_fc15.py +15 -4
  32. open_fdd/tests/ahu/test_ahu_fc16.py +15 -6
  33. open_fdd/tests/ahu/test_ahu_fc2.py +1 -0
  34. open_fdd/tests/ahu/test_ahu_fc3.py +1 -0
  35. open_fdd/tests/ahu/test_ahu_fc4.py +3 -1
  36. open_fdd/tests/ahu/test_ahu_fc5.py +1 -0
  37. open_fdd/tests/ahu/test_ahu_fc6.py +1 -0
  38. open_fdd/tests/ahu/test_ahu_fc7.py +1 -0
  39. open_fdd/tests/ahu/test_ahu_fc8.py +1 -0
  40. open_fdd/tests/ahu/test_ahu_fc9.py +1 -0
  41. open_fdd/tests/chiller/__init__.py +0 -0
  42. open_fdd/tests/chiller/test_chiller_fc1.py +122 -0
  43. open_fdd/tests/chiller/test_chiller_fc2.py +95 -0
  44. open_fdd-0.1.9.dist-info/METADATA +137 -0
  45. open_fdd-0.1.9.dist-info/RECORD +52 -0
  46. {open_fdd-0.1.7.dist-info → open_fdd-0.1.9.dist-info}/WHEEL +1 -1
  47. open_fdd/air_handling_unit/faults/fault_condition.py +0 -69
  48. open_fdd/air_handling_unit/faults/shared_utils.py +0 -90
  49. open_fdd-0.1.7.dist-info/METADATA +0 -95
  50. open_fdd-0.1.7.dist-info/RECORD +0 -31
  51. {open_fdd-0.1.7.dist-info → open_fdd-0.1.9.dist-info/licenses}/LICENSE +0 -0
  52. {open_fdd-0.1.7.dist-info → open_fdd-0.1.9.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,177 @@
1
+ import numpy as np
2
+ import pandas as pd
3
+
4
+ from open_fdd.core.base_fault import BaseFaultCondition
5
+ from open_fdd.core.components import FaultInputColumn, InstanceAttribute
6
+ from open_fdd.core.exceptions import InvalidParameterError
7
+ from open_fdd.core.mixins import FaultConditionMixin
8
+
9
+ INPUT_COLS = [
10
+ FaultInputColumn(
11
+ name="clg_coil_enter_temp_col",
12
+ constant_form="CLG_COIL_ENTER_TEMP_COL",
13
+ description="Cooling coil entering air temperature",
14
+ unit="°F",
15
+ required=True,
16
+ type=float,
17
+ ),
18
+ FaultInputColumn(
19
+ name="clg_coil_leave_temp_col",
20
+ constant_form="CLG_COIL_LEAVE_TEMP_COL",
21
+ description="Cooling coil leaving air temperature",
22
+ unit="°F",
23
+ required=True,
24
+ type=float,
25
+ ),
26
+ FaultInputColumn(
27
+ name="cooling_sig_col",
28
+ constant_form="COOLING_SIG_COL",
29
+ description="Cooling signal",
30
+ unit="%",
31
+ required=True,
32
+ type=float,
33
+ ),
34
+ FaultInputColumn(
35
+ name="heating_sig_col",
36
+ constant_form="HEATING_SIG_COL",
37
+ description="Heating signal",
38
+ unit="%",
39
+ required=True,
40
+ type=float,
41
+ ),
42
+ FaultInputColumn(
43
+ name="economizer_sig_col",
44
+ constant_form="ECONOMIZER_SIG_COL",
45
+ description="Economizer signal",
46
+ unit="%",
47
+ required=True,
48
+ type=float,
49
+ ),
50
+ FaultInputColumn(
51
+ name="supply_vfd_speed_col",
52
+ constant_form="SUPPLY_VFD_SPEED_COL",
53
+ description="Supply fan VFD speed",
54
+ unit="%",
55
+ required=True,
56
+ type=float,
57
+ ),
58
+ ]
59
+
60
+ FAULT_PARAMS = [
61
+ InstanceAttribute(
62
+ name="delta_t_supply_fan",
63
+ constant_form="DELTA_T_SUPPLY_FAN",
64
+ description="Temperature rise across supply fan",
65
+ unit="°F",
66
+ type=float,
67
+ range=(0.0, 5.0),
68
+ ),
69
+ InstanceAttribute(
70
+ name="coil_temp_enter_err_thres",
71
+ constant_form="COIL_TEMP_ENTER_ERR_THRES",
72
+ description="Cooling coil entering air temperature error threshold",
73
+ unit="°F",
74
+ type=float,
75
+ range=(0.0, 10.0),
76
+ ),
77
+ InstanceAttribute(
78
+ name="coil_temp_leave_err_thres",
79
+ constant_form="COIL_TEMP_LEAV_ERR_THRES",
80
+ description="Cooling coil leaving air temperature error threshold",
81
+ unit="°F",
82
+ type=float,
83
+ range=(0.0, 10.0),
84
+ ),
85
+ InstanceAttribute(
86
+ name="ahu_min_oa_dpr",
87
+ constant_form="AHU_MIN_OA_DPR",
88
+ description="Minimum outdoor air damper position",
89
+ unit="fraction",
90
+ type=float,
91
+ range=(0.0, 1.0),
92
+ ),
93
+ ]
94
+
95
+
96
+ class FaultConditionFourteen(BaseFaultCondition, FaultConditionMixin):
97
+ """Class provides the definitions for Fault Condition 14.
98
+ Temperature drop across inactive cooling coil in OS1 (heating) and OS2 (economizer) modes.
99
+ This fault checks if there is an unexpected temperature drop across the cooling coil
100
+ when it should be inactive.
101
+
102
+ py -3.12 -m pytest open_fdd/tests/ahu/test_ahu_fc14.py -rP -s
103
+ """
104
+
105
+ input_columns = INPUT_COLS
106
+ fault_params = FAULT_PARAMS
107
+ equation_string = (
108
+ "fc14_flag = 1 if (CLG_LEAVE < CLG_ENTER - √(εENTER² + εLEAVE²)) "
109
+ "in OS1 (heating) or OS2 (economizer) modes for N consecutive values else 0 \n"
110
+ )
111
+ description_string = (
112
+ "Fault Condition 14: Temperature drop across inactive cooling coil "
113
+ "in OS1 (heating) and OS2 (economizer) modes \n"
114
+ )
115
+ error_string = "One or more required columns are missing or None \n"
116
+
117
+ @FaultConditionMixin._handle_errors
118
+ def apply(self, df: pd.DataFrame) -> pd.DataFrame:
119
+ """Apply the fault condition to the DataFrame."""
120
+ # Apply common checks
121
+ self._apply_common_checks(df)
122
+
123
+ # Get column values using accessor methods
124
+ cooling_sig_col = self.get_input_column("cooling_sig_col")
125
+ heating_sig_col = self.get_input_column("heating_sig_col")
126
+ economizer_sig_col = self.get_input_column("economizer_sig_col")
127
+ supply_vfd_speed_col = self.get_input_column("supply_vfd_speed_col")
128
+ clg_coil_enter_temp_col = self.get_input_column("clg_coil_enter_temp_col")
129
+ clg_coil_leave_temp_col = self.get_input_column("clg_coil_leave_temp_col")
130
+
131
+ # Get parameter values using accessor methods
132
+ coil_temp_enter_err_thres = self.get_param("coil_temp_enter_err_thres")
133
+ coil_temp_leave_err_thres = self.get_param("coil_temp_leave_err_thres")
134
+ ahu_min_oa_dpr = self.get_param("ahu_min_oa_dpr")
135
+
136
+ # Check analog outputs [data with units of %] are floats only
137
+ columns_to_check = [
138
+ cooling_sig_col,
139
+ heating_sig_col,
140
+ economizer_sig_col,
141
+ supply_vfd_speed_col,
142
+ ]
143
+ self._apply_analog_checks(df, columns_to_check, check_greater_than_one=True)
144
+
145
+ # Calculate the threshold for temperature drop
146
+ temp_drop_threshold = np.sqrt(
147
+ coil_temp_enter_err_thres**2 + coil_temp_leave_err_thres**2
148
+ )
149
+
150
+ # Check if there's a significant temperature drop across the cooling coil
151
+ temp_drop = df[clg_coil_enter_temp_col] - df[clg_coil_leave_temp_col]
152
+ significant_temp_drop = temp_drop > temp_drop_threshold
153
+
154
+ # Check operating modes:
155
+ # OS1: Heating mode (HTG > 0, CLG = 0, ECO = MIN_OA)
156
+ os1_mode = (
157
+ (df[heating_sig_col] > 0.0)
158
+ & (df[cooling_sig_col] == 0.0)
159
+ & (df[economizer_sig_col] <= ahu_min_oa_dpr)
160
+ )
161
+
162
+ # OS2: Economizer mode (HTG = 0, CLG = 0, ECO > MIN_OA)
163
+ os2_mode = (
164
+ (df[heating_sig_col] == 0.0)
165
+ & (df[cooling_sig_col] == 0.0)
166
+ & (df[economizer_sig_col] > ahu_min_oa_dpr)
167
+ )
168
+
169
+ # Combine conditions:
170
+ # Fault occurs when there's a significant temperature drop across an inactive cooling coil
171
+ # in either OS1 (heating) or OS2 (economizer) mode
172
+ combined_check = significant_temp_drop & (os1_mode | os2_mode)
173
+
174
+ # Set fault flag
175
+ self._set_fault_flag(df, combined_check, "fc14_flag")
176
+
177
+ return df
@@ -0,0 +1,140 @@
1
+ import pandas as pd
2
+
3
+ from open_fdd.core.base_fault import BaseFaultCondition
4
+ from open_fdd.core.components import FaultInputColumn, InstanceAttribute
5
+ from open_fdd.core.exceptions import InvalidParameterError
6
+ from open_fdd.core.mixins import FaultConditionMixin
7
+
8
+ INPUT_COLS = [
9
+ FaultInputColumn(
10
+ name="sat_setpoint_col",
11
+ constant_form="SAT_SETPOINT_COL",
12
+ description="Supply air temperature setpoint",
13
+ unit="°F",
14
+ required=True,
15
+ type=float,
16
+ ),
17
+ FaultInputColumn(
18
+ name="oat_col",
19
+ constant_form="OAT_COL",
20
+ description="Outside air temperature",
21
+ unit="°F",
22
+ required=True,
23
+ type=float,
24
+ ),
25
+ FaultInputColumn(
26
+ name="cooling_sig_col",
27
+ constant_form="COOLING_SIG_COL",
28
+ description="Cooling signal",
29
+ unit="%",
30
+ required=True,
31
+ type=float,
32
+ ),
33
+ FaultInputColumn(
34
+ name="economizer_sig_col",
35
+ constant_form="ECONOMIZER_SIG_COL",
36
+ description="Economizer signal",
37
+ unit="%",
38
+ required=True,
39
+ type=float,
40
+ ),
41
+ ]
42
+
43
+ FAULT_PARAMS = [
44
+ InstanceAttribute(
45
+ name="delta_t_supply_fan",
46
+ constant_form="DELTA_T_SUPPLY_FAN",
47
+ description="Temperature rise across supply fan",
48
+ unit="°F",
49
+ type=float,
50
+ range=(0.0, 5.0),
51
+ ),
52
+ InstanceAttribute(
53
+ name="outdoor_degf_err_thres",
54
+ constant_form="OUTDOOR_DEGF_ERR_THRES",
55
+ description="Outdoor air temperature error threshold",
56
+ unit="°F",
57
+ type=float,
58
+ range=(0.0, 10.0),
59
+ ),
60
+ InstanceAttribute(
61
+ name="supply_degf_err_thres",
62
+ constant_form="SUPPLY_DEGF_ERR_THRES",
63
+ description="Supply air temperature error threshold",
64
+ unit="°F",
65
+ type=float,
66
+ range=(0.0, 10.0),
67
+ ),
68
+ InstanceAttribute(
69
+ name="ahu_min_oa_dpr",
70
+ constant_form="AHU_MIN_OA_DPR",
71
+ description="Minimum outdoor air damper position",
72
+ unit="fraction",
73
+ type=float,
74
+ range=(0.0, 1.0),
75
+ ),
76
+ ]
77
+
78
+
79
+ class FaultConditionNine(BaseFaultCondition, FaultConditionMixin):
80
+ """Class provides the definitions for Fault Condition 9.
81
+ Outside air temperature too high in free cooling without
82
+ additional mechanical cooling in economizer mode.
83
+
84
+ py -3.12 -m pytest open_fdd/tests/ahu/test_ahu_fc9.py -rP -s
85
+ """
86
+
87
+ input_columns = INPUT_COLS
88
+ fault_params = FAULT_PARAMS
89
+ equation_string = (
90
+ "fc9_flag = 1 if OAT > (SATSP - ΔT_fan + εSAT) "
91
+ "in free cooling mode for N consecutive values else 0 \n"
92
+ )
93
+ description_string = (
94
+ "Fault Condition 9: Outside air temperature too high in free cooling mode "
95
+ "without additional mechanical cooling in economizer mode \n"
96
+ )
97
+ error_string = "One or more required columns are missing or None \n"
98
+
99
+ @FaultConditionMixin._handle_errors
100
+ def apply(self, df: pd.DataFrame) -> pd.DataFrame:
101
+ """Apply the fault condition to the DataFrame."""
102
+ # Apply common checks
103
+ self._apply_common_checks(df)
104
+
105
+ # Get column values using accessor methods
106
+ oat_col = self.get_input_column("oat_col")
107
+ sat_setpoint_col = self.get_input_column("sat_setpoint_col")
108
+ economizer_sig_col = self.get_input_column("economizer_sig_col")
109
+ cooling_sig_col = self.get_input_column("cooling_sig_col")
110
+
111
+ # Get parameter values using accessor methods
112
+ outdoor_degf_err_thres = self.get_param("outdoor_degf_err_thres")
113
+ delta_t_supply_fan = self.get_param("delta_t_supply_fan")
114
+ supply_degf_err_thres = self.get_param("supply_degf_err_thres")
115
+ ahu_min_oa_dpr = self.get_param("ahu_min_oa_dpr")
116
+
117
+ # Check analog outputs [data with units of %] are floats only
118
+ columns_to_check = [
119
+ economizer_sig_col,
120
+ cooling_sig_col,
121
+ ]
122
+ self._apply_analog_checks(df, columns_to_check, check_greater_than_one=True)
123
+
124
+ # Perform calculations
125
+ oat_minus_oaterror = df[oat_col] - outdoor_degf_err_thres
126
+ satsp_delta_saterr = (
127
+ df[sat_setpoint_col] - delta_t_supply_fan + supply_degf_err_thres
128
+ )
129
+
130
+ combined_check = (
131
+ (oat_minus_oaterror > satsp_delta_saterr)
132
+ # verify AHU is in OS2 only free cooling mode
133
+ & (df[economizer_sig_col] > ahu_min_oa_dpr)
134
+ & (df[cooling_sig_col] < 0.1)
135
+ )
136
+
137
+ # Set fault flag
138
+ self._set_fault_flag(df, combined_check, "fc9_flag")
139
+
140
+ return df
@@ -0,0 +1,113 @@
1
+ import pandas as pd
2
+
3
+ from open_fdd.core.base_fault import BaseFaultCondition
4
+ from open_fdd.core.components import FaultInputColumn, InstanceAttribute
5
+ from open_fdd.core.mixins import FaultConditionMixin
6
+
7
+ INPUT_COLS = [
8
+ FaultInputColumn(
9
+ name="duct_static_col",
10
+ constant_form="DUCT_STATIC_COL",
11
+ description="Duct static pressure",
12
+ unit="inches of water",
13
+ required=True,
14
+ type=float,
15
+ ),
16
+ FaultInputColumn(
17
+ name="supply_vfd_speed_col",
18
+ constant_form="SUPPLY_VFD_SPEED_COL",
19
+ description="Supply fan VFD speed",
20
+ unit="%",
21
+ required=True,
22
+ type=float,
23
+ ),
24
+ FaultInputColumn(
25
+ name="duct_static_setpoint_col",
26
+ constant_form="DUCT_STATIC_SETPOINT_COL",
27
+ description="Duct static pressure setpoint",
28
+ unit="inches of water",
29
+ required=True,
30
+ type=float,
31
+ ),
32
+ ]
33
+
34
+ FAULT_PARAMS = [
35
+ InstanceAttribute(
36
+ name="duct_static_inches_err_thres",
37
+ constant_form="DUCT_STATIC_INCHES_ERR_THRES",
38
+ description="Duct static pressure error threshold",
39
+ unit="inches of water",
40
+ type=float,
41
+ range=(0.0, 1.0),
42
+ ),
43
+ InstanceAttribute(
44
+ name="vfd_speed_percent_max",
45
+ constant_form="VFD_SPEED_PERCENT_MAX",
46
+ description="Maximum VFD speed percentage",
47
+ unit="%",
48
+ type=float,
49
+ range=(0.0, 100.0),
50
+ ),
51
+ InstanceAttribute(
52
+ name="vfd_speed_percent_err_thres",
53
+ constant_form="VFD_SPEED_PERCENT_ERR_THRES",
54
+ description="VFD speed error threshold",
55
+ unit="%",
56
+ type=float,
57
+ range=(0.0, 100.0),
58
+ ),
59
+ ]
60
+
61
+
62
+ class FaultConditionOne(BaseFaultCondition, FaultConditionMixin):
63
+ """Class provides the definitions for Fault Condition 1.
64
+ AHU low duct static pressure fan fault.
65
+
66
+ py -3.12 -m pytest open_fdd/tests/ahu/test_ahu_fc1.py -rP -s
67
+ """
68
+
69
+ input_columns = INPUT_COLS
70
+ fault_params = FAULT_PARAMS
71
+ equation_string = "fc1_flag = 1 if (DP < DPSP - εDP) and (VFDSPD >= VFDSPD_max - εVFDSPD) for N consecutive values else 0 \n"
72
+ description_string = (
73
+ "Fault Condition 1: Duct static too low at fan at full speed \n"
74
+ )
75
+
76
+ error_string = "One or more required columns are missing or None \n"
77
+
78
+ @FaultConditionMixin._handle_errors
79
+ def apply(self, df: pd.DataFrame) -> pd.DataFrame:
80
+ self._apply_common_checks(df)
81
+
82
+ # Get column values using accessor methods
83
+ supply_vfd_speed_col = self.get_input_column("supply_vfd_speed_col")
84
+ duct_static_col = self.get_input_column("duct_static_col")
85
+ duct_static_setpoint_col = self.get_input_column("duct_static_setpoint_col")
86
+
87
+ # Get parameter values using accessor methods
88
+ vfd_speed_percent_max = self.get_param("vfd_speed_percent_max")
89
+ vfd_speed_percent_err_thres = self.get_param("vfd_speed_percent_err_thres")
90
+ duct_static_inches_err_thres = self.get_param("duct_static_inches_err_thres")
91
+
92
+ self._apply_analog_checks(
93
+ df, [supply_vfd_speed_col], check_greater_than_one=True
94
+ )
95
+
96
+ # Convert VFD speed from percentage to fraction if needed
97
+ if (df[supply_vfd_speed_col] > 1.0).any():
98
+ df[supply_vfd_speed_col] = df[supply_vfd_speed_col] / 100.0
99
+
100
+ # Convert thresholds from percentage to fraction
101
+ vfd_speed_max = vfd_speed_percent_max / 100.0
102
+ vfd_speed_err_thres = vfd_speed_percent_err_thres / 100.0
103
+
104
+ # Specific checks
105
+ static_check = (
106
+ df[duct_static_col]
107
+ < df[duct_static_setpoint_col] - duct_static_inches_err_thres
108
+ )
109
+ fan_check = df[supply_vfd_speed_col] >= vfd_speed_max - vfd_speed_err_thres
110
+ combined_check = static_check & fan_check
111
+
112
+ self._set_fault_flag(df, combined_check, "fc1_flag")
113
+ return df
@@ -0,0 +1,106 @@
1
+ import pandas as pd
2
+
3
+ from open_fdd.core.base_fault import BaseFaultCondition
4
+ from open_fdd.core.components import FaultInputColumn, InstanceAttribute
5
+ from open_fdd.core.exceptions import InvalidParameterError, MissingColumnError
6
+ from open_fdd.core.mixins import FaultConditionMixin
7
+
8
+ INPUT_COLS = [
9
+ FaultInputColumn(
10
+ name="sat_col",
11
+ constant_form="SAT_COL",
12
+ description="Supply air temperature",
13
+ unit="°F",
14
+ required=True,
15
+ type=float,
16
+ ),
17
+ FaultInputColumn(
18
+ name="sat_setpoint_col",
19
+ constant_form="SAT_SETPOINT_COL",
20
+ description="Supply air temperature setpoint",
21
+ unit="°F",
22
+ required=True,
23
+ type=float,
24
+ ),
25
+ FaultInputColumn(
26
+ name="heating_sig_col",
27
+ constant_form="HEATING_SIG_COL",
28
+ description="Heating signal",
29
+ unit="%",
30
+ required=True,
31
+ type=float,
32
+ ),
33
+ FaultInputColumn(
34
+ name="supply_vfd_speed_col",
35
+ constant_form="SUPPLY_VFD_SPEED_COL",
36
+ description="Supply fan VFD speed",
37
+ unit="%",
38
+ required=True,
39
+ type=float,
40
+ ),
41
+ ]
42
+
43
+ FAULT_PARAMS = [
44
+ InstanceAttribute(
45
+ name="supply_degf_err_thres",
46
+ constant_form="SUPPLY_DEGF_ERR_THRES",
47
+ description="Supply air temperature error threshold",
48
+ unit="°F",
49
+ type=float,
50
+ range=(0.0, 10.0),
51
+ ),
52
+ ]
53
+
54
+
55
+ class FaultConditionSeven(BaseFaultCondition, FaultConditionMixin):
56
+ """Class provides the definitions for Fault Condition 7.
57
+ Very similar to FC 13 but uses heating valve.
58
+ Supply air temperature too low in full heating.
59
+
60
+ py -3.12 -m pytest open_fdd/tests/ahu/test_ahu_fc7.py -rP -s
61
+ """
62
+
63
+ input_columns = INPUT_COLS
64
+ fault_params = FAULT_PARAMS
65
+ equation_string = (
66
+ "fc7_flag = 1 if SAT < (SATSP - εSAT) in full heating mode "
67
+ "and VFD speed > 0 for N consecutive values else 0 \n"
68
+ )
69
+ description_string = (
70
+ "Fault Condition 7: Supply air temperature too low in full heating mode "
71
+ "with heating valve fully open \n"
72
+ )
73
+ error_string = "One or more required columns are missing or None \n"
74
+
75
+ @FaultConditionMixin._handle_errors
76
+ def apply(self, df: pd.DataFrame) -> pd.DataFrame:
77
+ """Apply the fault condition to the DataFrame."""
78
+ # Apply common checks
79
+ self._apply_common_checks(df)
80
+
81
+ # Get column values using accessor methods
82
+ sat_col = self.get_input_column("sat_col")
83
+ sat_setpoint_col = self.get_input_column("sat_setpoint_col")
84
+ supply_vfd_speed_col = self.get_input_column("supply_vfd_speed_col")
85
+ heating_sig_col = self.get_input_column("heating_sig_col")
86
+
87
+ # Get parameter values using accessor methods
88
+ supply_degf_err_thres = self.get_param("supply_degf_err_thres")
89
+
90
+ # Check analog outputs [data with units of %] are floats only
91
+ columns_to_check = [supply_vfd_speed_col, heating_sig_col]
92
+ self._apply_analog_checks(df, columns_to_check, check_greater_than_one=True)
93
+
94
+ # Perform checks
95
+ sat_check = df[sat_setpoint_col] - supply_degf_err_thres
96
+
97
+ combined_check = (
98
+ (df[sat_col] < sat_check)
99
+ & (df[heating_sig_col] > 0.9)
100
+ & (df[supply_vfd_speed_col] > 0)
101
+ )
102
+
103
+ # Set fault flag
104
+ self._set_fault_flag(df, combined_check, "fc7_flag")
105
+
106
+ return df