open-fdd 0.1.4__py3-none-any.whl → 0.1.6__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 +301 -301
- open_fdd/air_handling_unit/reports/__init__.py +988 -0
- open_fdd/air_handling_unit/reports/fault_report.py +42 -0
- open_fdd/tests/ahu/test_ahu_fc16.py +190 -0
- {open_fdd-0.1.4.dist-info → open_fdd-0.1.6.dist-info}/METADATA +21 -12
- open_fdd-0.1.6.dist-info/RECORD +31 -0
- {open_fdd-0.1.4.dist-info → open_fdd-0.1.6.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.4.dist-info/RECORD +0 -82
- {open_fdd-0.1.4.dist-info → open_fdd-0.1.6.dist-info}/LICENSE +0 -0
- {open_fdd-0.1.4.dist-info → open_fdd-0.1.6.dist-info}/top_level.txt +0 -0
@@ -1,119 +0,0 @@
|
|
1
|
-
import matplotlib.pyplot as plt
|
2
|
-
import pandas as pd
|
3
|
-
import sys
|
4
|
-
|
5
|
-
|
6
|
-
class FaultCodeThreeReport:
|
7
|
-
def __init__(self, config):
|
8
|
-
self.mix_degf_err_thres = config["MIX_DEGF_ERR_THRES"]
|
9
|
-
self.return_degf_err_thres = config["RETURN_DEGF_ERR_THRES"]
|
10
|
-
self.outdoor_degf_err_thres = config["OUTDOOR_DEGF_ERR_THRES"]
|
11
|
-
self.mat_col = config["MAT_COL"]
|
12
|
-
self.rat_col = config["RAT_COL"]
|
13
|
-
self.oat_col = config["OAT_COL"]
|
14
|
-
self.supply_vfd_speed_col = config["SUPPLY_VFD_SPEED_COL"]
|
15
|
-
|
16
|
-
def create_plot(self, df: pd.DataFrame, output_col: str = None):
|
17
|
-
if output_col is None:
|
18
|
-
output_col = "fc3_flag"
|
19
|
-
|
20
|
-
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(25, 8))
|
21
|
-
fig.suptitle("Fault Conditions 3 Plot")
|
22
|
-
|
23
|
-
ax1.plot(df.index, df[self.mat_col], color="r", label="Mix Temp")
|
24
|
-
ax1.plot(df.index, df[self.rat_col], color="b", label="Return Temp")
|
25
|
-
ax1.plot(df.index, df[self.oat_col], color="g", label="Out Temp")
|
26
|
-
ax1.legend(loc="best")
|
27
|
-
ax1.set_ylabel("°F")
|
28
|
-
|
29
|
-
ax2.plot(df.index, df[output_col], label="Fault", color="k")
|
30
|
-
ax2.set_xlabel("Date")
|
31
|
-
ax2.set_ylabel("Fault Flags")
|
32
|
-
ax2.legend(loc="best")
|
33
|
-
|
34
|
-
plt.tight_layout(rect=[0, 0.03, 1, 0.95])
|
35
|
-
plt.show()
|
36
|
-
plt.close()
|
37
|
-
|
38
|
-
def summarize_fault_times(self, df: pd.DataFrame, output_col: str = None) -> dict:
|
39
|
-
if output_col is None:
|
40
|
-
output_col = "fc3_flag"
|
41
|
-
|
42
|
-
delta = df.index.to_series().diff()
|
43
|
-
summary = {
|
44
|
-
"total_days": round(delta.sum() / pd.Timedelta(days=1), 2),
|
45
|
-
"total_hours": round(delta.sum() / pd.Timedelta(hours=1)),
|
46
|
-
"hours_fc3_mode": round(
|
47
|
-
(delta * df[output_col]).sum() / pd.Timedelta(hours=1)
|
48
|
-
),
|
49
|
-
"percent_true": round(df[output_col].mean() * 100, 2),
|
50
|
-
"percent_false": round((100 - round(df[output_col].mean() * 100, 2)), 2),
|
51
|
-
"flag_true_mat": round(
|
52
|
-
df[self.mat_col].where(df[output_col] == 1).mean(), 2
|
53
|
-
),
|
54
|
-
"flag_true_oat": round(
|
55
|
-
df[self.oat_col].where(df[output_col] == 1).mean(), 2
|
56
|
-
),
|
57
|
-
"flag_true_rat": round(
|
58
|
-
df[self.rat_col].where(df[output_col] == 1).mean(), 2
|
59
|
-
),
|
60
|
-
"hours_motor_runtime": round(
|
61
|
-
(delta * df[self.supply_vfd_speed_col].gt(0.01).astype(int)).sum()
|
62
|
-
/ pd.Timedelta(hours=1),
|
63
|
-
2,
|
64
|
-
),
|
65
|
-
}
|
66
|
-
|
67
|
-
return summary
|
68
|
-
|
69
|
-
def create_hist_plot(self, df: pd.DataFrame, output_col: str = None):
|
70
|
-
if output_col is None:
|
71
|
-
output_col = "fc3_flag"
|
72
|
-
|
73
|
-
df["hour_of_the_day_fc3"] = df.index.hour.where(df[output_col] == 1)
|
74
|
-
|
75
|
-
fig, ax = plt.subplots(tight_layout=True, figsize=(25, 8))
|
76
|
-
ax.hist(df.hour_of_the_day_fc3.dropna())
|
77
|
-
ax.set_xlabel("24 Hour Number in Day")
|
78
|
-
ax.set_ylabel("Frequency")
|
79
|
-
ax.set_title(f"Hour-Of-Day When Fault Flag 3 is TRUE")
|
80
|
-
plt.show()
|
81
|
-
plt.close()
|
82
|
-
|
83
|
-
def display_report_in_ipython(self, df: pd.DataFrame, output_col: str = "fc3_flag"):
|
84
|
-
print(
|
85
|
-
"Fault Condition 3: Mix temperature too high; should be between outside and return air"
|
86
|
-
)
|
87
|
-
|
88
|
-
self.create_plot(df, output_col)
|
89
|
-
|
90
|
-
summary = self.summarize_fault_times(df, output_col)
|
91
|
-
|
92
|
-
for key, value in summary.items():
|
93
|
-
formatted_key = key.replace("_", " ")
|
94
|
-
print(f"{formatted_key}: {value}")
|
95
|
-
sys.stdout.flush()
|
96
|
-
|
97
|
-
fc_max_faults_found = df[output_col].max()
|
98
|
-
print("Fault Flag Count: ", fc_max_faults_found)
|
99
|
-
sys.stdout.flush()
|
100
|
-
|
101
|
-
if fc_max_faults_found != 0:
|
102
|
-
self.create_hist_plot(df, output_col)
|
103
|
-
|
104
|
-
print("Mix Air Temp Mean When In Fault: ", summary["flag_true_mat"])
|
105
|
-
print("Outside Air Temp Mean When In Fault: ", summary["flag_true_oat"])
|
106
|
-
print("Return Temp Mean When In Fault: ", summary["flag_true_rat"])
|
107
|
-
|
108
|
-
if summary["percent_true"] > 5.0:
|
109
|
-
print(
|
110
|
-
"The percent True metric that represents the amount of time for when the fault flag is True is high, indicating potential issues with the mix temperature. Verify sensor calibration and investigate possible mechanical problems."
|
111
|
-
)
|
112
|
-
else:
|
113
|
-
print(
|
114
|
-
"The percent True metric that represents the amount of time for when the fault flag is True is low, indicating the system is likely functioning correctly."
|
115
|
-
)
|
116
|
-
|
117
|
-
else:
|
118
|
-
print("NO FAULTS FOUND - Skipping time-of-day Histogram plot")
|
119
|
-
sys.stdout.flush()
|
@@ -1,148 +0,0 @@
|
|
1
|
-
import matplotlib.pyplot as plt
|
2
|
-
import pandas as pd
|
3
|
-
import sys
|
4
|
-
|
5
|
-
|
6
|
-
class FaultCodeFourReport:
|
7
|
-
"""Class provides the definitions for Fault Code 4 Report.
|
8
|
-
Reporting the time series average dataframe that calculates control states per hour.
|
9
|
-
"""
|
10
|
-
|
11
|
-
def __init__(self, config):
|
12
|
-
self.delta_os_max = config["DELTA_OS_MAX"]
|
13
|
-
self.heating_mode_calc_col = "heating_mode"
|
14
|
-
self.econ_only_cooling_mode_calc_col = "econ_only_cooling_mode"
|
15
|
-
self.econ_plus_mech_cooling_mode_calc_col = "econ_plus_mech_cooling_mode"
|
16
|
-
self.mech_cooling_only_mode_calc_col = "mech_cooling_only_mode"
|
17
|
-
|
18
|
-
def summarize_fault_times(
|
19
|
-
self, df: pd.DataFrame, output_col: str = "fc4_flag"
|
20
|
-
) -> dict:
|
21
|
-
delta_all_data = df.index.to_series().diff()
|
22
|
-
|
23
|
-
total_days_all_data = round(delta_all_data.sum() / pd.Timedelta(days=1), 2)
|
24
|
-
total_hours_all_data = round(delta_all_data.sum() / pd.Timedelta(hours=1))
|
25
|
-
hours_fc4_mode = round(
|
26
|
-
(delta_all_data * df[output_col]).sum() / pd.Timedelta(hours=1)
|
27
|
-
)
|
28
|
-
percent_true_fc4 = round(df[output_col].mean() * 100, 2)
|
29
|
-
percent_false_fc4 = round((100 - percent_true_fc4), 2)
|
30
|
-
|
31
|
-
# Heating mode runtime stats
|
32
|
-
delta_heating = df[self.heating_mode_calc_col].index.to_series().diff()
|
33
|
-
total_hours_heating = (
|
34
|
-
delta_heating * df[self.heating_mode_calc_col]
|
35
|
-
).sum() / pd.Timedelta(hours=1)
|
36
|
-
percent_heating = round(df[self.heating_mode_calc_col].mean() * 100, 2)
|
37
|
-
|
38
|
-
# Econ mode runtime stats
|
39
|
-
delta_econ = df[self.econ_only_cooling_mode_calc_col].index.to_series().diff()
|
40
|
-
total_hours_econ = (
|
41
|
-
delta_econ * df[self.econ_only_cooling_mode_calc_col]
|
42
|
-
).sum() / pd.Timedelta(hours=1)
|
43
|
-
percent_econ = round(df[self.econ_only_cooling_mode_calc_col].mean() * 100, 2)
|
44
|
-
|
45
|
-
# Econ plus mech cooling mode runtime stats
|
46
|
-
delta_econ_clg = (
|
47
|
-
df[self.econ_plus_mech_cooling_mode_calc_col].index.to_series().diff()
|
48
|
-
)
|
49
|
-
total_hours_econ_clg = (
|
50
|
-
delta_econ_clg * df[self.econ_plus_mech_cooling_mode_calc_col]
|
51
|
-
).sum() / pd.Timedelta(hours=1)
|
52
|
-
percent_econ_clg = round(
|
53
|
-
df[self.econ_plus_mech_cooling_mode_calc_col].mean() * 100, 2
|
54
|
-
)
|
55
|
-
|
56
|
-
# Mech cooling mode runtime stats
|
57
|
-
delta_clg = df[self.mech_cooling_only_mode_calc_col].index.to_series().diff()
|
58
|
-
total_hours_clg = (
|
59
|
-
delta_clg * df[self.mech_cooling_only_mode_calc_col]
|
60
|
-
).sum() / pd.Timedelta(hours=1)
|
61
|
-
percent_clg = round(df[self.mech_cooling_only_mode_calc_col].mean() * 100, 2)
|
62
|
-
|
63
|
-
return {
|
64
|
-
"total_days": total_days_all_data,
|
65
|
-
"total_hours": total_hours_all_data,
|
66
|
-
"hours_in_fault": hours_fc4_mode,
|
67
|
-
"percent_of_time_in_fault": percent_true_fc4,
|
68
|
-
"percent_of_time_not_in_fault": percent_false_fc4,
|
69
|
-
"percent_of_time_AHU_in_mech_clg_mode": percent_clg,
|
70
|
-
"percent_of_time_AHU_in_econ_plus_mech_clg_mode": percent_econ_clg,
|
71
|
-
"percent_of_time_AHU_in_econ_free_clg_mode": percent_econ,
|
72
|
-
"percent_of_time_AHU_in_heating_mode": percent_heating,
|
73
|
-
"total_hours_heating_mode": total_hours_heating,
|
74
|
-
"total_hours_econ_mode": total_hours_econ,
|
75
|
-
"total_hours_econ_mech_clg_mode": total_hours_econ_clg,
|
76
|
-
"total_hours_mech_clg_mode": total_hours_clg,
|
77
|
-
}
|
78
|
-
|
79
|
-
def create_plot(self, df: pd.DataFrame, output_col: str = "fc4_flag"):
|
80
|
-
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(25, 8))
|
81
|
-
fig.suptitle("Fault Condition 4 Plots")
|
82
|
-
|
83
|
-
ax1.plot(df.index, df[self.heating_mode_calc_col], label="Heat", color="orange")
|
84
|
-
ax1.plot(
|
85
|
-
df.index,
|
86
|
-
df[self.econ_only_cooling_mode_calc_col],
|
87
|
-
label="Econ Clg",
|
88
|
-
color="olive",
|
89
|
-
)
|
90
|
-
ax1.plot(
|
91
|
-
df.index,
|
92
|
-
df[self.econ_plus_mech_cooling_mode_calc_col],
|
93
|
-
label="Econ + Mech Clg",
|
94
|
-
color="c",
|
95
|
-
)
|
96
|
-
ax1.plot(
|
97
|
-
df.index,
|
98
|
-
df[self.mech_cooling_only_mode_calc_col],
|
99
|
-
label="Mech Clg",
|
100
|
-
color="m",
|
101
|
-
)
|
102
|
-
ax1.set_xlabel("Date")
|
103
|
-
ax1.set_ylabel("Calculated AHU Operating States")
|
104
|
-
ax1.legend(loc="best")
|
105
|
-
|
106
|
-
ax2.plot(df.index, df[output_col], label="Fault", color="k")
|
107
|
-
ax2.set_xlabel("Date")
|
108
|
-
ax2.set_ylabel("Fault Flags")
|
109
|
-
ax2.legend(loc="best")
|
110
|
-
|
111
|
-
plt.tight_layout()
|
112
|
-
plt.show()
|
113
|
-
plt.close()
|
114
|
-
|
115
|
-
def create_hist_plot(self, df: pd.DataFrame, output_col: str = "fc4_flag"):
|
116
|
-
df["hour_of_the_day"] = df.index.hour.where(df[output_col] == 1)
|
117
|
-
df = df.dropna(subset=["hour_of_the_day"])
|
118
|
-
print("\nTime-of-day Histogram Data")
|
119
|
-
print(df["hour_of_the_day"])
|
120
|
-
print()
|
121
|
-
sys.stdout.flush()
|
122
|
-
|
123
|
-
fig, ax = plt.subplots(tight_layout=True, figsize=(25, 8))
|
124
|
-
ax.hist(df.hour_of_the_day.dropna(), bins=24)
|
125
|
-
ax.set_xlabel("Hour of the Day")
|
126
|
-
ax.set_ylabel("Frequency")
|
127
|
-
ax.set_title("Hour-Of-Day When Fault Flag is TRUE")
|
128
|
-
plt.show()
|
129
|
-
plt.close()
|
130
|
-
|
131
|
-
def display_report_in_ipython(self, df: pd.DataFrame, output_col: str = "fc4_flag"):
|
132
|
-
print("Fault Condition 4: Hunting too many OS state changes")
|
133
|
-
|
134
|
-
self.create_plot(df, output_col)
|
135
|
-
|
136
|
-
summary = self.summarize_fault_times(df, output_col)
|
137
|
-
for key, value in summary.items():
|
138
|
-
formatted_key = key.replace("_", " ")
|
139
|
-
print(f"{formatted_key}: {value}")
|
140
|
-
sys.stdout.flush()
|
141
|
-
|
142
|
-
fc_max_faults_found = df[output_col].max()
|
143
|
-
print("Fault Flag Count: ", fc_max_faults_found)
|
144
|
-
sys.stdout.flush()
|
145
|
-
|
146
|
-
if fc_max_faults_found != 0:
|
147
|
-
self.create_hist_plot(df, output_col)
|
148
|
-
sys.stdout.flush()
|
@@ -1,132 +0,0 @@
|
|
1
|
-
import matplotlib.pyplot as plt
|
2
|
-
import pandas as pd
|
3
|
-
import numpy as np
|
4
|
-
import sys
|
5
|
-
|
6
|
-
|
7
|
-
class FaultCodeFiveReport:
|
8
|
-
"""Class provides the definitions for Fault Condition 5 Report."""
|
9
|
-
|
10
|
-
def __init__(self, config):
|
11
|
-
self.mix_degf_err_thres = config["MIX_DEGF_ERR_THRES"]
|
12
|
-
self.supply_degf_err_thres = config["SUPPLY_DEGF_ERR_THRES"]
|
13
|
-
self.delta_t_supply_fan = config["DELTA_T_SUPPLY_FAN"]
|
14
|
-
self.mat_col = config["MAT_COL"]
|
15
|
-
self.sat_col = config["SAT_COL"]
|
16
|
-
self.heating_sig_col = config["HEATING_SIG_COL"]
|
17
|
-
self.supply_vfd_speed_col = config["SUPPLY_VFD_SPEED_COL"]
|
18
|
-
|
19
|
-
def create_plot(self, df: pd.DataFrame, output_col: str = None):
|
20
|
-
if output_col is None:
|
21
|
-
output_col = "fc5_flag"
|
22
|
-
|
23
|
-
fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(25, 8))
|
24
|
-
fig.suptitle("Fault Conditions 5 Plot")
|
25
|
-
|
26
|
-
ax1.plot(df.index, df[self.mat_col], color="g", label="Mix Temp")
|
27
|
-
ax1.plot(df.index, df[self.sat_col], color="b", label="Supply Temp")
|
28
|
-
ax1.legend(loc="best")
|
29
|
-
ax1.set_ylabel("°F")
|
30
|
-
|
31
|
-
ax2.plot(df.index, df[self.heating_sig_col], label="Htg Valve", color="r")
|
32
|
-
ax2.set_xlabel("Date")
|
33
|
-
ax2.set_ylabel("%")
|
34
|
-
ax2.legend(loc="best")
|
35
|
-
|
36
|
-
ax3.plot(df.index, df[output_col], label="Fault", color="k")
|
37
|
-
ax3.set_xlabel("Date")
|
38
|
-
ax3.set_ylabel("Fault Flags")
|
39
|
-
ax3.legend(loc="best")
|
40
|
-
|
41
|
-
plt.tight_layout(rect=[0, 0.03, 1, 0.95])
|
42
|
-
plt.show()
|
43
|
-
plt.close()
|
44
|
-
|
45
|
-
def summarize_fault_times(self, df: pd.DataFrame, output_col: str = None) -> dict:
|
46
|
-
if output_col is None:
|
47
|
-
output_col = "fc5_flag"
|
48
|
-
|
49
|
-
delta = df.index.to_series().diff()
|
50
|
-
summary = {
|
51
|
-
"total_days": round(delta.sum() / pd.Timedelta(days=1), 2),
|
52
|
-
"total_hours": round(delta.sum() / pd.Timedelta(hours=1)),
|
53
|
-
"hours_fc5_mode": round(
|
54
|
-
(delta * df[output_col]).sum() / pd.Timedelta(hours=1)
|
55
|
-
),
|
56
|
-
"percent_true": round(df[output_col].mean() * 100, 2),
|
57
|
-
"percent_false": round((100 - round(df[output_col].mean() * 100, 2)), 2),
|
58
|
-
"flag_true_mat": round(
|
59
|
-
df[self.mat_col].where(df[output_col] == 1).mean(), 2
|
60
|
-
),
|
61
|
-
"flag_true_sat": round(
|
62
|
-
df[self.sat_col].where(df[output_col] == 1).mean(), 2
|
63
|
-
),
|
64
|
-
"hours_motor_runtime": round(
|
65
|
-
(delta * df[self.supply_vfd_speed_col].gt(0.01).astype(int)).sum()
|
66
|
-
/ pd.Timedelta(hours=1),
|
67
|
-
2,
|
68
|
-
),
|
69
|
-
}
|
70
|
-
|
71
|
-
return summary
|
72
|
-
|
73
|
-
def create_hist_plot(self, df: pd.DataFrame, output_col: str = None):
|
74
|
-
if output_col is None:
|
75
|
-
output_col = "fc5_flag"
|
76
|
-
|
77
|
-
# Calculate dataset statistics
|
78
|
-
df["hour_of_the_day_fc5"] = df.index.hour.where(df[output_col] == 1)
|
79
|
-
|
80
|
-
# Make hist plots fc5
|
81
|
-
fig, ax = plt.subplots(tight_layout=True, figsize=(25, 8))
|
82
|
-
ax.hist(df.hour_of_the_day_fc5.dropna())
|
83
|
-
ax.set_xlabel("24 Hour Number in Day")
|
84
|
-
ax.set_ylabel("Frequency")
|
85
|
-
ax.set_title(f"Hour-Of-Day When Fault Flag 5 is TRUE")
|
86
|
-
plt.show()
|
87
|
-
plt.close()
|
88
|
-
|
89
|
-
def display_report_in_ipython(self, df: pd.DataFrame, output_col: str = "fc5_flag"):
|
90
|
-
# Display report content in IPython
|
91
|
-
print(
|
92
|
-
"Fault Condition 5: Supply air temperature too low; should be higher than mix air"
|
93
|
-
)
|
94
|
-
|
95
|
-
# Display plot
|
96
|
-
self.create_plot(df, output_col)
|
97
|
-
|
98
|
-
# Display summary statistics
|
99
|
-
summary = self.summarize_fault_times(df, output_col)
|
100
|
-
|
101
|
-
for key, value in summary.items():
|
102
|
-
formatted_key = key.replace("_", " ")
|
103
|
-
print(f"{formatted_key}: {value}")
|
104
|
-
sys.stdout.flush()
|
105
|
-
|
106
|
-
fc_max_faults_found = df[output_col].max()
|
107
|
-
print("Fault Flag Count: ", fc_max_faults_found)
|
108
|
-
sys.stdout.flush()
|
109
|
-
|
110
|
-
if fc_max_faults_found != 0:
|
111
|
-
self.create_hist_plot(df, output_col)
|
112
|
-
|
113
|
-
flag_true_mat = round(df[self.mat_col].where(df[output_col] == 1).mean(), 2)
|
114
|
-
print("Mix Air Temp Mean When In Fault: ", flag_true_mat)
|
115
|
-
|
116
|
-
flag_true_sat = round(df[self.sat_col].where(df[output_col] == 1).mean(), 2)
|
117
|
-
print("Supply Air Temp Mean When In Fault: ", flag_true_sat)
|
118
|
-
|
119
|
-
sys.stdout.flush()
|
120
|
-
|
121
|
-
if summary["percent_true"] > 5.0:
|
122
|
-
print(
|
123
|
-
"The percent True metric that represents the amount of time for when the fault flag is True is high indicating the AHU temperature sensors for either the supply or mix temperature are out of calibration. Verify the mixing temperature sensor is not a probe type sensor but a long averaging type sensor that is installed properly inside the AHU mixing chamber to get a good solid true reading of the actual air mixing temperature. Poor duct design may also contribute to not having good air mixing, to troubleshoot install data loggers inside the mixing chamber or take measurements when the AHU is running of different locations in the mixing chamber to spot where better air blending needs to take place."
|
124
|
-
)
|
125
|
-
else:
|
126
|
-
print(
|
127
|
-
"The percent True metric that represents the amount of time for when the fault flag is True is low indicating the AHU temperature sensors are within calibration."
|
128
|
-
)
|
129
|
-
|
130
|
-
else:
|
131
|
-
print("NO FAULTS FOUND - Skipping time-of-day Histogram plot")
|
132
|
-
sys.stdout.flush()
|
@@ -1,156 +0,0 @@
|
|
1
|
-
import matplotlib.pyplot as plt
|
2
|
-
import pandas as pd
|
3
|
-
import numpy as np
|
4
|
-
import sys
|
5
|
-
|
6
|
-
|
7
|
-
class FaultCodeSixReport:
|
8
|
-
"""Class provides the definitions for Fault Condition 6 Report."""
|
9
|
-
|
10
|
-
def __init__(self, config):
|
11
|
-
self.supply_fan_air_volume_col = config["SUPPLY_FAN_AIR_VOLUME_COL"]
|
12
|
-
self.mat_col = config["MAT_COL"]
|
13
|
-
self.oat_col = config["OAT_COL"]
|
14
|
-
self.rat_col = config["RAT_COL"]
|
15
|
-
self.supply_vfd_speed_col = config["SUPPLY_VFD_SPEED_COL"]
|
16
|
-
|
17
|
-
def create_plot(self, df: pd.DataFrame, output_col: str = None):
|
18
|
-
if output_col is None:
|
19
|
-
output_col = "fc6_flag"
|
20
|
-
|
21
|
-
fig, (ax1, ax2, ax3, ax4, ax5) = plt.subplots(5, 1, figsize=(25, 8))
|
22
|
-
fig.suptitle("Fault Conditions 6 Plot")
|
23
|
-
|
24
|
-
ax1.plot(df.index, df["rat_minus_oat"], label="Rat Minus Oat")
|
25
|
-
ax1.legend(loc="best")
|
26
|
-
ax1.set_ylabel("°F")
|
27
|
-
|
28
|
-
ax2.plot(
|
29
|
-
df.index,
|
30
|
-
df[self.supply_fan_air_volume_col],
|
31
|
-
label="Total Air Flow",
|
32
|
-
color="r",
|
33
|
-
)
|
34
|
-
ax2.set_xlabel("Date")
|
35
|
-
ax2.set_ylabel("CFM")
|
36
|
-
ax2.legend(loc="best")
|
37
|
-
|
38
|
-
ax3.plot(df.index, df["percent_oa_calc"], label="OA Frac Calc", color="m")
|
39
|
-
ax3.plot(df.index, df["perc_OAmin"], label="OA Perc Min Calc", color="y")
|
40
|
-
ax3.set_xlabel("Date")
|
41
|
-
ax3.set_ylabel("%")
|
42
|
-
ax3.legend(loc="best")
|
43
|
-
|
44
|
-
ax4.plot(
|
45
|
-
df.index,
|
46
|
-
df["percent_oa_calc_minus_perc_OAmin"],
|
47
|
-
label="OA Error Frac Vs Perc Min Calc",
|
48
|
-
color="g",
|
49
|
-
)
|
50
|
-
ax4.set_xlabel("Date")
|
51
|
-
ax4.set_ylabel("%")
|
52
|
-
ax4.legend(loc="best")
|
53
|
-
|
54
|
-
ax5.plot(df.index, df[output_col], label="Fault", color="k")
|
55
|
-
ax5.set_xlabel("Date")
|
56
|
-
ax5.set_ylabel("Fault Flags")
|
57
|
-
ax5.legend(loc="best")
|
58
|
-
|
59
|
-
plt.tight_layout(rect=[0, 0.03, 1, 0.95])
|
60
|
-
plt.show()
|
61
|
-
plt.close()
|
62
|
-
|
63
|
-
def summarize_fault_times(self, df: pd.DataFrame, output_col: str = None) -> dict:
|
64
|
-
if output_col is None:
|
65
|
-
output_col = "fc6_flag"
|
66
|
-
|
67
|
-
delta = df.index.to_series().diff()
|
68
|
-
summary = {
|
69
|
-
"total_days": round(delta.sum() / pd.Timedelta(days=1), 2),
|
70
|
-
"total_hours": round(delta.sum() / pd.Timedelta(hours=1)),
|
71
|
-
"hours_fc6_mode": round(
|
72
|
-
(delta * df[output_col]).sum() / pd.Timedelta(hours=1)
|
73
|
-
),
|
74
|
-
"percent_true": round(df[output_col].mean() * 100, 2),
|
75
|
-
"percent_false": round((100 - round(df[output_col].mean() * 100, 2)), 2),
|
76
|
-
"flag_true_mat": round(
|
77
|
-
df[self.mat_col].where(df[output_col] == 1).mean(), 2
|
78
|
-
),
|
79
|
-
"flag_true_rat": round(
|
80
|
-
df[self.rat_col].where(df[output_col] == 1).mean(), 2
|
81
|
-
),
|
82
|
-
"flag_true_oat": round(
|
83
|
-
df[self.oat_col].where(df[output_col] == 1).mean(), 2
|
84
|
-
),
|
85
|
-
"hours_motor_runtime": round(
|
86
|
-
(delta * df[self.supply_vfd_speed_col].gt(0.01).astype(int)).sum()
|
87
|
-
/ pd.Timedelta(hours=1),
|
88
|
-
2,
|
89
|
-
),
|
90
|
-
}
|
91
|
-
|
92
|
-
return summary
|
93
|
-
|
94
|
-
def create_hist_plot(self, df: pd.DataFrame, output_col: str = None):
|
95
|
-
if output_col is None:
|
96
|
-
output_col = "fc6_flag"
|
97
|
-
|
98
|
-
# Calculate dataset statistics
|
99
|
-
df["hour_of_the_day_fc6"] = df.index.hour.where(df[output_col] == 1)
|
100
|
-
|
101
|
-
# Make hist plots fc6
|
102
|
-
fig, ax = plt.subplots(tight_layout=True, figsize=(25, 8))
|
103
|
-
ax.hist(df.hour_of_the_day_fc6.dropna())
|
104
|
-
ax.set_xlabel("24 Hour Number in Day")
|
105
|
-
ax.set_ylabel("Frequency")
|
106
|
-
ax.set_title(f"Hour-Of-Day When Fault Flag 6 is TRUE")
|
107
|
-
plt.show()
|
108
|
-
plt.close()
|
109
|
-
|
110
|
-
def display_report_in_ipython(self, df: pd.DataFrame, output_col: str = "fc6_flag"):
|
111
|
-
# Display report content in IPython
|
112
|
-
print(
|
113
|
-
"Fault Condition 6: OA fraction too low or too high; should equal to design % outdoor air requirement"
|
114
|
-
)
|
115
|
-
|
116
|
-
# Display plot
|
117
|
-
self.create_plot(df, output_col)
|
118
|
-
|
119
|
-
# Display summary statistics
|
120
|
-
summary = self.summarize_fault_times(df, output_col)
|
121
|
-
|
122
|
-
for key, value in summary.items():
|
123
|
-
formatted_key = key.replace("_", " ")
|
124
|
-
print(f"{formatted_key}: {value}")
|
125
|
-
sys.stdout.flush()
|
126
|
-
|
127
|
-
fc_max_faults_found = df[output_col].max()
|
128
|
-
print("Fault Flag Count: ", fc_max_faults_found)
|
129
|
-
sys.stdout.flush()
|
130
|
-
|
131
|
-
if fc_max_faults_found != 0:
|
132
|
-
self.create_hist_plot(df, output_col)
|
133
|
-
|
134
|
-
flag_true_mat = round(df[self.mat_col].where(df[output_col] == 1).mean(), 2)
|
135
|
-
print("Mix Air Temp Mean When In Fault: ", flag_true_mat)
|
136
|
-
|
137
|
-
flag_true_rat = round(df[self.rat_col].where(df[output_col] == 1).mean(), 2)
|
138
|
-
print("Return Air Temp Mean When In Fault: ", flag_true_rat)
|
139
|
-
|
140
|
-
flag_true_oat = round(df[self.oat_col].where(df[output_col] == 1).mean(), 2)
|
141
|
-
print("Outside Air Temp Mean When In Fault: ", flag_true_oat)
|
142
|
-
|
143
|
-
sys.stdout.flush()
|
144
|
-
|
145
|
-
if summary["percent_true"] > 5.0:
|
146
|
-
print(
|
147
|
-
"The percent True metric maybe yielding sensors are out of calibration either on the AHU outside, mix, or return air temperature sensors that handle the OA fraction calculation or the totalized air flow calculation handled by a totalizing all VAV box air flows or AHU AFMS. Air flow and/or AHU temperature sensor may require recalibration."
|
148
|
-
)
|
149
|
-
else:
|
150
|
-
print(
|
151
|
-
"The percent True metric that represents the amount of time for when the fault flag is True is low indicating the sensors are within calibration."
|
152
|
-
)
|
153
|
-
|
154
|
-
else:
|
155
|
-
print("NO FAULTS FOUND - Skipping time-of-day Histogram plot")
|
156
|
-
sys.stdout.flush()
|
@@ -1,126 +0,0 @@
|
|
1
|
-
import matplotlib.pyplot as plt
|
2
|
-
import pandas as pd
|
3
|
-
import sys
|
4
|
-
|
5
|
-
|
6
|
-
class FaultCodeSevenReport:
|
7
|
-
"""Class provides the definitions for Fault Condition 7 Report.
|
8
|
-
Very similar to FC 13 but uses heating valve
|
9
|
-
"""
|
10
|
-
|
11
|
-
def __init__(self, config):
|
12
|
-
self.sat_col = config["SAT_COL"]
|
13
|
-
self.sat_setpoint_col = config["SAT_SETPOINT_COL"]
|
14
|
-
self.heating_sig_col = config["HEATING_SIG_COL"]
|
15
|
-
self.supply_vfd_speed_col = config["SUPPLY_VFD_SPEED_COL"]
|
16
|
-
|
17
|
-
def create_plot(self, df: pd.DataFrame, output_col: str = None):
|
18
|
-
if output_col is None:
|
19
|
-
output_col = "fc7_flag"
|
20
|
-
|
21
|
-
fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(25, 8))
|
22
|
-
fig.suptitle("Fault Conditions 7 Plot")
|
23
|
-
|
24
|
-
ax1.plot(df.index, df[self.sat_col], label="SAT")
|
25
|
-
ax1.plot(df.index, df[self.sat_setpoint_col], label="SATsp")
|
26
|
-
ax1.legend(loc="best")
|
27
|
-
ax1.set_ylabel("AHU Supply Temps °F")
|
28
|
-
|
29
|
-
ax2.plot(df.index, df[self.heating_sig_col], color="r", label="AHU Heat Vlv")
|
30
|
-
ax2.legend(loc="best")
|
31
|
-
ax2.set_ylabel("%")
|
32
|
-
|
33
|
-
ax3.plot(df.index, df[output_col], label="Fault", color="k")
|
34
|
-
ax3.set_xlabel("Date")
|
35
|
-
ax3.set_ylabel("Fault Flags")
|
36
|
-
ax3.legend(loc="best")
|
37
|
-
|
38
|
-
plt.tight_layout(rect=[0, 0.03, 1, 0.95])
|
39
|
-
plt.show()
|
40
|
-
plt.close()
|
41
|
-
|
42
|
-
def summarize_fault_times(self, df: pd.DataFrame, output_col: str = None) -> dict:
|
43
|
-
if output_col is None:
|
44
|
-
output_col = "fc7_flag"
|
45
|
-
|
46
|
-
delta = df.index.to_series().diff()
|
47
|
-
summary = {
|
48
|
-
"total_days": round(delta.sum() / pd.Timedelta(days=1), 2),
|
49
|
-
"total_hours": round(delta.sum() / pd.Timedelta(hours=1)),
|
50
|
-
"hours_fc7_mode": round(
|
51
|
-
(delta * df[output_col]).sum() / pd.Timedelta(hours=1)
|
52
|
-
),
|
53
|
-
"percent_true": round(df[output_col].mean() * 100, 2),
|
54
|
-
"percent_false": round((100 - round(df[output_col].mean() * 100, 2)), 2),
|
55
|
-
"flag_true_satsp": round(
|
56
|
-
df[self.sat_setpoint_col].where(df[output_col] == 1).mean(), 2
|
57
|
-
),
|
58
|
-
"flag_true_sat": round(
|
59
|
-
df[self.sat_col].where(df[output_col] == 1).mean(), 2
|
60
|
-
),
|
61
|
-
"hours_motor_runtime": round(
|
62
|
-
(delta * df[self.supply_vfd_speed_col].gt(0.01).astype(int)).sum()
|
63
|
-
/ pd.Timedelta(hours=1),
|
64
|
-
2,
|
65
|
-
),
|
66
|
-
}
|
67
|
-
|
68
|
-
return summary
|
69
|
-
|
70
|
-
def create_hist_plot(self, df: pd.DataFrame, output_col: str = None):
|
71
|
-
if output_col is None:
|
72
|
-
output_col = "fc7_flag"
|
73
|
-
|
74
|
-
df["hour_of_the_day_fc7"] = df.index.hour.where(df[output_col] == 1)
|
75
|
-
|
76
|
-
fig, ax = plt.subplots(tight_layout=True, figsize=(25, 8))
|
77
|
-
ax.hist(df.hour_of_the_day_fc7.dropna())
|
78
|
-
ax.set_xlabel("24 Hour Number in Day")
|
79
|
-
ax.set_ylabel("Frequency")
|
80
|
-
ax.set_title(f"Hour-Of-Day When Fault Flag 7 is TRUE")
|
81
|
-
plt.show()
|
82
|
-
plt.close()
|
83
|
-
|
84
|
-
def display_report_in_ipython(self, df: pd.DataFrame, output_col: str = "fc7_flag"):
|
85
|
-
print(
|
86
|
-
"Fault Condition 7: Supply air temperature too low its not making supply air temperature setpoint in full heating mode"
|
87
|
-
)
|
88
|
-
|
89
|
-
self.create_plot(df, output_col)
|
90
|
-
|
91
|
-
summary = self.summarize_fault_times(df, output_col)
|
92
|
-
|
93
|
-
for key, value in summary.items():
|
94
|
-
formatted_key = key.replace("_", " ")
|
95
|
-
print(f"{formatted_key}: {value}")
|
96
|
-
sys.stdout.flush()
|
97
|
-
|
98
|
-
fc_max_faults_found = df[output_col].max()
|
99
|
-
print("Fault Flag Count: ", fc_max_faults_found)
|
100
|
-
sys.stdout.flush()
|
101
|
-
|
102
|
-
if fc_max_faults_found != 0:
|
103
|
-
self.create_hist_plot(df, output_col)
|
104
|
-
|
105
|
-
flag_true_satsp = round(
|
106
|
-
df[self.sat_setpoint_col].where(df[output_col] == 1).mean(), 2
|
107
|
-
)
|
108
|
-
print("Supply Air Temp Setpoint Mean When In Fault: ", flag_true_satsp)
|
109
|
-
|
110
|
-
flag_true_sat = round(df[self.sat_col].where(df[output_col] == 1).mean(), 2)
|
111
|
-
print("Supply Air Temp Mean When In Fault: ", flag_true_sat)
|
112
|
-
|
113
|
-
sys.stdout.flush()
|
114
|
-
|
115
|
-
if summary["percent_true"] > 5.0:
|
116
|
-
print(
|
117
|
-
"The percent True metric that represents the amount of time for when the fault flag is True is high indicating the AHU heating valve may be broken or there could be a flow issue with the amount of hot water flowing through the coil or that the boiler system reset is too aggressive and there isn’t enough heat being produced by this coil. It could be worth viewing mechanical blueprints for this AHU design schedule to see what hot water temperature this coil was designed for and compare it to actual hot water supply temperatures. Consult a mechanical design engineer to rectify if needed."
|
118
|
-
)
|
119
|
-
else:
|
120
|
-
print(
|
121
|
-
"The percent True metric that represents the amount of time for when the fault flag is True is low, indicating the AHU heating valve operates correctly."
|
122
|
-
)
|
123
|
-
|
124
|
-
else:
|
125
|
-
print("NO FAULTS FOUND - Skipping time-of-day Histogram plot")
|
126
|
-
sys.stdout.flush()
|