open-fdd 0.1.1__py3-none-any.whl → 0.1.4__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 (63) hide show
  1. open_fdd/air_handling_unit/faults/__init__.py +2253 -0
  2. open_fdd/air_handling_unit/faults/fault_condition.py +38 -18
  3. open_fdd/air_handling_unit/faults/fault_condition_eight.py +91 -31
  4. open_fdd/air_handling_unit/faults/fault_condition_eleven.py +93 -35
  5. open_fdd/air_handling_unit/faults/fault_condition_fifteen.py +111 -49
  6. open_fdd/air_handling_unit/faults/fault_condition_five.py +89 -34
  7. open_fdd/air_handling_unit/faults/fault_condition_four.py +136 -61
  8. open_fdd/air_handling_unit/faults/fault_condition_fourteen.py +103 -40
  9. open_fdd/air_handling_unit/faults/fault_condition_nine.py +95 -35
  10. open_fdd/air_handling_unit/faults/fault_condition_one.py +83 -31
  11. open_fdd/air_handling_unit/faults/fault_condition_seven.py +85 -26
  12. open_fdd/air_handling_unit/faults/fault_condition_six.py +134 -73
  13. open_fdd/air_handling_unit/faults/fault_condition_ten.py +91 -30
  14. open_fdd/air_handling_unit/faults/fault_condition_thirteen.py +95 -34
  15. open_fdd/air_handling_unit/faults/fault_condition_three.py +84 -29
  16. open_fdd/air_handling_unit/faults/fault_condition_twelve.py +98 -37
  17. open_fdd/air_handling_unit/faults/fault_condition_two.py +84 -32
  18. open_fdd/air_handling_unit/faults/helper_utils.py +295 -93
  19. open_fdd/air_handling_unit/images/ahu1_fc1_2024-06_1.jpg +0 -0
  20. open_fdd/air_handling_unit/images/ahu1_fc1_2024-06_2.jpg +0 -0
  21. open_fdd/air_handling_unit/images/example1.jpg +0 -0
  22. open_fdd/air_handling_unit/images/example2.jpg +0 -0
  23. open_fdd/air_handling_unit/images/fc10_definition.png +0 -0
  24. open_fdd/air_handling_unit/images/fc11_definition.png +0 -0
  25. open_fdd/air_handling_unit/images/fc12_definition.png +0 -0
  26. open_fdd/air_handling_unit/images/fc13_definition.png +0 -0
  27. open_fdd/air_handling_unit/images/fc1_definition.png +0 -0
  28. open_fdd/air_handling_unit/images/fc1_report_screenshot_all.png +0 -0
  29. open_fdd/air_handling_unit/images/fc2_definition.png +0 -0
  30. open_fdd/air_handling_unit/images/fc3_definition.png +0 -0
  31. open_fdd/air_handling_unit/images/fc4_definition.png +0 -0
  32. open_fdd/air_handling_unit/images/fc5_definition.png +0 -0
  33. open_fdd/air_handling_unit/images/fc6_definition.png +0 -0
  34. open_fdd/air_handling_unit/images/fc7_definition.png +0 -0
  35. open_fdd/air_handling_unit/images/fc8_definition.png +0 -0
  36. open_fdd/air_handling_unit/images/fc9_definition.png +0 -0
  37. open_fdd/air_handling_unit/images/latex_generator.py +175 -0
  38. open_fdd/air_handling_unit/images/params.docx +0 -0
  39. open_fdd/air_handling_unit/images/params.pdf +0 -0
  40. open_fdd/air_handling_unit/images/plot_for_repo.png +0 -0
  41. open_fdd/air_handling_unit/reports/base_report.py +47 -0
  42. open_fdd/air_handling_unit/reports/report_fc7.py +3 -1
  43. open_fdd/tests/ahu/test_ahu_fc1.py +18 -1
  44. open_fdd/tests/ahu/test_ahu_fc10.py +1 -1
  45. open_fdd/tests/ahu/test_ahu_fc11.py +1 -1
  46. open_fdd/tests/ahu/test_ahu_fc12.py +1 -1
  47. open_fdd/tests/ahu/test_ahu_fc13.py +1 -1
  48. open_fdd/tests/ahu/test_ahu_fc14.py +1 -1
  49. open_fdd/tests/ahu/test_ahu_fc15.py +1 -1
  50. open_fdd/tests/ahu/test_ahu_fc2.py +1 -1
  51. open_fdd/tests/ahu/test_ahu_fc3.py +1 -1
  52. open_fdd/tests/ahu/test_ahu_fc4.py +2 -2
  53. open_fdd/tests/ahu/test_ahu_fc5.py +1 -1
  54. open_fdd/tests/ahu/test_ahu_fc6.py +2 -2
  55. open_fdd/tests/ahu/test_ahu_fc7.py +1 -1
  56. open_fdd/tests/ahu/test_ahu_fc8.py +1 -1
  57. open_fdd/tests/ahu/test_ahu_fc9.py +1 -1
  58. {open_fdd-0.1.1.dist-info → open_fdd-0.1.4.dist-info}/METADATA +34 -5
  59. open_fdd-0.1.4.dist-info/RECORD +82 -0
  60. open_fdd-0.1.1.dist-info/RECORD +0 -59
  61. {open_fdd-0.1.1.dist-info → open_fdd-0.1.4.dist-info}/LICENSE +0 -0
  62. {open_fdd-0.1.1.dist-info → open_fdd-0.1.4.dist-info}/WHEEL +0 -0
  63. {open_fdd-0.1.1.dist-info → open_fdd-0.1.4.dist-info}/top_level.txt +0 -0
