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.
- open_fdd/air_handling_unit/faults/__init__.py +2253 -0
- open_fdd/air_handling_unit/faults/fault_condition.py +38 -18
- open_fdd/air_handling_unit/faults/fault_condition_eight.py +91 -31
- open_fdd/air_handling_unit/faults/fault_condition_eleven.py +93 -35
- open_fdd/air_handling_unit/faults/fault_condition_fifteen.py +111 -49
- open_fdd/air_handling_unit/faults/fault_condition_five.py +89 -34
- open_fdd/air_handling_unit/faults/fault_condition_four.py +136 -61
- open_fdd/air_handling_unit/faults/fault_condition_fourteen.py +103 -40
- open_fdd/air_handling_unit/faults/fault_condition_nine.py +95 -35
- open_fdd/air_handling_unit/faults/fault_condition_one.py +83 -31
- open_fdd/air_handling_unit/faults/fault_condition_seven.py +85 -26
- open_fdd/air_handling_unit/faults/fault_condition_six.py +134 -73
- open_fdd/air_handling_unit/faults/fault_condition_ten.py +91 -30
- open_fdd/air_handling_unit/faults/fault_condition_thirteen.py +95 -34
- open_fdd/air_handling_unit/faults/fault_condition_three.py +84 -29
- open_fdd/air_handling_unit/faults/fault_condition_twelve.py +98 -37
- open_fdd/air_handling_unit/faults/fault_condition_two.py +84 -32
- open_fdd/air_handling_unit/faults/helper_utils.py +295 -93
- 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 +175 -0
- 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 +47 -0
- open_fdd/air_handling_unit/reports/report_fc7.py +3 -1
- open_fdd/tests/ahu/test_ahu_fc1.py +18 -1
- open_fdd/tests/ahu/test_ahu_fc10.py +1 -1
- open_fdd/tests/ahu/test_ahu_fc11.py +1 -1
- open_fdd/tests/ahu/test_ahu_fc12.py +1 -1
- open_fdd/tests/ahu/test_ahu_fc13.py +1 -1
- open_fdd/tests/ahu/test_ahu_fc14.py +1 -1
- open_fdd/tests/ahu/test_ahu_fc15.py +1 -1
- open_fdd/tests/ahu/test_ahu_fc2.py +1 -1
- open_fdd/tests/ahu/test_ahu_fc3.py +1 -1
- open_fdd/tests/ahu/test_ahu_fc4.py +2 -2
- open_fdd/tests/ahu/test_ahu_fc5.py +1 -1
- open_fdd/tests/ahu/test_ahu_fc6.py +2 -2
- open_fdd/tests/ahu/test_ahu_fc7.py +1 -1
- open_fdd/tests/ahu/test_ahu_fc8.py +1 -1
- open_fdd/tests/ahu/test_ahu_fc9.py +1 -1
- {open_fdd-0.1.1.dist-info → open_fdd-0.1.4.dist-info}/METADATA +34 -5
- open_fdd-0.1.4.dist-info/RECORD +82 -0
- open_fdd-0.1.1.dist-info/RECORD +0 -59
- {open_fdd-0.1.1.dist-info → open_fdd-0.1.4.dist-info}/LICENSE +0 -0
- {open_fdd-0.1.1.dist-info → open_fdd-0.1.4.dist-info}/WHEEL +0 -0
- {open_fdd-0.1.1.dist-info → open_fdd-0.1.4.dist-info}/top_level.txt +0 -0
@@ -1,28 +1,52 @@
|
|
1
|
+
import pandas as pd
|
1
2
|
import pandas.api.types as pdtypes
|
2
3
|
from open_fdd.air_handling_unit.faults.helper_utils import HelperUtils
|
3
4
|
import sys
|
4
5
|
|
6
|
+
"""see __init__.py for fault classes"""
|
7
|
+
|
8
|
+
|
9
|
+
class MissingColumnError(Exception):
|
10
|
+
"""Custom exception raised when a required column is missing or None."""
|
11
|
+
|
12
|
+
def __init__(self, message):
|
13
|
+
self.message = message
|
14
|
+
super().__init__(self.message)
|
15
|
+
|
16
|
+
|
17
|
+
class InvalidParameterError(Exception):
|
18
|
+
"""Custom exception raised when a parameter is not valid (e.g., not a float)."""
|
19
|
+
|
20
|
+
def __init__(self, message):
|
21
|
+
self.message = message
|
22
|
+
super().__init__(self.message)
|
23
|
+
|
5
24
|
|
6
25
|
class FaultCondition:
|
7
26
|
"""Parent class for Fault Conditions. Methods are inherited to all children."""
|
8
27
|
|
9
|
-
def
|
10
|
-
|
11
|
-
attributes_dict to set only the attributes that match from dict_.
|
28
|
+
def __init__(self):
|
29
|
+
self.required_columns = []
|
12
30
|
|
13
|
-
|
14
|
-
"""
|
15
|
-
for attribute in self.__dict__:
|
31
|
+
def set_attributes(self, dict_):
|
32
|
+
"""Passes dictionary into initialization of class instance"""
|
33
|
+
for attribute in self.__dict__.keys():
|
16
34
|
upper = attribute.upper()
|
17
|
-
|
18
|
-
|
35
|
+
if upper in dict_:
|
36
|
+
value = dict_[upper]
|
37
|
+
self.__setattr__(attribute, value)
|
19
38
|
|
20
|
-
def
|
21
|
-
"""
|
39
|
+
def check_required_columns(self, df: pd.DataFrame):
|
40
|
+
"""Checks if required columns are present in the DataFrame."""
|
41
|
+
missing_columns = [
|
42
|
+
col for col in self.required_columns if col is None or col not in df.columns
|
43
|
+
]
|
22
44
|
|
23
|
-
|
24
|
-
|
25
|
-
|
45
|
+
if missing_columns:
|
46
|
+
raise MissingColumnError(f"Missing required columns: {missing_columns}")
|
47
|
+
|
48
|
+
def troubleshoot_cols(self, df):
|
49
|
+
"""Print troubleshoot columns mapping."""
|
26
50
|
print("Troubleshoot mode enabled - not removing helper columns")
|
27
51
|
for col in df.columns:
|
28
52
|
print(
|
@@ -36,11 +60,7 @@ class FaultCondition:
|
|
36
60
|
sys.stdout.flush()
|
37
61
|
|
38
62
|
def check_analog_pct(self, df, columns):
|
39
|
-
"""
|
40
|
-
|
41
|
-
:param columns:
|
42
|
-
:return:
|
43
|
-
"""
|
63
|
+
"""Check analog outputs [data with units of %] are floats only."""
|
44
64
|
helper = HelperUtils()
|
45
65
|
for col in columns:
|
46
66
|
if not pdtypes.is_float_dtype(df[col]):
|
@@ -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
|
4
|
-
|
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 FaultConditionEight(FaultCondition):
|
|
12
14
|
"""
|
13
15
|
|
14
16
|
def __init__(self, dict_):
|
17
|
+
super().__init__()
|
15
18
|
self.delta_t_supply_fan = float
|
16
19
|
self.mix_degf_err_thres = float
|
17
20
|
self.supply_degf_err_thres = float
|
@@ -23,45 +26,102 @@ class FaultConditionEight(FaultCondition):
|
|
23
26
|
self.troubleshoot_mode = bool # default should be False
|
24
27
|
self.rolling_window_size = int
|
25
28
|
|
26
|
-
self.
|
29
|
+
self.equation_string = (
|
30
|
+
"fc8_flag = 1 if |SAT - MAT - ΔT_fan| > √(εSAT² + εMAT²) "
|
31
|
+
"in economizer mode for N consecutive values else 0 \n"
|
32
|
+
)
|
33
|
+
self.description_string = (
|
34
|
+
"Fault Condition 8: Supply air temperature and mixed air temperature should "
|
35
|
+
"be approximately equal in economizer mode \n"
|
36
|
+
)
|
37
|
+
self.required_column_description = (
|
38
|
+
"Required inputs are the mixed air temperature, supply air temperature, "
|
39
|
+
"economizer signal, and cooling signal \n"
|
40
|
+
)
|
41
|
+
self.error_string = f"One or more required columns are missing or None \n"
|
27
42
|
|
28
|
-
|
29
|
-
if self.troubleshoot_mode:
|
30
|
-
self.troubleshoot_cols(df)
|
43
|
+
self.set_attributes(dict_)
|
31
44
|
|
32
|
-
#
|
33
|
-
|
45
|
+
# Set required columns specific to this fault condition
|
46
|
+
self.required_columns = [
|
47
|
+
self.mat_col,
|
48
|
+
self.sat_col,
|
34
49
|
self.economizer_sig_col,
|
35
50
|
self.cooling_sig_col,
|
36
51
|
]
|
37
52
|
|
38
|
-
|
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
|
+
)
|
39
62
|
|
40
|
-
|
41
|
-
|
42
|
-
)
|
43
|
-
df["sat_mat_sqrted"] = np.sqrt(
|
44
|
-
self.supply_degf_err_thres**2 + self.mix_degf_err_thres**2
|
45
|
-
)
|
63
|
+
# Ensure all required columns are strings
|
64
|
+
self.required_columns = [str(col) for col in self.required_columns]
|
46
65
|
|
47
|
-
|
48
|
-
|
49
|
-
& (df[self.economizer_sig_col] > self.ahu_min_oa_dpr)
|
50
|
-
& (df[self.cooling_sig_col] < 0.1)
|
66
|
+
self.mapped_columns = (
|
67
|
+
f"Your config dictionary is mapped as: {', '.join(self.required_columns)}"
|
51
68
|
)
|
52
69
|
|
53
|
-
|
54
|
-
|
55
|
-
|
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}"
|
56
77
|
)
|
57
|
-
# Set flag to 1 if rolling sum equals the window size
|
58
|
-
df["fc8_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
|
59
78
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
del df["sat_mat_sqrted"]
|
65
|
-
del df["combined_check"]
|
79
|
+
def apply(self, df: pd.DataFrame) -> pd.DataFrame:
|
80
|
+
try:
|
81
|
+
# Ensure all required columns are present
|
82
|
+
self.check_required_columns(df)
|
66
83
|
|
67
|
-
|
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
|
+
|
93
|
+
self.check_analog_pct(df, columns_to_check)
|
94
|
+
|
95
|
+
df["sat_fan_mat"] = abs(
|
96
|
+
df[self.sat_col] - self.delta_t_supply_fan - df[self.mat_col]
|
97
|
+
)
|
98
|
+
df["sat_mat_sqrted"] = np.sqrt(
|
99
|
+
self.supply_degf_err_thres**2 + self.mix_degf_err_thres**2
|
100
|
+
)
|
101
|
+
|
102
|
+
df["combined_check"] = (
|
103
|
+
(df["sat_fan_mat"] > df["sat_mat_sqrted"])
|
104
|
+
& (df[self.economizer_sig_col] > self.ahu_min_oa_dpr)
|
105
|
+
& (df[self.cooling_sig_col] < 0.1)
|
106
|
+
)
|
107
|
+
|
108
|
+
# Rolling sum to count consecutive trues
|
109
|
+
rolling_sum = (
|
110
|
+
df["combined_check"].rolling(window=self.rolling_window_size).sum()
|
111
|
+
)
|
112
|
+
# Set flag to 1 if rolling sum equals the window size
|
113
|
+
df["fc8_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
|
114
|
+
|
115
|
+
if self.troubleshoot_mode:
|
116
|
+
print("Troubleshoot mode enabled - not removing helper columns")
|
117
|
+
sys.stdout.flush()
|
118
|
+
del df["sat_fan_mat"]
|
119
|
+
del df["sat_mat_sqrted"]
|
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,7 +1,8 @@
|
|
1
1
|
import pandas as pd
|
2
|
-
|
3
|
-
|
4
|
-
|
2
|
+
from open_fdd.air_handling_unit.faults.fault_condition import (
|
3
|
+
FaultCondition,
|
4
|
+
MissingColumnError,
|
5
|
+
)
|
5
6
|
import sys
|
6
7
|
|
7
8
|
|
@@ -9,11 +10,11 @@ class FaultConditionEleven(FaultCondition):
|
|
9
10
|
"""Class provides the definitions for Fault Condition 11.
|
10
11
|
Outside air temperature too low for 100% outdoor
|
11
12
|
air cooling in economizer cooling mode.
|
12
|
-
|
13
13
|
Economizer performance fault
|
14
14
|
"""
|
15
15
|
|
16
16
|
def __init__(self, dict_):
|
17
|
+
super().__init__()
|
17
18
|
self.delta_t_supply_fan = float
|
18
19
|
self.outdoor_degf_err_thres = float
|
19
20
|
self.supply_degf_err_thres = float
|
@@ -24,45 +25,102 @@ class FaultConditionEleven(FaultCondition):
|
|
24
25
|
self.troubleshoot_mode = bool # default False
|
25
26
|
self.rolling_window_size = int
|
26
27
|
|
27
|
-
self.
|
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"
|
28
41
|
|
29
|
-
|
30
|
-
if self.troubleshoot_mode:
|
31
|
-
self.troubleshoot_cols(df)
|
42
|
+
self.set_attributes(dict_)
|
32
43
|
|
33
|
-
#
|
34
|
-
|
35
|
-
self.
|
44
|
+
# Set required columns specific to this fault condition
|
45
|
+
self.required_columns = [
|
46
|
+
self.sat_setpoint_col,
|
47
|
+
self.oat_col,
|
36
48
|
self.cooling_sig_col,
|
49
|
+
self.economizer_sig_col,
|
37
50
|
]
|
38
|
-
self.check_analog_pct(df, columns_to_check)
|
39
51
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
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]
|
46
64
|
|
47
|
-
|
48
|
-
|
49
|
-
# verify ahu is running in OS 3 clg mode in 100 OA
|
50
|
-
& (df[self.cooling_sig_col] > 0.01)
|
51
|
-
& (df[self.economizer_sig_col] > 0.9)
|
65
|
+
self.mapped_columns = (
|
66
|
+
f"Your config dictionary is mapped as: {', '.join(self.required_columns)}"
|
52
67
|
)
|
53
68
|
|
54
|
-
|
55
|
-
|
56
|
-
|
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}"
|
57
76
|
)
|
58
|
-
# Set flag to 1 if rolling sum equals the window size
|
59
|
-
df["fc11_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
|
60
77
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
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
|
+
)
|
67
99
|
|
68
|
-
|
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,17 +1,20 @@
|
|
1
1
|
import pandas as pd
|
2
2
|
import numpy as np
|
3
|
-
from open_fdd.air_handling_unit.faults.fault_condition import
|
4
|
-
|
3
|
+
from open_fdd.air_handling_unit.faults.fault_condition import (
|
4
|
+
FaultCondition,
|
5
|
+
MissingColumnError,
|
6
|
+
)
|
5
7
|
import sys
|
6
8
|
|
7
9
|
|
8
10
|
class FaultConditionFifteen(FaultCondition):
|
9
11
|
"""Class provides the definitions for Fault Condition 15.
|
10
|
-
Temperature rise across inactive heating
|
12
|
+
Temperature rise across inactive heating coil.
|
11
13
|
Requires coil leaving temp sensor.
|
12
14
|
"""
|
13
15
|
|
14
16
|
def __init__(self, dict_):
|
17
|
+
super().__init__()
|
15
18
|
self.delta_supply_fan = float
|
16
19
|
self.coil_temp_enter_err_thres = float
|
17
20
|
self.coil_temp_leav_err_thres = float
|
@@ -25,66 +28,125 @@ class FaultConditionFifteen(FaultCondition):
|
|
25
28
|
self.troubleshoot_mode = bool # default to False
|
26
29
|
self.rolling_window_size = int
|
27
30
|
|
28
|
-
self.
|
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"
|
29
44
|
|
30
|
-
|
31
|
-
if self.troubleshoot_mode:
|
32
|
-
self.troubleshoot_cols(df)
|
45
|
+
self.set_attributes(dict_)
|
33
46
|
|
34
|
-
#
|
35
|
-
|
36
|
-
self.
|
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,
|
37
51
|
self.cooling_sig_col,
|
38
52
|
self.heating_sig_col,
|
53
|
+
self.economizer_sig_col,
|
39
54
|
self.supply_vfd_speed_col,
|
40
55
|
]
|
41
|
-
self.check_analog_pct(df, columns_to_check)
|
42
56
|
|
43
|
-
#
|
44
|
-
|
45
|
-
|
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)}"
|
46
72
|
)
|
47
73
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
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}"
|
53
81
|
)
|
54
82
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
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
|
61
110
|
)
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
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
|
+
)
|
67
131
|
)
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
& (df[self.economizer_sig_col] > 0.9)
|
132
|
+
|
133
|
+
# Rolling sum to count consecutive trues
|
134
|
+
rolling_sum = (
|
135
|
+
df["combined_check"].rolling(window=self.rolling_window_size).sum()
|
73
136
|
)
|
74
|
-
|
137
|
+
# Set flag to 1 if rolling sum equals the window size
|
138
|
+
df["fc15_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
|
75
139
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
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"]
|
82
146
|
|
83
|
-
|
84
|
-
print("Troubleshoot mode enabled - not removing helper columns")
|
85
|
-
sys.stdout.flush()
|
86
|
-
del df["htg_delta_temp"]
|
87
|
-
del df["htg_delta_sqrted"]
|
88
|
-
del df["combined_check"]
|
147
|
+
return df
|
89
148
|
|
90
|
-
|
149
|
+
except MissingColumnError as e:
|
150
|
+
print(f"Error: {e.message}")
|
151
|
+
sys.stdout.flush()
|
152
|
+
raise e
|