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,175 +0,0 @@
|
|
1
|
-
import streamlit as st
|
2
|
-
|
3
|
-
|
4
|
-
# On windows 10 python 3.10.6
|
5
|
-
# py -3.10 -m streamlit run .\latex_generator.py
|
6
|
-
|
7
|
-
# display Fault Equation 1
|
8
|
-
st.title("Fault Equation 1")
|
9
|
-
st.caption("Duct static pressure too low with fan at full speed")
|
10
|
-
st.latex(
|
11
|
-
r"""
|
12
|
-
DSP < DPSP - eDSP \quad \text{and} \quad VFDSPD \geq 99\% - eVFDSPD
|
13
|
-
"""
|
14
|
-
)
|
15
|
-
|
16
|
-
# Display legend
|
17
|
-
st.markdown("Legend:")
|
18
|
-
st.markdown("- DSP: Duct Static Pressure")
|
19
|
-
st.markdown("- DPSP: Duct Static Pressure Setpoint")
|
20
|
-
st.markdown("- VFDSPD: VFD Speed Reference in Percent")
|
21
|
-
st.markdown("- eVFDSPD: VFD Speed Reference Error Threshold")
|
22
|
-
|
23
|
-
|
24
|
-
# display Fault Equation 2
|
25
|
-
st.title("Fault Equation 2")
|
26
|
-
st.caption('Mix air temperature too low; should be between outside and return')
|
27
|
-
st.latex(r'''
|
28
|
-
MAT_{avg} + eMAT < \min[(RAT_{avg} - eRAT), - OAT_{avg} - eOAT)]
|
29
|
-
''')
|
30
|
-
|
31
|
-
|
32
|
-
# display Fault Equation 3
|
33
|
-
st.title("Fault Equation 3")
|
34
|
-
st.caption('Mix air temperature too high; should be between outside and return')
|
35
|
-
st.latex(r'''
|
36
|
-
MAT_{avg} - eMAT > \min[(RAT_{avg} + eRAT), - OAT_{avg} + eOAT)]
|
37
|
-
''')
|
38
|
-
|
39
|
-
|
40
|
-
# display Fault Equation 4
|
41
|
-
st.title("Fault Equation 4")
|
42
|
-
st.caption('Too many AHU operating state changes due to PID hunting and/or excessive cycling during low load conditions.')
|
43
|
-
st.latex(r'''
|
44
|
-
\Delta OS > \Delta OS_{max}
|
45
|
-
''')
|
46
|
-
|
47
|
-
|
48
|
-
# display Fault Equation 5
|
49
|
-
st.title("Fault Equation 5")
|
50
|
-
st.caption('Supply air temperature too high')
|
51
|
-
st.latex(r'''
|
52
|
-
SAT_{avg} + eSAT \leq MAT_{avg} - eMAT + \Delta TSF
|
53
|
-
''')
|
54
|
-
|
55
|
-
|
56
|
-
# display Fault Equation 6
|
57
|
-
st.title("Fault Equation 6")
|
58
|
-
st.caption('Temperature and outside air percentage deviation from setpoints')
|
59
|
-
st.latex(r'''
|
60
|
-
|\text{RAT}_{\text{avg}} - \text{OAT}_{\text{avg}}| \geq \Delta T_{\text{min}} \quad \text{and} \quad |\%OA - \%OA_{\text{min}}| > eF
|
61
|
-
''')
|
62
|
-
|
63
|
-
|
64
|
-
# display Fault Equation 7
|
65
|
-
st.title("Fault Equation 7")
|
66
|
-
st.caption('Supply air temperature too low and heating coil status')
|
67
|
-
st.latex(r'''
|
68
|
-
\text{SAT}_{\text{avg}} < \text{SATSP} - eSAT \quad \text{and} \quad \text{HC} \geq 99\%
|
69
|
-
''')
|
70
|
-
|
71
|
-
# display Fault Equation 8
|
72
|
-
st.title("Fault Equation 8")
|
73
|
-
st.caption('Deviation between supply air temperature and mixed air temperature')
|
74
|
-
st.latex(r'''
|
75
|
-
| \text{SAT}_{\text{avg}} - \Delta \text{TSF} - \text{MAT}_{\text{avg}} | > \sqrt{{eSAT}^2 + {eMAT}^2}
|
76
|
-
''')
|
77
|
-
|
78
|
-
|
79
|
-
# display Fault Equation 9
|
80
|
-
st.title("Fault Equation 9")
|
81
|
-
st.caption('Outside air temperature deviation from setpoint')
|
82
|
-
st.latex(r'''
|
83
|
-
\text{OAT}_{\text{avg}} - eOAT > \text{SATSP} - \Delta \text{SF} + eSAT
|
84
|
-
''')
|
85
|
-
|
86
|
-
# display Fault Equation 10
|
87
|
-
st.title("Fault Equation 10")
|
88
|
-
st.caption('Temperature difference between mixed air and outside air')
|
89
|
-
st.latex(r'''
|
90
|
-
| \text{MAT}_{\text{avg}} - \text{OAT}_{\text{avg}} | > \sqrt{eMAT^2 + eOAT^2}
|
91
|
-
''')
|
92
|
-
|
93
|
-
# display Fault Equation 11
|
94
|
-
st.title("Fault Equation 11")
|
95
|
-
st.caption('Outside air temperature and supply air temperature deviation')
|
96
|
-
st.latex(r'''
|
97
|
-
\text{OAT}_{\text{avg}} + eOAT < \text{SATSP} - \Delta \text{TSF} - eSAT
|
98
|
-
''')
|
99
|
-
|
100
|
-
# display Fault Equation 12
|
101
|
-
st.title("Fault Equation 12")
|
102
|
-
st.caption('Supply air temperature deviation from mixed air temperature')
|
103
|
-
st.latex(r'''
|
104
|
-
\text{SAT}_{\text{avg}} - eSAT - \Delta \text{TSF} \geq \text{MAT}_{\text{avg}} + eMAT
|
105
|
-
''')
|
106
|
-
|
107
|
-
# display Fault Equation 13
|
108
|
-
st.title("Fault Equation 13")
|
109
|
-
st.caption('Supply air temperature too high')
|
110
|
-
st.latex(r'''
|
111
|
-
\text{SAT}_{\text{avg}} < \text{SATSP} + eSAT \quad \text{and} \quad \text{CC} \geq 99\%
|
112
|
-
''')
|
113
|
-
|
114
|
-
|
115
|
-
st.title("find_closest_weather_dates Function")
|
116
|
-
st.caption('Finding closest weather dates based on given criteria')
|
117
|
-
st.latex(r'''
|
118
|
-
1. A' = \{a \in A : a < d_{test}\} \\
|
119
|
-
2. B' = \{b \in B : b < d_{test}\} \\
|
120
|
-
3. C = \{a \in A' : a \notin B'\} \\
|
121
|
-
4. \text{if } |C| < 10 \text{ then remove } \max(A') \text{ and repeat step 3} \\
|
122
|
-
5. A = A \cap C, \text{calculate } \mu(A)
|
123
|
-
''')
|
124
|
-
st.caption('''
|
125
|
-
In this notation:
|
126
|
-
- $A$ represents the "all_data" dataset.
|
127
|
-
- $B$ represents the "suitable_baseline_no" dataset.
|
128
|
-
- $d_{test}$ is the "test_case_date".
|
129
|
-
- $A'$ and $B'$ are subsets of $A$ and $B$ that only include dates prior to $d_{test}$.
|
130
|
-
- $C$ is a set of dates in $A'$ not found in $B'$.
|
131
|
-
- $|C|$ represents the count of elements in set $C$.
|
132
|
-
- $\max(A')$ is the latest date in $A'$.
|
133
|
-
- $\mu(A)$ is the mean of the remaining elements in $A$ after filtering by set $C$.
|
134
|
-
''')
|
135
|
-
|
136
|
-
|
137
|
-
st.title("find_previous_10_days Function")
|
138
|
-
st.caption('Finding previous 10 weekdays based on given criteria')
|
139
|
-
st.latex(r'''
|
140
|
-
1. A' = \{a \in A : a < d_{test}\} \\
|
141
|
-
2. B' = \{b \in B : b < d_{test}\} \\
|
142
|
-
3. C = \{a \in A' : a \notin B'\} \\
|
143
|
-
4. \text{if } |C| < 10 \text{ then remove } \max(A') \text{ and repeat step 3} \\
|
144
|
-
5. A = A \cap C
|
145
|
-
''')
|
146
|
-
st.caption('''
|
147
|
-
In this notation:
|
148
|
-
- $A$ represents the "all_data" dataset.
|
149
|
-
- $B$ represents the "suitable_baseline_no" dataset.
|
150
|
-
- $d_{test}$ is the "test_case_date".
|
151
|
-
- $A'$ and $B'$ are subsets of $A$ and $B$ that only include dates prior to $d_{test}$.
|
152
|
-
- $C$ is a set of dates in $A'$ not found in $B'$.
|
153
|
-
- $|C|$ represents the count of elements in set $C$.
|
154
|
-
- $\max(A')$ is the latest date in $A'$.
|
155
|
-
''')
|
156
|
-
|
157
|
-
|
158
|
-
st.title("calculate_power_averages Function")
|
159
|
-
st.caption('Calculating average power for each type and time step')
|
160
|
-
st.latex(r'''
|
161
|
-
1. P_{type,i} = \{p : p \text{ is a power value at time step } t_i\} \quad \text{for each type and } i \in \{1,2,\dots,96\} \\
|
162
|
-
2. A_{type,i} = \frac{1}{|P_{type,i}|}\sum_{p \in P_{type,i}} p \quad \text{for each type and } i \in \{1,2,\dots,96\}
|
163
|
-
''')
|
164
|
-
st.caption('''
|
165
|
-
In this notation:
|
166
|
-
- $P_{type,i}$ represents the set of power values at time step $t_i$ for a specific power type (main, ahu, or solar).
|
167
|
-
- $p$ represents a power value in the set $P_{type,i}$.
|
168
|
-
- $|P_{type,i}|$ represents the count of elements in set $P_{type,i}$.
|
169
|
-
- $A_{type,i}$ represents the average power at time step $t_i$ for a specific power type.
|
170
|
-
''')
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
Binary file
|
Binary file
|
Binary file
|
@@ -1,47 +0,0 @@
|
|
1
|
-
import matplotlib.pyplot as plt
|
2
|
-
import pandas as pd
|
3
|
-
from io import BytesIO
|
4
|
-
import sys
|
5
|
-
|
6
|
-
class BaseReport:
|
7
|
-
def __init__(self, config):
|
8
|
-
self.config = config
|
9
|
-
|
10
|
-
def summarize_fault_times(self, df: pd.DataFrame, output_col: str) -> dict:
|
11
|
-
delta = df.index.to_series().diff().dt.total_seconds()
|
12
|
-
total_days = round(delta.sum() / 86400, 2)
|
13
|
-
total_hours = round(delta.sum() / 3600, 2)
|
14
|
-
hours_fault_mode = (delta * df[output_col]).sum() / 3600
|
15
|
-
percent_true = round(df[output_col].mean() * 100, 2)
|
16
|
-
percent_false = round((100 - percent_true), 2)
|
17
|
-
|
18
|
-
# Calculate motor runtime
|
19
|
-
motor_on = df[self.config['SUPPLY_VFD_SPEED_COL']].gt(.01).astype(int)
|
20
|
-
hours_motor_runtime = round((delta * motor_on).sum() / 3600, 2)
|
21
|
-
|
22
|
-
summary = {
|
23
|
-
'total_days': total_days,
|
24
|
-
'total_hours': total_hours,
|
25
|
-
'hours_fault_mode': hours_fault_mode,
|
26
|
-
'percent_true': percent_true,
|
27
|
-
'percent_false': percent_false,
|
28
|
-
'hours_motor_runtime': hours_motor_runtime
|
29
|
-
}
|
30
|
-
return summary
|
31
|
-
|
32
|
-
def create_hist_plot(self, df: pd.DataFrame, output_col: str):
|
33
|
-
df["hour_of_the_day"] = df.index.hour.where(df[output_col] == 1)
|
34
|
-
df = df.dropna(subset=["hour_of_the_day"])
|
35
|
-
print()
|
36
|
-
print("Time-of-day Histogram Data")
|
37
|
-
print(df["hour_of_the_day"])
|
38
|
-
print()
|
39
|
-
sys.stdout.flush()
|
40
|
-
|
41
|
-
fig, ax = plt.subplots(tight_layout=True, figsize=(25, 8))
|
42
|
-
ax.hist(df.hour_of_the_day.dropna(), bins=24)
|
43
|
-
ax.set_xlabel("Hour of the Day")
|
44
|
-
ax.set_ylabel("Frequency")
|
45
|
-
ax.set_title("Hour-Of-Day When Fault Flag is TRUE")
|
46
|
-
plt.show()
|
47
|
-
plt.close()
|
@@ -1,115 +0,0 @@
|
|
1
|
-
import matplotlib.pyplot as plt
|
2
|
-
import pandas as pd
|
3
|
-
import sys
|
4
|
-
|
5
|
-
|
6
|
-
class FaultCodeOneReport:
|
7
|
-
def __init__(self, config):
|
8
|
-
self.vfd_speed_percent_err_thres = config["VFD_SPEED_PERCENT_ERR_THRES"]
|
9
|
-
self.duct_static_col = config["DUCT_STATIC_COL"]
|
10
|
-
self.supply_vfd_speed_col = config["SUPPLY_VFD_SPEED_COL"]
|
11
|
-
self.duct_static_setpoint_col = config["DUCT_STATIC_SETPOINT_COL"]
|
12
|
-
|
13
|
-
def create_plot(self, df: pd.DataFrame, output_col: str = None):
|
14
|
-
if output_col is None:
|
15
|
-
output_col = "fc1_flag"
|
16
|
-
|
17
|
-
fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(25, 8))
|
18
|
-
fig.suptitle("Fault Conditions 1 Plot")
|
19
|
-
|
20
|
-
ax1.plot(df.index, df[self.duct_static_col], label="STATIC")
|
21
|
-
ax1.legend(loc="best")
|
22
|
-
ax1.set_ylabel("Inch WC")
|
23
|
-
|
24
|
-
ax2.plot(df.index, df[self.supply_vfd_speed_col], color="g", label="FAN")
|
25
|
-
ax2.legend(loc="best")
|
26
|
-
ax2.set_ylabel("%")
|
27
|
-
|
28
|
-
ax3.plot(df.index, df[output_col], label="Fault", color="k")
|
29
|
-
ax3.set_xlabel("Date")
|
30
|
-
ax3.set_ylabel("Fault Flags")
|
31
|
-
ax3.legend(loc="best")
|
32
|
-
|
33
|
-
plt.tight_layout(rect=[0, 0.03, 1, 0.95])
|
34
|
-
plt.show()
|
35
|
-
plt.close()
|
36
|
-
|
37
|
-
def summarize_fault_times(self, df: pd.DataFrame, output_col: str = None) -> dict:
|
38
|
-
if output_col is None:
|
39
|
-
output_col = "fc1_flag"
|
40
|
-
|
41
|
-
delta = df.index.to_series().diff()
|
42
|
-
summary = {
|
43
|
-
"total_days": round(delta.sum() / pd.Timedelta(days=1), 2),
|
44
|
-
"total_hours": round(delta.sum() / pd.Timedelta(hours=1)),
|
45
|
-
"hours_fc1_mode": round(
|
46
|
-
(delta * df[output_col]).sum() / pd.Timedelta(hours=1)
|
47
|
-
),
|
48
|
-
"percent_true": round(df[output_col].mean() * 100, 2),
|
49
|
-
"percent_false": round((100 - round(df[output_col].mean() * 100, 2)), 2),
|
50
|
-
"flag_true_duct_static": round(
|
51
|
-
df[self.duct_static_col].where(df[output_col] == 1).mean(), 2
|
52
|
-
),
|
53
|
-
"flag_true_duct_static_spt": round(
|
54
|
-
df[self.duct_static_setpoint_col].where(df[output_col] == 1).mean(), 2
|
55
|
-
),
|
56
|
-
"hours_motor_runtime": round(
|
57
|
-
(delta * df[self.supply_vfd_speed_col].gt(0.01).astype(int)).sum()
|
58
|
-
/ pd.Timedelta(hours=1),
|
59
|
-
2,
|
60
|
-
),
|
61
|
-
}
|
62
|
-
|
63
|
-
return summary
|
64
|
-
|
65
|
-
def create_hist_plot(self, df: pd.DataFrame, output_col: str = None):
|
66
|
-
if output_col is None:
|
67
|
-
output_col = "fc1_flag"
|
68
|
-
|
69
|
-
df["hour_of_the_day_fc1"] = df.index.hour.where(df[output_col] == 1)
|
70
|
-
|
71
|
-
fig, ax = plt.subplots(tight_layout=True, figsize=(25, 8))
|
72
|
-
ax.hist(df.hour_of_the_day_fc1.dropna())
|
73
|
-
ax.set_xlabel("24 Hour Number in Day")
|
74
|
-
ax.set_ylabel("Frequency")
|
75
|
-
ax.set_title(f"Hour-Of-Day When Fault Flag 1 is TRUE")
|
76
|
-
plt.show()
|
77
|
-
plt.close()
|
78
|
-
|
79
|
-
def display_report_in_ipython(self, df: pd.DataFrame, output_col: str = "fc1_flag"):
|
80
|
-
print("Fault Condition 1: Duct static too low at fan at full speed")
|
81
|
-
|
82
|
-
self.create_plot(df, output_col)
|
83
|
-
|
84
|
-
summary = self.summarize_fault_times(df, output_col)
|
85
|
-
|
86
|
-
for key, value in summary.items():
|
87
|
-
formatted_key = key.replace("_", " ")
|
88
|
-
print(f"{formatted_key}: {value}")
|
89
|
-
sys.stdout.flush()
|
90
|
-
|
91
|
-
fc_max_faults_found = df[output_col].max()
|
92
|
-
print("Fault Flag Count: ", fc_max_faults_found)
|
93
|
-
sys.stdout.flush()
|
94
|
-
|
95
|
-
if fc_max_faults_found != 0:
|
96
|
-
self.create_hist_plot(df, output_col)
|
97
|
-
|
98
|
-
print("Duct Static Mean When In Fault: ", summary["flag_true_duct_static"])
|
99
|
-
print(
|
100
|
-
"Duct Static Setpoint Mean When In Fault: ",
|
101
|
-
summary["flag_true_duct_static_spt"],
|
102
|
-
)
|
103
|
-
|
104
|
-
if summary["percent_true"] > 5.0:
|
105
|
-
print(
|
106
|
-
"The percent True metric that represents the amount of time for when the fault flag is True is high, indicating potential duct issues. Verify system calibration and investigate possible mechanical problems."
|
107
|
-
)
|
108
|
-
else:
|
109
|
-
print(
|
110
|
-
"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."
|
111
|
-
)
|
112
|
-
|
113
|
-
else:
|
114
|
-
print("NO FAULTS FOUND - Skipping time-of-day Histogram plot")
|
115
|
-
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 FaultCodeTenReport:
|
7
|
-
"""Class provides the definitions for Fault Condition 10 Report."""
|
8
|
-
|
9
|
-
def __init__(self, config):
|
10
|
-
self.oat_col = config["OAT_COL"]
|
11
|
-
self.mat_col = config["MAT_COL"]
|
12
|
-
self.cooling_sig_col = config["COOLING_SIG_COL"]
|
13
|
-
self.economizer_sig_col = config["ECONOMIZER_SIG_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 = "fc10_flag"
|
19
|
-
|
20
|
-
fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(25, 8))
|
21
|
-
fig.suptitle("Fault Conditions 10 Plot")
|
22
|
-
|
23
|
-
ax1.plot(df.index, df[self.mat_col], label="MAT")
|
24
|
-
ax1.plot(df.index, df[self.oat_col], label="OAT")
|
25
|
-
ax1.legend(loc="best")
|
26
|
-
ax1.set_ylabel("AHU Temps °F")
|
27
|
-
|
28
|
-
ax2.plot(df.index, df[self.cooling_sig_col], label="AHU Cool Vlv", color="r")
|
29
|
-
ax2.plot(df.index, df[self.economizer_sig_col], label="AHU Dpr Cmd", color="g")
|
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 = "fc10_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_fc10_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_oat": round(
|
56
|
-
df[self.oat_col].where(df[output_col] == 1).mean(), 2
|
57
|
-
),
|
58
|
-
"flag_true_mat": round(
|
59
|
-
df[self.mat_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 = "fc10_flag"
|
73
|
-
|
74
|
-
df["hour_of_the_day_fc10"] = 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_fc10.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 10 is TRUE")
|
81
|
-
plt.show()
|
82
|
-
plt.close()
|
83
|
-
|
84
|
-
def display_report_in_ipython(
|
85
|
-
self, df: pd.DataFrame, output_col: str = "fc10_flag"
|
86
|
-
):
|
87
|
-
print(
|
88
|
-
"Fault Condition 10: Outdoor air temperature and mix air temperature should be approximately equal in economizer plus mech cooling mode"
|
89
|
-
)
|
90
|
-
|
91
|
-
self.create_plot(df, output_col)
|
92
|
-
|
93
|
-
summary = self.summarize_fault_times(df, output_col)
|
94
|
-
|
95
|
-
for key, value in summary.items():
|
96
|
-
formatted_key = key.replace("_", " ")
|
97
|
-
print(f"{formatted_key}: {value}")
|
98
|
-
sys.stdout.flush()
|
99
|
-
|
100
|
-
fc_max_faults_found = df[output_col].max()
|
101
|
-
print("Fault Flag Count: ", fc_max_faults_found)
|
102
|
-
sys.stdout.flush()
|
103
|
-
|
104
|
-
if fc_max_faults_found != 0:
|
105
|
-
self.create_hist_plot(df, output_col)
|
106
|
-
|
107
|
-
flag_true_oat = round(df[self.oat_col].where(df[output_col] == 1).mean(), 2)
|
108
|
-
print("Outside Air Temp Mean When In Fault: ", flag_true_oat)
|
109
|
-
|
110
|
-
flag_true_mat = round(df[self.mat_col].where(df[output_col] == 1).mean(), 2)
|
111
|
-
print("Mix Air Temp Mean When In Fault: ", flag_true_mat)
|
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 temperature sensor error or the mixing air dampers are stuck or broken with the inability for the AHU to go into a proper 100 percent outside air mode. Verify the actual installation of temperature sensors, damper operation, and related equipment to address potential issues."
|
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 components are within calibration for this fault equation."
|
122
|
-
)
|
123
|
-
|
124
|
-
else:
|
125
|
-
print("NO FAULTS FOUND - Skipping time-of-day Histogram plot")
|
126
|
-
sys.stdout.flush()
|
@@ -1,128 +0,0 @@
|
|
1
|
-
import matplotlib.pyplot as plt
|
2
|
-
import pandas as pd
|
3
|
-
import sys
|
4
|
-
|
5
|
-
|
6
|
-
class FaultCodeElevenReport:
|
7
|
-
"""Class provides the definitions for Fault Condition 11 Report."""
|
8
|
-
|
9
|
-
def __init__(self, config):
|
10
|
-
self.sat_setpoint_col = config["SAT_SETPOINT_COL"]
|
11
|
-
self.oat_col = config["OAT_COL"]
|
12
|
-
self.cooling_sig_col = config["COOLING_SIG_COL"]
|
13
|
-
self.economizer_sig_col = config["ECONOMIZER_SIG_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 = "fc11_flag"
|
19
|
-
|
20
|
-
fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(25, 8))
|
21
|
-
fig.suptitle("Fault Conditions 11 Plot")
|
22
|
-
|
23
|
-
ax1.plot(df.index, df[self.sat_setpoint_col], label="SATSP")
|
24
|
-
ax1.plot(df.index, df[self.oat_col], label="OAT")
|
25
|
-
ax1.legend(loc="best")
|
26
|
-
ax1.set_ylabel("AHU Temps °F")
|
27
|
-
|
28
|
-
ax2.plot(df.index, df[self.cooling_sig_col], label="AHU Cool Vlv", color="r")
|
29
|
-
ax2.plot(df.index, df[self.economizer_sig_col], label="AHU Dpr Cmd", color="g")
|
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 = "fc11_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_fc11_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_oat": round(
|
56
|
-
df[self.oat_col].where(df[output_col] == 1).mean(), 2
|
57
|
-
),
|
58
|
-
"flag_true_sat_sp": round(
|
59
|
-
df[self.sat_setpoint_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 = "fc11_flag"
|
73
|
-
|
74
|
-
df["hour_of_the_day_fc11"] = 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_fc11.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 11 is TRUE")
|
81
|
-
plt.show()
|
82
|
-
plt.close()
|
83
|
-
|
84
|
-
def display_report_in_ipython(
|
85
|
-
self, df: pd.DataFrame, output_col: str = "fc11_flag"
|
86
|
-
):
|
87
|
-
print(
|
88
|
-
"Fault Condition 11: Outside air temperature too low for 100% outside air cooling in economizer mode"
|
89
|
-
)
|
90
|
-
|
91
|
-
self.create_plot(df, output_col)
|
92
|
-
|
93
|
-
summary = self.summarize_fault_times(df, output_col)
|
94
|
-
|
95
|
-
for key, value in summary.items():
|
96
|
-
formatted_key = key.replace("_", " ")
|
97
|
-
print(f"{formatted_key}: {value}")
|
98
|
-
sys.stdout.flush()
|
99
|
-
|
100
|
-
fc_max_faults_found = df[output_col].max()
|
101
|
-
print("Fault Flag Count: ", fc_max_faults_found)
|
102
|
-
sys.stdout.flush()
|
103
|
-
|
104
|
-
if fc_max_faults_found != 0:
|
105
|
-
self.create_hist_plot(df, output_col)
|
106
|
-
|
107
|
-
flag_true_oat = round(df[self.oat_col].where(df[output_col] == 1).mean(), 2)
|
108
|
-
print("Outside Air Temp Mean When In Fault: ", flag_true_oat)
|
109
|
-
|
110
|
-
flag_true_sat_sp = round(
|
111
|
-
df[self.sat_setpoint_col].where(df[output_col] == 1).mean(), 2
|
112
|
-
)
|
113
|
-
print("Supply Air Temp Setpoint Mean When In Fault: ", flag_true_sat_sp)
|
114
|
-
|
115
|
-
sys.stdout.flush()
|
116
|
-
|
117
|
-
if summary["percent_true"] > 5.0:
|
118
|
-
print(
|
119
|
-
"The percent True metric that represents the amount of time for when the fault flag is True is high, indicating temperature sensor error or the heating coil could be leaking, potentially creating simultaneous heating/cooling scenarios. Visually verify damper operation, and consider tuning the BAS programming for proper AHU operation."
|
120
|
-
)
|
121
|
-
else:
|
122
|
-
print(
|
123
|
-
"The percent True metric that represents the amount of time for when the fault flag is True is low, indicating the AHU components are within calibration for this fault equation."
|
124
|
-
)
|
125
|
-
|
126
|
-
else:
|
127
|
-
print("NO FAULTS FOUND - Skipping time-of-day Histogram plot")
|
128
|
-
sys.stdout.flush()
|