open-fdd 0.1.4__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.
@@ -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
@@ -0,0 +1,41 @@
1
+ import matplotlib.pyplot as plt
2
+ import pandas as pd
3
+ import numpy as np
4
+ import sys
5
+
6
+
7
+ class BaseFaultReport:
8
+ def __init__(self, config, fault_col):
9
+ self.config = config
10
+ self.fault_col = fault_col
11
+
12
+ def create_plot(self, df: pd.DataFrame):
13
+ raise NotImplementedError
14
+
15
+ def summarize_fault_times(self, df: pd.DataFrame) -> dict:
16
+ raise NotImplementedError
17
+
18
+ def create_hist_plot(self, df: pd.DataFrame):
19
+ df[f"hour_of_the_day_{self.fault_col}"] = df.index.hour.where(
20
+ df[self.fault_col] == 1
21
+ )
22
+ fig, ax = plt.subplots(tight_layout=True, figsize=(25, 8))
23
+ ax.hist(df[f"hour_of_the_day_{self.fault_col}"].dropna())
24
+ ax.set_xlabel("Hour of the Day")
25
+ ax.set_ylabel("Frequency")
26
+ ax.set_title(f"Hour-Of-Day When Fault Flag {self.fault_col} is TRUE")
27
+ plt.show()
28
+ plt.close()
29
+
30
+ def display_report_in_ipython(self, df: pd.DataFrame):
31
+ summary = self.summarize_fault_times(df)
32
+ for key, value in summary.items():
33
+ formatted_key = key.replace("_", " ")
34
+ print(f"{formatted_key}: {value}")
35
+ sys.stdout.flush()
36
+
37
+ if df[self.fault_col].max() != 0:
38
+ self.create_hist_plot(df)
39
+ else:
40
+ print("NO FAULTS FOUND - Skipping time-of-day Histogram plot")
41
+ sys.stdout.flush()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: open_fdd
3
- Version: 0.1.4
3
+ Version: 0.1.5
4
4
  Summary: A package for fault detection and diagnosis in HVAC systems
5
5
  Home-page: https://github.com/bbartling/open-fdd
6
6
  Author: Ben Bartling
@@ -39,20 +39,28 @@ pip install open-fdd
39
39
  For running Jupyter notebooks, I recommend using Visual Studio Code with the Jupyter notebook extension installed, which offers a seamless experience directly within the editor. Be sure to explore the `examples` directory for Jupyter notebook tutorials. If you have your own FDD experiences to share, feel free to contribute by creating a notebook (`.ipynb`). You’re welcome to reach out to me directly, and I can push your example to GitHub on your behalf, which might be a simpler process than submitting a pull request (PR), especially if you're just sharing an example rather than developing `open-fdd`.
40
40
 
41
41
  ## Project goals
42
- These are some basic project goals to make this into an interactive FDD application.
43
- - [x] finish `air_handling_unit` faults and reports based on ASHRAE and NIST
44
- - [x] publish to PyPI as Python library
45
- - [ ] make a few IPython notebook tutorials AHU FDD examples with `BRICK` meta data integration.
46
- - [ ] make a guide for fault `parameters` like error thresholds, etc.
47
- - [ ] make `central_plant` faults, IPython reports, and examples.
48
- - [ ] make `energy_efficiency` faults, IPython reports, and examples to `optimize` in reducing energy consumption.
49
- - [ ] make `metering`, faults, IPython reports, and examples to possibly model utility metering data.
50
- - [ ] create SQL example to read data from time series db and write back to SQL to then read faults in Grafana.
51
- - [ ] other? Certainly! As ChatGPT would eagerly say!
42
+ The following are key objectives to enhance this project into a fully interactive Fault Detection and Diagnostics (FDD) application.
52
43
 
44
+ ### Completed
45
+ - [x] Develop and finalize `air_handling_unit` fault conditions and reports, aligning with ASHRAE and NIST standards.
46
+ - [x] Publish the project as a Python library on PyPI.
53
47
 
54
- ## Contribute
48
+ ### In Progress
49
+ - [ ] Create IPython notebook tutorials showcasing AHU FDD examples, incorporating BRICK metadata integration.
50
+ - [ ] Develop a comprehensive guide on a github.io website (or other?) for defining fault parameters, including error thresholds and other critical settings.
51
+
52
+ ### Upcoming
53
+ - [ ] Extend the project to include `central_plant` fault conditions, IPython reports, and example applications.
54
+ - [ ] Design `energy_efficiency` fault detection modules, including IPython reports and examples focused on optimizing energy consumption.
55
+ - [ ] Develop `metering` fault conditions, along with IPython reports and examples, potentially modeling utility metering data.
56
+ - [ ] Implement SQL integration examples for reading data from a time series database, writing back to SQL, and visualizing faults in Grafana.
57
+
58
+ ### Future Considerations
59
+ Explore additional features and enhancements as the project evolves.
60
+ - [ ] Explore additional features and enhancements as the project evolves.
55
61
 
