open-fdd 0.1.0__py3-none-any.whl → 0.1.3__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/fault_condition.py +26 -8
- open_fdd/air_handling_unit/faults/fault_condition_eight.py +61 -37
- open_fdd/air_handling_unit/faults/fault_condition_eleven.py +59 -37
- open_fdd/air_handling_unit/faults/fault_condition_fifteen.py +77 -51
- open_fdd/air_handling_unit/faults/fault_condition_five.py +60 -41
- open_fdd/air_handling_unit/faults/fault_condition_four.py +108 -65
- open_fdd/air_handling_unit/faults/fault_condition_fourteen.py +71 -44
- open_fdd/air_handling_unit/faults/fault_condition_nine.py +60 -36
- open_fdd/air_handling_unit/faults/fault_condition_one.py +58 -37
- open_fdd/air_handling_unit/faults/fault_condition_seven.py +55 -32
- open_fdd/air_handling_unit/faults/fault_condition_six.py +100 -76
- open_fdd/air_handling_unit/faults/fault_condition_ten.py +62 -37
- open_fdd/air_handling_unit/faults/fault_condition_thirteen.py +61 -36
- open_fdd/air_handling_unit/faults/fault_condition_three.py +58 -33
- open_fdd/air_handling_unit/faults/fault_condition_twelve.py +63 -39
- open_fdd/air_handling_unit/faults/fault_condition_two.py +58 -36
- open_fdd/air_handling_unit/faults/helper_utils.py +294 -64
- 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 +17 -0
- open_fdd/tests/ahu/test_ahu_fc4.py +127 -199
- open_fdd-0.1.3.dist-info/METADATA +87 -0
- {open_fdd-0.1.0.dist-info → open_fdd-0.1.3.dist-info}/RECORD +48 -25
- open_fdd-0.1.0.dist-info/METADATA +0 -65
- {open_fdd-0.1.0.dist-info → open_fdd-0.1.3.dist-info}/LICENSE +0 -0
- {open_fdd-0.1.0.dist-info → open_fdd-0.1.3.dist-info}/WHEEL +0 -0
- {open_fdd-0.1.0.dist-info → open_fdd-0.1.3.dist-info}/top_level.txt +0 -0
@@ -1,21 +1,39 @@
|
|
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
|
|
5
6
|
|
7
|
+
class MissingColumnError(Exception):
|
8
|
+
"""Custom exception raised when a required column is missing or None."""
|
9
|
+
|
10
|
+
def __init__(self, message):
|
11
|
+
self.message = message
|
12
|
+
super().__init__(self.message)
|
13
|
+
|
14
|
+
|
6
15
|
class FaultCondition:
|
7
16
|
"""Parent class for Fault Conditions. Methods are inherited to all children."""
|
8
17
|
|
9
|
-
def
|
10
|
-
|
11
|
-
attributes_dict to set only the attributes that match from dict_.
|
18
|
+
def __init__(self):
|
19
|
+
self.required_columns = []
|
12
20
|
|
13
|
-
|
14
|
-
"""
|
15
|
-
for attribute in self.__dict__:
|
21
|
+
def set_attributes(self, dict_):
|
22
|
+
"""Passes dictionary into initialization of class instance"""
|
23
|
+
for attribute in self.__dict__.keys():
|
16
24
|
upper = attribute.upper()
|
17
|
-
|
18
|
-
|
25
|
+
if upper in dict_:
|
26
|
+
value = dict_[upper]
|
27
|
+
self.__setattr__(attribute, value)
|
28
|
+
|
29
|
+
def check_required_columns(self, df: pd.DataFrame):
|
30
|
+
"""Checks if required columns are present in the DataFrame."""
|
31
|
+
missing_columns = [
|
32
|
+
col for col in self.required_columns if col is None or col not in df.columns
|
33
|
+
]
|
34
|
+
|
35
|
+
if missing_columns:
|
36
|
+
raise MissingColumnError(f"Missing required columns: {missing_columns}")
|
19
37
|
|
20
38
|
def troubleshoot_cols(self, df):
|
21
39
|
"""print troubleshoot columns mapping
|
@@ -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
|
@@ -25,43 +28,64 @@ class FaultConditionEight(FaultCondition):
|
|
25
28
|
|
26
29
|
self.set_attributes(dict_)
|
27
30
|
|
28
|
-
|
29
|
-
|
30
|
-
self.
|
31
|
-
|
32
|
-
# Check analog outputs [data with units of %] are floats only
|
33
|
-
columns_to_check = [
|
31
|
+
# Set required columns specific to this fault condition
|
32
|
+
self.required_columns = [
|
33
|
+
self.mat_col,
|
34
|
+
self.sat_col,
|
34
35
|
self.economizer_sig_col,
|
35
36
|
self.cooling_sig_col,
|
36
37
|
]
|
37
38
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
df["combined_check"].rolling(window=self.rolling_window_size).sum()
|
56
|
-
)
|
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
|
-
|
60
|
-
if self.troubleshoot_mode:
|
61
|
-
print("Troubleshoot mode enabled - not removing helper columns")
|
62
|
-
sys.stdout.flush()
|
63
|
-
del df["sat_fan_mat"]
|
64
|
-
del df["sat_mat_sqrted"]
|
65
|
-
del df["combined_check"]
|
39
|
+
def get_required_columns(self) -> str:
|
40
|
+
"""Returns a string representation of the required columns."""
|
41
|
+
return f"Required columns for FaultConditionEight: {', '.join(self.required_columns)}"
|
42
|
+
|
43
|
+
def apply(self, df: pd.DataFrame) -> pd.DataFrame:
|
44
|
+
try:
|
45
|
+
# Ensure all required columns are present
|
46
|
+
self.check_required_columns(df)
|
47
|
+
|
48
|
+
if self.troubleshoot_mode:
|
49
|
+
self.troubleshoot_cols(df)
|
50
|
+
|
51
|
+
# Check analog outputs [data with units of %] are floats only
|
52
|
+
columns_to_check = [
|
53
|
+
self.economizer_sig_col,
|
54
|
+
self.cooling_sig_col,
|
55
|
+
]
|
66
56
|
|
67
|
-
|
57
|
+
self.check_analog_pct(df, columns_to_check)
|
58
|
+
|
59
|
+
df["sat_fan_mat"] = abs(
|
60
|
+
df[self.sat_col] - self.delta_t_supply_fan - df[self.mat_col]
|
61
|
+
)
|
62
|
+
df["sat_mat_sqrted"] = np.sqrt(
|
63
|
+
self.supply_degf_err_thres**2 + self.mix_degf_err_thres**2
|
64
|
+
)
|
65
|
+
|
66
|
+
df["combined_check"] = (
|
67
|
+
(df["sat_fan_mat"] > df["sat_mat_sqrted"])
|
68
|
+
& (df[self.economizer_sig_col] > self.ahu_min_oa_dpr)
|
69
|
+
& (df[self.cooling_sig_col] < 0.1)
|
70
|
+
)
|
71
|
+
|
72
|
+
# Rolling sum to count consecutive trues
|
73
|
+
rolling_sum = (
|
74
|
+
df["combined_check"].rolling(window=self.rolling_window_size).sum()
|
75
|
+
)
|
76
|
+
# Set flag to 1 if rolling sum equals the window size
|
77
|
+
df["fc8_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
|
78
|
+
|
79
|
+
if self.troubleshoot_mode:
|
80
|
+
print("Troubleshoot mode enabled - not removing helper columns")
|
81
|
+
sys.stdout.flush()
|
82
|
+
del df["sat_fan_mat"]
|
83
|
+
del df["sat_mat_sqrted"]
|
84
|
+
del df["combined_check"]
|
85
|
+
|
86
|
+
return df
|
87
|
+
|
88
|
+
except MissingColumnError as e:
|
89
|
+
print(f"Error: {e.message}")
|
90
|
+
sys.stdout.flush()
|
91
|
+
raise e # Re-raise the exception so it can be caught by pytest
|
@@ -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
|
@@ -26,43 +27,64 @@ class FaultConditionEleven(FaultCondition):
|
|
26
27
|
|
27
28
|
self.set_attributes(dict_)
|
28
29
|
|
29
|
-
|
30
|
-
|
31
|
-
self.
|
32
|
-
|
33
|
-
# Check analog outputs [data with units of %] are floats only
|
34
|
-
columns_to_check = [
|
35
|
-
self.economizer_sig_col,
|
30
|
+
# Set required columns specific to this fault condition
|
31
|
+
self.required_columns = [
|
32
|
+
self.sat_setpoint_col,
|
33
|
+
self.oat_col,
|
36
34
|
self.cooling_sig_col,
|
35
|
+
self.economizer_sig_col,
|
37
36
|
]
|
38
|
-
self.check_analog_pct(df, columns_to_check)
|
39
37
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
- self.delta_t_supply_fan
|
44
|
-
- self.supply_degf_err_thres
|
45
|
-
)
|
38
|
+
def get_required_columns(self) -> str:
|
39
|
+
"""Returns a string representation of the required columns."""
|
40
|
+
return f"Required columns for FaultConditionEleven: {', '.join(self.required_columns)}"
|
46
41
|
|
47
|
-
|
48
|
-
|
49
|
-
#
|
50
|
-
|
51
|
-
& (df[self.economizer_sig_col] > 0.9)
|
52
|
-
)
|
42
|
+
def apply(self, df: pd.DataFrame) -> pd.DataFrame:
|
43
|
+
try:
|
44
|
+
# Ensure all required columns are present
|
45
|
+
self.check_required_columns(df)
|
53
46
|
|
54
|
-
|
55
|
-
|
56
|
-
df["combined_check"].rolling(window=self.rolling_window_size).sum()
|
57
|
-
)
|
58
|
-
# Set flag to 1 if rolling sum equals the window size
|
59
|
-
df["fc11_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
|
47
|
+
if self.troubleshoot_mode:
|
48
|
+
self.troubleshoot_cols(df)
|
60
49
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
50
|
+
# Check analog outputs [data with units of %] are floats only
|
51
|
+
columns_to_check = [
|
52
|
+
self.economizer_sig_col,
|
53
|
+
self.cooling_sig_col,
|
54
|
+
]
|
55
|
+
self.check_analog_pct(df, columns_to_check)
|
67
56
|
|
68
|
-
|
57
|
+
df["oat_plus_oaterror"] = df[self.oat_col] + self.outdoor_degf_err_thres
|
58
|
+
df["satsp_delta_saterr"] = (
|
59
|
+
df[self.sat_setpoint_col]
|
60
|
+
- self.delta_t_supply_fan
|
61
|
+
- self.supply_degf_err_thres
|
62
|
+
)
|
63
|
+
|
64
|
+
df["combined_check"] = (
|
65
|
+
(df["oat_plus_oaterror"] < df["satsp_delta_saterr"])
|
66
|
+
# verify ahu is running in OS 3 clg mode in 100 OA
|
67
|
+
& (df[self.cooling_sig_col] > 0.01)
|
68
|
+
& (df[self.economizer_sig_col] > 0.9)
|
69
|
+
)
|
70
|
+
|
71
|
+
# Rolling sum to count consecutive trues
|
72
|
+
rolling_sum = (
|
73
|
+
df["combined_check"].rolling(window=self.rolling_window_size).sum()
|
74
|
+
)
|
75
|
+
# Set flag to 1 if rolling sum equals the window size
|
76
|
+
df["fc11_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
|
77
|
+
|
78
|
+
if self.troubleshoot_mode:
|
79
|
+
print("Troubleshoot mode enabled - not removing helper columns")
|
80
|
+
sys.stdout.flush()
|
81
|
+
del df["oat_plus_oaterror"]
|
82
|
+
del df["satsp_delta_saterr"]
|
83
|
+
del df["combined_check"]
|
84
|
+
|
85
|
+
return df
|
86
|
+
|
87
|
+
except MissingColumnError as e:
|
88
|
+
print(f"Error: {e.message}")
|
89
|
+
sys.stdout.flush()
|
90
|
+
raise e # Re-raise the exception so it can be caught by pytest
|
@@ -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
|
@@ -27,64 +30,87 @@ class FaultConditionFifteen(FaultCondition):
|
|
27
30
|
|
28
31
|
self.set_attributes(dict_)
|
29
32
|
|
30
|
-
|
31
|
-
|
32
|
-
self.
|
33
|
-
|
34
|
-
# Check analog outputs [data with units of %] are floats only
|
35
|
-
columns_to_check = [
|
36
|
-
self.economizer_sig_col,
|
33
|
+
# Set required columns specific to this fault condition
|
34
|
+
self.required_columns = [
|
35
|
+
self.htg_coil_enter_temp_col,
|
36
|
+
self.htg_coil_leave_temp_col,
|
37
37
|
self.cooling_sig_col,
|
38
38
|
self.heating_sig_col,
|
39
|
+
self.economizer_sig_col,
|
39
40
|
self.supply_vfd_speed_col,
|
40
41
|
]
|
41
|
-
self.check_analog_pct(df, columns_to_check)
|
42
42
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
43
|
+
def get_required_columns(self) -> str:
|
44
|
+
"""Returns a string representation of the required columns."""
|
45
|
+
return f"Required columns for FaultConditionFifteen: {', '.join(self.required_columns)}"
|
46
|
+
|
47
|
+
def apply(self, df: pd.DataFrame) -> pd.DataFrame:
|
48
|
+
try:
|
49
|
+
# Ensure all required columns are present
|
50
|
+
self.check_required_columns(df)
|
51
|
+
|
52
|
+
if self.troubleshoot_mode:
|
53
|
+
self.troubleshoot_cols(df)
|
47
54
|
|
48
|
-
|
49
|
-
|
50
|
-
self.
|
55
|
+
# Check analog outputs [data with units of %] are floats only
|
56
|
+
columns_to_check = [
|
57
|
+
self.economizer_sig_col,
|
58
|
+
self.cooling_sig_col,
|
59
|
+
self.heating_sig_col,
|
60
|
+
self.supply_vfd_speed_col,
|
61
|
+
]
|
62
|
+
self.check_analog_pct(df, columns_to_check)
|
63
|
+
|
64
|
+
# Create helper columns
|
65
|
+
df["htg_delta_temp"] = (
|
66
|
+
df[self.htg_coil_leave_temp_col] - df[self.htg_coil_enter_temp_col]
|
51
67
|
)
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
# verify AHU is in OS2 only free cooling mode
|
59
|
-
& (df[self.economizer_sig_col] > self.ahu_min_oa_dpr)
|
60
|
-
& (df[self.cooling_sig_col] < 0.1)
|
68
|
+
|
69
|
+
df["htg_delta_sqrted"] = (
|
70
|
+
np.sqrt(
|
71
|
+
self.coil_temp_enter_err_thres**2 + self.coil_temp_leav_err_thres**2
|
72
|
+
)
|
73
|
+
+ self.delta_supply_fan
|
61
74
|
)
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
75
|
+
|
76
|
+
df["combined_check"] = (
|
77
|
+
(
|
78
|
+
(df["htg_delta_temp"] >= df["htg_delta_sqrted"])
|
79
|
+
# verify AHU is in OS2 only free cooling mode
|
80
|
+
& (df[self.economizer_sig_col] > self.ahu_min_oa_dpr)
|
81
|
+
& (df[self.cooling_sig_col] < 0.1)
|
82
|
+
)
|
83
|
+
| (
|
84
|
+
(df["htg_delta_temp"] >= df["htg_delta_sqrted"])
|
85
|
+
# OS4 AHU state clg @ min OA
|
86
|
+
& (df[self.cooling_sig_col] > 0.01)
|
87
|
+
& (df[self.economizer_sig_col] == self.ahu_min_oa_dpr)
|
88
|
+
)
|
89
|
+
| (
|
90
|
+
(df["htg_delta_temp"] >= df["htg_delta_sqrted"])
|
91
|
+
# verify AHU is running in OS 3 clg mode in 100 OA
|
92
|
+
& (df[self.cooling_sig_col] > 0.01)
|
93
|
+
& (df[self.economizer_sig_col] > 0.9)
|
94
|
+
)
|
67
95
|
)
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
& (df[self.economizer_sig_col] > 0.9)
|
96
|
+
|
97
|
+
# Rolling sum to count consecutive trues
|
98
|
+
rolling_sum = (
|
99
|
+
df["combined_check"].rolling(window=self.rolling_window_size).sum()
|
73
100
|
)
|
74
|
-
|
101
|
+
# Set flag to 1 if rolling sum equals the window size
|
102
|
+
df["fc15_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
|
75
103
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
104
|
+
if self.troubleshoot_mode:
|
105
|
+
print("Troubleshoot mode enabled - not removing helper columns")
|
106
|
+
sys.stdout.flush()
|
107
|
+
del df["htg_delta_temp"]
|
108
|
+
del df["htg_delta_sqrted"]
|
109
|
+
del df["combined_check"]
|
82
110
|
|
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"]
|
111
|
+
return df
|
89
112
|
|
90
|
-
|
113
|
+
except MissingColumnError as e:
|
114
|
+
print(f"Error: {e.message}")
|
115
|
+
sys.stdout.flush()
|
116
|
+
raise e # Re-raise the exception so it can be caught by pytest
|
@@ -1,7 +1,9 @@
|
|
1
1
|
import pandas as pd
|
2
2
|
import pandas.api.types as pdtypes
|
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
|
|
@@ -13,6 +15,7 @@ class FaultConditionFive(FaultCondition):
|
|
13
15
|
"""
|
14
16
|
|
15
17
|
def __init__(self, dict_):
|
18
|
+
super().__init__()
|
16
19
|
self.mix_degf_err_thres = float
|
17
20
|
self.supply_degf_err_thres = float
|
18
21
|
self.delta_t_supply_fan = float
|
@@ -25,44 +28,60 @@ class FaultConditionFive(FaultCondition):
|
|
25
28
|
|
26
29
|
self.set_attributes(dict_)
|
27
30
|
|
28
|
-
|
29
|
-
|
31
|
+
# Set required columns specific to this fault condition
|
32
|
+
self.required_columns = [
|
33
|
+
self.mat_col,
|
34
|
+
self.sat_col,
|
35
|
+
self.heating_sig_col,
|
36
|
+
self.supply_vfd_speed_col,
|
37
|
+
]
|
38
|
+
|
39
|
+
def get_required_columns(self) -> str:
|
40
|
+
"""Returns a string representation of the required columns."""
|
41
|
+
return f"Required columns for FaultConditionFive: {', '.join(self.required_columns)}"
|
42
|
+
|
30
43
|
def apply(self, df: pd.DataFrame) -> pd.DataFrame:
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
)
|
44
|
-
|
45
|
-
df["combined_check"] = (
|
46
|
-
(df["sat_check"] <= df["mat_check"])
|
47
|
-
# this is to make fault only active in OS1 for htg mode only
|
48
|
-
# and fan is running. Some control programming may use htg
|
49
|
-
# vlv when AHU is off to prevent low limit freeze alarms
|
50
|
-
& (df[self.heating_sig_col] > 0.01)
|
51
|
-
& (df[self.supply_vfd_speed_col] > 0.01)
|
52
|
-
)
|
53
|
-
|
54
|
-
# Rolling sum to count consecutive trues
|
55
|
-
rolling_sum = (
|
56
|
-
df["combined_check"].rolling(window=self.rolling_window_size).sum()
|
57
|
-
)
|
58
|
-
# Set flag to 1 if rolling sum equals the window size
|
59
|
-
df["fc5_flag"] = (rolling_sum == self.rolling_window_size).astype(int)
|
60
|
-
|
61
|
-
if self.troubleshoot_mode:
|
62
|
-
print("Troubleshoot mode enabled - not removing helper columns")
|
63
|
-
sys.stdout.flush()
|
64
|
-
del df["mat_check"]
|
65
|
-
del df["sat_check"]
|
66
|
-
del df["combined_check"]
|
44
|
+
try:
|
45
|
+
# Ensure all required columns are present
|
46
|
+
self.check_required_columns(df)
|
47
|
+
|
48
|
+
if self.troubleshoot_mode:
|
49
|
+
self.troubleshoot_cols(df)
|
50
|
+
|
51
|
+
# Check analog outputs [data with units of %] are floats only
|
52
|
+
columns_to_check = [self.supply_vfd_speed_col, self.heating_sig_col]
|
53
|
+
|
54
|
+
for col in columns_to_check:
|
55
|
+
self.check_analog_pct(df, [col])
|
67
56
|
|
68
|
-
|
57
|
+
df["sat_check"] = df[self.sat_col] + self.supply_degf_err_thres
|
58
|
+
df["mat_check"] = (
|
59
|
+
df[self.mat_col] - self.mix_degf_err_thres + self.delta_t_supply_fan
|
60
|
+
)
|
61
|
+
|
62
|
+
df["combined_check"] = (
|
63
|
+
(df["sat_check"] <= df["mat_check"])
|
64
|
+
& (df[self.heating_sig_col] > 0.01)
|
65
|
+
& (df[self.supply_vfd_speed_col] > 0.01)
|
66
|
+
)
|
67
|
+
|
68
|
+
# Rolling sum to count consecutive trues
|
69
|
+
rolling_sum = (
|
70
|
+
df["combined_check"].rolling(window=self.rolling_window_size).sum()
|
71
|
+
)
|
72
|
+
# Set flag to 1 if rolling sum equals the window size
|
73
|
+
df["fc5_flag"] = (rolling_sum == self.rolling_window_size).astype(int)
|
74
|
+
|
75
|
+
if self.troubleshoot_mode:
|
76
|
+
print("Troubleshoot mode enabled - not removing helper columns")
|
77
|
+
sys.stdout.flush()
|
78
|
+
del df["mat_check"]
|
79
|
+
del df["sat_check"]
|
80
|
+
del df["combined_check"]
|
81
|
+
|
82
|
+
return df
|
83
|
+
|
84
|
+
except MissingColumnError as e:
|
85
|
+
print(f"Error: {e.message}")
|
86
|
+
sys.stdout.flush()
|
87
|
+
raise e # Re-raise the exception so it can be caught by pytest
|