@@ -1,33 +1,29 @@
1
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
2
  import operator
3
+ from open_fdd.air_handling_unit.faults.fault_condition import (
4
+ FaultCondition,
5
+ MissingColumnError,
6
+ )
6
7
  import sys
7
8
 
8
9
 
9
10
  class FaultConditionSix(FaultCondition):
10
11
  """Class provides the definitions for Fault Condition 6.
11
12
 
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
13
+ This fault related to knowing the design air flow for
14
+ ventilation AHU_MIN_CFM_DESIGN which comes from the
15
+ design mech engineered records where then the fault
16
+ tries to calculate that based on totalized measured
17
+ AHU air flow and outside air fraction calc from
18
+ AHU temp sensors. The fault could flag issues where
19
+ flow stations are either not in calibration, temp
20
+ sensors used in the OA frac calc, or possibly the AHU
21
+ not bringing in design air flow when not operating in
22
+ economizer free cooling modes.
28
23
  """
29
24
 
30
25
  def __init__(self, dict_):
26
+ super().__init__()
31
27
  self.airflow_err_thres = float
32
28
  self.ahu_min_oa_cfm_design = float
33
29
  self.outdoor_degf_err_thres = float
@@ -45,76 +41,141 @@ class FaultConditionSix(FaultCondition):
45
41
  self.troubleshoot_mode = bool # default should be False
46
42
  self.rolling_window_size = int
47
43
 
48
- self.set_attributes(dict_)
44
+ self.equation_string = (
45
+ "fc6_flag = 1 if |OA_frac_calc - OA_min| > airflow_err_thres "
46
+ "in non-economizer modes, considering htg and mech clg OS \n"
47
+ )
48
+ self.description_string = (
49
+ "Fault Condition 6: Issues detected with OA fraction calculation or AHU "
50
+ "not maintaining design air flow in non-economizer conditions \n"
51
+ )
52
+ self.required_column_description = (
53
+ "Required inputs are the supply fan air volume, mixed air temperature, "
54
+ "outside air temperature, return air temperature, and VFD speed. "
55
+ "Optional inputs include economizer signal, heating signal, and cooling signal \n"
56
+ )
57
+ self.error_string = f"One or more required columns are missing or None \n"
49
58
 
50
- def apply(self, df: pd.DataFrame) -> pd.DataFrame:
51
- if self.troubleshoot_mode:
52
- self.troubleshoot_cols(df)
59
+ self.set_attributes(dict_)
53
60
 
