open-fdd 0.1.5__py3-none-any.whl → 0.1.7__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 (66) hide show
  1. open_fdd/air_handling_unit/faults/__init__.py +331 -300
  2. open_fdd/air_handling_unit/faults/helper_utils.py +3 -0
  3. open_fdd/air_handling_unit/faults/shared_utils.py +16 -1
  4. open_fdd/air_handling_unit/reports/__init__.py +107 -0
  5. open_fdd/air_handling_unit/reports/fault_report.py +1 -0
  6. open_fdd/tests/ahu/test_ahu_fc1.py +1 -0
  7. open_fdd/tests/ahu/test_ahu_fc16.py +205 -0
  8. {open_fdd-0.1.5.dist-info → open_fdd-0.1.7.dist-info}/METADATA +4 -3
  9. open_fdd-0.1.7.dist-info/RECORD +31 -0
  10. {open_fdd-0.1.5.dist-info → open_fdd-0.1.7.dist-info}/WHEEL +1 -1
  11. open_fdd/air_handling_unit/faults/fault_condition_eight.py +0 -127
  12. open_fdd/air_handling_unit/faults/fault_condition_eleven.py +0 -126
  13. open_fdd/air_handling_unit/faults/fault_condition_fifteen.py +0 -152
  14. open_fdd/air_handling_unit/faults/fault_condition_five.py +0 -123
  15. open_fdd/air_handling_unit/faults/fault_condition_four.py +0 -168
  16. open_fdd/air_handling_unit/faults/fault_condition_fourteen.py +0 -143
  17. open_fdd/air_handling_unit/faults/fault_condition_nine.py +0 -128
  18. open_fdd/air_handling_unit/faults/fault_condition_one.py +0 -112
  19. open_fdd/air_handling_unit/faults/fault_condition_seven.py +0 -114
  20. open_fdd/air_handling_unit/faults/fault_condition_six.py +0 -181
  21. open_fdd/air_handling_unit/faults/fault_condition_ten.py +0 -123
  22. open_fdd/air_handling_unit/faults/fault_condition_thirteen.py +0 -127
  23. open_fdd/air_handling_unit/faults/fault_condition_three.py +0 -113
  24. open_fdd/air_handling_unit/faults/fault_condition_twelve.py +0 -132
  25. open_fdd/air_handling_unit/faults/fault_condition_two.py +0 -113
  26. open_fdd/air_handling_unit/images/ahu1_fc1_2024-06_1.jpg +0 -0
  27. open_fdd/air_handling_unit/images/ahu1_fc1_2024-06_2.jpg +0 -0
  28. open_fdd/air_handling_unit/images/example1.jpg +0 -0
  29. open_fdd/air_handling_unit/images/example2.jpg +0 -0
  30. open_fdd/air_handling_unit/images/fc10_definition.png +0 -0
  31. open_fdd/air_handling_unit/images/fc11_definition.png +0 -0
  32. open_fdd/air_handling_unit/images/fc12_definition.png +0 -0
  33. open_fdd/air_handling_unit/images/fc13_definition.png +0 -0
  34. open_fdd/air_handling_unit/images/fc1_definition.png +0 -0
  35. open_fdd/air_handling_unit/images/fc1_report_screenshot_all.png +0 -0
  36. open_fdd/air_handling_unit/images/fc2_definition.png +0 -0
  37. open_fdd/air_handling_unit/images/fc3_definition.png +0 -0
  38. open_fdd/air_handling_unit/images/fc4_definition.png +0 -0
  39. open_fdd/air_handling_unit/images/fc5_definition.png +0 -0
  40. open_fdd/air_handling_unit/images/fc6_definition.png +0 -0
  41. open_fdd/air_handling_unit/images/fc7_definition.png +0 -0
  42. open_fdd/air_handling_unit/images/fc8_definition.png +0 -0
  43. open_fdd/air_handling_unit/images/fc9_definition.png +0 -0
  44. open_fdd/air_handling_unit/images/latex_generator.py +0 -175
  45. open_fdd/air_handling_unit/images/params.docx +0 -0
  46. open_fdd/air_handling_unit/images/params.pdf +0 -0
  47. open_fdd/air_handling_unit/images/plot_for_repo.png +0 -0
  48. open_fdd/air_handling_unit/reports/base_report.py +0 -47
  49. open_fdd/air_handling_unit/reports/report_fc1.py +0 -115
  50. open_fdd/air_handling_unit/reports/report_fc10.py +0 -126
  51. open_fdd/air_handling_unit/reports/report_fc11.py +0 -128
  52. open_fdd/air_handling_unit/reports/report_fc12.py +0 -126
  53. open_fdd/air_handling_unit/reports/report_fc13.py +0 -126
  54. open_fdd/air_handling_unit/reports/report_fc14.py +0 -124
  55. open_fdd/air_handling_unit/reports/report_fc15.py +0 -124
  56. open_fdd/air_handling_unit/reports/report_fc2.py +0 -119
  57. open_fdd/air_handling_unit/reports/report_fc3.py +0 -119
  58. open_fdd/air_handling_unit/reports/report_fc4.py +0 -148
  59. open_fdd/air_handling_unit/reports/report_fc5.py +0 -132
  60. open_fdd/air_handling_unit/reports/report_fc6.py +0 -156
  61. open_fdd/air_handling_unit/reports/report_fc7.py +0 -126
  62. open_fdd/air_handling_unit/reports/report_fc8.py +0 -118
  63. open_fdd/air_handling_unit/reports/report_fc9.py +0 -120
  64. open_fdd-0.1.5.dist-info/RECORD +0 -83
  65. {open_fdd-0.1.5.dist-info → open_fdd-0.1.7.dist-info}/LICENSE +0 -0
  66. {open_fdd-0.1.5.dist-info → open_fdd-0.1.7.dist-info}/top_level.txt +0 -0