62
+
63
+ ## Contribute
56
64
  If you have suggestions for improving developer best practices or solutions, please feel free to reach out to me directly using my contact information or Git issue/discussion. I primarily work on Windows with multiple versions of Python installed, with Python 3.12.x as my default version. You can download the latest version of Python here:
57
65
  * https://www.python.org/downloads/
58
66
 
@@ -41,8 +41,9 @@ open_fdd/air_handling_unit/images/latex_generator.py,sha256=9kotpUZjQrECXiB8ewcv
41
41
  open_fdd/air_handling_unit/images/params.docx,sha256=_mh1G-avi-lsssaW_qH33vGVaYwS_XL_IOQweYpQvAo,157634
42
42
  open_fdd/air_handling_unit/images/params.pdf,sha256=2rmxh2yxuB7x3Q2Jyq0MnFoTJAQqxwIoxGvzzJirWkU,175678
43
43
  open_fdd/air_handling_unit/images/plot_for_repo.png,sha256=u4OIzZUyGk6G4o1AusEERUU6aen4vVX-CGPl14P3qkA,651969
44
- open_fdd/air_handling_unit/reports/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
44
+ open_fdd/air_handling_unit/reports/__init__.py,sha256=tIU1N_3cH7A6tWeoAIsVLLFUlnizIOiMCgMVDeoFGL4,36276
45
45
  open_fdd/air_handling_unit/reports/base_report.py,sha256=oZX6_qXUjuPEHKpF89DhgpPbAkhHeoo3jpDL30u4njs,1778
46
+ open_fdd/air_handling_unit/reports/fault_report.py,sha256=v2yki8kFWsGmHdgY15kbH_mS-8KFGAqtEuHenFn0Ils,1408
46
47
  open_fdd/air_handling_unit/reports/report_fc1.py,sha256=ZlfTP4Pzv0l0hBHRWmTFMXKWPnuMNW38mHPPD8QW460,4565
47
48
  open_fdd/air_handling_unit/reports/report_fc10.py,sha256=Dgh0L4A-D-VYqTfVcMwhxdw67R-31c24ydtrpJ-gLyU,5179
48
49
  open_fdd/air_handling_unit/reports/report_fc11.py,sha256=fLb004y59i_2LegfGlpgEpEzQadqsCWrJPNLZ6obgqg,5197
@@ -75,8 +76,8 @@ open_fdd/tests/ahu/test_ahu_fc6.py,sha256=66dwv0EBU_ujZK-J9Ki5a3fnXlk17nOwmtKDiQ
75
76
  open_fdd/tests/ahu/test_ahu_fc7.py,sha256=sABbw2m7WlAXbsqfDD323vfEfg606ThI0QzQyB-OjFo,2469
76
77
  open_fdd/tests/ahu/test_ahu_fc8.py,sha256=UZy6BP2PgV1FROUPqMORTx8YnT5ZvqVDhut_Ar81494,4663
77
78
  open_fdd/tests/ahu/test_ahu_fc9.py,sha256=b-eIzhNzjZUjVNsP0JAHkOgZu-BtDuPeNnblVVm-jU8,4796
78
- open_fdd-0.1.4.dist-info/LICENSE,sha256=eghao_GGx_0gB2Sll3x2vV29knONEzUQKrkaXpX1F7w,1087
79
- open_fdd-0.1.4.dist-info/METADATA,sha256=mnTD5a5-ob9hYjWMbcfxHdB8RAIJ-GOWC33kWtc_JJw,6420
80
- open_fdd-0.1.4.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
81
- open_fdd-0.1.4.dist-info/top_level.txt,sha256=Q7sB6UB2d8Ch1v_xIsTiNegmgcCXPkwkrxK3ug6VEOs,9
82
- open_fdd-0.1.4.dist-info/RECORD,,
79
+ open_fdd-0.1.5.dist-info/LICENSE,sha256=eghao_GGx_0gB2Sll3x2vV29knONEzUQKrkaXpX1F7w,1087
80
+ open_fdd-0.1.5.dist-info/METADATA,sha256=rQBnlFoVuYXgFtGbTJ7VKWiSI62DHpTK2TOzA3-XqmI,6925
81
+ open_fdd-0.1.5.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
82
+ open_fdd-0.1.5.dist-info/top_level.txt,sha256=Q7sB6UB2d8Ch1v_xIsTiNegmgcCXPkwkrxK3ug6VEOs,9
83
+ open_fdd-0.1.5.dist-info/RECORD,,