54
- # check analog outputs [data with units of %] are floats only
55
- columns_to_check = [
61
+ # Set required columns specific to this fault condition
62
+ self.required_columns = [
63
+ self.supply_fan_air_volume_col,
64
+ self.mat_col,
65
+ self.oat_col,
66
+ self.rat_col,
56
67
  self.supply_vfd_speed_col,
57
68
  self.economizer_sig_col,
58
69
  self.heating_sig_col,
59
70
  self.cooling_sig_col,
60
71
  ]
61
72
 
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
- )
73
+ # Check if any of the required columns are None
74
+ if any(col is None for col in self.required_columns):
75
+ raise MissingColumnError(
76
+ f"{self.error_string}"
77
+ f"{self.equation_string}"
78
+ f"{self.description_string}"
79
+ f"{self.required_column_description}"
80
+ f"{self.required_columns}"
81
+ )
70
82
 
71
- # weed out any negative values
72
- df["percent_oa_calc"] = df["percent_oa_calc"].apply(lambda x: x if x > 0 else 0)
83
+ # Ensure all required columns are strings
84
+ self.required_columns = [str(col) for col in self.required_columns]
73
85
 
74
- df["perc_OAmin"] = (
75
- self.ahu_min_oa_cfm_design / df[self.supply_fan_air_volume_col]
86
+ self.mapped_columns = (
87
+ f"Your config dictionary is mapped as: {', '.join(self.required_columns)}"
76
88
  )
77
89
 
78
- df["percent_oa_calc_minus_perc_OAmin"] = abs(
79
- df["percent_oa_calc"] - df["perc_OAmin"]
90
+ def get_required_columns(self) -> str:
91
+ """Returns a string representation of the required columns."""
92
+ return (
93
+ f"{self.equation_string}"
94
+ f"{self.description_string}"
95
+ f"{self.required_column_description}"
96
+ f"{self.mapped_columns}"
80
97
  )
81
98
 
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)
99
+ def apply(self, df: pd.DataFrame) -> pd.DataFrame:
100
+ try:
101
+ # Ensure all required columns are present
102
+ self.check_required_columns(df)
103
+
104
+ if self.troubleshoot_mode:
105
+ self.troubleshoot_cols(df)
106
+
107
+ # Check analog outputs [data with units of %] are floats only
108
+ columns_to_check = [
109
+ self.supply_vfd_speed_col,
110
+ self.economizer_sig_col,
111
+ self.heating_sig_col,
112
+ self.cooling_sig_col,
113
+ ]
114
+
115
+ for col in columns_to_check:
116
+ self.check_analog_pct(df, [col])
117
+
118
+ # Create helper columns
119
+ df["rat_minus_oat"] = abs(df[self.rat_col] - df[self.oat_col])
120
+ df["percent_oa_calc"] = (df[self.mat_col] - df[self.rat_col]) / (
121
+ df[self.oat_col] - df[self.rat_col]
122
+ )
123
+
124
+ # Weed out any negative values
125
+ df["percent_oa_calc"] = df["percent_oa_calc"].apply(
126
+ lambda x: x if x > 0 else 0
87
127
  )
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)
128
+
129
+ df["perc_OAmin"] = (
130
+ self.ahu_min_oa_cfm_design / df[self.supply_fan_air_volume_col]
96
131
  )
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
132
 
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)
133
+ df["percent_oa_calc_minus_perc_OAmin"] = abs(
134
+ df["percent_oa_calc"] - df["perc_OAmin"]
135
+ )
110
136
 
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"]
137
+ df["combined_check"] = operator.or_(
138
+ # OS 1 htg mode
139
+ (
140
+ (df["rat_minus_oat"] >= self.oat_rat_delta_min)
141
+ & (df["percent_oa_calc_minus_perc_OAmin"] > self.airflow_err_thres)
142
+ )
143
+ # Verify AHU is running in OS 1 htg mode in min OA
144
+ & (
145
+ (df[self.heating_sig_col] > 0.0)
146
+ & (df[self.supply_vfd_speed_col] > 0.0)
147
+ ), # OR
148
+ # OS 4 mech clg mode
149
+ (
150
+ (df["rat_minus_oat"] >= self.oat_rat_delta_min)
151
+ & (df["percent_oa_calc_minus_perc_OAmin"] > self.airflow_err_thres)
152
+ )
153
+ # Verify AHU is running in OS 4 clg mode in min OA
154
+ & (df[self.heating_sig_col] == 0.0)
155
+ & (df[self.cooling_sig_col] > 0.0)
156
+ & (df[self.supply_vfd_speed_col] > 0.0)
157
+ & (df[self.economizer_sig_col] == self.ahu_min_oa_dpr),
158
+ )
119
159
 
