open-fdd 0.1.1__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.1.dist-info → open_fdd-0.1.3.dist-info}/METADATA +35 -5
- {open_fdd-0.1.1.dist-info → open_fdd-0.1.3.dist-info}/RECORD +48 -25
- {open_fdd-0.1.1.dist-info → open_fdd-0.1.3.dist-info}/LICENSE +0 -0
- {open_fdd-0.1.1.dist-info → open_fdd-0.1.3.dist-info}/WHEEL +0 -0
- {open_fdd-0.1.1.dist-info → open_fdd-0.1.3.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,8 @@
|
|
1
1
|
import pandas as pd
|
2
|
-
from open_fdd.air_handling_unit.faults.fault_condition import
|
3
|
-
|
2
|
+
from open_fdd.air_handling_unit.faults.fault_condition import (
|
3
|
+
FaultCondition,
|
4
|
+
MissingColumnError,
|
5
|
+
)
|
4
6
|
import sys
|
5
7
|
|
6
8
|
|
@@ -8,14 +10,14 @@ class FaultConditionFour(FaultCondition):
|
|
8
10
|
"""Class provides the definitions for Fault Condition 4.
|
9
11
|
|
10
12
|
This fault flags excessive operating states on the AHU
|
11
|
-
if
|
13
|
+
if it's hunting between heating, econ, econ+mech, and
|
12
14
|
a mech clg modes. The code counts how many operating
|
13
15
|
changes in an hour and will throw a fault if there is
|
14
16
|
excessive OS changes to flag control sys hunting.
|
15
|
-
|
16
17
|
"""
|
17
18
|
|
18
19
|
def __init__(self, dict_):
|
20
|
+
super().__init__()
|
19
21
|
self.delta_os_max = float
|
20
22
|
self.ahu_min_oa_dpr = float
|
21
23
|
self.economizer_sig_col = str
|
@@ -26,68 +28,109 @@ class FaultConditionFour(FaultCondition):
|
|
26
28
|
|
27
29
|
self.set_attributes(dict_)
|
28
30
|
|
29
|
-
|
30
|
-
|
31
|
-
if self.troubleshoot_mode:
|
32
|
-
self.troubleshoot_cols(df)
|
33
|
-
|
34
|
-
# check analog outputs [data with units of %] are floats only
|
35
|
-
columns_to_check = [
|
31
|
+
# Set required columns, making heating and cooling optional
|
32
|
+
self.required_columns = [
|
36
33
|
self.economizer_sig_col,
|
37
|
-
self.heating_sig_col,
|
38
|
-
self.cooling_sig_col,
|
39
34
|
self.supply_vfd_speed_col,
|
40
35
|
]
|
41
36
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
(
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
37
|
+
# If heating or cooling columns are provided, add them to the required columns
|
38
|
+
if self.heating_sig_col:
|
39
|
+
self.required_columns.append(self.heating_sig_col)
|
40
|
+
if self.cooling_sig_col:
|
41
|
+
self.required_columns.append(self.cooling_sig_col)
|
42
|
+
|
43
|
+
def get_required_columns(self) -> str:
|
44
|
+
"""Returns a string representation of the required columns."""
|
45
|
+
return f"Required columns for FaultConditionFour: {', '.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 the optional columns are not present, create them with all values set to 0.0
|
53
|
+
if self.heating_sig_col not in df.columns:
|
54
|
+
df[self.heating_sig_col] = 0.0
|
55
|
+
if self.cooling_sig_col not in df.columns:
|
56
|
+
df[self.cooling_sig_col] = 0.0
|
57
|
+
|
58
|
+
if self.troubleshoot_mode:
|
59
|
+
self.troubleshoot_cols(df)
|
60
|
+
|
61
|
+
# Check analog outputs [data with units of %] are floats only
|
62
|
+
columns_to_check = [
|
63
|
+
self.economizer_sig_col,
|
64
|
+
self.heating_sig_col,
|
65
|
+
self.cooling_sig_col,
|
66
|
+
self.supply_vfd_speed_col,
|
67
|
+
]
|
68
|
+
|
69
|
+
for col in columns_to_check:
|
70
|
+
self.check_analog_pct(df, [col])
|
71
|
+
|
72
|
+
print("=" * 50)
|
73
|
+
print("Warning: The program is in FC4 and resampling the data")
|
74
|
+
print("to compute AHU OS state changes per hour")
|
75
|
+
print("to flag any hunting issue")
|
76
|
+
print("and this usually takes a while to run...")
|
77
|
+
print("=" * 50)
|
78
|
+
|
79
|
+
sys.stdout.flush()
|
80
|
+
|
81
|
+
# AHU htg only mode based on OA damper @ min oa and only htg pid/vlv modulating
|
82
|
+
df["heating_mode"] = (
|
83
|
+
(df[self.heating_sig_col] > 0)
|
84
|
+
& (df[self.cooling_sig_col] == 0)
|
85
|
+
& (df[self.supply_vfd_speed_col] > 0)
|
86
|
+
& (df[self.economizer_sig_col] == self.ahu_min_oa_dpr)
|
87
|
+
)
|
88
|
+
|
89
|
+
# AHU econ only mode based on OA damper modulating and clg htg = zero
|
90
|
+
df["econ_only_cooling_mode"] = (
|
91
|
+
(df[self.heating_sig_col] == 0)
|
92
|
+
& (df[self.cooling_sig_col] == 0)
|
93
|
+
& (df[self.supply_vfd_speed_col] > 0)
|
94
|
+
& (df[self.economizer_sig_col] > self.ahu_min_oa_dpr)
|
95
|
+
)
|
96
|
+
|
97
|
+
# AHU econ+mech clg mode based on OA damper modulating for cooling and clg pid/vlv modulating
|
98
|
+
df["econ_plus_mech_cooling_mode"] = (
|
99
|
+
(df[self.heating_sig_col] == 0)
|
100
|
+
& (df[self.cooling_sig_col] > 0)
|
101
|
+
& (df[self.supply_vfd_speed_col] > 0)
|
102
|
+
& (df[self.economizer_sig_col] > self.ahu_min_oa_dpr)
|
103
|
+
)
|
104
|
+
|
105
|
+
# AHU mech mode based on OA damper @ min OA and clg pid/vlv modulating
|
106
|
+
df["mech_cooling_only_mode"] = (
|
107
|
+
(df[self.heating_sig_col] == 0)
|
108
|
+
& (df[self.cooling_sig_col] > 0)
|
109
|
+
& (df[self.supply_vfd_speed_col] > 0)
|
110
|
+
& (df[self.economizer_sig_col] == self.ahu_min_oa_dpr)
|
111
|
+
)
|
112
|
+
|
113
|
+
# AHU minimum OA mode without heating or cooling (ventilation mode)
|
114
|
+
df["min_oa_mode_only"] = (
|
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
|
+
# Fill non-finite values with zero or drop them
|
122
|
+
df = df.fillna(0)
|
123
|
+
|
124
|
+
df = df.astype(int)
|
125
|
+
df = df.resample("60min").apply(lambda x: (x.eq(1) & x.shift().ne(1)).sum())
|
126
|
+
|
127
|
+
df["fc4_flag"] = (
|
128
|
+
df[df.columns].gt(self.delta_os_max).any(axis=1).astype(int)
|
129
|
+
)
|
130
|
+
|
131
|
+
return df
|
132
|
+
|
133
|
+
except MissingColumnError as e:
|
134
|
+
print(f"Error: {e.message}")
|
135
|
+
sys.stdout.flush()
|
136
|
+
raise e # Re-raise the exception so it can be caught by pytest
|
@@ -1,8 +1,10 @@
|
|
1
1
|
import pandas as pd
|
2
2
|
import numpy as np
|
3
|
-
from open_fdd.air_handling_unit.faults.fault_condition import FaultCondition
|
4
|
-
from open_fdd.air_handling_unit.faults.helper_utils import HelperUtils
|
5
3
|
import operator
|
4
|
+
from open_fdd.air_handling_unit.faults.fault_condition import (
|
5
|
+
FaultCondition,
|
6
|
+
MissingColumnError,
|
7
|
+
)
|
6
8
|
import sys
|
7
9
|
|
8
10
|
|
@@ -13,6 +15,7 @@ class FaultConditionFourteen(FaultCondition):
|
|
13
15
|
"""
|
14
16
|
|
15
17
|
def __init__(self, dict_):
|
18
|
+
super().__init__()
|
16
19
|
self.delta_t_supply_fan = float
|
17
20
|
self.coil_temp_enter_err_thres = float
|
18
21
|
self.coil_temp_leav_err_thres = float
|
@@ -28,53 +31,77 @@ class FaultConditionFourteen(FaultCondition):
|
|
28
31
|
|
29
32
|
self.set_attributes(dict_)
|
30
33
|
|
31
|
-
|
32
|
-
|
33
|
-
self.
|
34
|
-
|
35
|
-
# Check analog outputs [data with units of %] are floats only
|
36
|
-
columns_to_check = [
|
37
|
-
self.economizer_sig_col,
|
34
|
+
# Set required columns specific to this fault condition
|
35
|
+
self.required_columns = [
|
36
|
+
self.clg_coil_enter_temp_col,
|
37
|
+
self.clg_coil_leave_temp_col,
|
38
38
|
self.cooling_sig_col,
|
39
39
|
self.heating_sig_col,
|
40
|
+
self.economizer_sig_col,
|
40
41
|
self.supply_vfd_speed_col,
|
41
42
|
]
|
42
|
-
self.check_analog_pct(df, columns_to_check)
|
43
43
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
)
|
44
|
+
def get_required_columns(self) -> str:
|
45
|
+
"""Returns a string representation of the required columns."""
|
46
|
+
return f"Required columns for FaultConditionFourteen: {', '.join(self.required_columns)}"
|
48
47
|
|
49
|
-
|
50
|
-
|
51
|
-
|
48
|
+
def apply(self, df: pd.DataFrame) -> pd.DataFrame:
|
49
|
+
try:
|
50
|
+
# Ensure all required columns are present
|
51
|
+
self.check_required_columns(df)
|
52
|
+
|
53
|
+
if self.troubleshoot_mode:
|
54
|
+
self.troubleshoot_cols(df)
|
55
|
+
|
56
|
+
# Check analog outputs [data with units of %] are floats only
|
57
|
+
columns_to_check = [
|
58
|
+
self.economizer_sig_col,
|
59
|
+
self.cooling_sig_col,
|
60
|
+
self.heating_sig_col,
|
61
|
+
self.supply_vfd_speed_col,
|
62
|
+
]
|
63
|
+
self.check_analog_pct(df, columns_to_check)
|
64
|
+
|
65
|
+
# Create helper columns
|
66
|
+
df["clg_delta_temp"] = (
|
67
|
+
df[self.clg_coil_enter_temp_col] - df[self.clg_coil_leave_temp_col]
|
52
68
|
)
|
53
|
-
+ self.delta_t_supply_fan
|
54
|
-
)
|
55
|
-
|
56
|
-
df["combined_check"] = operator.or_(
|
57
|
-
(df["clg_delta_temp"] >= df["clg_delta_sqrted"])
|
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), # OR
|
61
|
-
(df["clg_delta_temp"] >= df["clg_delta_sqrted"])
|
62
|
-
# verify AHU is running in OS 1 at near full heat
|
63
|
-
& (df[self.heating_sig_col] > 0.0) & (df[self.supply_vfd_speed_col] > 0.0),
|
64
|
-
)
|
65
|
-
|
66
|
-
# Rolling sum to count consecutive trues
|
67
|
-
rolling_sum = (
|
68
|
-
df["combined_check"].rolling(window=self.rolling_window_size).sum()
|
69
|
-
)
|
70
|
-
# Set flag to 1 if rolling sum equals the window size
|
71
|
-
df["fc14_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
|
72
|
-
|
73
|
-
if self.troubleshoot_mode:
|
74
|
-
print("Troubleshoot mode enabled - not removing helper columns")
|
75
|
-
sys.stdout.flush()
|
76
|
-
del df["clg_delta_temp"]
|
77
|
-
del df["clg_delta_sqrted"]
|
78
|
-
del df["combined_check"]
|
79
69
|
|
80
|
-
|
70
|
+
df["clg_delta_sqrted"] = (
|
71
|
+
np.sqrt(
|
72
|
+
self.coil_temp_enter_err_thres**2 + self.coil_temp_leav_err_thres**2
|
73
|
+
)
|
74
|
+
+ self.delta_t_supply_fan
|
75
|
+
)
|
76
|
+
|
77
|
+
df["combined_check"] = operator.or_(
|
78
|
+
(df["clg_delta_temp"] >= df["clg_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), # OR
|
82
|
+
(df["clg_delta_temp"] >= df["clg_delta_sqrted"])
|
83
|
+
# verify AHU is running in OS 1 at near full heat
|
84
|
+
& (df[self.heating_sig_col] > 0.0)
|
85
|
+
& (df[self.supply_vfd_speed_col] > 0.0),
|
86
|
+
)
|
87
|
+
|
88
|
+
# Rolling sum to count consecutive trues
|
89
|
+
rolling_sum = (
|
90
|
+
df["combined_check"].rolling(window=self.rolling_window_size).sum()
|
91
|
+
)
|
92
|
+
# Set flag to 1 if rolling sum equals the window size
|
93
|
+
df["fc14_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
|
94
|
+
|
95
|
+
if self.troubleshoot_mode:
|
96
|
+
print("Troubleshoot mode enabled - not removing helper columns")
|
97
|
+
sys.stdout.flush()
|
98
|
+
del df["clg_delta_temp"]
|
99
|
+
del df["clg_delta_sqrted"]
|
100
|
+
del df["combined_check"]
|
101
|
+
|
102
|
+
return df
|
103
|
+
|
104
|
+
except MissingColumnError as e:
|
105
|
+
print(f"Error: {e.message}")
|
106
|
+
sys.stdout.flush()
|
107
|
+
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 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 FaultConditionNine(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.outdoor_degf_err_thres = float
|
17
20
|
self.supply_degf_err_thres = float
|
@@ -25,44 +28,65 @@ class FaultConditionNine(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 = [
|
34
|
-
self.economizer_sig_col,
|
31
|
+
# Set required columns specific to this fault condition
|
32
|
+
self.required_columns = [
|
33
|
+
self.sat_setpoint_col,
|
34
|
+
self.oat_col,
|
35
35
|
self.cooling_sig_col,
|
36
|
+
self.economizer_sig_col,
|
36
37
|
]
|
37
|
-
self.check_analog_pct(df, columns_to_check)
|
38
38
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
df[self.sat_setpoint_col]
|
43
|
-
- self.delta_t_supply_fan
|
44
|
-
+ self.supply_degf_err_thres
|
45
|
-
)
|
39
|
+
def get_required_columns(self) -> str:
|
40
|
+
"""Returns a string representation of the required columns."""
|
41
|
+
return f"Required columns for FaultConditionNine: {', '.join(self.required_columns)}"
|
46
42
|
|
47
|
-
|
48
|
-
|
49
|
-
#
|
50
|
-
|
51
|
-
& (df[self.cooling_sig_col] < 0.1)
|
52
|
-
)
|
43
|
+
def apply(self, df: pd.DataFrame) -> pd.DataFrame:
|
44
|
+
try:
|
45
|
+
# Ensure all required columns are present
|
46
|
+
self.check_required_columns(df)
|
53
47
|
|
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["fc9_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
|
48
|
+
if self.troubleshoot_mode:
|
49
|
+
self.troubleshoot_cols(df)
|
60
50
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
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
|
+
]
|
56
|
+
self.check_analog_pct(df, columns_to_check)
|
67
57
|
|
68
|
-
|
58
|
+
# Create helper columns
|
59
|
+
df["oat_minus_oaterror"] = df[self.oat_col] - self.outdoor_degf_err_thres
|
60
|
+
df["satsp_delta_saterr"] = (
|
61
|
+
df[self.sat_setpoint_col]
|
62
|
+
- self.delta_t_supply_fan
|
63
|
+
+ self.supply_degf_err_thres
|
64
|
+
)
|
65
|
+
|
66
|
+
df["combined_check"] = (
|
67
|
+
(df["oat_minus_oaterror"] > df["satsp_delta_saterr"])
|
68
|
+
# verify AHU is in OS2 only free cooling mode
|
69
|
+
& (df[self.economizer_sig_col] > self.ahu_min_oa_dpr)
|
70
|
+
& (df[self.cooling_sig_col] < 0.1)
|
71
|
+
)
|
72
|
+
|
73
|
+
# Rolling sum to count consecutive trues
|
74
|
+
rolling_sum = (
|
75
|
+
df["combined_check"].rolling(window=self.rolling_window_size).sum()
|
76
|
+
)
|
77
|
+
# Set flag to 1 if rolling sum equals the window size
|
78
|
+
df["fc9_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
|
79
|
+
|
80
|
+
if self.troubleshoot_mode:
|
81
|
+
print("Troubleshoot mode enabled - not removing helper columns")
|
82
|
+
sys.stdout.flush()
|
83
|
+
del df["oat_minus_oaterror"]
|
84
|
+
del df["satsp_delta_saterr"]
|
85
|
+
del df["combined_check"]
|
86
|
+
|
87
|
+
return df
|
88
|
+
|
89
|
+
except MissingColumnError as e:
|
90
|
+
print(f"Error: {e.message}")
|
91
|
+
sys.stdout.flush()
|
92
|
+
raise e # Re-raise the exception so it can be caught by pytest
|
@@ -1,5 +1,8 @@
|
|
1
1
|
import pandas as pd
|
2
|
-
from open_fdd.air_handling_unit.faults.fault_condition import
|
2
|
+
from open_fdd.air_handling_unit.faults.fault_condition import (
|
3
|
+
FaultCondition,
|
4
|
+
MissingColumnError,
|
5
|
+
)
|
3
6
|
import sys
|
4
7
|
|
5
8
|
|
@@ -9,9 +12,7 @@ class FaultConditionOne(FaultCondition):
|
|
9
12
|
"""
|
10
13
|
|
11
14
|
def __init__(self, dict_):
|
12
|
-
|
13
|
-
:param dict_:
|
14
|
-
"""
|
15
|
+
super().__init__()
|
15
16
|
self.vfd_speed_percent_err_thres = float
|
16
17
|
self.vfd_speed_percent_max = float
|
17
18
|
self.duct_static_inches_err_thres = float
|
@@ -23,38 +24,58 @@ class FaultConditionOne(FaultCondition):
|
|
23
24
|
|
24
25
|
self.set_attributes(dict_)
|
25
26
|
|
27
|
+
# Set required columns specific to this fault condition manually
|
28
|
+
self.required_columns = [
|
29
|
+
self.duct_static_col,
|
30
|
+
self.supply_vfd_speed_col,
|
31
|
+
self.duct_static_setpoint_col,
|
32
|
+
]
|
33
|
+
|
34
|
+
def get_required_columns(self) -> str:
|
35
|
+
"""Returns a string representation of the required columns."""
|
36
|
+
return f"Required columns for FaultConditionOne: {', '.join(self.required_columns)}"
|
37
|
+
|
26
38
|
def apply(self, df: pd.DataFrame) -> pd.DataFrame:
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
# Rolling sum to count consecutive trues
|
47
|
-
rolling_sum = (
|
48
|
-
df["combined_check"].rolling(window=self.rolling_window_size).sum()
|
49
|
-
)
|
50
|
-
# Set flag to 1 if rolling sum equals the window size
|
51
|
-
df["fc1_flag"] = (rolling_sum == self.rolling_window_size).astype(int)
|
52
|
-
|
53
|
-
if self.troubleshoot_mode:
|
54
|
-
print("Troubleshoot mode enabled - not removing helper columns")
|
55
|
-
sys.stdout.flush()
|
56
|
-
del df["static_check_"]
|
57
|
-
del df["fan_check_"]
|
58
|
-
del df["combined_check"]
|
39
|
+
try:
|
40
|
+
# Ensure all required columns are present
|
41
|
+
self.check_required_columns(df)
|
42
|
+
|
43
|
+
if self.troubleshoot_mode:
|
44
|
+
self.troubleshoot_cols(df)
|
45
|
+
|
46
|
+
# Check analog outputs [data with units of %] are floats only
|
47
|
+
columns_to_check = [self.supply_vfd_speed_col]
|
48
|
+
self.check_analog_pct(df, columns_to_check)
|
49
|
+
|
50
|
+
df["static_check_"] = (
|
51
|
+
df[self.duct_static_col]
|
52
|
+
< df[self.duct_static_setpoint_col] - self.duct_static_inches_err_thres
|
53
|
+
)
|
54
|
+
df["fan_check_"] = (
|
55
|
+
df[self.supply_vfd_speed_col]
|
56
|
+
>= self.vfd_speed_percent_max - self.vfd_speed_percent_err_thres
|
57
|
+
)
|
59
58
|
|
60
|
-
|
59
|
+
# Combined condition check
|
60
|
+
df["combined_check"] = df["static_check_"] & df["fan_check_"]
|
61
|
+
|
62
|
+
# Rolling sum to count consecutive trues
|
63
|
+
rolling_sum = (
|
64
|
+
df["combined_check"].rolling(window=self.rolling_window_size).sum()
|
65
|
+
)
|
66
|
+
# Set flag to 1 if rolling sum equals the window size
|
67
|
+
df["fc1_flag"] = (rolling_sum == self.rolling_window_size).astype(int)
|
68
|
+
|
69
|
+
if self.troubleshoot_mode:
|
70
|
+
print("Troubleshoot mode enabled - not removing helper columns")
|
71
|
+
sys.stdout.flush()
|
72
|
+
del df["static_check_"]
|
73
|
+
del df["fan_check_"]
|
74
|
+
del df["combined_check"]
|
75
|
+
|
76
|
+
return df
|
77
|
+
|
78
|
+
except MissingColumnError as e:
|
79
|
+
print(f"Error: {e.message}")
|
80
|
+
sys.stdout.flush()
|
81
|
+
raise e # Re-raise the exception so it can be caught by pytest
|