@@ -1,123 +0,0 @@
1
- import pandas as pd
2
- import numpy as np
3
- from open_fdd.air_handling_unit.faults.fault_condition import (
4
- FaultCondition,
5
- MissingColumnError,
6
- )
7
- import sys
8
-
9
-
10
- class FaultConditionTen(FaultCondition):
11
- """Class provides the definitions for Fault Condition 10.
12
- Outdoor air temperature and mix air temperature should
13
- be approx equal in economizer plus mech cooling mode.
14
- """
15
-
16
- def __init__(self, dict_):
17
- super().__init__()
18
- self.outdoor_degf_err_thres = float
19
- self.mix_degf_err_thres = float
20
- self.oat_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.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"
40
-
41
- self.set_attributes(dict_)
42
-
43
- # Set required columns specific to this fault condition
44
- self.required_columns = [
45
- self.oat_col,
46
- self.mat_col,
47
- self.cooling_sig_col,
48
- self.economizer_sig_col,
49
- ]
50
-
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]
63
-
64
- self.mapped_columns = (
65
- f"Your config dictionary is mapped as: {', '.join(self.required_columns)}"
66
- )
67
-
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}"
75
- )
76
-
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
119
-
120
- except MissingColumnError as e:
121
- print(f"Error: {e.message}")
122
- sys.stdout.flush()
123
- raise e
@@ -1,127 +0,0 @@
1
- import pandas as pd
2
- import operator
3
- from open_fdd.air_handling_unit.faults.fault_condition import (
4
- FaultCondition,
5
- MissingColumnError,
6
- )
7
- import sys
8
-
9
-
10
- class FaultConditionThirteen(FaultCondition):
11
- """Class provides the definitions for Fault Condition 13.
12
- Supply air temperature too high in full cooling
13
- in economizer plus mech cooling mode
14
- """
15
-
16
- def __init__(self, dict_):
17
- super().__init__()
18
- self.supply_degf_err_thres = float
19
- self.ahu_min_oa_dpr = float
20
- self.sat_col = str
21
- self.sat_setpoint_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.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"
40
-
41
- self.set_attributes(dict_)
42
-
43
- # Set required columns specific to this fault condition
44
- self.required_columns = [
45
- self.sat_col,
46
- self.sat_setpoint_col,
47
- self.cooling_sig_col,
48
- self.economizer_sig_col,
49
- ]
50
-
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]
63
-
64
- self.mapped_columns = (
65
- f"Your config dictionary is mapped as: {', '.join(self.required_columns)}"
66
- )
67
-
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}"
75
- )
76
-
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
- )
97
-
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
@@ -1,113 +0,0 @@
1
- import pandas as pd
2
- import numpy as np
3
- from open_fdd.air_handling_unit.faults.fault_condition import (
4
- FaultCondition,
5
- MissingColumnError,
6
- )
7
- import sys
8
-
9
-
10
- class FaultConditionThree(FaultCondition):
11
- """Class provides the definitions for Fault Condition 3.
12
- Mix temperature too high; should be between outside and return air.
13
- """
14
-
15
- def __init__(self, dict_):
16
- super().__init__()
17
- self.mix_degf_err_thres = float
18
- self.return_degf_err_thres = float
19
- self.outdoor_degf_err_thres = float
20
- self.mat_col = str
21
- self.rat_col = str
22
- self.oat_col = str
23
- self.supply_vfd_speed_col = str
24
- self.troubleshoot_mode = bool # default to False
25
- self.rolling_window_size = int
26
-
27
- self.equation_string = (
28
- "fc3_flag = 1 if (MAT - εMAT > max(RAT + εRAT, OAT + εOAT)) and (VFDSPD > 0) "
29
- "for N consecutive values else 0 \n"
30
- )
31
- self.description_string = "Fault Condition 3: Mix temperature too high; should be between outside and return air \n"
32
- self.required_column_description = "Required inputs are the mix air temperature, return air temperature, outside air temperature, and supply fan VFD speed \n"
33
- self.error_string = f"One or more required columns are missing or None \n"
34
-
35
- self.set_attributes(dict_)
36
-
37
- # Set required columns specific to this fault condition
38
- self.required_columns = [
39
- self.mat_col,
40
- self.rat_col,
41
- self.oat_col,
42
- self.supply_vfd_speed_col,
43
- ]
44
-
45
- # Check if any of the required columns are None
46
- if any(col is None for col in self.required_columns):
47
- raise MissingColumnError(
48
- f"{self.error_string}"
49
- f"{self.equation_string}"
50
- f"{self.description_string}"
51
- f"{self.required_column_description}"
52
- f"{self.required_columns}"
53
- )
54
-
55
- # Ensure all required columns are strings
56
- self.required_columns = [str(col) for col in self.required_columns]
57
-
58
- self.mapped_columns = (
59
- f"Your config dictionary is mapped as: {', '.join(self.required_columns)}"
60
- )
61
-
62
- def get_required_columns(self) -> str:
63
- """Returns a string representation of the required columns."""
64
- return (
65
- f"{self.equation_string}"
66
- f"{self.description_string}"
67
- f"{self.required_column_description}"
68
- f"{self.mapped_columns}"
69
- )
70
-
71
- def apply(self, df: pd.DataFrame) -> pd.DataFrame:
72
- try:
73
- # Ensure all required columns are present
74
- self.check_required_columns(df)
75
-
76
- if self.troubleshoot_mode:
77
- self.troubleshoot_cols(df)
78
-
79
- # Check analog outputs [data with units of %] are floats only
80
- columns_to_check = [self.supply_vfd_speed_col]
81
- self.check_analog_pct(df, columns_to_check)
82
-
83
- # Fault condition-specific checks / flags
84
- df["mat_check"] = df[self.mat_col] - self.mix_degf_err_thres
85
- df["temp_min_check"] = np.maximum(
86
- df[self.rat_col] + self.return_degf_err_thres,
87
- df[self.oat_col] + self.outdoor_degf_err_thres,
88
- )
89
-
90
- df["combined_check"] = (df["mat_check"] > df["temp_min_check"]) & (
91
- df[self.supply_vfd_speed_col] > 0.01
92
- )
93
-
94
- # Rolling sum to count consecutive trues
95
- rolling_sum = (
96
- df["combined_check"].rolling(window=self.rolling_window_size).sum()
97
- )
98
- # Set flag to 1 if rolling sum equals the window size
99
- df["fc3_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
100
-
101
- if self.troubleshoot_mode:
102
- print("Troubleshoot mode enabled - not removing helper columns")
103
- sys.stdout.flush()
104
- del df["mat_check"]
105
- del df["temp_min_check"]
106
- del df["combined_check"]
107
-
108
- return df
109
-
110
- except MissingColumnError as e:
111
- print(f"Error: {e.message}")
112
- sys.stdout.flush()
113
- raise e
@@ -1,132 +0,0 @@
1
- import pandas as pd
2
- import numpy as np
3
- import operator
4
- from open_fdd.air_handling_unit.faults.fault_condition import (
5
- FaultCondition,
6
- MissingColumnError,
7
- )
8
- import sys
9
-
10
-
11
- class FaultConditionTwelve(FaultCondition):
12
- """Class provides the definitions for Fault Condition 12.
13
- Supply air temperature too high; should be less than
14
- mix air temperature in economizer plus mech cooling mode.
15
- """
16
-
17
- def __init__(self, dict_):
18
- super().__init__()
19
- self.delta_t_supply_fan = float
20
- self.mix_degf_err_thres = float
21
- self.supply_degf_err_thres = float
22
- self.ahu_min_oa_dpr = float
23
- self.sat_col = str
24
- self.mat_col = str
25
- self.cooling_sig_col = str
26
- self.economizer_sig_col = str
27
- self.troubleshoot_mode = bool # default False
28
- self.rolling_window_size = int
29
-
30
- self.equation_string = (
31
- "fc12_flag = 1 if SAT >= MAT + εMAT in "
32
- "economizer + mech cooling mode for N consecutive values else 0 \n"
33
- )
34
- self.description_string = (
35
- "Fault Condition 12: Supply air temperature too high; should be less than "
36
- "mixed air temperature in economizer plus mechanical cooling mode \n"
37
- )
38
- self.required_column_description = (
39
- "Required inputs are the supply air temperature, mixed air temperature, "
40
- "cooling signal, and economizer signal \n"
41
- )
42
- self.error_string = f"One or more required columns are missing or None \n"
43
-
44
- self.set_attributes(dict_)
45
-
46
- # Set required columns specific to this fault condition
47
- self.required_columns = [
48
- self.sat_col,
49
- self.mat_col,
50
- self.cooling_sig_col,
51
- self.economizer_sig_col,
52
- ]
53
-
54
- # Check if any of the required columns are None
55
- if any(col is None for col in self.required_columns):
56
- raise MissingColumnError(
57
- f"{self.error_string}"
58
- f"{self.equation_string}"
59
- f"{self.description_string}"
60
- f"{self.required_column_description}"
61
- f"{self.required_columns}"
62
- )
63
-
64
- # Ensure all required columns are strings
65
- self.required_columns = [str(col) for col in self.required_columns]
66
-
67
- self.mapped_columns = (
68
- f"Your config dictionary is mapped as: {', '.join(self.required_columns)}"
69
- )
70
-
71
- def get_required_columns(self) -> str:
72
- """Returns a string representation of the required columns."""
73
- return (
74
- f"{self.equation_string}"
75
- f"{self.description_string}"
76
- f"{self.required_column_description}"
77
- f"{self.mapped_columns}"
78
- )
79
-
80
- def apply(self, df: pd.DataFrame) -> pd.DataFrame:
81
- try:
82
- # Ensure all required columns are present
83
- self.check_required_columns(df)
84
-
85
- if self.troubleshoot_mode:
86
- self.troubleshoot_cols(df)
87
-
88
- # Check analog outputs [data with units of %] are floats only
89
- columns_to_check = [
90
- self.economizer_sig_col,
91
- self.cooling_sig_col,
92
- ]
93
- self.check_analog_pct(df, columns_to_check)
94
-
95
- # Create helper columns
96
- df["sat_minus_saterr_delta_supply_fan"] = (
97
- df[self.sat_col] - self.supply_degf_err_thres - self.delta_t_supply_fan
98
- )
99
- df["mat_plus_materr"] = df[self.mat_col] + self.mix_degf_err_thres
100
-
101
- df["combined_check"] = operator.or_(
102
- # OS4 AHU state clg @ min OA
103
- (df["sat_minus_saterr_delta_supply_fan"] > df["mat_plus_materr"])
104
- # verify AHU in OS4 mode
105
- & (df[self.cooling_sig_col] > 0.01)
106
- & (df[self.economizer_sig_col] == self.ahu_min_oa_dpr), # OR
107
- (df["sat_minus_saterr_delta_supply_fan"] > df["mat_plus_materr"])
108
- # verify AHU is running in OS 3 clg mode in 100 OA
109
- & (df[self.cooling_sig_col] > 0.01)
110
- & (df[self.economizer_sig_col] > 0.9),
111
- )
112
-
113
- # Rolling sum to count consecutive trues
114
- rolling_sum = (
115
- df["combined_check"].rolling(window=self.rolling_window_size).sum()
116
- )
117
- # Set flag to 1 if rolling sum equals the window size
118
- df["fc12_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
119
-
120
- if self.troubleshoot_mode:
121
- print("Troubleshoot mode enabled - not removing helper columns")
122
- sys.stdout.flush()
123
- del df["sat_minus_saterr_delta_supply_fan"]
124
- del df["mat_plus_materr"]
125
- del df["combined_check"]
126
-
127
- return df
128
-
129
- except MissingColumnError as e:
130
- print(f"Error: {e.message}")
131
- sys.stdout.flush()
132
- raise e
@@ -1,113 +0,0 @@
1
- import pandas as pd
2
- import numpy as np
3
- from open_fdd.air_handling_unit.faults.fault_condition import (
4
- FaultCondition,
5
- MissingColumnError,
6
- )
7
- import sys
8
-
9
-
10
- class FaultConditionTwo(FaultCondition):
11
- """Class provides the definitions for Fault Condition 2.
12
- Mix temperature too low; should be between outside and return air.
13
- """
14
-
15
- def __init__(self, dict_):
16
- super().__init__()
17
- self.mix_degf_err_thres = float
18
- self.return_degf_err_thres = float
19
- self.outdoor_degf_err_thres = float
20
- self.mat_col = str
21
- self.rat_col = str
22
- self.oat_col = str
23
- self.supply_vfd_speed_col = str
24
- self.troubleshoot_mode = bool # default to False
25
- self.rolling_window_size = int
26
-
27
- self.equation_string = (
28
- "fc2_flag = 1 if (MAT + εMAT < min(RAT - εRAT, OAT - εOAT)) and (VFDSPD > 0) "
29
- "for N consecutive values else 0 \n"
30
- )
31
- self.description_string = "Fault Condition 2: Mix temperature too low; should be between outside and return air \n"
32
- self.required_column_description = "Required inputs are the mix air temperature, return air temperature, outside air temperature, and supply fan VFD speed \n"
33
- self.error_string = f"One or more required columns are missing or None \n"
34
-
35
- self.set_attributes(dict_)
36
-
37
- # Set required columns specific to this fault condition
38
- self.required_columns = [
39
- self.mat_col,
40
- self.rat_col,
41
- self.oat_col,
42
- self.supply_vfd_speed_col,
43
- ]
44
-
45
- # Check if any of the required columns are None
46
- if any(col is None for col in self.required_columns):
47
- raise MissingColumnError(
48
- f"{self.error_string}"
49
- f"{self.equation_string}"
50
- f"{self.description_string}"
51
- f"{self.required_column_description}"
52
- f"{self.required_columns}"
53
- )
54
-
55
- # Ensure all required columns are strings
56
- self.required_columns = [str(col) for col in self.required_columns]
57
-
58
- self.mapped_columns = (
59
- f"Your config dictionary is mapped as: {', '.join(self.required_columns)}"
60
- )
61
-
62
- def get_required_columns(self) -> str:
63
- """Returns a string representation of the required columns."""
64
- return (
65
- f"{self.equation_string}"
66
- f"{self.description_string}"
67
- f"{self.required_column_description}"
68
- f"{self.mapped_columns}"
69
- )
70
-
71
- def apply(self, df: pd.DataFrame) -> pd.DataFrame:
72
- try:
73
- # Ensure all required columns are present
74
- self.check_required_columns(df)
75
-
76
- if self.troubleshoot_mode:
77
- self.troubleshoot_cols(df)
78
-
79
- # Check analog outputs [data with units of %] are floats only
80
- columns_to_check = [self.supply_vfd_speed_col]
81
- self.check_analog_pct(df, columns_to_check)
82
-
83
- # Fault condition-specific checks / flags
84
- df["mat_check"] = df[self.mat_col] + self.mix_degf_err_thres
85
- df["temp_min_check"] = np.minimum(
86
- df[self.rat_col] - self.return_degf_err_thres,
87
- df[self.oat_col] - self.outdoor_degf_err_thres,
88
- )
89
-
90
- df["combined_check"] = (df["mat_check"] < df["temp_min_check"]) & (
91
- df[self.supply_vfd_speed_col] > 0.01
92
- )
93
-
94
- # Rolling sum to count consecutive trues
95
- rolling_sum = (
96
- df["combined_check"].rolling(window=self.rolling_window_size).sum()
97
- )
98
- # Set flag to 1 if rolling sum equals the window size
99
- df["fc2_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
100
-
101
- if self.troubleshoot_mode:
102
- print("Troubleshoot mode enabled - not removing helper columns")
103
- sys.stdout.flush()
104
- del df["mat_check"]
105
- del df["temp_min_check"]
106
- del df["combined_check"]
107
-
108
- return df
109
-
110
- except MissingColumnError as e:
111
- print(f"Error: {e.message}")
112
- sys.stdout.flush()
113
- raise e