120
- return df
160
+ # Rolling sum to count consecutive trues
161
+ rolling_sum = (
162
+ df["combined_check"].rolling(window=self.rolling_window_size).sum()
163
+ )
164
+ # Set flag to 1 if rolling sum equals the window size
165
+ df["fc6_flag"] = (rolling_sum == self.rolling_window_size).astype(int)
166
+
167
+ if self.troubleshoot_mode:
168
+ print("Troubleshoot mode enabled - not removing helper columns")
169
+ sys.stdout.flush()
170
+ del df["rat_minus_oat"]
171
+ del df["percent_oa_calc"]
172
+ del df["perc_OAmin"]
173
+ del df["percent_oa_calc_minus_perc_OAmin"]
174
+ del df["combined_check"]
175
+
176
+ return df
177
+
178
+ except MissingColumnError as e:
179
+ print(f"Error: {e.message}")
180
+ sys.stdout.flush()
181
+ raise e
@@ -1,7 +1,9 @@
1
1
  import pandas as pd
2
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
3
+ from open_fdd.air_handling_unit.faults.fault_condition import (
4
+ FaultCondition,
5
+ MissingColumnError,
6
+ )
5
7
  import sys
6
8
 
7
9
 
@@ -12,6 +14,7 @@ class FaultConditionTen(FaultCondition):
12
14
  """
13
15
 
14
16
  def __init__(self, dict_):
17
+ super().__init__()
15
18
  self.outdoor_degf_err_thres = float
16
19
  self.mix_degf_err_thres = float
17
20
  self.oat_col = str
@@ -21,42 +24,100 @@ class FaultConditionTen(FaultCondition):
21
24
  self.troubleshoot_mode = bool # default False,
22
25
  self.rolling_window_size = int
23
26
 
24
- self.set_attributes(dict_)
27
+ self.equation_string = (
28
+ "fc10_flag = 1 if |OAT - MAT| > √(εOAT² + εMAT²) in "
29
+ "economizer + mech cooling mode for N consecutive values else 0 \n"
30
+ )
31
+ self.description_string = (
32
+ "Fault Condition 10: Outdoor air temperature and mixed air temperature "
33
+ "should be approximately equal in economizer plus mechanical cooling mode \n"
34
+ )
35
+ self.required_column_description = (
36
+ "Required inputs are the outside air temperature, mixed air temperature, "
37
+ "cooling signal, and economizer signal \n"
38
+ )
39
+ self.error_string = f"One or more required columns are missing or None \n"
25
40
 
26
- def apply(self, df: pd.DataFrame) -> pd.DataFrame:
27
- if self.troubleshoot_mode:
28
- self.troubleshoot_cols(df)
41
+ self.set_attributes(dict_)
29
42
 
30
- # Check analog outputs [data with units of %] are floats only
31
- columns_to_check = [
32
- self.economizer_sig_col,
43
+ # Set required columns specific to this fault condition
44
+ self.required_columns = [
45
+ self.oat_col,
46
+ self.mat_col,
33
47
  self.cooling_sig_col,
48
+ self.economizer_sig_col,
34
49
  ]
35
- self.check_analog_pct(df, columns_to_check)
36
50
 
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
- )
51
+ # Check if any of the required columns are None
52
+ if any(col is None for col in self.required_columns):
53
+ raise MissingColumnError(
54
+ f"{self.error_string}"
55
+ f"{self.equation_string}"
56
+ f"{self.description_string}"
57
+ f"{self.required_column_description}"
58
+ f"{self.required_columns}"
59
+ )
41
60
 
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)
61
+ # Ensure all required columns are strings
62
+ self.required_columns = [str(col) for col in self.required_columns]
63
+
64
+ self.mapped_columns = (
65
+ f"Your config dictionary is mapped as: {', '.join(self.required_columns)}"
47
66
  )
48
67
 
49
- # Rolling sum to count consecutive trues
50
- rolling_sum = (
51
- df["combined_check"].rolling(window=self.rolling_window_size).sum()
68
+ def get_required_columns(self) -> str:
69
+ """Returns a string representation of the required columns."""
70
+ return (
71
+ f"{self.equation_string}"
72
+ f"{self.description_string}"
73
+ f"{self.required_column_description}"
74
+ f"{self.mapped_columns}"
52
75
  )
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
76
 
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"]
77
+ def apply(self, df: pd.DataFrame) -> pd.DataFrame:
78
+ try:
79
+ # Ensure all required columns are present
80
+ self.check_required_columns(df)
81
+
82
+ if self.troubleshoot_mode:
83
+ self.troubleshoot_cols(df)
84
+
85
+ # Check analog outputs [data with units of %] are floats only
86
+ columns_to_check = [
87
+ self.economizer_sig_col,
88
+ self.cooling_sig_col,
89
+ ]
90
+ self.check_analog_pct(df, columns_to_check)
91
+
92
+ df["abs_mat_minus_oat"] = abs(df[self.mat_col] - df[self.oat_col])
93
+ df["mat_oat_sqrted"] = np.sqrt(
94
+ self.mix_degf_err_thres**2 + self.outdoor_degf_err_thres**2
95
+ )
96
+
97
+ df["combined_check"] = (
98
+ (df["abs_mat_minus_oat"] > df["mat_oat_sqrted"])
99
+ # verify AHU is running in OS 3 clg mode in min OA
100
+ & (df[self.cooling_sig_col] > 0.01)
101
+ & (df[self.economizer_sig_col] > 0.9)
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["fc10_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["abs_mat_minus_oat"]
115
+ del df["mat_oat_sqrted"]
116
+ del df["combined_check"]
117
+
118
+ return df
61
119
 
62
- return df
120
+ except MissingColumnError as e:
121
+ print(f"Error: {e.message}")
122
+ sys.stdout.flush()
123
+ raise e
@@ -1,8 +1,9 @@
1
1
  import pandas as pd
2
- import numpy as np
3
2
  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
3
+ from open_fdd.air_handling_unit.faults.fault_condition import (
4
+ FaultCondition,
5
+ MissingColumnError,
6
+ )
6
7
  import sys
7
8
 
8
9
 
@@ -13,6 +14,7 @@ class FaultConditionThirteen(FaultCondition):
13
14
  """
