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.
- open_fdd/air_handling_unit/faults/__init__.py +331 -300
- open_fdd/air_handling_unit/faults/helper_utils.py +3 -0
- open_fdd/air_handling_unit/faults/shared_utils.py +16 -1
- open_fdd/air_handling_unit/reports/__init__.py +107 -0
- open_fdd/air_handling_unit/reports/fault_report.py +1 -0
- open_fdd/tests/ahu/test_ahu_fc1.py +1 -0
- open_fdd/tests/ahu/test_ahu_fc16.py +205 -0
- {open_fdd-0.1.5.dist-info → open_fdd-0.1.7.dist-info}/METADATA +4 -3
- open_fdd-0.1.7.dist-info/RECORD +31 -0
- {open_fdd-0.1.5.dist-info → open_fdd-0.1.7.dist-info}/WHEEL +1 -1
- open_fdd/air_handling_unit/faults/fault_condition_eight.py +0 -127
- open_fdd/air_handling_unit/faults/fault_condition_eleven.py +0 -126
- open_fdd/air_handling_unit/faults/fault_condition_fifteen.py +0 -152
- open_fdd/air_handling_unit/faults/fault_condition_five.py +0 -123
- open_fdd/air_handling_unit/faults/fault_condition_four.py +0 -168
- open_fdd/air_handling_unit/faults/fault_condition_fourteen.py +0 -143
- open_fdd/air_handling_unit/faults/fault_condition_nine.py +0 -128
- open_fdd/air_handling_unit/faults/fault_condition_one.py +0 -112
- open_fdd/air_handling_unit/faults/fault_condition_seven.py +0 -114
- open_fdd/air_handling_unit/faults/fault_condition_six.py +0 -181
- open_fdd/air_handling_unit/faults/fault_condition_ten.py +0 -123
- open_fdd/air_handling_unit/faults/fault_condition_thirteen.py +0 -127
- open_fdd/air_handling_unit/faults/fault_condition_three.py +0 -113
- open_fdd/air_handling_unit/faults/fault_condition_twelve.py +0 -132
- open_fdd/air_handling_unit/faults/fault_condition_two.py +0 -113
- open_fdd/air_handling_unit/images/ahu1_fc1_2024-06_1.jpg +0 -0
- open_fdd/air_handling_unit/images/ahu1_fc1_2024-06_2.jpg +0 -0
- open_fdd/air_handling_unit/images/example1.jpg +0 -0
- open_fdd/air_handling_unit/images/example2.jpg +0 -0
- open_fdd/air_handling_unit/images/fc10_definition.png +0 -0
- open_fdd/air_handling_unit/images/fc11_definition.png +0 -0
- open_fdd/air_handling_unit/images/fc12_definition.png +0 -0
- open_fdd/air_handling_unit/images/fc13_definition.png +0 -0
- open_fdd/air_handling_unit/images/fc1_definition.png +0 -0
- open_fdd/air_handling_unit/images/fc1_report_screenshot_all.png +0 -0
- open_fdd/air_handling_unit/images/fc2_definition.png +0 -0
- open_fdd/air_handling_unit/images/fc3_definition.png +0 -0
- open_fdd/air_handling_unit/images/fc4_definition.png +0 -0
- open_fdd/air_handling_unit/images/fc5_definition.png +0 -0
- open_fdd/air_handling_unit/images/fc6_definition.png +0 -0
- open_fdd/air_handling_unit/images/fc7_definition.png +0 -0
- open_fdd/air_handling_unit/images/fc8_definition.png +0 -0
- open_fdd/air_handling_unit/images/fc9_definition.png +0 -0
- open_fdd/air_handling_unit/images/latex_generator.py +0 -175
- open_fdd/air_handling_unit/images/params.docx +0 -0
- open_fdd/air_handling_unit/images/params.pdf +0 -0
- open_fdd/air_handling_unit/images/plot_for_repo.png +0 -0
- open_fdd/air_handling_unit/reports/base_report.py +0 -47
- open_fdd/air_handling_unit/reports/report_fc1.py +0 -115
- open_fdd/air_handling_unit/reports/report_fc10.py +0 -126
- open_fdd/air_handling_unit/reports/report_fc11.py +0 -128
- open_fdd/air_handling_unit/reports/report_fc12.py +0 -126
- open_fdd/air_handling_unit/reports/report_fc13.py +0 -126
- open_fdd/air_handling_unit/reports/report_fc14.py +0 -124
- open_fdd/air_handling_unit/reports/report_fc15.py +0 -124
- open_fdd/air_handling_unit/reports/report_fc2.py +0 -119
- open_fdd/air_handling_unit/reports/report_fc3.py +0 -119
- open_fdd/air_handling_unit/reports/report_fc4.py +0 -148
- open_fdd/air_handling_unit/reports/report_fc5.py +0 -132
- open_fdd/air_handling_unit/reports/report_fc6.py +0 -156
- open_fdd/air_handling_unit/reports/report_fc7.py +0 -126
- open_fdd/air_handling_unit/reports/report_fc8.py +0 -118
- open_fdd/air_handling_unit/reports/report_fc9.py +0 -120
- open_fdd-0.1.5.dist-info/RECORD +0 -83
- {open_fdd-0.1.5.dist-info → open_fdd-0.1.7.dist-info}/LICENSE +0 -0
- {open_fdd-0.1.5.dist-info → open_fdd-0.1.7.dist-info}/top_level.txt +0 -0
@@ -1,126 +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 FaultConditionEleven(FaultCondition):
|
10
|
-
"""Class provides the definitions for Fault Condition 11.
|
11
|
-
Outside air temperature too low for 100% outdoor
|
12
|
-
air cooling in economizer cooling mode.
|
13
|
-
Economizer performance fault
|
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.sat_setpoint_col = str
|
22
|
-
self.oat_col = str
|
23
|
-
self.cooling_sig_col = str
|
24
|
-
self.economizer_sig_col = str
|
25
|
-
self.troubleshoot_mode = bool # default False
|
26
|
-
self.rolling_window_size = int
|
27
|
-
|
28
|
-
self.equation_string = (
|
29
|
-
"fc11_flag = 1 if OAT < (SATSP - ΔT_fan - εSAT) in "
|
30
|
-
"economizer cooling mode for N consecutive values else 0 \n"
|
31
|
-
)
|
32
|
-
self.description_string = (
|
33
|
-
"Fault Condition 11: Outside air temperature too low for 100% outdoor air cooling "
|
34
|
-
"in economizer cooling mode (Economizer performance fault) \n"
|
35
|
-
)
|
36
|
-
self.required_column_description = (
|
37
|
-
"Required inputs are the supply air temperature setpoint, outside air temperature, "
|
38
|
-
"cooling signal, and economizer signal \n"
|
39
|
-
)
|
40
|
-
self.error_string = f"One or more required columns are missing or None \n"
|
41
|
-
|
42
|
-
self.set_attributes(dict_)
|
43
|
-
|
44
|
-
# Set required columns specific to this fault condition
|
45
|
-
self.required_columns = [
|
46
|
-
self.sat_setpoint_col,
|
47
|
-
self.oat_col,
|
48
|
-
self.cooling_sig_col,
|
49
|
-
self.economizer_sig_col,
|
50
|
-
]
|
51
|
-
|
52
|
-
# Check if any of the required columns are None
|
53
|
-
if any(col is None for col in self.required_columns):
|
54
|
-
raise MissingColumnError(
|
55
|
-
f"{self.error_string}"
|
56
|
-
f"{self.equation_string}"
|
57
|
-
f"{self.description_string}"
|
58
|
-
f"{self.required_column_description}"
|
59
|
-
f"{self.required_columns}"
|
60
|
-
)
|
61
|
-
|
62
|
-
# Ensure all required columns are strings
|
63
|
-
self.required_columns = [str(col) for col in self.required_columns]
|
64
|
-
|
65
|
-
self.mapped_columns = (
|
66
|
-
f"Your config dictionary is mapped as: {', '.join(self.required_columns)}"
|
67
|
-
)
|
68
|
-
|
69
|
-
def get_required_columns(self) -> str:
|
70
|
-
"""Returns a string representation of the required columns."""
|
71
|
-
return (
|
72
|
-
f"{self.equation_string}"
|
73
|
-
f"{self.description_string}"
|
74
|
-
f"{self.required_column_description}"
|
75
|
-
f"{self.mapped_columns}"
|
76
|
-
)
|
77
|
-
|
78
|
-
def apply(self, df: pd.DataFrame) -> pd.DataFrame:
|
79
|
-
try:
|
80
|
-
# Ensure all required columns are present
|
81
|
-
self.check_required_columns(df)
|
82
|
-
|
83
|
-
if self.troubleshoot_mode:
|
84
|
-
self.troubleshoot_cols(df)
|
85
|
-
|
86
|
-
# Check analog outputs [data with units of %] are floats only
|
87
|
-
columns_to_check = [
|
88
|
-
self.economizer_sig_col,
|
89
|
-
self.cooling_sig_col,
|
90
|
-
]
|
91
|
-
self.check_analog_pct(df, columns_to_check)
|
92
|
-
|
93
|
-
df["oat_plus_oaterror"] = df[self.oat_col] + self.outdoor_degf_err_thres
|
94
|
-
df["satsp_delta_saterr"] = (
|
95
|
-
df[self.sat_setpoint_col]
|
96
|
-
- self.delta_t_supply_fan
|
97
|
-
- self.supply_degf_err_thres
|
98
|
-
)
|
99
|
-
|
100
|
-
df["combined_check"] = (
|
101
|
-
(df["oat_plus_oaterror"] < df["satsp_delta_saterr"])
|
102
|
-
# verify ahu is running in OS 3 clg mode in 100 OA
|
103
|
-
& (df[self.cooling_sig_col] > 0.01)
|
104
|
-
& (df[self.economizer_sig_col] > 0.9)
|
105
|
-
)
|
106
|
-
|
107
|
-
# Rolling sum to count consecutive trues
|
108
|
-
rolling_sum = (
|
109
|
-
df["combined_check"].rolling(window=self.rolling_window_size).sum()
|
110
|
-
)
|
111
|
-
# Set flag to 1 if rolling sum equals the window size
|
112
|
-
df["fc11_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
|
113
|
-
|
114
|
-
if self.troubleshoot_mode:
|
115
|
-
print("Troubleshoot mode enabled - not removing helper columns")
|
116
|
-
sys.stdout.flush()
|
117
|
-
del df["oat_plus_oaterror"]
|
118
|
-
del df["satsp_delta_saterr"]
|
119
|
-
del df["combined_check"]
|
120
|
-
|
121
|
-
return df
|
122
|
-
|
123
|
-
except MissingColumnError as e:
|
124
|
-
print(f"Error: {e.message}")
|
125
|
-
sys.stdout.flush()
|
126
|
-
raise e
|
@@ -1,152 +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 FaultConditionFifteen(FaultCondition):
|
11
|
-
"""Class provides the definitions for Fault Condition 15.
|
12
|
-
Temperature rise across inactive heating coil.
|
13
|
-
Requires coil leaving temp sensor.
|
14
|
-
"""
|
15
|
-
|
16
|
-
def __init__(self, dict_):
|
17
|
-
super().__init__()
|
18
|
-
self.delta_supply_fan = float
|
19
|
-
self.coil_temp_enter_err_thres = float
|
20
|
-
self.coil_temp_leav_err_thres = float
|
21
|
-
self.htg_coil_enter_temp_col = str
|
22
|
-
self.htg_coil_leave_temp_col = str
|
23
|
-
self.ahu_min_oa_dpr = float
|
24
|
-
self.cooling_sig_col = str
|
25
|
-
self.heating_sig_col = str
|
26
|
-
self.economizer_sig_col = str
|
27
|
-
self.supply_vfd_speed_col = str
|
28
|
-
self.troubleshoot_mode = bool # default to False
|
29
|
-
self.rolling_window_size = int
|
30
|
-
|
31
|
-
self.equation_string = (
|
32
|
-
"fc15_flag = 1 if ΔT_coil >= √(εcoil_enter² + εcoil_leave²) + ΔT_fan "
|
33
|
-
"in inactive heating coil mode for N consecutive values else 0 \n"
|
34
|
-
)
|
35
|
-
self.description_string = (
|
36
|
-
"Fault Condition 15: Temperature rise across inactive heating coil "
|
37
|
-
"detected, requiring coil leaving temperature sensor \n"
|
38
|
-
)
|
39
|
-
self.required_column_description = (
|
40
|
-
"Required inputs are the heating coil entering temperature, heating coil leaving temperature, "
|
41
|
-
"cooling signal, heating signal, economizer signal, and supply fan VFD speed \n"
|
42
|
-
)
|
43
|
-
self.error_string = f"One or more required columns are missing or None \n"
|
44
|
-
|
45
|
-
self.set_attributes(dict_)
|
46
|
-
|
47
|
-
# Set required columns specific to this fault condition
|
48
|
-
self.required_columns = [
|
49
|
-
self.htg_coil_enter_temp_col,
|
50
|
-
self.htg_coil_leave_temp_col,
|
51
|
-
self.cooling_sig_col,
|
52
|
-
self.heating_sig_col,
|
53
|
-
self.economizer_sig_col,
|
54
|
-
self.supply_vfd_speed_col,
|
55
|
-
]
|
56
|
-
|
57
|
-
# Check if any of the required columns are None
|
58
|
-
if any(col is None for col in self.required_columns):
|
59
|
-
raise MissingColumnError(
|
60
|
-
f"{self.error_string}"
|
61
|
-
f"{self.equation_string}"
|
62
|
-
f"{self.description_string}"
|
63
|
-
f"{self.required_column_description}"
|
64
|
-
f"{self.required_columns}"
|
65
|
-
)
|
66
|
-
|
67
|
-
# Ensure all required columns are strings
|
68
|
-
self.required_columns = [str(col) for col in self.required_columns]
|
69
|
-
|
70
|
-
self.mapped_columns = (
|
71
|
-
f"Your config dictionary is mapped as: {', '.join(self.required_columns)}"
|
72
|
-
)
|
73
|
-
|
74
|
-
def get_required_columns(self) -> str:
|
75
|
-
"""Returns a string representation of the required columns."""
|
76
|
-
return (
|
77
|
-
f"{self.equation_string}"
|
78
|
-
f"{self.description_string}"
|
79
|
-
f"{self.required_column_description}"
|
80
|
-
f"{self.mapped_columns}"
|
81
|
-
)
|
82
|
-
|
83
|
-
def apply(self, df: pd.DataFrame) -> pd.DataFrame:
|
84
|
-
try:
|
85
|
-
# Ensure all required columns are present
|
86
|
-
self.check_required_columns(df)
|
87
|
-
|
88
|
-
if self.troubleshoot_mode:
|
89
|
-
self.troubleshoot_cols(df)
|
90
|
-
|
91
|
-
# Check analog outputs [data with units of %] are floats only
|
92
|
-
columns_to_check = [
|
93
|
-
self.economizer_sig_col,
|
94
|
-
self.cooling_sig_col,
|
95
|
-
self.heating_sig_col,
|
96
|
-
self.supply_vfd_speed_col,
|
97
|
-
]
|
98
|
-
self.check_analog_pct(df, columns_to_check)
|
99
|
-
|
100
|
-
# Create helper columns
|
101
|
-
df["htg_delta_temp"] = (
|
102
|
-
df[self.htg_coil_leave_temp_col] - df[self.htg_coil_enter_temp_col]
|
103
|
-
)
|
104
|
-
|
105
|
-
df["htg_delta_sqrted"] = (
|
106
|
-
np.sqrt(
|
107
|
-
self.coil_temp_enter_err_thres**2 + self.coil_temp_leav_err_thres**2
|
108
|
-
)
|
109
|
-
+ self.delta_supply_fan
|
110
|
-
)
|
111
|
-
|
112
|
-
df["combined_check"] = (
|
113
|
-
(
|
114
|
-
(df["htg_delta_temp"] >= df["htg_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)
|
118
|
-
)
|
119
|
-
| (
|
120
|
-
(df["htg_delta_temp"] >= df["htg_delta_sqrted"])
|
121
|
-
# OS4 AHU state clg @ min OA
|
122
|
-
& (df[self.cooling_sig_col] > 0.01)
|
123
|
-
& (df[self.economizer_sig_col] == self.ahu_min_oa_dpr)
|
124
|
-
)
|
125
|
-
| (
|
126
|
-
(df["htg_delta_temp"] >= df["htg_delta_sqrted"])
|
127
|
-
# verify AHU is running in OS 3 clg mode in 100 OA
|
128
|
-
& (df[self.cooling_sig_col] > 0.01)
|
129
|
-
& (df[self.economizer_sig_col] > 0.9)
|
130
|
-
)
|
131
|
-
)
|
132
|
-
|
133
|
-
# Rolling sum to count consecutive trues
|
134
|
-
rolling_sum = (
|
135
|
-
df["combined_check"].rolling(window=self.rolling_window_size).sum()
|
136
|
-
)
|
137
|
-
# Set flag to 1 if rolling sum equals the window size
|
138
|
-
df["fc15_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
|
139
|
-
|
140
|
-
if self.troubleshoot_mode:
|
141
|
-
print("Troubleshoot mode enabled - not removing helper columns")
|
142
|
-
sys.stdout.flush()
|
143
|
-
del df["htg_delta_temp"]
|
144
|
-
del df["htg_delta_sqrted"]
|
145
|
-
del df["combined_check"]
|
146
|
-
|
147
|
-
return df
|
148
|
-
|
149
|
-
except MissingColumnError as e:
|
150
|
-
print(f"Error: {e.message}")
|
151
|
-
sys.stdout.flush()
|
152
|
-
raise e
|
@@ -1,123 +0,0 @@
|
|
1
|
-
import pandas as pd
|
2
|
-
import pandas.api.types as pdtypes
|
3
|
-
from open_fdd.air_handling_unit.faults.fault_condition import (
|
4
|
-
FaultCondition,
|
5
|
-
MissingColumnError,
|
6
|
-
)
|
7
|
-
import sys
|
8
|
-
|
9
|
-
|
10
|
-
class FaultConditionFive(FaultCondition):
|
11
|
-
"""Class provides the definitions for Fault Condition 5.
|
12
|
-
SAT too low; should be higher than MAT in HTG MODE
|
13
|
-
--Broken heating valve or other mechanical issue
|
14
|
-
related to heat valve not working as designed
|
15
|
-
"""
|
16
|
-
|
17
|
-
def __init__(self, dict_):
|
18
|
-
super().__init__()
|
19
|
-
self.mix_degf_err_thres = float
|
20
|
-
self.supply_degf_err_thres = float
|
21
|
-
self.delta_t_supply_fan = float
|
22
|
-
self.mat_col = str
|
23
|
-
self.sat_col = str
|
24
|
-
self.heating_sig_col = str
|
25
|
-
self.supply_vfd_speed_col = str
|
26
|
-
self.troubleshoot_mode = bool # default to False
|
27
|
-
self.rolling_window_size = int
|
28
|
-
|
29
|
-
self.equation_string = (
|
30
|
-
"fc5_flag = 1 if (SAT + εSAT <= MAT - εMAT + ΔT_supply_fan) and "
|
31
|
-
"(heating signal > 0) and (VFDSPD > 0) for N consecutive values else 0 \n"
|
32
|
-
)
|
33
|
-
self.description_string = (
|
34
|
-
"Fault Condition 5: SAT too low; should be higher than MAT in HTG MODE, "
|
35
|
-
"potential broken heating valve or mechanical issue \n"
|
36
|
-
)
|
37
|
-
self.required_column_description = (
|
38
|
-
"Required inputs are the mixed air temperature, supply air temperature, "
|
39
|
-
"heating signal, and supply fan VFD speed \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.mat_col,
|
48
|
-
self.sat_col,
|
49
|
-
self.heating_sig_col,
|
50
|
-
self.supply_vfd_speed_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 = [self.supply_vfd_speed_col, self.heating_sig_col]
|
89
|
-
|
90
|
-
for col in columns_to_check:
|
91
|
-
self.check_analog_pct(df, [col])
|
92
|
-
|
93
|
-
df["sat_check"] = df[self.sat_col] + self.supply_degf_err_thres
|
94
|
-
df["mat_check"] = (
|
95
|
-
df[self.mat_col] - self.mix_degf_err_thres + self.delta_t_supply_fan
|
96
|
-
)
|
97
|
-
|
98
|
-
df["combined_check"] = (
|
99
|
-
(df["sat_check"] <= df["mat_check"])
|
100
|
-
& (df[self.heating_sig_col] > 0.01)
|
101
|
-
& (df[self.supply_vfd_speed_col] > 0.01)
|
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["fc5_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["mat_check"]
|
115
|
-
del df["sat_check"]
|
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,168 +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 FaultConditionFour(FaultCondition):
|
10
|
-
"""Class provides the definitions for Fault Condition 4.
|
11
|
-
|
12
|
-
This fault flags excessive operating states on the AHU
|
13
|
-
if it's hunting between heating, econ, econ+mech, and
|
14
|
-
a mech clg modes. The code counts how many operating
|
15
|
-
changes in an hour and will throw a fault if there is
|
16
|
-
excessive OS changes to flag control sys hunting.
|
17
|
-
"""
|
18
|
-
|
19
|
-
def __init__(self, dict_):
|
20
|
-
super().__init__()
|
21
|
-
self.delta_os_max = float
|
22
|
-
self.ahu_min_oa_dpr = float
|
23
|
-
self.economizer_sig_col = str
|
24
|
-
self.heating_sig_col = str
|
25
|
-
self.cooling_sig_col = str
|
26
|
-
self.supply_vfd_speed_col = str
|
27
|
-
self.troubleshoot_mode = bool # default to False
|
28
|
-
|
29
|
-
self.equation_string = (
|
30
|
-
"fc4_flag = 1 if excessive mode changes (> δOS_max) occur "
|
31
|
-
"within an hour across heating, econ, econ+mech, mech clg, and min OA modes \n"
|
32
|
-
)
|
33
|
-
self.description_string = "Fault Condition 4: Excessive AHU operating state changes detected (hunting behavior) \n"
|
34
|
-
self.required_column_description = (
|
35
|
-
"Required inputs are the economizer signal, supply fan VFD speed, "
|
36
|
-
"and optionally heating and cooling signals \n"
|
37
|
-
)
|
38
|
-
self.error_string = f"One or more required columns are missing or None \n"
|
39
|
-
|
40
|
-
self.set_attributes(dict_)
|
41
|
-
|
42
|
-
# Set required columns, making heating and cooling optional
|
43
|
-
self.required_columns = [
|
44
|
-
self.economizer_sig_col,
|
45
|
-
self.supply_vfd_speed_col,
|
46
|
-
]
|
47
|
-
|
48
|
-
# If heating or cooling columns are provided, add them to the required columns
|
49
|
-
if self.heating_sig_col:
|
50
|
-
self.required_columns.append(self.heating_sig_col)
|
51
|
-
if self.cooling_sig_col:
|
52
|
-
self.required_columns.append(self.cooling_sig_col)
|
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
|
-
# 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 the optional columns are not present, create them with all values set to 0.0
|
85
|
-
if self.heating_sig_col not in df.columns:
|
86
|
-
df[self.heating_sig_col] = 0.0
|
87
|
-
if self.cooling_sig_col not in df.columns:
|
88
|
-
df[self.cooling_sig_col] = 0.0
|
89
|
-
|
90
|
-
if self.troubleshoot_mode:
|
91
|
-
self.troubleshoot_cols(df)
|
92
|
-
|
93
|
-
# Check analog outputs [data with units of %] are floats only
|
94
|
-
columns_to_check = [
|
95
|
-
self.economizer_sig_col,
|
96
|
-
self.heating_sig_col,
|
97
|
-
self.cooling_sig_col,
|
98
|
-
self.supply_vfd_speed_col,
|
99
|
-
]
|
100
|
-
|
101
|
-
for col in columns_to_check:
|
102
|
-
self.check_analog_pct(df, [col])
|
103
|
-
|
104
|
-
print("=" * 50)
|
105
|
-
print("Warning: The program is in FC4 and resampling the data")
|
106
|
-
print("to compute AHU OS state changes per hour")
|
107
|
-
print("to flag any hunting issue")
|
108
|
-
print("and this usually takes a while to run...")
|
109
|
-
print("=" * 50)
|
110
|
-
|
111
|
-
sys.stdout.flush()
|
112
|
-
|
113
|
-
# AHU htg only mode based on OA damper @ min oa and only htg pid/vlv modulating
|
114
|
-
df["heating_mode"] = (
|
115
|
-
(df[self.heating_sig_col] > 0)
|
116
|
-
& (df[self.cooling_sig_col] == 0)
|
117
|
-
& (df[self.supply_vfd_speed_col] > 0)
|
118
|
-
& (df[self.economizer_sig_col] == self.ahu_min_oa_dpr)
|
119
|
-
)
|
120
|
-
|
121
|
-
# AHU econ only mode based on OA damper modulating and clg htg = zero
|
122
|
-
df["econ_only_cooling_mode"] = (
|
123
|
-
(df[self.heating_sig_col] == 0)
|
124
|
-
& (df[self.cooling_sig_col] == 0)
|
125
|
-
& (df[self.supply_vfd_speed_col] > 0)
|
126
|
-
& (df[self.economizer_sig_col] > self.ahu_min_oa_dpr)
|
127
|
-
)
|
128
|
-
|
129
|
-
# AHU econ+mech clg mode based on OA damper modulating for cooling and clg pid/vlv modulating
|
130
|
-
df["econ_plus_mech_cooling_mode"] = (
|
131
|
-
(df[self.heating_sig_col] == 0)
|
132
|
-
& (df[self.cooling_sig_col] > 0)
|
133
|
-
& (df[self.supply_vfd_speed_col] > 0)
|
134
|
-
& (df[self.economizer_sig_col] > self.ahu_min_oa_dpr)
|
135
|
-
)
|
136
|
-
|
137
|
-
# AHU mech mode based on OA damper @ min OA and clg pid/vlv modulating
|
138
|
-
df["mech_cooling_only_mode"] = (
|
139
|
-
(df[self.heating_sig_col] == 0)
|
140
|
-
& (df[self.cooling_sig_col] > 0)
|
141
|
-
& (df[self.supply_vfd_speed_col] > 0)
|
142
|
-
& (df[self.economizer_sig_col] == self.ahu_min_oa_dpr)
|
143
|
-
)
|
144
|
-
|
145
|
-
# AHU minimum OA mode without heating or cooling (ventilation mode)
|
146
|
-
df["min_oa_mode_only"] = (
|
147
|
-
(df[self.heating_sig_col] == 0)
|
148
|
-
& (df[self.cooling_sig_col] == 0)
|
149
|
-
& (df[self.supply_vfd_speed_col] > 0)
|
150
|
-
& (df[self.economizer_sig_col] == self.ahu_min_oa_dpr)
|
151
|
-
)
|
152
|
-
|
153
|
-
# Fill non-finite values with zero or drop them
|
154
|
-
df = df.fillna(0)
|
155
|
-
|
156
|
-
df = df.astype(int)
|
157
|
-
df = df.resample("60min").apply(lambda x: (x.eq(1) & x.shift().ne(1)).sum())
|
158
|
-
|
159
|
-
df["fc4_flag"] = (
|
160
|
-
df[df.columns].gt(self.delta_os_max).any(axis=1).astype(int)
|
161
|
-
)
|
162
|
-
|
163
|
-
return df
|
164
|
-
|
165
|
-
except MissingColumnError as e:
|
166
|
-
print(f"Error: {e.message}")
|
167
|
-
sys.stdout.flush()
|
168
|
-
raise e
|