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