14
15
 
15
16
  def __init__(self, dict_):
17
+ super().__init__()
16
18
  self.supply_degf_err_thres = float
17
19
  self.ahu_min_oa_dpr = float
18
20
  self.sat_col = str
@@ -22,45 +24,104 @@ class FaultConditionThirteen(FaultCondition):
22
24
  self.troubleshoot_mode = bool # default False
23
25
  self.rolling_window_size = int
24
26
 
25
- self.set_attributes(dict_)
27
+ self.equation_string = (
28
+ "fc13_flag = 1 if SAT > (SATSP + εSAT) in "
29
+ "economizer + mech cooling mode for N consecutive values else 0 \n"
30
+ )
31
+ self.description_string = (
32
+ "Fault Condition 13: Supply air temperature too high in full cooling "
33
+ "in economizer plus mechanical cooling mode \n"
34
+ )
35
+ self.required_column_description = (
36
+ "Required inputs are the supply air temperature, supply air temperature setpoint, "
37
+ "cooling signal, and economizer signal \n"
38
+ )
39
+ self.error_string = f"One or more required columns are missing or None \n"
26
40
 
27
- def apply(self, df: pd.DataFrame) -> pd.DataFrame:
28
- if self.troubleshoot_mode:
29
- self.troubleshoot_cols(df)
41
+ self.set_attributes(dict_)
30
42
 
