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,60 @@
1
+ import pandas as pd
2
+ from open_fdd.air_handling_unit.faults.fault_condition import FaultCondition
3
+ import sys
4
+
5
+
6
+ class FaultConditionOne(FaultCondition):
7
+ """Class provides the definitions for Fault Condition 1.
8
+ AHU low duct static pressure fan fault.
9
+ """
10
+
11
+ def __init__(self, dict_):
12
+ """
13
+ :param dict_:
14
+ """
15
+ self.vfd_speed_percent_err_thres = float
16
+ self.vfd_speed_percent_max = float
17
+ self.duct_static_inches_err_thres = float
18
+ self.duct_static_col = str
19
+ self.supply_vfd_speed_col = str
20
+ self.duct_static_setpoint_col = str
21
+ self.troubleshoot_mode = bool # default should be False
22
+ self.rolling_window_size = int
23
+
24
+ self.set_attributes(dict_)
25
+
26
+ def apply(self, df: pd.DataFrame) -> pd.DataFrame:
27
+ if self.troubleshoot_mode:
28
+ self.troubleshoot_cols(df)
29
+
30
+ # check analog outputs [data with units of %] are floats only
31
+ columns_to_check = [self.supply_vfd_speed_col]
32
+ self.check_analog_pct(df, columns_to_check)
33
+
34
+ df["static_check_"] = (
35
+ df[self.duct_static_col]
36
+ < df[self.duct_static_setpoint_col] - self.duct_static_inches_err_thres
37
+ )
38
+ df["fan_check_"] = (
39
+ df[self.supply_vfd_speed_col]
40
+ >= self.vfd_speed_percent_max - self.vfd_speed_percent_err_thres
41
+ )
42
+
43
+ # Combined condition check
44
+ df["combined_check"] = df["static_check_"] & df["fan_check_"]
45
+
46
+ # Rolling sum to count consecutive trues
47
+ rolling_sum = (
48
+ df["combined_check"].rolling(window=self.rolling_window_size).sum()
49
+ )
50
+ # Set flag to 1 if rolling sum equals the window size
51
+ df["fc1_flag"] = (rolling_sum == self.rolling_window_size).astype(int)
52
+
53
+ if self.troubleshoot_mode:
54
+ print("Troubleshoot mode enabled - not removing helper columns")
55
+ sys.stdout.flush()
56
+ del df["static_check_"]
57
+ del df["fan_check_"]
58
+ del df["combined_check"]
59
+
60
+ return df
@@ -0,0 +1,55 @@
1
+ import pandas as pd
2
+ import numpy as np
3
+ from open_fdd.air_handling_unit.faults.fault_condition import FaultCondition
4
+ from open_fdd.air_handling_unit.faults.helper_utils import HelperUtils
5
+ import sys
6
+
7
+
8
+ class FaultConditionSeven(FaultCondition):
9
+ """Class provides the definitions for Fault Condition 7.
10
+ Very similar to FC 13 but uses heating valve.
11
+ Supply air temperature too low in full heating.
12
+ """
13
+
14
+ def __init__(self, dict_):
15
+ self.supply_degf_err_thres = float
16
+ self.sat_col = str
17
+ self.sat_setpoint_col = str
18
+ self.heating_sig_col = str
19
+ self.supply_vfd_speed_col = str
20
+ self.troubleshoot_mode = bool # default to False
21
+ self.rolling_window_size = int
22
+
23
+ self.set_attributes(dict_)
24
+
25
+ def apply(self, df: pd.DataFrame) -> pd.DataFrame:
26
+ if self.troubleshoot_mode:
27
+ self.troubleshoot_cols(df)
28
+
29
+ # Check analog outputs [data with units of %] are floats only
30
+ columns_to_check = [self.supply_vfd_speed_col, self.heating_sig_col]
31
+ self.check_analog_pct(df, columns_to_check)
32
+
33
+ # Fault condition-specific checks / flags
34
+ df["sat_check"] = df[self.sat_setpoint_col] - self.supply_degf_err_thres
35
+
36
+ df["combined_check"] = (
37
+ (df[self.sat_col] < df["sat_check"])
38
+ & (df[self.heating_sig_col] > 0.9)
39
+ & (df[self.supply_vfd_speed_col] > 0)
40
+ )
41
+
42
+ # Rolling sum to count consecutive trues
43
+ rolling_sum = (
44
+ df["combined_check"].rolling(window=self.rolling_window_size).sum()
45
+ )
46
+ # Set flag to 1 if rolling sum equals the window size
47
+ df["fc7_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
48
+
49
+ if self.troubleshoot_mode:
50
+ print("Troubleshoot mode enabled - not removing helper columns")
51
+ sys.stdout.flush()
52
+ del df["sat_check"]
53
+ del df["combined_check"]
54
+
55
+ return df
@@ -0,0 +1,120 @@
1
+ import pandas as pd
2
+ import pandas.api.types as pdtypes
3
+ from open_fdd.air_handling_unit.faults.fault_condition import FaultCondition
4
+ from open_fdd.air_handling_unit.faults.helper_utils import HelperUtils
5
+ import operator
6
+ import sys
7
+
8
+
9
+ class FaultConditionSix(FaultCondition):
10
+ """Class provides the definitions for Fault Condition 6.
11
+
12
+ This fault related to knowing the design air flow for
13
+ ventilation AHU_MIN_CFM_DESIGN which comes from the
14
+ design mech engineered records where then the fault
15
+ tries to calculate that based on totalized measured
16
+ AHU air flow and outside air fraction calc from
17
+ AHU temp sensors. The fault could flag issues where
18
+ flow stations are either not in calibration, temp
19
+ sensors used in the OA frac calc, or possibly the AHU
20
+ not bringing in design air flow when not operating in
21
+ economizer free cooling modes. Troubleshoot by TAB tech
22
+ verifying flow sensor and temp sensor precisions from
23
+ 3rd party sensing tools.
24
+
25
+ this fault is confusing if you want to play around
26
+ in py code sandbox try this:
27
+ https://gist.github.com/bbartling/e0fb8427b1e0d148a06e3f09121ed5dc#file-fc6-py
28
+ """
29
+
30
+ def __init__(self, dict_):
31
+ self.airflow_err_thres = float
32
+ self.ahu_min_oa_cfm_design = float
33
+ self.outdoor_degf_err_thres = float
34
+ self.return_degf_err_thres = float
35
+ self.oat_rat_delta_min = float
36
+ self.ahu_min_oa_dpr = float
37
+ self.supply_fan_air_volume_col = str
38
+ self.mat_col = str
39
+ self.oat_col = str
40
+ self.rat_col = str
41
+ self.supply_vfd_speed_col = str
42
+ self.economizer_sig_col = str
43
+ self.heating_sig_col = str
44
+ self.cooling_sig_col = str
45
+ self.troubleshoot_mode = bool # default should be False
46
+ self.rolling_window_size = int
47
+
48
+ self.set_attributes(dict_)
49
+
50
+ def apply(self, df: pd.DataFrame) -> pd.DataFrame:
51
+ if self.troubleshoot_mode:
52
+ self.troubleshoot_cols(df)
53
+
54
+ # check analog outputs [data with units of %] are floats only
55
+ columns_to_check = [
56
+ self.supply_vfd_speed_col,
57
+ self.economizer_sig_col,
58
+ self.heating_sig_col,
59
+ self.cooling_sig_col,
60
+ ]
61
+
62
+ for col in columns_to_check:
63
+ self.check_analog_pct(df, [col])
64
+
65
+ # create helper columns
66
+ df["rat_minus_oat"] = abs(df[self.rat_col] - df[self.oat_col])
67
+ df["percent_oa_calc"] = (df[self.mat_col] - df[self.rat_col]) / (
68
+ df[self.oat_col] - df[self.rat_col]
69
+ )
70
+
71
+ # weed out any negative values
72
+ df["percent_oa_calc"] = df["percent_oa_calc"].apply(lambda x: x if x > 0 else 0)
73
+
74
+ df["perc_OAmin"] = (
75
+ self.ahu_min_oa_cfm_design / df[self.supply_fan_air_volume_col]
76
+ )
77
+
78
+ df["percent_oa_calc_minus_perc_OAmin"] = abs(
79
+ df["percent_oa_calc"] - df["perc_OAmin"]
80
+ )
81
+
82
+ df["combined_check"] = operator.or_(
83
+ # OS 1 htg mode
84
+ (
85
+ (df["rat_minus_oat"] >= self.oat_rat_delta_min)
86
+ & (df["percent_oa_calc_minus_perc_OAmin"] > self.airflow_err_thres)
87
+ )
88
+ # verify ahu is running in OS 1 htg mode in min OA
89
+ & (
90
+ (df[self.heating_sig_col] > 0.0) & (df[self.supply_vfd_speed_col] > 0.0)
91
+ ), # OR
92
+ # OS 4 mech clg mode
93
+ (
94
+ (df["rat_minus_oat"] >= self.oat_rat_delta_min)
95
+ & (df["percent_oa_calc_minus_perc_OAmin"] > self.airflow_err_thres)
96
+ )
97
+ # verify ahu is running in OS 4 clg mode in min OA
98
+ & (df[self.heating_sig_col] == 0.0)
99
+ & (df[self.cooling_sig_col] > 0.0)
100
+ & (df[self.supply_vfd_speed_col] > 0.0)
101
+ & (df[self.economizer_sig_col] == self.ahu_min_oa_dpr),
102
+ )
103
+
104
+ # Rolling sum to count consecutive trues
105
+ rolling_sum = (
106
+ df["combined_check"].rolling(window=self.rolling_window_size).sum()
107
+ )
108
+ # Set flag to 1 if rolling sum equals the window size
109
+ df["fc6_flag"] = (rolling_sum == self.rolling_window_size).astype(int)
110
+
111
+ if self.troubleshoot_mode:
112
+ print("Troubleshoot mode enabled - not removing helper columns")
113
+ sys.stdout.flush()
114
+ del df["rat_minus_oat"]
115
+ del df["percent_oa_calc"]
116
+ del df["perc_OAmin"]
117
+ del df["percent_oa_calc_minus_perc_OAmin"]
118
+ del df["combined_check"]
119
+
120
+ return df
@@ -0,0 +1,62 @@
1
+ import pandas as pd
2
+ import numpy as np
3
+ from open_fdd.air_handling_unit.faults.fault_condition import FaultCondition
4
+ from open_fdd.air_handling_unit.faults.helper_utils import HelperUtils
5
+ import sys
6
+
7
+
8
+ class FaultConditionTen(FaultCondition):
9
+ """Class provides the definitions for Fault Condition 10.
10
+ Outdoor air temperature and mix air temperature should
11
+ be approx equal in economizer plus mech cooling mode.
12
+ """
13
+
14
+ def __init__(self, dict_):
15
+ self.outdoor_degf_err_thres = float
16
+ self.mix_degf_err_thres = float
17
+ self.oat_col = str
18
+ self.mat_col = str
19
+ self.cooling_sig_col = str
20
+ self.economizer_sig_col = str
21
+ self.troubleshoot_mode = bool # default False,
22
+ self.rolling_window_size = int
23
+
24
+ self.set_attributes(dict_)
25
+
26
+ def apply(self, df: pd.DataFrame) -> pd.DataFrame:
27
+ if self.troubleshoot_mode:
28
+ self.troubleshoot_cols(df)
29
+
30
+ # Check analog outputs [data with units of %] are floats only
31
+ columns_to_check = [
32
+ self.economizer_sig_col,
33
+ self.cooling_sig_col,
34
+ ]
35
+ self.check_analog_pct(df, columns_to_check)
36
+
37
+ df["abs_mat_minus_oat"] = abs(df[self.mat_col] - df[self.oat_col])
38
+ df["mat_oat_sqrted"] = np.sqrt(
39
+ self.mix_degf_err_thres**2 + self.outdoor_degf_err_thres**2
40
+ )
41
+
42
+ df["combined_check"] = (
43
+ (df["abs_mat_minus_oat"] > df["mat_oat_sqrted"])
44
+ # verify ahu is running in OS 3 clg mode in min OA
45
+ & (df[self.cooling_sig_col] > 0.01)
46
+ & (df[self.economizer_sig_col] > 0.9)
47
+ )
48
+
49
+ # Rolling sum to count consecutive trues
50
+ rolling_sum = (
51
+ df["combined_check"].rolling(window=self.rolling_window_size).sum()
52
+ )
53
+ # Set flag to 1 if rolling sum equals the window size
54
+ df["fc10_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
55
+
56
+ if self.troubleshoot_mode:
57
+ print("Troubleshoot mode enabled - not removing helper columns")
58
+ del df["abs_mat_minus_oat"]
59
+ del df["mat_oat_sqrted"]
60
+ del df["combined_check"]
61
+
62
+ return df
@@ -0,0 +1,66 @@
1
+ import pandas as pd
2
+ import numpy as np
3
+ import operator
4
+ from open_fdd.air_handling_unit.faults.fault_condition import FaultCondition
5
+ from open_fdd.air_handling_unit.faults.helper_utils import HelperUtils
6
+ import sys
7
+
8
+
9
+ class FaultConditionThirteen(FaultCondition):
10
+ """Class provides the definitions for Fault Condition 13.
11
+ Supply air temperature too high in full cooling
12
+ in economizer plus mech cooling mode
13
+ """
14
+
15
+ def __init__(self, dict_):
16
+ self.supply_degf_err_thres = float
17
+ self.ahu_min_oa_dpr = float
18
+ self.sat_col = str
19
+ self.sat_setpoint_col = str
20
+ self.cooling_sig_col = str
21
+ self.economizer_sig_col = str
22
+ self.troubleshoot_mode = bool # default False
23
+ self.rolling_window_size = int
24
+
25
+ self.set_attributes(dict_)
26
+
27
+ def apply(self, df: pd.DataFrame) -> pd.DataFrame:
28
+ if self.troubleshoot_mode:
29
+ self.troubleshoot_cols(df)
30
+
31
+ # Check analog outputs [data with units of %] are floats only
32
+ columns_to_check = [
33
+ self.economizer_sig_col,
34
+ self.cooling_sig_col,
35
+ ]
36
+ self.check_analog_pct(df, columns_to_check)
37
+
38
+ # Create helper columns
39
+ df["sat_greater_than_sp_calc"] = (
40
+ df[self.sat_col] > df[self.sat_setpoint_col] + self.supply_degf_err_thres
41
+ )
42
+
43
+ df["combined_check"] = operator.or_(
44
+ ((df["sat_greater_than_sp_calc"]))
45
+ # OS4 AHU state clg @ min OA
46
+ & (df[self.cooling_sig_col] > 0.01)
47
+ & (df[self.economizer_sig_col] == self.ahu_min_oa_dpr), # OR
48
+ ((df["sat_greater_than_sp_calc"]))
49
+ # verify ahu is running in OS 3 clg mode in 100 OA
50
+ & (df[self.cooling_sig_col] > 0.01) & (df[self.economizer_sig_col] > 0.9),
51
+ )
52
+
53
+ # Rolling sum to count consecutive trues
54
+ rolling_sum = (
55
+ df["combined_check"].rolling(window=self.rolling_window_size).sum()
56
+ )
57
+ # Set flag to 1 if rolling sum equals the window size
58
+ df["fc13_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
59
+
60
+ if self.troubleshoot_mode:
61
+ print("Troubleshoot mode enabled - not removing helper columns")
62
+ sys.stdout.flush()
63
+ del df["sat_greater_than_sp_calc"]
64
+ del df["combined_check"]
65
+
66
+ return df
@@ -0,0 +1,58 @@
1
+ import pandas as pd
2
+ import numpy as np
3
+ from open_fdd.air_handling_unit.faults.fault_condition import FaultCondition
4
+ import sys
5
+
6
+
7
+ class FaultConditionThree(FaultCondition):
8
+ """Class provides the definitions for Fault Condition 3.
9
+ Mix temperature too high; should be between outside and return air.
10
+ """
11
+
12
+ def __init__(self, dict_):
13
+ self.mix_degf_err_thres = float
14
+ self.return_degf_err_thres = float
15
+ self.outdoor_degf_err_thres = float
16
+ self.mat_col = str
17
+ self.rat_col = str
18
+ self.oat_col = str
19
+ self.supply_vfd_speed_col = str
20
+ self.troubleshoot_mode = bool # default to False
21
+ self.rolling_window_size = int
22
+
23
+ self.set_attributes(dict_)
24
+
25
+ def apply(self, df: pd.DataFrame) -> pd.DataFrame:
26
+ if self.troubleshoot_mode:
27
+ self.troubleshoot_cols(df)
28
+
29
+ # Check analog outputs [data with units of %] are floats only
30
+ columns_to_check = [self.supply_vfd_speed_col]
31
+ self.check_analog_pct(df, columns_to_check)
32
+
33
+ # Fault condition-specific checks / flags
34
+ df["mat_check"] = df[self.mat_col] - self.mix_degf_err_thres
35
+ df["temp_min_check"] = np.maximum(
36
+ df[self.rat_col] + self.return_degf_err_thres,
37
+ df[self.oat_col] + self.outdoor_degf_err_thres,
38
+ )
39
+
40
+ df["combined_check"] = (df["mat_check"] > df["temp_min_check"]) & (
41
+ df[self.supply_vfd_speed_col] > 0.01
42
+ )
43
+
44
+ # Rolling sum to count consecutive trues
45
+ rolling_sum = (
46
+ df["combined_check"].rolling(window=self.rolling_window_size).sum()
47
+ )
48
+ # Set flag to 1 if rolling sum equals the window size
49
+ df["fc3_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
50
+
51
+ if self.troubleshoot_mode:
52
+ print("Troubleshoot mode enabled - not removing helper columns")
53
+ sys.stdout.flush()
54
+ del df["mat_check"]
55
+ del df["temp_min_check"]
56
+ del df["combined_check"]
57
+
58
+ return df
@@ -0,0 +1,71 @@
1
+ import pandas as pd
2
+ import numpy as np
3
+ import operator
4
+ from open_fdd.air_handling_unit.faults.fault_condition import FaultCondition
5
+ from open_fdd.air_handling_unit.faults.helper_utils import HelperUtils
6
+ import sys
7
+
8
+
9
+ class FaultConditionTwelve(FaultCondition):
10
+ """Class provides the definitions for Fault Condition 12.
11
+ Supply air temperature too high; should be less than
12
+ mix air temperature in economizer plus mech cooling mode.
13
+ """
14
+
15
+ def __init__(self, dict_):
16
+ self.delta_t_supply_fan = float
17
+ self.mix_degf_err_thres = float
18
+ self.supply_degf_err_thres = float
19
+ self.ahu_min_oa_dpr = float
20
+ self.sat_col = str
21
+ self.mat_col = str
22
+ self.cooling_sig_col = str
23
+ self.economizer_sig_col = str
24
+ self.troubleshoot_mode = bool # default False
25
+ self.rolling_window_size = int
26
+
27
+ self.set_attributes(dict_)
28
+
29
+ def apply(self, df: pd.DataFrame) -> pd.DataFrame:
30
+ if self.troubleshoot_mode:
31
+ self.troubleshoot_cols(df)
32
+
33
+ # Check analog outputs [data with units of %] are floats only
34
+ columns_to_check = [
35
+ self.economizer_sig_col,
36
+ self.cooling_sig_col,
37
+ ]
38
+ self.check_analog_pct(df, columns_to_check)
39
+
40
+ # Create helper columns
41
+ df["sat_minus_saterr_delta_supply_fan"] = (
42
+ df[self.sat_col] - self.supply_degf_err_thres - self.delta_t_supply_fan
43
+ )
44
+ df["mat_plus_materr"] = df[self.mat_col] + self.mix_degf_err_thres
45
+
46
+ df["combined_check"] = operator.or_(
47
+ # OS4 AHU state clg @ min OA
48
+ (df["sat_minus_saterr_delta_supply_fan"] > df["mat_plus_materr"])
49
+ # verify AHU in OS4 mode
50
+ & (df[self.cooling_sig_col] > 0.01)
51
+ & (df[self.economizer_sig_col] == self.ahu_min_oa_dpr), # OR
52
+ (df["sat_minus_saterr_delta_supply_fan"] > df["mat_plus_materr"])
53
+ # verify ahu is running in OS 3 clg mode in 100 OA
54
+ & (df[self.cooling_sig_col] > 0.01) & (df[self.economizer_sig_col] > 0.9),
55
+ )
56
+
57
+ # Rolling sum to count consecutive trues
58
+ rolling_sum = (
59
+ df["combined_check"].rolling(window=self.rolling_window_size).sum()
60
+ )
61
+ # Set flag to 1 if rolling sum equals the window size
62
+ df["fc12_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
63
+
64
+ if self.troubleshoot_mode:
65
+ print("Troubleshoot mode enabled - not removing helper columns")
66
+ sys.stdout.flush()
67
+ del df["sat_minus_saterr_delta_supply_fan"]
68
+ del df["mat_plus_materr"]
69
+ del df["combined_check"]
70
+
71
+ return df
@@ -0,0 +1,61 @@
1
+ import pandas as pd
2
+ import numpy as np
3
+ from open_fdd.air_handling_unit.faults.fault_condition import FaultCondition
4
+ import sys
5
+
6
+
7
+ class FaultConditionTwo(FaultCondition):
8
+ """Class provides the definitions for Fault Condition 2.
9
+ Mix temperature too low; should be between outside and return air.
10
+ """
11
+
12
+ def __init__(self, dict_):
13
+ """
14
+ :param dict_:
15
+ """
16
+ self.mix_degf_err_thres = float
17
+ self.return_degf_err_thres = float
18
+ self.outdoor_degf_err_thres = float
19
+ self.mat_col = str
20
+ self.rat_col = str
21
+ self.oat_col = str
22
+ self.supply_vfd_speed_col = str
23
+ self.troubleshoot_mode = bool # default to False
24
+ self.rolling_window_size = int
25
+
26
+ self.set_attributes(dict_)
27
+
28
+ def apply(self, df: pd.DataFrame) -> pd.DataFrame:
29
+ if self.troubleshoot_mode:
30
+ self.troubleshoot_cols(df)
31
+
32
+ # Check analog outputs [data with units of %] are floats only
33
+ columns_to_check = [self.supply_vfd_speed_col]
34
+ self.check_analog_pct(df, columns_to_check)
35
+
36
+ # Fault condition-specific checks / flags
37
+ df["mat_check"] = df[self.mat_col] + self.mix_degf_err_thres
38
+ df["temp_min_check"] = np.minimum(
39
+ df[self.rat_col] - self.return_degf_err_thres,
40
+ df[self.oat_col] - self.outdoor_degf_err_thres,
41
+ )
42
+
43
+ df["combined_check"] = (df["mat_check"] < df["temp_min_check"]) & (
44
+ df[self.supply_vfd_speed_col] > 0.01
45
+ )
46
+
47
+ # Rolling sum to count consecutive trues
48
+ rolling_sum = (
49
+ df["combined_check"].rolling(window=self.rolling_window_size).sum()
50
+ )
51
+ # Set flag to 1 if rolling sum equals the window size
52
+ df["fc2_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
53
+
54
+ if self.troubleshoot_mode:
55
+ print("Troubleshoot mode enabled - not removing helper columns")
56
+ sys.stdout.flush()
57
+ del df["mat_check"]
58
+ del df["temp_min_check"]
59
+ del df["combined_check"]
60
+
61
+ return df