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.
Files changed (63) hide show
  1. open_fdd/air_handling_unit/faults/__init__.py +301 -301
  2. open_fdd/air_handling_unit/reports/__init__.py +988 -0
  3. open_fdd/air_handling_unit/reports/fault_report.py +42 -0
  4. open_fdd/tests/ahu/test_ahu_fc16.py +190 -0
  5. {open_fdd-0.1.4.dist-info → open_fdd-0.1.6.dist-info}/METADATA +21 -12
  6. open_fdd-0.1.6.dist-info/RECORD +31 -0
  7. {open_fdd-0.1.4.dist-info → open_fdd-0.1.6.dist-info}/WHEEL +1 -1
  8. open_fdd/air_handling_unit/faults/fault_condition_eight.py +0 -127
  9. open_fdd/air_handling_unit/faults/fault_condition_eleven.py +0 -126
  10. open_fdd/air_handling_unit/faults/fault_condition_fifteen.py +0 -152
  11. open_fdd/air_handling_unit/faults/fault_condition_five.py +0 -123
  12. open_fdd/air_handling_unit/faults/fault_condition_four.py +0 -168
  13. open_fdd/air_handling_unit/faults/fault_condition_fourteen.py +0 -143
  14. open_fdd/air_handling_unit/faults/fault_condition_nine.py +0 -128
  15. open_fdd/air_handling_unit/faults/fault_condition_one.py +0 -112
  16. open_fdd/air_handling_unit/faults/fault_condition_seven.py +0 -114
  17. open_fdd/air_handling_unit/faults/fault_condition_six.py +0 -181
  18. open_fdd/air_handling_unit/faults/fault_condition_ten.py +0 -123
  19. open_fdd/air_handling_unit/faults/fault_condition_thirteen.py +0 -127
  20. open_fdd/air_handling_unit/faults/fault_condition_three.py +0 -113
  21. open_fdd/air_handling_unit/faults/fault_condition_twelve.py +0 -132
  22. open_fdd/air_handling_unit/faults/fault_condition_two.py +0 -113
  23. open_fdd/air_handling_unit/images/ahu1_fc1_2024-06_1.jpg +0 -0
  24. open_fdd/air_handling_unit/images/ahu1_fc1_2024-06_2.jpg +0 -0
  25. open_fdd/air_handling_unit/images/example1.jpg +0 -0
  26. open_fdd/air_handling_unit/images/example2.jpg +0 -0
  27. open_fdd/air_handling_unit/images/fc10_definition.png +0 -0
  28. open_fdd/air_handling_unit/images/fc11_definition.png +0 -0
  29. open_fdd/air_handling_unit/images/fc12_definition.png +0 -0
  30. open_fdd/air_handling_unit/images/fc13_definition.png +0 -0
  31. open_fdd/air_handling_unit/images/fc1_definition.png +0 -0
  32. open_fdd/air_handling_unit/images/fc1_report_screenshot_all.png +0 -0
  33. open_fdd/air_handling_unit/images/fc2_definition.png +0 -0
  34. open_fdd/air_handling_unit/images/fc3_definition.png +0 -0
  35. open_fdd/air_handling_unit/images/fc4_definition.png +0 -0
  36. open_fdd/air_handling_unit/images/fc5_definition.png +0 -0
  37. open_fdd/air_handling_unit/images/fc6_definition.png +0 -0
  38. open_fdd/air_handling_unit/images/fc7_definition.png +0 -0
  39. open_fdd/air_handling_unit/images/fc8_definition.png +0 -0
  40. open_fdd/air_handling_unit/images/fc9_definition.png +0 -0
  41. open_fdd/air_handling_unit/images/latex_generator.py +0 -175
  42. open_fdd/air_handling_unit/images/params.docx +0 -0
  43. open_fdd/air_handling_unit/images/params.pdf +0 -0
  44. open_fdd/air_handling_unit/images/plot_for_repo.png +0 -0
  45. open_fdd/air_handling_unit/reports/base_report.py +0 -47
  46. open_fdd/air_handling_unit/reports/report_fc1.py +0 -115
  47. open_fdd/air_handling_unit/reports/report_fc10.py +0 -126
  48. open_fdd/air_handling_unit/reports/report_fc11.py +0 -128
  49. open_fdd/air_handling_unit/reports/report_fc12.py +0 -126
  50. open_fdd/air_handling_unit/reports/report_fc13.py +0 -126
  51. open_fdd/air_handling_unit/reports/report_fc14.py +0 -124
  52. open_fdd/air_handling_unit/reports/report_fc15.py +0 -124
  53. open_fdd/air_handling_unit/reports/report_fc2.py +0 -119
  54. open_fdd/air_handling_unit/reports/report_fc3.py +0 -119
  55. open_fdd/air_handling_unit/reports/report_fc4.py +0 -148
  56. open_fdd/air_handling_unit/reports/report_fc5.py +0 -132
  57. open_fdd/air_handling_unit/reports/report_fc6.py +0 -156
  58. open_fdd/air_handling_unit/reports/report_fc7.py +0 -126
  59. open_fdd/air_handling_unit/reports/report_fc8.py +0 -118
  60. open_fdd/air_handling_unit/reports/report_fc9.py +0 -120
  61. open_fdd-0.1.4.dist-info/RECORD +0 -82
  62. {open_fdd-0.1.4.dist-info → open_fdd-0.1.6.dist-info}/LICENSE +0 -0
  63. {open_fdd-0.1.4.dist-info → open_fdd-0.1.6.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,988 @@
1
+ import matplotlib.pyplot as plt
2
+ from open_fdd.air_handling_unit.reports.fault_report import BaseFaultReport
3
+ from open_fdd.air_handling_unit.faults import FaultConditionSixteen
4
+ import pandas as pd
5
+ import numpy as np
6
+ import sys
7
+
8
+
9
+ class FaultCodeOneReport(BaseFaultReport):
10
+ def __init__(self, config):
11
+ super().__init__(config, "fc1_flag")
12
+ self.vfd_speed_percent_err_thres = config["VFD_SPEED_PERCENT_ERR_THRES"]
13
+ self.duct_static_col = config["DUCT_STATIC_COL"]
14
+ self.supply_vfd_speed_col = config["SUPPLY_VFD_SPEED_COL"]
15
+ self.duct_static_setpoint_col = config["DUCT_STATIC_SETPOINT_COL"]
16
+
17
+ def create_plot(self, df: pd.DataFrame):
18
+ fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(25, 8))
19
+ fig.suptitle("Fault Conditions 1 Plot")
20
+ ax1.plot(df.index, df[self.duct_static_col], label="STATIC")
21
+ ax1.legend(loc="best")
22
+ ax1.set_ylabel("Inch WC")
23
+ ax2.plot(df.index, df[self.supply_vfd_speed_col], color="g", label="FAN")
24
+ ax2.legend(loc="best")
25
+ ax2.set_ylabel("%")
26
+ ax3.plot(df.index, df[self.fault_col], label="Fault", color="k")
27
+ ax3.set_xlabel("Date")
28
+ ax3.set_ylabel("Fault Flags")
29
+ ax3.legend(loc="best")
30
+ plt.tight_layout(rect=[0, 0.03, 1, 0.95])
31
+ plt.show()
32
+ plt.close()
33
+
34
+ def summarize_fault_times(self, df: pd.DataFrame) -> dict:
35
+ delta = df.index.to_series().diff()
36
+ summary = {
37
+ "total_days": round(delta.sum() / pd.Timedelta(days=1), 2),
38
+ "total_hours": round(delta.sum() / pd.Timedelta(hours=1)),
39
+ "hours_fc1_mode": round(
40
+ (delta * df[self.fault_col]).sum() / pd.Timedelta(hours=1)
41
+ ),
42
+ "percent_true": round(df[self.fault_col].mean() * 100, 2),
43
+ "percent_false": round((100 - df[self.fault_col].mean() * 100), 2),
44
+ "flag_true_duct_static": round(
45
+ df[self.duct_static_col].where(df[self.fault_col] == 1).mean(), 2
46
+ ),
47
+ "flag_true_duct_static_spt": round(
48
+ df[self.duct_static_setpoint_col].where(df[self.fault_col] == 1).mean(),
49
+ 2,
50
+ ),
51
+ "hours_motor_runtime": round(
52
+ (delta * df[self.supply_vfd_speed_col].gt(0.01).astype(int)).sum()
53
+ / pd.Timedelta(hours=1),
54
+ 2,
55
+ ),
56
+ }
57
+ return summary
58
+
59
+
60
+ class FaultCodeTwoReport(BaseFaultReport):
61
+ def __init__(self, config):
62
+ super().__init__(config, "fc2_flag")
63
+ self.mix_degf_err_thres = config["MIX_DEGF_ERR_THRES"]
64
+ self.return_degf_err_thres = config["RETURN_DEGF_ERR_THRES"]
65
+ self.outdoor_degf_err_thres = config["OUTDOOR_DEGF_ERR_THRES"]
66
+ self.mat_col = config["MAT_COL"]
67
+ self.rat_col = config["RAT_COL"]
68
+ self.oat_col = config["OAT_COL"]
69
+ self.supply_vfd_speed_col = config["SUPPLY_VFD_SPEED_COL"]
70
+
71
+ def create_plot(self, df: pd.DataFrame):
72
+ fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(25, 8))
73
+ fig.suptitle("Fault Conditions 2 Plot")
74
+
75
+ ax1.plot(df.index, df[self.mat_col], color="r", label="Mix Temp")
76
+ ax1.plot(df.index, df[self.rat_col], color="b", label="Return Temp")
77
+ ax1.plot(df.index, df[self.oat_col], color="g", label="Out Temp")
78
+ ax1.legend(loc="best")
79
+ ax1.set_ylabel("°F")
80
+
81
+ ax2.plot(df.index, df[self.fault_col], label="Fault", color="k")
82
+ ax2.set_xlabel("Date")
83
+ ax2.set_ylabel("Fault Flags")
84
+ ax2.legend(loc="best")
85
+
86
+ plt.tight_layout(rect=[0, 0.03, 1, 0.95])
87
+ plt.show()
88
+ plt.close()
89
+
90
+ def summarize_fault_times(self, df: pd.DataFrame) -> dict:
91
+ delta = df.index.to_series().diff()
92
+ summary = {
93
+ "total_days": round(delta.sum() / pd.Timedelta(days=1), 2),
94
+ "total_hours": round(delta.sum() / pd.Timedelta(hours=1)),
95
+ "hours_fc2_mode": round(
96
+ (delta * df[self.fault_col]).sum() / pd.Timedelta(hours=1)
97
+ ),
98
+ "percent_true": round(df[self.fault_col].mean() * 100, 2),
99
+ "percent_false": round((100 - df[self.fault_col].mean() * 100), 2),
100
+ "flag_true_mat": round(
101
+ df[self.mat_col].where(df[self.fault_col] == 1).mean(), 2
102
+ ),
103
+ "flag_true_oat": round(
104
+ df[self.oat_col].where(df[self.fault_col] == 1).mean(), 2
105
+ ),
106
+ "flag_true_rat": round(
107
+ df[self.rat_col].where(df[self.fault_col] == 1).mean(), 2
108
+ ),
109
+ "hours_motor_runtime": round(
110
+ (delta * df[self.supply_vfd_speed_col].gt(0.01).astype(int)).sum()
111
+ / pd.Timedelta(hours=1),
112
+ 2,
113
+ ),
114
+ }
115
+ return summary
116
+
117
+
118
+ class FaultCodeThreeReport(BaseFaultReport):
119
+ def __init__(self, config):
120
+ super().__init__(config, "fc3_flag")
121
+ self.mix_degf_err_thres = config["MIX_DEGF_ERR_THRES"]
122
+ self.return_degf_err_thres = config["RETURN_DEGF_ERR_THRES"]
123
+ self.outdoor_degf_err_thres = config["OUTDOOR_DEGF_ERR_THRES"]
124
+ self.mat_col = config["MAT_COL"]
125
+ self.rat_col = config["RAT_COL"]
126
+ self.oat_col = config["OAT_COL"]
127
+ self.supply_vfd_speed_col = config["SUPPLY_VFD_SPEED_COL"]
128
+
129
+ def create_plot(self, df: pd.DataFrame):
130
+ fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(25, 8))
131
+ fig.suptitle("Fault Conditions 3 Plot")
132
+
133
+ ax1.plot(df.index, df[self.mat_col], color="r", label="Mix Temp")
134
+ ax1.plot(df.index, df[self.rat_col], color="b", label="Return Temp")
135
+ ax1.plot(df.index, df[self.oat_col], color="g", label="Out Temp")
136
+ ax1.legend(loc="best")
137
+ ax1.set_ylabel("°F")
138
+
139
+ ax2.plot(df.index, df[self.fault_col], label="Fault", color="k")
140
+ ax2.set_xlabel("Date")
141
+ ax2.set_ylabel("Fault Flags")
142
+ ax2.legend(loc="best")
143
+
144
+ plt.tight_layout(rect=[0, 0.03, 1, 0.95])
145
+ plt.show()
146
+ plt.close()
147
+
148
+ def summarize_fault_times(self, df: pd.DataFrame) -> dict:
149
+ delta = df.index.to_series().diff()
150
+ summary = {
151
+ "total_days": round(delta.sum() / pd.Timedelta(days=1), 2),
152
+ "total_hours": round(delta.sum() / pd.Timedelta(hours=1)),
153
+ "hours_fc3_mode": round(
154
+ (delta * df[self.fault_col]).sum() / pd.Timedelta(hours=1)
155
+ ),
156
+ "percent_true": round(df[self.fault_col].mean() * 100, 2),
157
+ "percent_false": round((100 - df[self.fault_col].mean() * 100), 2),
158
+ "flag_true_mat": round(
159
+ df[self.mat_col].where(df[self.fault_col] == 1).mean(), 2
160
+ ),
161
+ "flag_true_oat": round(
162
+ df[self.oat_col].where(df[self.fault_col] == 1).mean(), 2
163
+ ),
164
+ "flag_true_rat": round(
165
+ df[self.rat_col].where(df[self.fault_col] == 1).mean(), 2
166
+ ),
167
+ "hours_motor_runtime": round(
168
+ (delta * df[self.supply_vfd_speed_col].gt(0.01).astype(int)).sum()
169
+ / pd.Timedelta(hours=1),
170
+ 2,
171
+ ),
172
+ }
173
+ return summary
174
+
175
+
176
+ class FaultCodeFourReport(BaseFaultReport):
177
+ def __init__(self, config):
178
+ super().__init__(config, "fc4_flag")
179
+ self.delta_os_max = config["DELTA_OS_MAX"]
180
+ self.heating_mode_calc_col = "heating_mode"
181
+ self.econ_only_cooling_mode_calc_col = "econ_only_cooling_mode"
182
+ self.econ_plus_mech_cooling_mode_calc_col = "econ_plus_mech_cooling_mode"
183
+ self.mech_cooling_only_mode_calc_col = "mech_cooling_only_mode"
184
+
185
+ def create_plot(self, df: pd.DataFrame):
186
+ fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(25, 8))
187
+ fig.suptitle("Fault Condition 4 Plots")
188
+
189
+ ax1.plot(df.index, df[self.heating_mode_calc_col], label="Heat", color="orange")
190
+ ax1.plot(
191
+ df.index,
192
+ df[self.econ_only_cooling_mode_calc_col],
193
+ label="Econ Clg",
194
+ color="olive",
195
+ )
196
+ ax1.plot(
197
+ df.index,
198
+ df[self.econ_plus_mech_cooling_mode_calc_col],
199
+ label="Econ + Mech Clg",
200
+ color="c",
201
+ )
202
+ ax1.plot(
203
+ df.index,
204
+ df[self.mech_cooling_only_mode_calc_col],
205
+ label="Mech Clg",
206
+ color="m",
207
+ )
208
+ ax1.set_xlabel("Date")
209
+ ax1.set_ylabel("Calculated AHU Operating States")
210
+ ax1.legend(loc="best")
211
+
212
+ ax2.plot(df.index, df[self.fault_col], label="Fault", color="k")
213
+ ax2.set_xlabel("Date")
214
+ ax2.set_ylabel("Fault Flags")
215
+ ax2.legend(loc="best")
216
+
217
+ plt.tight_layout()
218
+ plt.show()
219
+ plt.close()
220
+
221
+ def summarize_fault_times(self, df: pd.DataFrame) -> dict:
222
+ delta = df.index.to_series().diff()
223
+ summary = {
224
+ "total_days": round(delta.sum() / pd.Timedelta(days=1), 2),
225
+ "total_hours": round(delta.sum() / pd.Timedelta(hours=1)),
226
+ "hours_fc4_mode": round(
227
+ (delta * df[self.fault_col]).sum() / pd.Timedelta(hours=1)
228
+ ),
229
+ "percent_true": round(df[self.fault_col].mean() * 100, 2),
230
+ "percent_false": round((100 - df[self.fault_col].mean() * 100), 2),
231
+ "percent_of_time_AHU_in_mech_clg_mode": round(
232
+ df[self.mech_cooling_only_mode_calc_col].mean() * 100, 2
233
+ ),
234
+ "percent_of_time_AHU_in_econ_plus_mech_clg_mode": round(
235
+ df[self.econ_plus_mech_cooling_mode_calc_col].mean() * 100, 2
236
+ ),
237
+ "percent_of_time_AHU_in_econ_free_clg_mode": round(
238
+ df[self.econ_only_cooling_mode_calc_col].mean() * 100, 2
239
+ ),
240
+ "percent_of_time_AHU_in_heating_mode": round(
241
+ df[self.heating_mode_calc_col].mean() * 100, 2
242
+ ),
243
+ "total_hours_heating_mode": round(
244
+ (delta * df[self.heating_mode_calc_col]).sum() / pd.Timedelta(hours=1),
245
+ 2,
246
+ ),
247
+ "total_hours_econ_mode": round(
248
+ (delta * df[self.econ_only_cooling_mode_calc_col]).sum()
249
+ / pd.Timedelta(hours=1),
250
+ 2,
251
+ ),
252
+ "total_hours_econ_mech_clg_mode": round(
253
+ (delta * df[self.econ_plus_mech_cooling_mode_calc_col]).sum()
254
+ / pd.Timedelta(hours=1),
255
+ 2,
256
+ ),
257
+ "total_hours_mech_clg_mode": round(
258
+ (delta * df[self.mech_cooling_only_mode_calc_col]).sum()
259
+ / pd.Timedelta(hours=1),
260
+ 2,
261
+ ),
262
+ }
263
+ return summary
264
+
265
+
266
+ class FaultCodeFiveReport(BaseFaultReport):
267
+ def __init__(self, config):
268
+ super().__init__(config, "fc5_flag")
269
+ self.mix_degf_err_thres = config["MIX_DEGF_ERR_THRES"]
270
+ self.supply_degf_err_thres = config["SUPPLY_DEGF_ERR_THRES"]
271
+ self.delta_t_supply_fan = config["DELTA_T_SUPPLY_FAN"]
272
+ self.mat_col = config["MAT_COL"]
273
+ self.sat_col = config["SAT_COL"]
274
+ self.heating_sig_col = config["HEATING_SIG_COL"]
275
+ self.supply_vfd_speed_col = config["SUPPLY_VFD_SPEED_COL"]
276
+
277
+ def create_plot(self, df: pd.DataFrame):
278
+ fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(25, 8))
279
+ fig.suptitle("Fault Conditions 5 Plot")
280
+
281
+ ax1.plot(df.index, df[self.mat_col], color="g", label="Mix Temp")
282
+ ax1.plot(df.index, df[self.sat_col], color="b", label="Supply Temp")
283
+ ax1.legend(loc="best")
284
+ ax1.set_ylabel("°F")
285
+
286
+ ax2.plot(df.index, df[self.heating_sig_col], label="Htg Valve", color="r")
287
+ ax2.set_xlabel("Date")
288
+ ax2.set_ylabel("%")
289
+ ax2.legend(loc="best")
290
+
291
+ ax3.plot(df.index, df[self.fault_col], label="Fault", color="k")
292
+ ax3.set_xlabel("Date")
293
+ ax3.set_ylabel("Fault Flags")
294
+ ax3.legend(loc="best")
295
+
296
+ plt.tight_layout(rect=[0, 0.03, 1, 0.95])
297
+ plt.show()
298
+ plt.close()
299
+
300
+ def summarize_fault_times(self, df: pd.DataFrame) -> dict:
301
+ delta = df.index.to_series().diff()
302
+ summary = {
303
+ "total_days": round(delta.sum() / pd.Timedelta(days=1), 2),
304
+ "total_hours": round(delta.sum() / pd.Timedelta(hours=1)),
305
+ "hours_fc5_mode": round(
306
+ (delta * df[self.fault_col]).sum() / pd.Timedelta(hours=1)
307
+ ),
308
+ "percent_true": round(df[self.fault_col].mean() * 100, 2),
309
+ "percent_false": round((100 - df[self.fault_col].mean() * 100), 2),
310
+ "flag_true_mat": round(
311
+ df[self.mat_col].where(df[self.fault_col] == 1).mean(), 2
312
+ ),
313
+ "flag_true_sat": round(
314
+ df[self.sat_col].where(df[self.fault_col] == 1).mean(), 2
315
+ ),
316
+ "hours_motor_runtime": round(
317
+ (delta * df[self.supply_vfd_speed_col].gt(0.01).astype(int)).sum()
318
+ / pd.Timedelta(hours=1),
319
+ 2,
320
+ ),
321
+ }
322
+ return summary
323
+
324
+
325
+ class FaultCodeSixReport(BaseFaultReport):
326
+ def __init__(self, config):
327
+ super().__init__(config, "fc6_flag")
328
+ self.supply_fan_air_volume_col = config["SUPPLY_FAN_AIR_VOLUME_COL"]
329
+ self.mat_col = config["MAT_COL"]
330
+ self.oat_col = config["OAT_COL"]
331
+ self.rat_col = config["RAT_COL"]
332
+ self.supply_vfd_speed_col = config["SUPPLY_VFD_SPEED_COL"]
333
+
334
+ def create_plot(self, df: pd.DataFrame):
335
+ fig, (ax1, ax2, ax3, ax4, ax5) = plt.subplots(5, 1, figsize=(25, 8))
336
+ fig.suptitle("Fault Conditions 6 Plot")
337
+
338
+ ax1.plot(df.index, df["rat_minus_oat"], label="Rat Minus Oat")
339
+ ax1.legend(loc="best")
340
+ ax1.set_ylabel("°F")
341
+
342
+ ax2.plot(
343
+ df.index,
344
+ df[self.supply_fan_air_volume_col],
345
+ label="Total Air Flow",
346
+ color="r",
347
+ )
348
+ ax2.set_xlabel("Date")
349
+ ax2.set_ylabel("CFM")
350
+ ax2.legend(loc="best")
351
+
352
+ ax3.plot(df.index, df["percent_oa_calc"], label="OA Frac Calc", color="m")
353
+ ax3.plot(df.index, df["perc_OAmin"], label="OA Perc Min Calc", color="y")
354
+ ax3.set_xlabel("Date")
355
+ ax3.set_ylabel("%")
356
+ ax3.legend(loc="best")
357
+
358
+ ax4.plot(
359
+ df.index,
360
+ df["percent_oa_calc_minus_perc_OAmin"],
361
+ label="OA Error Frac Vs Perc Min Calc",
362
+ color="g",
363
+ )
364
+ ax4.set_xlabel("Date")
365
+ ax4.set_ylabel("%")
366
+ ax4.legend(loc="best")
367
+
368
+ ax5.plot(df.index, df[self.fault_col], label="Fault", color="k")
369
+ ax5.set_xlabel("Date")
370
+ ax5.set_ylabel("Fault Flags")
371
+ ax5.legend(loc="best")
372
+
373
+ plt.tight_layout(rect=[0, 0.03, 1, 0.95])
374
+ plt.show()
375
+ plt.close()
376
+
377
+ def summarize_fault_times(self, df: pd.DataFrame) -> dict:
378
+ delta = df.index.to_series().diff()
379
+ summary = {
380
+ "total_days": round(delta.sum() / pd.Timedelta(days=1), 2),
381
+ "total_hours": round(delta.sum() / pd.Timedelta(hours=1)),
382
+ "hours_fc6_mode": round(
383
+ (delta * df[self.fault_col]).sum() / pd.Timedelta(hours=1)
384
+ ),
385
+ "percent_true": round(df[self.fault_col].mean() * 100, 2),
386
+ "percent_false": round((100 - df[self.fault_col].mean() * 100), 2),
387
+ "flag_true_mat": round(
388
+ df[self.mat_col].where(df[self.fault_col] == 1).mean(), 2
389
+ ),
390
+ "flag_true_rat": round(
391
+ df[self.rat_col].where(df[self.fault_col] == 1).mean(), 2
392
+ ),
393
+ "flag_true_oat": round(
394
+ df[self.oat_col].where(df[self.fault_col] == 1).mean(), 2
395
+ ),
396
+ "hours_motor_runtime": round(
397
+ (delta * df[self.supply_vfd_speed_col].gt(0.01).astype(int)).sum()
398
+ / pd.Timedelta(hours=1),
399
+ 2,
400
+ ),
401
+ }
402
+ return summary
403
+
404
+
405
+ class FaultCodeSevenReport(BaseFaultReport):
406
+ def __init__(self, config):
407
+ super().__init__(config, "fc7_flag")
408
+ self.sat_col = config["SAT_COL"]
409
+ self.sat_setpoint_col = config["SAT_SETPOINT_COL"]
410
+ self.heating_sig_col = config["HEATING_SIG_COL"]
411
+ self.supply_vfd_speed_col = config["SUPPLY_VFD_SPEED_COL"]
412
+
413
+ def create_plot(self, df: pd.DataFrame):
414
+ fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(25, 8))
415
+ fig.suptitle("Fault Conditions 7 Plot")
416
+
417
+ ax1.plot(df.index, df[self.sat_col], label="SAT")
418
+ ax1.plot(df.index, df[self.sat_setpoint_col], label="SATsp")
419
+ ax1.legend(loc="best")
420
+ ax1.set_ylabel("AHU Supply Temps °F")
421
+
422
+ ax2.plot(df.index, df[self.heating_sig_col], color="r", label="AHU Heat Vlv")
423
+ ax2.legend(loc="best")
424
+ ax2.set_ylabel("%")
425
+
426
+ ax3.plot(df.index, df[self.fault_col], label="Fault", color="k")
427
+ ax3.set_xlabel("Date")
428
+ ax3.set_ylabel("Fault Flags")
429
+ ax3.legend(loc="best")
430
+
431
+ plt.tight_layout(rect=[0, 0.03, 1, 0.95])
432
+ plt.show()
433
+ plt.close()
434
+
435
+ def summarize_fault_times(self, df: pd.DataFrame) -> dict:
436
+ delta = df.index.to_series().diff()
437
+ summary = {
438
+ "total_days": round(delta.sum() / pd.Timedelta(days=1), 2),
439
+ "total_hours": round(delta.sum() / pd.Timedelta(hours=1)),
440
+ "hours_fc7_mode": round(
441
+ (delta * df[self.fault_col]).sum() / pd.Timedelta(hours=1)
442
+ ),
443
+ "percent_true": round(df[self.fault_col].mean() * 100, 2),
444
+ "percent_false": round((100 - df[self.fault_col].mean() * 100), 2),
445
+ "flag_true_satsp": round(
446
+ df[self.sat_setpoint_col].where(df[self.fault_col] == 1).mean(), 2
447
+ ),
448
+ "flag_true_sat": round(
449
+ df[self.sat_col].where(df[self.fault_col] == 1).mean(), 2
450
+ ),
451
+ "hours_motor_runtime": round(
452
+ (delta * df[self.supply_vfd_speed_col].gt(0.01).astype(int)).sum()
453
+ / pd.Timedelta(hours=1),
454
+ 2,
455
+ ),
456
+ }
457
+ return summary
458
+
459
+
460
+ class FaultCodeEightReport(BaseFaultReport):
461
+ def __init__(self, config):
462
+ super().__init__(config, "fc8_flag")
463
+ self.sat_col = config["SAT_COL"]
464
+ self.mat_col = config["MAT_COL"]
465
+ self.supply_vfd_speed_col = config["SUPPLY_VFD_SPEED_COL"]
466
+ self.economizer_sig_col = config["ECONOMIZER_SIG_COL"]
467
+
468
+ def create_plot(self, df: pd.DataFrame):
469
+ fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(25, 8))
470
+ fig.suptitle("Fault Conditions 8 Plot")
471
+
472
+ ax1.plot(df.index, df[self.sat_col], label="SAT")
473
+ ax1.plot(df.index, df[self.mat_col], label="MAT")
474
+ ax1.legend(loc="best")
475
+ ax1.set_ylabel("AHU Temps °F")
476
+
477
+ ax2.plot(df.index, df[self.fault_col], label="Fault", color="k")
478
+ ax2.set_xlabel("Date")
479
+ ax2.set_ylabel("Fault Flags")
480
+ ax2.legend(loc="best")
481
+
482
+ plt.tight_layout(rect=[0, 0.03, 1, 0.95])
483
+ plt.show()
484
+ plt.close()
485
+
486
+ def summarize_fault_times(self, df: pd.DataFrame) -> dict:
487
+ delta = df.index.to_series().diff()
488
+ summary = {
489
+ "total_days": round(delta.sum() / pd.Timedelta(days=1), 2),
490
+ "total_hours": round(delta.sum() / pd.Timedelta(hours=1)),
491
+ "hours_fc8_mode": round(
492
+ (delta * df[self.fault_col]).sum() / pd.Timedelta(hours=1)
493
+ ),
494
+ "percent_true": round(df[self.fault_col].mean() * 100, 2),
495
+ "percent_false": round((100 - df[self.fault_col].mean() * 100), 2),
496
+ "flag_true_mat": round(
497
+ df[self.mat_col].where(df[self.fault_col] == 1).mean(), 2
498
+ ),
499
+ "flag_true_sat": round(
500
+ df[self.sat_col].where(df[self.fault_col] == 1).mean(), 2
501
+ ),
502
+ "hours_motor_runtime": round(
503
+ (delta * df[self.supply_vfd_speed_col].gt(0.01).astype(int)).sum()
504
+ / pd.Timedelta(hours=1),
505
+ 2,
506
+ ),
507
+ }
508
+ return summary
509
+
510
+
511
+ class FaultCodeNineReport(BaseFaultReport):
512
+ def __init__(self, config):
513
+ super().__init__(config, "fc9_flag")
514
+ self.sat_setpoint_col = config["SAT_SETPOINT_COL"]
515
+ self.oat_col = config["OAT_COL"]
516
+ self.supply_vfd_speed_col = config["SUPPLY_VFD_SPEED_COL"]
517
+ self.economizer_sig_col = config["ECONOMIZER_SIG_COL"]
518
+
519
+ def create_plot(self, df: pd.DataFrame):
520
+ fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(25, 8))
521
+ fig.suptitle("Fault Conditions 9 Plot")
522
+
523
+ ax1.plot(df.index, df[self.sat_setpoint_col], label="SATSP")
524
+ ax1.plot(df.index, df[self.oat_col], label="OAT")
525
+ ax1.legend(loc="best")
526
+ ax1.set_ylabel("AHU Temps °F")
527
+
528
+ ax2.plot(df.index, df[self.fault_col], label="Fault", color="k")
529
+ ax2.set_xlabel("Date")
530
+ ax2.set_ylabel("Fault Flags")
531
+ ax2.legend(loc="best")
532
+
533
+ plt.tight_layout(rect=[0, 0.03, 1, 0.95])
534
+ plt.show()
535
+ plt.close()
536
+
537
+ def summarize_fault_times(self, df: pd.DataFrame) -> dict:
538
+ delta = df.index.to_series().diff()
539
+ summary = {
540
+ "total_days": round(delta.sum() / pd.Timedelta(days=1), 2),
541
+ "total_hours": round(delta.sum() / pd.Timedelta(hours=1)),
542
+ "hours_fc9_mode": round(
543
+ (delta * df[self.fault_col]).sum() / pd.Timedelta(hours=1)
544
+ ),
545
+ "percent_true": round(df[self.fault_col].mean() * 100, 2),
546
+ "percent_false": round((100 - df[self.fault_col].mean() * 100), 2),
547
+ "flag_true_oat": round(
548
+ df[self.oat_col].where(df[self.fault_col] == 1).mean(), 2
549
+ ),
550
+ "flag_true_satsp": round(
551
+ df[self.sat_setpoint_col].where(df[self.fault_col] == 1).mean(), 2
552
+ ),
553
+ "hours_motor_runtime": round(
554
+ (delta * df[self.supply_vfd_speed_col].gt(0.01).astype(int)).sum()
555
+ / pd.Timedelta(hours=1),
556
+ 2,
557
+ ),
558
+ }
559
+ return summary
560
+
561
+
562
+ class FaultCodeTenReport(BaseFaultReport):
563
+ def __init__(self, config):
564
+ super().__init__(config, "fc10_flag")
565
+ self.oat_col = config["OAT_COL"]
566
+ self.mat_col = config["MAT_COL"]
567
+ self.cooling_sig_col = config["COOLING_SIG_COL"]
568
+ self.economizer_sig_col = config["ECONOMIZER_SIG_COL"]
569
+ self.supply_vfd_speed_col = config["SUPPLY_VFD_SPEED_COL"]
570
+
571
+ def create_plot(self, df: pd.DataFrame):
572
+ fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(25, 8))
573
+ fig.suptitle("Fault Conditions 10 Plot")
574
+
575
+ ax1.plot(df.index, df[self.mat_col], label="MAT")
576
+ ax1.plot(df.index, df[self.oat_col], label="OAT")
577
+ ax1.legend(loc="best")
578
+ ax1.set_ylabel("AHU Temps °F")
579
+
580
+ ax2.plot(df.index, df[self.cooling_sig_col], label="AHU Cool Vlv", color="r")
581
+ ax2.plot(df.index, df[self.economizer_sig_col], label="AHU Dpr Cmd", color="g")
582
+ ax2.legend(loc="best")
583
+ ax2.set_ylabel("%")
584
+
585
+ ax3.plot(df.index, df[self.fault_col], label="Fault", color="k")
586
+ ax3.set_xlabel("Date")
587
+ ax3.set_ylabel("Fault Flags")
588
+ ax3.legend(loc="best")
589
+
590
+ plt.tight_layout(rect=[0, 0.03, 1, 0.95])
591
+ plt.show()
592
+ plt.close()
593
+
594
+ def summarize_fault_times(self, df: pd.DataFrame) -> dict:
595
+ delta = df.index.to_series().diff()
596
+ summary = {
597
+ "total_days": round(delta.sum() / pd.Timedelta(days=1), 2),
598
+ "total_hours": round(delta.sum() / pd.Timedelta(hours=1)),
599
+ "hours_fc10_mode": round(
600
+ (delta * df[self.fault_col]).sum() / pd.Timedelta(hours=1)
601
+ ),
602
+ "percent_true": round(df[self.fault_col].mean() * 100, 2),
603
+ "percent_false": round((100 - df[self.fault_col].mean() * 100), 2),
604
+ "flag_true_oat": round(
605
+ df[self.oat_col].where(df[self.fault_col] == 1).mean(), 2
606
+ ),
607
+ "flag_true_mat": round(
608
+ df[self.mat_col].where(df[self.fault_col] == 1).mean(), 2
609
+ ),
610
+ "hours_motor_runtime": round(
611
+ (delta * df[self.supply_vfd_speed_col].gt(0.01).astype(int)).sum()
612
+ / pd.Timedelta(hours=1),
613
+ 2,
614
+ ),
615
+ }
616
+ return summary
617
+
618
+
619
+ class FaultCodeElevenReport(BaseFaultReport):
620
+ def __init__(self, config):
621
+ super().__init__(config, "fc11_flag")
622
+ self.sat_setpoint_col = config["SAT_SETPOINT_COL"]
623
+ self.oat_col = config["OAT_COL"]
624
+ self.cooling_sig_col = config["COOLING_SIG_COL"]
625
+ self.economizer_sig_col = config["ECONOMIZER_SIG_COL"]
626
+ self.supply_vfd_speed_col = config["SUPPLY_VFD_SPEED_COL"]
627
+
628
+ def create_plot(self, df: pd.DataFrame):
629
+ fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(25, 8))
630
+ fig.suptitle("Fault Conditions 11 Plot")
631
+
632
+ ax1.plot(df.index, df[self.sat_setpoint_col], label="SATSP")
633
+ ax1.plot(df.index, df[self.oat_col], label="OAT")
634
+ ax1.legend(loc="best")
635
+ ax1.set_ylabel("AHU Temps °F")
636
+
637
+ ax2.plot(df.index, df[self.cooling_sig_col], label="AHU Cool Vlv", color="r")
638
+ ax2.plot(df.index, df[self.economizer_sig_col], label="AHU Dpr Cmd", color="g")
639
+ ax2.legend(loc="best")
640
+ ax2.set_ylabel("%")
641
+
642
+ ax3.plot(df.index, df[self.fault_col], label="Fault", color="k")
643
+ ax3.set_xlabel("Date")
644
+ ax3.set_ylabel("Fault Flags")
645
+ ax3.legend(loc="best")
646
+
647
+ plt.tight_layout(rect=[0, 0.03, 1, 0.95])
648
+ plt.show()
649
+ plt.close()
650
+
651
+ def summarize_fault_times(self, df: pd.DataFrame) -> dict:
652
+ delta = df.index.to_series().diff()
653
+ summary = {
654
+ "total_days": round(delta.sum() / pd.Timedelta(days=1), 2),
655
+ "total_hours": round(delta.sum() / pd.Timedelta(hours=1)),
656
+ "hours_fc11_mode": round(
657
+ (delta * df[self.fault_col]).sum() / pd.Timedelta(hours=1)
658
+ ),
659
+ "percent_true": round(df[self.fault_col].mean() * 100, 2),
660
+ "percent_false": round((100 - df[self.fault_col].mean() * 100), 2),
661
+ "flag_true_oat": round(
662
+ df[self.oat_col].where(df[self.fault_col] == 1).mean(), 2
663
+ ),
664
+ "flag_true_sat_sp": round(
665
+ df[self.sat_setpoint_col].where(df[self.fault_col] == 1).mean(), 2
666
+ ),
667
+ "hours_motor_runtime": round(
668
+ (delta * df[self.supply_vfd_speed_col].gt(0.01).astype(int)).sum()
669
+ / pd.Timedelta(hours=1),
670
+ 2,
671
+ ),
672
+ }
673
+ return summary
674
+
675
+
676
+ class FaultCodeTwelveReport(BaseFaultReport):
677
+ def __init__(self, config):
678
+ super().__init__(config, "fc12_flag")
679
+ self.sat_col = config["SAT_COL"]
680
+ self.mat_col = config["MAT_COL"]
681
+ self.cooling_sig_col = config["COOLING_SIG_COL"]
682
+ self.economizer_sig_col = config["ECONOMIZER_SIG_COL"]
683
+ self.supply_vfd_speed_col = config["SUPPLY_VFD_SPEED_COL"]
684
+
685
+ def create_plot(self, df: pd.DataFrame):
686
+ fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(25, 8))
687
+ fig.suptitle("Fault Conditions 12 Plot")
688
+
689
+ ax1.plot(df.index, df[self.sat_col], label="SAT")
690
+ ax1.plot(df.index, df[self.mat_col], label="MAT")
691
+ ax1.legend(loc="best")
692
+ ax1.set_ylabel("AHU Temps °F")
693
+
694
+ ax2.plot(df.index, df[self.cooling_sig_col], label="AHU Cool Vlv", color="r")
695
+ ax2.plot(df.index, df[self.economizer_sig_col], label="AHU Dpr Cmd", color="g")
696
+ ax2.legend(loc="best")
697
+ ax2.set_ylabel("%")
698
+
699
+ ax3.plot(df.index, df[self.fault_col], label="Fault", color="k")
700
+ ax3.set_xlabel("Date")
701
+ ax3.set_ylabel("Fault Flags")
702
+ ax3.legend(loc="best")
703
+
704
+ plt.tight_layout(rect=[0, 0.03, 1, 0.95])
705
+ plt.show()
706
+ plt.close()
707
+
708
+ def summarize_fault_times(self, df: pd.DataFrame) -> dict:
709
+ delta = df.index.to_series().diff()
710
+ summary = {
711
+ "total_days": round(delta.sum() / pd.Timedelta(days=1), 2),
712
+ "total_hours": round(delta.sum() / pd.Timedelta(hours=1)),
713
+ "hours_fc12_mode": round(
714
+ (delta * df[self.fault_col]).sum() / pd.Timedelta(hours=1)
715
+ ),
716
+ "percent_true": round(df[self.fault_col].mean() * 100, 2),
717
+ "percent_false": round((100 - df[self.fault_col].mean() * 100), 2),
718
+ "flag_true_mat": round(
719
+ df[self.mat_col].where(df[self.fault_col] == 1).mean(), 2
720
+ ),
721
+ "flag_true_sat": round(
722
+ df[self.sat_col].where(df[self.fault_col] == 1).mean(), 2
723
+ ),
724
+ "hours_motor_runtime": round(
725
+ (delta * df[self.supply_vfd_speed_col].gt(0.01).astype(int)).sum()
726
+ / pd.Timedelta(hours=1),
727
+ 2,
728
+ ),
729
+ }
730
+ return summary
731
+
732
+
733
+ class FaultCodeThirteenReport(BaseFaultReport):
734
+ def __init__(self, config):
735
+ super().__init__(config, "fc13_flag")
736
+ self.sat_col = config["SAT_COL"]
737
+ self.mat_col = config["MAT_COL"]
738
+ self.cooling_sig_col = config["COOLING_SIG_COL"]
739
+ self.economizer_sig_col = config["ECONOMIZER_SIG_COL"]
740
+ self.supply_vfd_speed_col = config["SUPPLY_VFD_SPEED_COL"]
741
+
742
+ def create_plot(self, df: pd.DataFrame):
743
+ fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(25, 8))
744
+ fig.suptitle("Fault Conditions 13 Plot")
745
+
746
+ ax1.plot(df.index, df[self.sat_col], label="SAT")
747
+ ax1.plot(df.index, df[self.mat_col], label="MAT")
748
+ ax1.legend(loc="best")
749
+ ax1.set_ylabel("AHU Temps °F")
750
+
751
+ ax2.plot(df.index, df[self.cooling_sig_col], label="AHU Cool Vlv", color="r")
752
+ ax2.plot(df.index, df[self.economizer_sig_col], label="AHU Dpr Cmd", color="g")
753
+ ax2.legend(loc="best")
754
+ ax2.set_ylabel("%")
755
+
756
+ ax3.plot(df.index, df[self.fault_col], label="Fault", color="k")
757
+ ax3.set_xlabel("Date")
758
+ ax3.set_ylabel("Fault Flags")
759
+ ax3.legend(loc="best")
760
+
761
+ plt.tight_layout(rect=[0, 0.03, 1, 0.95])
762
+ plt.show()
763
+ plt.close()
764
+
765
+ def summarize_fault_times(self, df: pd.DataFrame) -> dict:
766
+ delta = df.index.to_series().diff()
767
+ summary = {
768
+ "total_days": round(delta.sum() / pd.Timedelta(days=1), 2),
769
+ "total_hours": round(delta.sum() / pd.Timedelta(hours=1)),
770
+ "hours_fc13_mode": round(
771
+ (delta * df[self.fault_col]).sum() / pd.Timedelta(hours=1)
772
+ ),
773
+ "percent_true": round(df[self.fault_col].mean() * 100, 2),
774
+ "percent_false": round((100 - df[self.fault_col].mean() * 100), 2),
775
+ "flag_true_mat": round(
776
+ df[self.mat_col].where(df[self.fault_col] == 1).mean(), 2
777
+ ),
778
+ "flag_true_sat": round(
779
+ df[self.sat_col].where(df[self.fault_col] == 1).mean(), 2
780
+ ),
781
+ "hours_motor_runtime": round(
782
+ (delta * df[self.supply_vfd_speed_col].gt(0.01).astype(int)).sum()
783
+ / pd.Timedelta(hours=1),
784
+ 2,
785
+ ),
786
+ }
787
+ return summary
788
+
789
+
790
+ class FaultCodeFourteenReport(BaseFaultReport):
791
+ def __init__(self, config):
792
+ super().__init__(config, "fc14_flag")
793
+ self.sat_col = config["SAT_COL"]
794
+ self.clt_col = config["CLT_COL"]
795
+ self.cooling_sig_col = config["COOLING_SIG_COL"]
796
+ self.supply_vfd_speed_col = config["SUPPLY_VFD_SPEED_COL"]
797
+
798
+ def create_plot(self, df: pd.DataFrame):
799
+ fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(25, 8))
800
+ fig.suptitle("Fault Conditions 14 Plot")
801
+
802
+ ax1.plot(df.index, df[self.sat_col], label="SAT")
803
+ ax1.plot(df.index, df[self.clt_col], label="CLT")
804
+ ax1.legend(loc="best")
805
+ ax1.set_ylabel("AHU Temps °F")
806
+
807
+ ax2.plot(df.index, df[self.cooling_sig_col], label="AHU Cool Vlv", color="r")
808
+ ax2.legend(loc="best")
809
+ ax2.set_ylabel("%")
810
+
811
+ ax3.plot(df.index, df[self.fault_col], label="Fault", color="k")
812
+ ax3.set_xlabel("Date")
813
+ ax3.set_ylabel("Fault Flags")
814
+ ax3.legend(loc="best")
815
+
816
+ plt.tight_layout(rect=[0, 0.03, 1, 0.95])
817
+ plt.show()
818
+ plt.close()
819
+
820
+ def summarize_fault_times(self, df: pd.DataFrame) -> dict:
821
+ delta = df.index.to_series().diff()
822
+ summary = {
823
+ "total_days": round(delta.sum() / pd.Timedelta(days=1), 2),
824
+ "total_hours": round(delta.sum() / pd.Timedelta(hours=1)),
825
+ "hours_fc14_mode": round(
826
+ (delta * df[self.fault_col]).sum() / pd.Timedelta(hours=1)
827
+ ),
828
+ "percent_true": round(df[self.fault_col].mean() * 100, 2),
829
+ "percent_false": round((100 - df[self.fault_col].mean() * 100), 2),
830
+ "flag_true_clt": round(
831
+ df[self.clt_col].where(df[self.fault_col] == 1).mean(), 2
832
+ ),
833
+ "flag_true_sat": round(
834
+ df[self.sat_col].where(df[self.fault_col] == 1).mean(), 2
835
+ ),
836
+ "hours_motor_runtime": round(
837
+ (delta * df[self.supply_vfd_speed_col].gt(0.01).astype(int)).sum()
838
+ / pd.Timedelta(hours=1),
839
+ 2,
840
+ ),
841
+ }
842
+ return summary
843
+
844
+
845
+ class FaultCodeFifteenReport(BaseFaultReport):
846
+ def __init__(self, config):
847
+ super().__init__(config, "fc15_flag")
848
+ self.sat_col = config["SAT_COL"]
849
+ self.hlt_col = config["HLT_COL"]
850
+ self.heating_sig_col = config["HEATING_SIG_COL"]
851
+ self.supply_vfd_speed_col = config["SUPPLY_VFD_SPEED_COL"]
852
+
853
+ def create_plot(self, df: pd.DataFrame):
854
+ fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(25, 8))
855
+ fig.suptitle("Fault Conditions 15 Plot")
856
+
857
+ ax1.plot(df.index, df[self.sat_col], label="SAT")
858
+ ax1.plot(df.index, df[self.hlt_col], label="HLT")
859
+ ax1.legend(loc="best")
860
+ ax1.set_ylabel("AHU Temps °F")
861
+
862
+ ax2.plot(df.index, df[self.heating_sig_col], label="AHU Heat Vlv", color="r")
863
+ ax2.legend(loc="best")
864
+ ax2.set_ylabel("%")
865
+
866
+ ax3.plot(df.index, df[self.fault_col], label="Fault", color="k")
867
+ ax3.set_xlabel("Date")
868
+ ax3.set_ylabel("Fault Flags")
869
+ ax3.legend(loc="best")
870
+
871
+ plt.tight_layout(rect=[0, 0.03, 1, 0.95])
872
+ plt.show()
873
+ plt.close()
874
+
875
+ def summarize_fault_times(self, df: pd.DataFrame) -> dict:
876
+ delta = df.index.to_series().diff()
877
+ summary = {
878
+ "total_days": round(delta.sum() / pd.Timedelta(days=1), 2),
879
+ "total_hours": round(delta.sum() / pd.Timedelta(hours=1)),
880
+ "hours_fc15_mode": round(
881
+ (delta * df[self.fault_col]).sum() / pd.Timedelta(hours=1)
882
+ ),
883
+ "percent_true": round(df[self.fault_col].mean() * 100, 2),
884
+ "percent_false": round((100 - df[self.fault_col].mean() * 100), 2),
885
+ "flag_true_hlt": round(
886
+ df[self.hlt_col].where(df[self.fault_col] == 1).mean(), 2
887
+ ),
888
+ "flag_true_sat": round(
889
+ df[self.sat_col].where(df[self.fault_col] == 1).mean(), 2
890
+ ),
891
+ "hours_motor_runtime": round(
892
+ (delta * df[self.supply_vfd_speed_col].gt(0.01).astype(int)).sum()
893
+ / pd.Timedelta(hours=1),
894
+ 2,
895
+ ),
896
+ }
897
+ return summary
898
+
899
+
900
+ class FaultCodeSixteenReport(BaseFaultReport):
901
+ def __init__(self, config):
902
+ super().__init__(config, "fc16_flag")
903
+
904
+ self.supply_vfd_speed_col = config["SUPPLY_VFD_SPEED_COL"]
905
+ self.erv_oat_enter_col = config["ERV_OAT_ENTER_COL"]
906
+ self.erv_oat_leaving_col = config["ERV_OAT_LEAVING_COL"]
907
+ self.erv_eat_enter_col = config["ERV_EAT_ENTER_COL"]
908
+ self.erv_eat_leaving_col = config["ERV_EAT_LEAVING_COL"]
909
+
910
+ # Instantiate FaultConditionSixteen to access its methods
911
+ self.fc16 = FaultConditionSixteen(config)
912
+
913
+ def create_plot(self, df: pd.DataFrame):
914
+ # Calculate the efficiency before plotting using FaultConditionSixteen method
915
+ df = self.fc16.calculate_erv_efficiency(df)
916
+
917
+ print("=" * 50)
918
+ print("Info: ERV calculated efficiency ")
919
+ print("summary statistics ")
920
+ print(df["erv_efficiency_oa"].describe())
921
+ print("=" * 50)
922
+
923
+ sys.stdout.flush()
924
+
925
+ # Create the plot with four subplots
926
+ fig, (ax1, ax2, ax3, ax4) = plt.subplots(4, 1, figsize=(25, 10))
927
+ fig.suptitle("Fault Conditions 16 Plot")
928
+
929
+ # Plot ERV Outdoor Air Side Temps
930
+ ax1.plot(df.index, df[self.erv_oat_enter_col], label="Enter", color="blue")
931
+ ax1.plot(df.index, df[self.erv_oat_leaving_col], label="Leaving", color="green")
932
+ ax1.legend(loc="best")
933
+ ax1.set_ylabel("ERV Outdoor Air Side Temps °F")
934
+
935
+ # Plot ERV Exhaust Air Side Temps
936
+ ax2.plot(df.index, df[self.erv_eat_enter_col], label="Enter", color="red")
937
+ ax2.plot(
938
+ df.index, df[self.erv_eat_leaving_col], label="Leaving", color="purple"
939
+ )
940
+ ax2.legend(loc="best")
941
+ ax2.set_ylabel("ERV Exhaust Air Side Temps °F")
942
+
943
+ # Plot ERV Efficiency
944
+ ax3.plot(
945
+ df.index, df["erv_efficiency_oa"], label="ERV Efficiency OA", color="b"
946
+ )
947
+ ax3.legend(loc="best")
948
+ ax3.set_ylabel("ERV Efficiency OA")
949
+
950
+ # Plot Fault Flags
951
+ ax4.plot(df.index, df[self.fault_col], label="Fault", color="k")
952
+ ax4.set_xlabel("Date")
953
+ ax4.set_ylabel("Fault Flags")
954
+ ax4.legend(loc="best")
955
+
956
+ plt.tight_layout(rect=[0, 0.03, 1, 0.95])
957
+ plt.show()
958
+ plt.close()
959
+
960
+ def summarize_fault_times(self, df: pd.DataFrame) -> dict:
961
+ delta = df.index.to_series().diff()
962
+ summary = {
963
+ "total_days": round(delta.sum() / pd.Timedelta(days=1), 2),
964
+ "total_hours": round(delta.sum() / pd.Timedelta(hours=1)),
965
+ "hours_fc16_mode": round(
966
+ (delta * df[self.fault_col]).sum() / pd.Timedelta(hours=1)
967
+ ),
968
+ "percent_true": round(df[self.fault_col].mean() * 100, 2),
969
+ "percent_false": round((100 - df[self.fault_col].mean() * 100), 2),
970
+ "flag_true_erv_oat_enter_temp": round(
971
+ df[self.erv_oat_enter_col].where(df[self.fault_col] == 1).mean(), 2
972
+ ),
973
+ "flag_true_erv_oat_leave_temp": round(
974
+ df[self.erv_oat_leaving_col].where(df[self.fault_col] == 1).mean(), 2
975
+ ),
976
+ "flag_true_erv_eat_enter_temp": round(
977
+ df[self.erv_eat_enter_col].where(df[self.fault_col] == 1).mean(), 2
978
+ ),
979
+ "flag_true_erv_eat_leave_temp": round(
980
+ df[self.erv_eat_leaving_col].where(df[self.fault_col] == 1).mean(), 2
981
+ ),
982
+ "hours_motor_runtime": round(
983
+ (delta * df[self.supply_vfd_speed_col].gt(0.01).astype(int)).sum()
984
+ / pd.Timedelta(hours=1),
985
+ 2,
986
+ ),
987
+ }
988
+ return summary