31
- # Check analog outputs [data with units of %] are floats only
32
- columns_to_check = [
33
- self.economizer_sig_col,
43
+ # Set required columns specific to this fault condition
44
+ self.required_columns = [
45
+ self.sat_col,
46
+ self.sat_setpoint_col,
34
47
  self.cooling_sig_col,
48
+ self.economizer_sig_col,
35
49
  ]
36
- self.check_analog_pct(df, columns_to_check)
37
50
 
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
- )
51
+ # Check if any of the required columns are None
52
+ if any(col is None for col in self.required_columns):
53
+ raise MissingColumnError(
54
+ f"{self.error_string}"
55
+ f"{self.equation_string}"
56
+ f"{self.description_string}"
57
+ f"{self.required_column_description}"
58
+ f"{self.required_columns}"
59
+ )
60
+
61
+ # Ensure all required columns are strings
62
+ self.required_columns = [str(col) for col in self.required_columns]
42
63
 
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),
64
+ self.mapped_columns = (
65
+ f"Your config dictionary is mapped as: {', '.join(self.required_columns)}"
51
66
  )
52
67
 
53
- # Rolling sum to count consecutive trues
54
- rolling_sum = (
55
- df["combined_check"].rolling(window=self.rolling_window_size).sum()
68
+ def get_required_columns(self) -> str:
69
+ """Returns a string representation of the required columns."""
70
+ return (
71
+ f"{self.equation_string}"
72
+ f"{self.description_string}"
73
+ f"{self.required_column_description}"
74
+ f"{self.mapped_columns}"
56
75
  )
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
76
 
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"]
77
+ def apply(self, df: pd.DataFrame) -> pd.DataFrame:
78
+ try:
79
+ # Ensure all required columns are present
80
+ self.check_required_columns(df)
81
+
82
+ if self.troubleshoot_mode:
83
+ self.troubleshoot_cols(df)
84
+
85
+ # Check analog outputs [data with units of %] are floats only
86
+ columns_to_check = [
87
+ self.economizer_sig_col,
88
+ self.cooling_sig_col,
89
+ ]
90
+ self.check_analog_pct(df, columns_to_check)
91
+
92
+ # Create helper columns
93
+ df["sat_greater_than_sp_calc"] = (
94
+ df[self.sat_col]
95
+ > df[self.sat_setpoint_col] + self.supply_degf_err_thres
96
+ )
65
97
 
66
- return df
98
+ df["combined_check"] = operator.or_(
99
+ ((df["sat_greater_than_sp_calc"]))
100
+ # OS4 AHU state clg @ min OA
101
+ & (df[self.cooling_sig_col] > 0.01)
102
+ & (df[self.economizer_sig_col] == self.ahu_min_oa_dpr), # OR
103
+ ((df["sat_greater_than_sp_calc"]))
104
+ # verify ahu is running in OS 3 clg mode in 100 OA
105
+ & (df[self.cooling_sig_col] > 0.01)
106
+ & (df[self.economizer_sig_col] > 0.9),
107
+ )
108
+
109
+ # Rolling sum to count consecutive trues
110
+ rolling_sum = (
111
+ df["combined_check"].rolling(window=self.rolling_window_size).sum()
112
+ )
113
+ # Set flag to 1 if rolling sum equals the window size
114
+ df["fc13_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
115
+
116
+ if self.troubleshoot_mode:
117
+ print("Troubleshoot mode enabled - not removing helper columns")
118
+ sys.stdout.flush()
119
+ del df["sat_greater_than_sp_calc"]
120
+ del df["combined_check"]
121
+
122
+ return df
123
+
124
+ except MissingColumnError as e:
125
+ print(f"Error: {e.message}")
126
+ sys.stdout.flush()
127
+ raise e