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,143 +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 FaultConditionFourteen(FaultCondition):
12
- """Class provides the definitions for Fault Condition 14.
13
- Temperature drop across inactive cooling coil.
14
- Requires coil leaving temp sensor.
15
- """
16
-
17
- def __init__(self, dict_):
18
- super().__init__()
19
- self.delta_t_supply_fan = float
20
- self.coil_temp_enter_err_thres = float
21
- self.coil_temp_leav_err_thres = float
22
- self.clg_coil_enter_temp_col = str
23
- self.clg_coil_leave_temp_col = str
24
- self.ahu_min_oa_dpr = float
25
- self.cooling_sig_col = str
26
- self.heating_sig_col = str
27
- self.economizer_sig_col = str
28
- self.supply_vfd_speed_col = str
29
- self.troubleshoot_mode = bool # default to False
30
- self.rolling_window_size = int
31
-
32
- self.equation_string = (
33
- "fc14_flag = 1 if ΔT_coil >= √(εcoil_enter² + εcoil_leave²) + ΔT_fan "
34
- "in inactive cooling coil mode for N consecutive values else 0 \n"
35
- )
36
- self.description_string = (
37
- "Fault Condition 14: Temperature drop across inactive cooling coil "
38
- "detected, requiring coil leaving temperature sensor \n"
39
- )
40
- self.required_column_description = (
41
- "Required inputs are the cooling coil entering temperature, cooling coil leaving temperature, "
42
- "cooling signal, heating signal, economizer signal, and supply fan VFD speed \n"
43
- )
44
- self.error_string = f"One or more required columns are missing or None \n"
45
-
46
- self.set_attributes(dict_)
47
-
48
- # Set required columns specific to this fault condition
49
- self.required_columns = [
50
- self.clg_coil_enter_temp_col,
51
- self.clg_coil_leave_temp_col,
52
- self.cooling_sig_col,
53
- self.heating_sig_col,
54
- self.economizer_sig_col,
55
- self.supply_vfd_speed_col,
56
- ]
57
-
58
- # Check if any of the required columns are None
59
- if any(col is None for col in self.required_columns):
60
- raise MissingColumnError(
61
- f"{self.error_string}"
62
- f"{self.equation_string}"
63
- f"{self.description_string}"
64
- f"{self.required_column_description}"
65
- f"{self.required_columns}"
66
- )
67
-
68
- # Ensure all required columns are strings
69
- self.required_columns = [str(col) for col in self.required_columns]
70
-
71
- self.mapped_columns = (
72
- f"Your config dictionary is mapped as: {', '.join(self.required_columns)}"
73
- )
74
-
75
- def get_required_columns(self) -> str:
76
- """Returns a string representation of the required columns."""
77
- return (
78
- f"{self.equation_string}"
79
- f"{self.description_string}"
80
- f"{self.required_column_description}"
81
- f"{self.mapped_columns}"
82
- )
83
-
84
- def apply(self, df: pd.DataFrame) -> pd.DataFrame:
85
- try:
86
- # Ensure all required columns are present
87
- self.check_required_columns(df)
88
-
89
- if self.troubleshoot_mode:
90
- self.troubleshoot_cols(df)
91
-
92
- # Check analog outputs [data with units of %] are floats only
93
- columns_to_check = [
94
- self.economizer_sig_col,
95
- self.cooling_sig_col,
96
- self.heating_sig_col,
97
- self.supply_vfd_speed_col,
98
- ]
99
- self.check_analog_pct(df, columns_to_check)
100
-
101
- # Create helper columns
102
- df["clg_delta_temp"] = (
103
- df[self.clg_coil_enter_temp_col] - df[self.clg_coil_leave_temp_col]
104
- )
105
-
106
- df["clg_delta_sqrted"] = (
107
- np.sqrt(
108
- self.coil_temp_enter_err_thres**2 + self.coil_temp_leav_err_thres**2
109
- )
110
- + self.delta_t_supply_fan
111
- )
112
-
113
- df["combined_check"] = operator.or_(
114
- (df["clg_delta_temp"] >= df["clg_delta_sqrted"])
115
- # verify AHU is in OS2 only free cooling mode
116
- & (df[self.economizer_sig_col] > self.ahu_min_oa_dpr)
117
- & (df[self.cooling_sig_col] < 0.1), # OR
118
- (df["clg_delta_temp"] >= df["clg_delta_sqrted"])
119
- # verify AHU is running in OS 1 at near full heat
120
- & (df[self.heating_sig_col] > 0.0)
121
- & (df[self.supply_vfd_speed_col] > 0.0),
122
- )
123
-
124
- # Rolling sum to count consecutive trues
125
- rolling_sum = (
126
- df["combined_check"].rolling(window=self.rolling_window_size).sum()
127
- )
128
- # Set flag to 1 if rolling sum equals the window size
129
- df["fc14_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
130
-
131
- if self.troubleshoot_mode:
132
- print("Troubleshoot mode enabled - not removing helper columns")
133
- sys.stdout.flush()
134
- del df["clg_delta_temp"]
135
- del df["clg_delta_sqrted"]
136
- del df["combined_check"]
137
-
138
- return df
139
-
140
- except MissingColumnError as e:
141
- print(f"Error: {e.message}")
142
- sys.stdout.flush()
143
- raise e
@@ -1,128 +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 FaultConditionNine(FaultCondition):
11
- """Class provides the definitions for Fault Condition 9.
12
- Outside air temperature too high in free cooling without
13
- additional mechanical cooling in economizer mode.
14
- """
15
-
16
- def __init__(self, dict_):
17
- super().__init__()
18
- self.delta_t_supply_fan = float
19
- self.outdoor_degf_err_thres = float
20
- self.supply_degf_err_thres = float
21
- self.ahu_min_oa_dpr = float
22
- self.sat_setpoint_col = str
23
- self.oat_col = str
24
- self.cooling_sig_col = str
25
- self.economizer_sig_col = str
26
- self.troubleshoot_mode = bool # default should be False
27
- self.rolling_window_size = int
28
-
29
- self.equation_string = (
30
- "fc9_flag = 1 if OAT > (SATSP - ΔT_fan + εSAT) "
31
- "in free cooling mode for N consecutive values else 0 \n"
32
- )
33
- self.description_string = (
34
- "Fault Condition 9: Outside air temperature too high in free cooling mode "
35
- "without additional mechanical cooling in economizer mode \n"
36
- )
37
- self.required_column_description = (
38
- "Required inputs are the supply air temperature setpoint, outside air temperature, "
39
- "cooling signal, and economizer signal \n"
40
- )
41
- self.error_string = f"One or more required columns are missing or None \n"
42
-
43
- self.set_attributes(dict_)
44
-
45
- # Set required columns specific to this fault condition
46
- self.required_columns = [
47
- self.sat_setpoint_col,
48
- self.oat_col,
49
- self.cooling_sig_col,
50
- self.economizer_sig_col,
51
- ]
52
-
53
- # Check if any of the required columns are None
54
- if any(col is None for col in self.required_columns):
55
- raise MissingColumnError(
56
- f"{self.error_string}"
57
- f"{self.equation_string}"
58
- f"{self.description_string}"
59
- f"{self.required_column_description}"
60
- f"{self.required_columns}"
61
- )
62
-
63
- # Ensure all required columns are strings
64
- self.required_columns = [str(col) for col in self.required_columns]
65
-
66
- self.mapped_columns = (
67
- f"Your config dictionary is mapped as: {', '.join(self.required_columns)}"
68
- )
69
-
70
- def get_required_columns(self) -> str:
71
- """Returns a string representation of the required columns."""
72
- return (
73
- f"{self.equation_string}"
74
- f"{self.description_string}"
75
- f"{self.required_column_description}"
76
- f"{self.mapped_columns}"
77
- )
78
-
79
- def apply(self, df: pd.DataFrame) -> pd.DataFrame:
80
- try:
81
- # Ensure all required columns are present
82
- self.check_required_columns(df)
83
-
84
- if self.troubleshoot_mode:
85
- self.troubleshoot_cols(df)
86
-
87
- # Check analog outputs [data with units of %] are floats only
88
- columns_to_check = [
89
- self.economizer_sig_col,
90
- self.cooling_sig_col,
91
- ]
92
- self.check_analog_pct(df, columns_to_check)
93
-
94
- # Create helper columns
95
- df["oat_minus_oaterror"] = df[self.oat_col] - self.outdoor_degf_err_thres
96
- df["satsp_delta_saterr"] = (
97
- df[self.sat_setpoint_col]
98
- - self.delta_t_supply_fan
99
- + self.supply_degf_err_thres
100
- )
101
-
102
- df["combined_check"] = (
103
- (df["oat_minus_oaterror"] > df["satsp_delta_saterr"])
104
- # verify AHU is in OS2 only free cooling mode
105
- & (df[self.economizer_sig_col] > self.ahu_min_oa_dpr)
106
- & (df[self.cooling_sig_col] < 0.1)
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["fc9_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["oat_minus_oaterror"]
120
- del df["satsp_delta_saterr"]
121
- del df["combined_check"]
122
-
123
- return df
124
-
125
- except MissingColumnError as e:
126
- print(f"Error: {e.message}")
127
- sys.stdout.flush()
128
- raise e
@@ -1,112 +0,0 @@
1
- import pandas as pd
2
- from open_fdd.air_handling_unit.faults.fault_condition import (
3
- FaultCondition,
4
- MissingColumnError,
5
- )
6
- import sys
7
-
8
-
9
- class FaultConditionOne(FaultCondition):
10
- """Class provides the definitions for Fault Condition 1.
11
- AHU low duct static pressure fan fault.
12
- """
13
-
14
- def __init__(self, dict_):
15
- super().__init__()
16
- self.vfd_speed_percent_err_thres = float
17
- self.vfd_speed_percent_max = float
18
- self.duct_static_inches_err_thres = float
19
- self.duct_static_col = str
20
- self.supply_vfd_speed_col = str
21
- self.duct_static_setpoint_col = str
22
- self.troubleshoot_mode = bool # default should be False
23
- self.rolling_window_size = int
24
-
25
- self.equation_string = "fc1_flag = 1 if (DSP < DPSP - εDSP) and (VFDSPD >= VFDSPD_max - εVFDSPD) for N consecutive values else 0 \n"
26
- self.description_string = (
27
- "Fault Condition 1: Duct static too low at fan at full speed \n"
28
- )
29
- self.required_column_description = "Required inputs are the duct static pressure, setpoint, and supply fan VFD speed \n"
30
- self.error_string = f"One or more required columns are missing or None \n"
31
-
32
- self.set_attributes(dict_)
33
-
34
- # Set required columns specific to this fault condition manually
35
- self.required_columns = [
36
- self.duct_static_col,
37
- self.supply_vfd_speed_col,
38
- self.duct_static_setpoint_col,
39
- ]
40
-
41
- # Check if any of the required columns are None
42
- if any(col is None for col in self.required_columns):
43
- raise MissingColumnError(
44
- f"{self.error_string}"
45
- f"{self.equation_string}"
46
- f"{self.description_string}"
47
- f"{self.required_column_description}"
48
- f"{self.required_columns}"
49
- )
50
-
51
- # Ensure all required columns are strings
52
- self.required_columns = [str(col) for col in self.required_columns]
53
-
54
- self.mapped_columns = (
55
- f"Your config dictionary is mapped as: {', '.join(self.required_columns)}"
56
- )
57
-
58
- def get_required_columns(self) -> str:
59
- """called from IPython to print out"""
60
- return (
61
- f"{self.equation_string}"
62
- f"{self.description_string}"
63
- f"{self.required_column_description}"
64
- f"{self.mapped_columns}"
65
- )
66
-
67
- def apply(self, df: pd.DataFrame) -> pd.DataFrame:
68
- try:
69
- # Ensure all required columns are present
70
- self.check_required_columns(df)
71
-
72
- if self.troubleshoot_mode:
73
- self.troubleshoot_cols(df)
74
-
75
- # Check analog outputs [data with units of %] are floats only
76
- columns_to_check = [self.supply_vfd_speed_col]
77
- self.check_analog_pct(df, columns_to_check)
78
-
79
- df["static_check_"] = (
80
- df[self.duct_static_col]
81
- < df[self.duct_static_setpoint_col] - self.duct_static_inches_err_thres
82
- )
83
- df["fan_check_"] = (
84
- df[self.supply_vfd_speed_col]
85
- >= self.vfd_speed_percent_max - self.vfd_speed_percent_err_thres
86
- )
87
-
88
- # Combined condition check
89
- df["combined_check"] = df["static_check_"] & df["fan_check_"]
90
-
91
- # Rolling sum to count consecutive trues
92
- rolling_sum = (
93
- df["combined_check"].rolling(window=self.rolling_window_size).sum()
94
- )
95
- # Set flag to 1 if rolling sum equals the window size
96
- df["fc1_flag"] = (rolling_sum == self.rolling_window_size).astype(int)
97
-
98
- if self.troubleshoot_mode:
99
- print("Troubleshoot mode enabled - not removing helper columns")
100
- sys.stdout.flush()
101
-
102
- # Optionally remove temporary columns
103
- df.drop(
104
- columns=["static_check_", "fan_check_", "combined_check"], inplace=True
105
- )
106
-
107
- return df
108
-
109
- except MissingColumnError as e:
110
- print(f"Error: {e.message}")
111
- sys.stdout.flush()
112
- raise e
@@ -1,114 +0,0 @@
1
- import pandas as pd
2
- from open_fdd.air_handling_unit.faults.fault_condition import (
3
- FaultCondition,
4
- MissingColumnError,
5
- )
6
- import sys
7
-
8
-
9
- class FaultConditionSeven(FaultCondition):
10
- """Class provides the definitions for Fault Condition 7.
11
- Very similar to FC 13 but uses heating valve.
12
- Supply air temperature too low in full heating.
13
- """
14
-
15
- def __init__(self, dict_):
16
- super().__init__()
17
- self.supply_degf_err_thres = float
18
- self.sat_col = str
19
- self.sat_setpoint_col = str
20
- self.heating_sig_col = str
21
- self.supply_vfd_speed_col = str
22
- self.troubleshoot_mode = bool # default to False
23
- self.rolling_window_size = int
24
-
25
- self.equation_string = (
26
- "fc7_flag = 1 if SAT < (SATSP - εSAT) in full heating mode "
27
- "and VFD speed > 0 for N consecutive values else 0 \n"
28
- )
29
- self.description_string = (
30
- "Fault Condition 7: Supply air temperature too low in full heating mode "
31
- "with heating valve fully open \n"
32
- )
33
- self.required_column_description = (
34
- "Required inputs are the supply air temperature, supply air temperature setpoint, "
35
- "heating signal, and supply fan VFD speed \n"
36
- )
37
- self.error_string = f"One or more required columns are missing or None \n"
38
-
39
- self.set_attributes(dict_)
40
-
41
- # Set required columns specific to this fault condition
42
- self.required_columns = [
43
- self.sat_col,
44
- self.sat_setpoint_col,
45
- self.heating_sig_col,
46
- self.supply_vfd_speed_col,
47
- ]
48
-
49
- # Check if any of the required columns are None
50
- if any(col is None for col in self.required_columns):
51
- raise MissingColumnError(
52
- f"{self.error_string}"
53
- f"{self.equation_string}"
54
- f"{self.description_string}"
55
- f"{self.required_column_description}"
56
- f"{self.required_columns}"
57
- )
58
-
59
- # Ensure all required columns are strings
60
- self.required_columns = [str(col) for col in self.required_columns]
61
-
62
- self.mapped_columns = (
63
- f"Your config dictionary is mapped as: {', '.join(self.required_columns)}"
64
- )
65
-
66
- def get_required_columns(self) -> str:
67
- """Returns a string representation of the required columns."""
68
- return (
69
- f"{self.equation_string}"
70
- f"{self.description_string}"
71
- f"{self.required_column_description}"
72
- f"{self.mapped_columns}"
73
- )
74
-
75
- def apply(self, df: pd.DataFrame) -> pd.DataFrame:
76
- try:
77
- # Ensure all required columns are present
78
- self.check_required_columns(df)
79
-
80
- if self.troubleshoot_mode:
81
- self.troubleshoot_cols(df)
82
-
83
- # Check analog outputs [data with units of %] are floats only
84
- columns_to_check = [self.supply_vfd_speed_col, self.heating_sig_col]
85
- self.check_analog_pct(df, columns_to_check)
86
-
87
- # Fault condition-specific checks / flags
88
- df["sat_check"] = df[self.sat_setpoint_col] - self.supply_degf_err_thres
89
-
90
- df["combined_check"] = (
91
- (df[self.sat_col] < df["sat_check"])
92
- & (df[self.heating_sig_col] > 0.9)
93
- & (df[self.supply_vfd_speed_col] > 0)
94
- )
95
-
96
- # Rolling sum to count consecutive trues
97
- rolling_sum = (
98
- df["combined_check"].rolling(window=self.rolling_window_size).sum()
99
- )
100
- # Set flag to 1 if rolling sum equals the window size
101
- df["fc7_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
102
-
103
- if self.troubleshoot_mode:
104
- print("Troubleshoot mode enabled - not removing helper columns")
105
- sys.stdout.flush()
106
- del df["sat_check"]
107
- del df["combined_check"]
108
-
109
- return df
110
-
111
- except MissingColumnError as e:
112
- print(f"Error: {e.message}")
113
- sys.stdout.flush()
114
- raise e
@@ -1,181 +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 FaultConditionSix(FaultCondition):
11
- """Class provides the definitions for Fault Condition 6.
12
-
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.
23
- """
24
-
25
- def __init__(self, dict_):
26
- super().__init__()
27
- self.airflow_err_thres = float
28
- self.ahu_min_oa_cfm_design = float
29
- self.outdoor_degf_err_thres = float
30
- self.return_degf_err_thres = float
31
- self.oat_rat_delta_min = float
32
- self.ahu_min_oa_dpr = float
33
- self.supply_fan_air_volume_col = str
34
- self.mat_col = str
35
- self.oat_col = str
36
- self.rat_col = str
37
- self.supply_vfd_speed_col = str
38
- self.economizer_sig_col = str
39
- self.heating_sig_col = str
40
- self.cooling_sig_col = str
41
- self.troubleshoot_mode = bool # default should be False
42
- self.rolling_window_size = int
43
-
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"
58
-
59
- self.set_attributes(dict_)
60
-
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,
67
- self.supply_vfd_speed_col,
68
- self.economizer_sig_col,
69
- self.heating_sig_col,
70
- self.cooling_sig_col,
71
- ]
72
-
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
- )
82
-
83
- # Ensure all required columns are strings
84
- self.required_columns = [str(col) for col in self.required_columns]
85
-
86
- self.mapped_columns = (
87
- f"Your config dictionary is mapped as: {', '.join(self.required_columns)}"
88
- )
89
-
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}"
97
- )
98
-
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
127
- )
128
-
129
- df["perc_OAmin"] = (
130
- self.ahu_min_oa_cfm_design / df[self.supply_fan_air_volume_col]
131
- )
132
-
133
- df["percent_oa_calc_minus_perc_OAmin"] = abs(
134
- df["percent_oa_calc"] - df["perc_OAmin"]
135
- )
136
-
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
- )
159
-
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