open-fdd 0.1.4__py3-none-any.whl → 0.1.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- open_fdd/air_handling_unit/faults/__init__.py +301 -301
- open_fdd/air_handling_unit/reports/__init__.py +988 -0
- open_fdd/air_handling_unit/reports/fault_report.py +42 -0
- open_fdd/tests/ahu/test_ahu_fc16.py +190 -0
- {open_fdd-0.1.4.dist-info → open_fdd-0.1.6.dist-info}/METADATA +21 -12
- open_fdd-0.1.6.dist-info/RECORD +31 -0
- {open_fdd-0.1.4.dist-info → open_fdd-0.1.6.dist-info}/WHEEL +1 -1
- open_fdd/air_handling_unit/faults/fault_condition_eight.py +0 -127
- open_fdd/air_handling_unit/faults/fault_condition_eleven.py +0 -126
- open_fdd/air_handling_unit/faults/fault_condition_fifteen.py +0 -152
- open_fdd/air_handling_unit/faults/fault_condition_five.py +0 -123
- open_fdd/air_handling_unit/faults/fault_condition_four.py +0 -168
- open_fdd/air_handling_unit/faults/fault_condition_fourteen.py +0 -143
- open_fdd/air_handling_unit/faults/fault_condition_nine.py +0 -128
- open_fdd/air_handling_unit/faults/fault_condition_one.py +0 -112
- open_fdd/air_handling_unit/faults/fault_condition_seven.py +0 -114
- open_fdd/air_handling_unit/faults/fault_condition_six.py +0 -181
- open_fdd/air_handling_unit/faults/fault_condition_ten.py +0 -123
- open_fdd/air_handling_unit/faults/fault_condition_thirteen.py +0 -127
- open_fdd/air_handling_unit/faults/fault_condition_three.py +0 -113
- open_fdd/air_handling_unit/faults/fault_condition_twelve.py +0 -132
- open_fdd/air_handling_unit/faults/fault_condition_two.py +0 -113
- open_fdd/air_handling_unit/images/ahu1_fc1_2024-06_1.jpg +0 -0
- open_fdd/air_handling_unit/images/ahu1_fc1_2024-06_2.jpg +0 -0
- open_fdd/air_handling_unit/images/example1.jpg +0 -0
- open_fdd/air_handling_unit/images/example2.jpg +0 -0
- open_fdd/air_handling_unit/images/fc10_definition.png +0 -0
- open_fdd/air_handling_unit/images/fc11_definition.png +0 -0
- open_fdd/air_handling_unit/images/fc12_definition.png +0 -0
- open_fdd/air_handling_unit/images/fc13_definition.png +0 -0
- open_fdd/air_handling_unit/images/fc1_definition.png +0 -0
- open_fdd/air_handling_unit/images/fc1_report_screenshot_all.png +0 -0
- open_fdd/air_handling_unit/images/fc2_definition.png +0 -0
- open_fdd/air_handling_unit/images/fc3_definition.png +0 -0
- open_fdd/air_handling_unit/images/fc4_definition.png +0 -0
- open_fdd/air_handling_unit/images/fc5_definition.png +0 -0
- open_fdd/air_handling_unit/images/fc6_definition.png +0 -0
- open_fdd/air_handling_unit/images/fc7_definition.png +0 -0
- open_fdd/air_handling_unit/images/fc8_definition.png +0 -0
- open_fdd/air_handling_unit/images/fc9_definition.png +0 -0
- open_fdd/air_handling_unit/images/latex_generator.py +0 -175
- open_fdd/air_handling_unit/images/params.docx +0 -0
- open_fdd/air_handling_unit/images/params.pdf +0 -0
- open_fdd/air_handling_unit/images/plot_for_repo.png +0 -0
- open_fdd/air_handling_unit/reports/base_report.py +0 -47
- open_fdd/air_handling_unit/reports/report_fc1.py +0 -115
- open_fdd/air_handling_unit/reports/report_fc10.py +0 -126
- open_fdd/air_handling_unit/reports/report_fc11.py +0 -128
- open_fdd/air_handling_unit/reports/report_fc12.py +0 -126
- open_fdd/air_handling_unit/reports/report_fc13.py +0 -126
- open_fdd/air_handling_unit/reports/report_fc14.py +0 -124
- open_fdd/air_handling_unit/reports/report_fc15.py +0 -124
- open_fdd/air_handling_unit/reports/report_fc2.py +0 -119
- open_fdd/air_handling_unit/reports/report_fc3.py +0 -119
- open_fdd/air_handling_unit/reports/report_fc4.py +0 -148
- open_fdd/air_handling_unit/reports/report_fc5.py +0 -132
- open_fdd/air_handling_unit/reports/report_fc6.py +0 -156
- open_fdd/air_handling_unit/reports/report_fc7.py +0 -126
- open_fdd/air_handling_unit/reports/report_fc8.py +0 -118
- open_fdd/air_handling_unit/reports/report_fc9.py +0 -120
- open_fdd-0.1.4.dist-info/RECORD +0 -82
- {open_fdd-0.1.4.dist-info → open_fdd-0.1.6.dist-info}/LICENSE +0 -0
- {open_fdd-0.1.4.dist-info → open_fdd-0.1.6.dist-info}/top_level.txt +0 -0
@@ -12,6 +12,8 @@ import sys
|
|
12
12
|
class FaultConditionOne(FaultCondition):
|
13
13
|
"""Class provides the definitions for Fault Condition 1.
|
14
14
|
AHU low duct static pressure fan fault.
|
15
|
+
|
16
|
+
py -3.12 -m pytest open_fdd/tests/ahu/test_ahu_fc1.py -rP -s
|
15
17
|
"""
|
16
18
|
|
17
19
|
def __init__(self, dict_):
|
@@ -101,34 +103,25 @@ class FaultConditionOne(FaultCondition):
|
|
101
103
|
columns_to_check = [self.supply_vfd_speed_col]
|
102
104
|
self.check_analog_pct(df, columns_to_check)
|
103
105
|
|
104
|
-
|
106
|
+
# Perform checks
|
107
|
+
static_check = (
|
105
108
|
df[self.duct_static_col]
|
106
109
|
< df[self.duct_static_setpoint_col] - self.duct_static_inches_err_thres
|
107
110
|
)
|
108
|
-
|
111
|
+
fan_check = (
|
109
112
|
df[self.supply_vfd_speed_col]
|
110
113
|
>= self.vfd_speed_percent_max - self.vfd_speed_percent_err_thres
|
111
114
|
)
|
112
115
|
|
113
116
|
# Combined condition check
|
114
|
-
|
117
|
+
combined_check = static_check & fan_check
|
115
118
|
|
116
119
|
# Rolling sum to count consecutive trues
|
117
|
-
rolling_sum = (
|
118
|
-
|
119
|
-
)
|
120
|
+
rolling_sum = combined_check.rolling(window=self.rolling_window_size).sum()
|
121
|
+
|
120
122
|
# Set flag to 1 if rolling sum equals the window size
|
121
123
|
df["fc1_flag"] = (rolling_sum == self.rolling_window_size).astype(int)
|
122
124
|
|
123
|
-
if self.troubleshoot_mode:
|
124
|
-
print("Troubleshoot mode enabled - not removing helper columns")
|
125
|
-
sys.stdout.flush()
|
126
|
-
|
127
|
-
# Optionally remove temporary columns
|
128
|
-
df.drop(
|
129
|
-
columns=["static_check_", "fan_check_", "combined_check"], inplace=True
|
130
|
-
)
|
131
|
-
|
132
125
|
return df
|
133
126
|
|
134
127
|
except MissingColumnError as e:
|
@@ -232,33 +225,23 @@ class FaultConditionTwo(FaultCondition):
|
|
232
225
|
columns_to_check = [self.supply_vfd_speed_col]
|
233
226
|
self.check_analog_pct(df, columns_to_check)
|
234
227
|
|
235
|
-
#
|
236
|
-
|
237
|
-
|
228
|
+
# Perform checks
|
229
|
+
mat_check = df[self.mat_col] + self.mix_degf_err_thres
|
230
|
+
temp_min_check = np.minimum(
|
238
231
|
df[self.rat_col] - self.return_degf_err_thres,
|
239
232
|
df[self.oat_col] - self.outdoor_degf_err_thres,
|
240
233
|
)
|
241
234
|
|
242
|
-
|
235
|
+
combined_check = (mat_check < temp_min_check) & (
|
243
236
|
df[self.supply_vfd_speed_col] > 0.01
|
244
237
|
)
|
245
238
|
|
246
239
|
# Rolling sum to count consecutive trues
|
247
|
-
rolling_sum = (
|
248
|
-
|
249
|
-
)
|
240
|
+
rolling_sum = combined_check.rolling(window=self.rolling_window_size).sum()
|
241
|
+
|
250
242
|
# Set flag to 1 if rolling sum equals the window size
|
251
243
|
df["fc2_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
|
252
244
|
|
253
|
-
if self.troubleshoot_mode:
|
254
|
-
print("Troubleshoot mode enabled - not removing helper columns")
|
255
|
-
sys.stdout.flush()
|
256
|
-
|
257
|
-
# Optionally remove temporary columns
|
258
|
-
df.drop(
|
259
|
-
columns=["mat_check", "temp_min_check", "combined_check"], inplace=True
|
260
|
-
)
|
261
|
-
|
262
245
|
return df
|
263
246
|
|
264
247
|
except MissingColumnError as e:
|
@@ -362,33 +345,23 @@ class FaultConditionThree(FaultCondition):
|
|
362
345
|
columns_to_check = [self.supply_vfd_speed_col]
|
363
346
|
self.check_analog_pct(df, columns_to_check)
|
364
347
|
|
365
|
-
#
|
366
|
-
|
367
|
-
|
348
|
+
# Perform checks
|
349
|
+
mat_check = df[self.mat_col] - self.mix_degf_err_thres
|
350
|
+
temp_max_check = np.maximum(
|
368
351
|
df[self.rat_col] + self.return_degf_err_thres,
|
369
352
|
df[self.oat_col] + self.outdoor_degf_err_thres,
|
370
353
|
)
|
371
354
|
|
372
|
-
|
355
|
+
combined_check = (mat_check > temp_max_check) & (
|
373
356
|
df[self.supply_vfd_speed_col] > 0.01
|
374
357
|
)
|
375
358
|
|
376
359
|
# Rolling sum to count consecutive trues
|
377
|
-
rolling_sum = (
|
378
|
-
|
379
|
-
)
|
360
|
+
rolling_sum = combined_check.rolling(window=self.rolling_window_size).sum()
|
361
|
+
|
380
362
|
# Set flag to 1 if rolling sum equals the window size
|
381
363
|
df["fc3_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
|
382
364
|
|
383
|
-
if self.troubleshoot_mode:
|
384
|
-
print("Troubleshoot mode enabled - not removing helper columns")
|
385
|
-
sys.stdout.flush()
|
386
|
-
|
387
|
-
# Optionally remove temporary columns
|
388
|
-
df.drop(
|
389
|
-
columns=["mat_check", "temp_max_check", "combined_check"], inplace=True
|
390
|
-
)
|
391
|
-
|
392
365
|
return df
|
393
366
|
|
394
367
|
except MissingColumnError as e:
|
@@ -675,40 +648,28 @@ class FaultConditionFive(FaultCondition):
|
|
675
648
|
# Ensure all required columns are present
|
676
649
|
self.check_required_columns(df)
|
677
650
|
|
678
|
-
if self.troubleshoot_mode:
|
679
|
-
self.troubleshoot_cols(df)
|
680
|
-
|
681
651
|
# Check analog outputs [data with units of %] are floats only
|
682
652
|
columns_to_check = [self.supply_vfd_speed_col, self.heating_sig_col]
|
653
|
+
self.check_analog_pct(df, columns_to_check)
|
683
654
|
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
df["sat_check"] = df[self.sat_col] + self.supply_degf_err_thres
|
688
|
-
df["mat_check"] = (
|
655
|
+
# Perform checks
|
656
|
+
sat_check = df[self.sat_col] + self.supply_degf_err_thres
|
657
|
+
mat_check = (
|
689
658
|
df[self.mat_col] - self.mix_degf_err_thres + self.delta_t_supply_fan
|
690
659
|
)
|
691
660
|
|
692
|
-
|
693
|
-
(
|
661
|
+
combined_check = (
|
662
|
+
(sat_check <= mat_check)
|
694
663
|
& (df[self.heating_sig_col] > 0.01)
|
695
664
|
& (df[self.supply_vfd_speed_col] > 0.01)
|
696
665
|
)
|
697
666
|
|
698
667
|
# Rolling sum to count consecutive trues
|
699
|
-
rolling_sum = (
|
700
|
-
|
701
|
-
)
|
668
|
+
rolling_sum = combined_check.rolling(window=self.rolling_window_size).sum()
|
669
|
+
|
702
670
|
# Set flag to 1 if rolling sum equals the window size
|
703
671
|
df["fc5_flag"] = (rolling_sum == self.rolling_window_size).astype(int)
|
704
672
|
|
705
|
-
if self.troubleshoot_mode:
|
706
|
-
print("Troubleshoot mode enabled - not removing helper columns")
|
707
|
-
sys.stdout.flush()
|
708
|
-
|
709
|
-
# Optionally remove temporary columns
|
710
|
-
df.drop(columns=["mat_check", "sat_check", "combined_check"], inplace=True)
|
711
|
-
|
712
673
|
return df
|
713
674
|
|
714
675
|
except MissingColumnError as e:
|
@@ -839,9 +800,6 @@ class FaultConditionSix(FaultCondition):
|
|
839
800
|
# Ensure all required columns are present
|
840
801
|
self.check_required_columns(df)
|
841
802
|
|
842
|
-
if self.troubleshoot_mode:
|
843
|
-
self.troubleshoot_cols(df)
|
844
|
-
|
845
803
|
# Check analog outputs [data with units of %] are floats only
|
846
804
|
columns_to_check = [
|
847
805
|
self.supply_vfd_speed_col,
|
@@ -849,75 +807,45 @@ class FaultConditionSix(FaultCondition):
|
|
849
807
|
self.heating_sig_col,
|
850
808
|
self.cooling_sig_col,
|
851
809
|
]
|
810
|
+
self.check_analog_pct(df, columns_to_check)
|
852
811
|
|
853
|
-
|
854
|
-
|
855
|
-
|
856
|
-
# Create helper columns
|
857
|
-
df["rat_minus_oat"] = abs(df[self.rat_col] - df[self.oat_col])
|
858
|
-
df["percent_oa_calc"] = (df[self.mat_col] - df[self.rat_col]) / (
|
812
|
+
# Calculate intermediate values
|
813
|
+
rat_minus_oat = abs(df[self.rat_col] - df[self.oat_col])
|
814
|
+
percent_oa_calc = (df[self.mat_col] - df[self.rat_col]) / (
|
859
815
|
df[self.oat_col] - df[self.rat_col]
|
860
816
|
)
|
861
817
|
|
862
|
-
#
|
863
|
-
|
864
|
-
lambda x: x if x > 0 else 0
|
865
|
-
)
|
818
|
+
# Replace negative values in percent_oa_calc with zero using vectorized operation
|
819
|
+
percent_oa_calc = percent_oa_calc.clip(lower=0)
|
866
820
|
|
867
|
-
|
868
|
-
|
869
|
-
)
|
821
|
+
perc_OAmin = self.ahu_min_oa_cfm_design / df[self.supply_fan_air_volume_col]
|
822
|
+
percent_oa_calc_minus_perc_OAmin = abs(percent_oa_calc - perc_OAmin)
|
870
823
|
|
871
|
-
|
872
|
-
|
824
|
+
# Combined checks for OS 1 and OS 4 modes
|
825
|
+
os1_htg_mode_check = (
|
826
|
+
(rat_minus_oat >= self.oat_rat_delta_min)
|
827
|
+
& (percent_oa_calc_minus_perc_OAmin > self.airflow_err_thres)
|
828
|
+
& (df[self.heating_sig_col] > 0.0)
|
829
|
+
& (df[self.supply_vfd_speed_col] > 0.0)
|
873
830
|
)
|
874
831
|
|
875
|
-
|
876
|
-
|
877
|
-
(
|
878
|
-
(df["rat_minus_oat"] >= self.oat_rat_delta_min)
|
879
|
-
& (df["percent_oa_calc_minus_perc_OAmin"] > self.airflow_err_thres)
|
880
|
-
)
|
881
|
-
# Verify AHU is running in OS 1 htg mode in min OA
|
882
|
-
& (
|
883
|
-
(df[self.heating_sig_col] > 0.0)
|
884
|
-
& (df[self.supply_vfd_speed_col] > 0.0)
|
885
|
-
), # OR
|
886
|
-
# OS 4 mech clg mode
|
887
|
-
(
|
888
|
-
(df["rat_minus_oat"] >= self.oat_rat_delta_min)
|
889
|
-
& (df["percent_oa_calc_minus_perc_OAmin"] > self.airflow_err_thres)
|
890
|
-
)
|
891
|
-
# Verify AHU is running in OS 4 clg mode in min OA
|
832
|
+
os4_clg_mode_check = (
|
833
|
+
(rat_minus_oat >= self.oat_rat_delta_min)
|
834
|
+
& (percent_oa_calc_minus_perc_OAmin > self.airflow_err_thres)
|
892
835
|
& (df[self.heating_sig_col] == 0.0)
|
893
836
|
& (df[self.cooling_sig_col] > 0.0)
|
894
837
|
& (df[self.supply_vfd_speed_col] > 0.0)
|
895
|
-
& (df[self.economizer_sig_col] == self.ahu_min_oa_dpr)
|
838
|
+
& (df[self.economizer_sig_col] == self.ahu_min_oa_dpr)
|
896
839
|
)
|
897
840
|
|
841
|
+
combined_check = os1_htg_mode_check | os4_clg_mode_check
|
842
|
+
|
898
843
|
# Rolling sum to count consecutive trues
|
899
|
-
rolling_sum = (
|
900
|
-
|
901
|
-
)
|
844
|
+
rolling_sum = combined_check.rolling(window=self.rolling_window_size).sum()
|
845
|
+
|
902
846
|
# Set flag to 1 if rolling sum equals the window size
|
903
847
|
df["fc6_flag"] = (rolling_sum == self.rolling_window_size).astype(int)
|
904
848
|
|
905
|
-
if self.troubleshoot_mode:
|
906
|
-
print("Troubleshoot mode enabled - not removing helper columns")
|
907
|
-
sys.stdout.flush()
|
908
|
-
|
909
|
-
# Optionally remove temporary columns
|
910
|
-
df.drop(
|
911
|
-
columns=[
|
912
|
-
"rat_minus_oat",
|
913
|
-
"percent_oa_calc",
|
914
|
-
"perc_OAmin",
|
915
|
-
"percent_oa_calc_minus_perc_OAmin",
|
916
|
-
"combined_check",
|
917
|
-
],
|
918
|
-
inplace=True,
|
919
|
-
)
|
920
|
-
|
921
849
|
return df
|
922
850
|
|
923
851
|
except MissingColumnError as e:
|
@@ -1013,36 +941,25 @@ class FaultConditionSeven(FaultCondition):
|
|
1013
941
|
# Ensure all required columns are present
|
1014
942
|
self.check_required_columns(df)
|
1015
943
|
|
1016
|
-
if self.troubleshoot_mode:
|
1017
|
-
self.troubleshoot_cols(df)
|
1018
|
-
|
1019
944
|
# Check analog outputs [data with units of %] are floats only
|
1020
945
|
columns_to_check = [self.supply_vfd_speed_col, self.heating_sig_col]
|
1021
946
|
self.check_analog_pct(df, columns_to_check)
|
1022
947
|
|
1023
|
-
#
|
1024
|
-
|
948
|
+
# Perform checks
|
949
|
+
sat_check = df[self.sat_setpoint_col] - self.supply_degf_err_thres
|
1025
950
|
|
1026
|
-
|
1027
|
-
(df[self.sat_col] <
|
951
|
+
combined_check = (
|
952
|
+
(df[self.sat_col] < sat_check)
|
1028
953
|
& (df[self.heating_sig_col] > 0.9)
|
1029
954
|
& (df[self.supply_vfd_speed_col] > 0)
|
1030
955
|
)
|
1031
956
|
|
1032
957
|
# Rolling sum to count consecutive trues
|
1033
|
-
rolling_sum = (
|
1034
|
-
|
1035
|
-
)
|
958
|
+
rolling_sum = combined_check.rolling(window=self.rolling_window_size).sum()
|
959
|
+
|
1036
960
|
# Set flag to 1 if rolling sum equals the window size
|
1037
961
|
df["fc7_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
|
1038
962
|
|
1039
|
-
if self.troubleshoot_mode:
|
1040
|
-
print("Troubleshoot mode enabled - not removing helper columns")
|
1041
|
-
sys.stdout.flush()
|
1042
|
-
|
1043
|
-
# Optionally remove temporary columns
|
1044
|
-
df.drop(columns=["sat_check", "combined_check"], inplace=True)
|
1045
|
-
|
1046
963
|
return df
|
1047
964
|
|
1048
965
|
except MissingColumnError as e:
|
@@ -1147,9 +1064,6 @@ class FaultConditionEight(FaultCondition):
|
|
1147
1064
|
# Ensure all required columns are present
|
1148
1065
|
self.check_required_columns(df)
|
1149
1066
|
|
1150
|
-
if self.troubleshoot_mode:
|
1151
|
-
self.troubleshoot_cols(df)
|
1152
|
-
|
1153
1067
|
# Check analog outputs [data with units of %] are floats only
|
1154
1068
|
columns_to_check = [
|
1155
1069
|
self.economizer_sig_col,
|
@@ -1157,36 +1071,26 @@ class FaultConditionEight(FaultCondition):
|
|
1157
1071
|
]
|
1158
1072
|
self.check_analog_pct(df, columns_to_check)
|
1159
1073
|
|
1160
|
-
|
1074
|
+
# Perform checks
|
1075
|
+
sat_fan_mat = abs(
|
1161
1076
|
df[self.sat_col] - self.delta_t_supply_fan - df[self.mat_col]
|
1162
1077
|
)
|
1163
|
-
|
1078
|
+
sat_mat_sqrted = np.sqrt(
|
1164
1079
|
self.supply_degf_err_thres**2 + self.mix_degf_err_thres**2
|
1165
1080
|
)
|
1166
1081
|
|
1167
|
-
|
1168
|
-
(
|
1082
|
+
combined_check = (
|
1083
|
+
(sat_fan_mat > sat_mat_sqrted)
|
1169
1084
|
& (df[self.economizer_sig_col] > self.ahu_min_oa_dpr)
|
1170
1085
|
& (df[self.cooling_sig_col] < 0.1)
|
1171
1086
|
)
|
1172
1087
|
|
1173
1088
|
# Rolling sum to count consecutive trues
|
1174
|
-
rolling_sum = (
|
1175
|
-
|
1176
|
-
)
|
1089
|
+
rolling_sum = combined_check.rolling(window=self.rolling_window_size).sum()
|
1090
|
+
|
1177
1091
|
# Set flag to 1 if rolling sum equals the window size
|
1178
1092
|
df["fc8_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
|
1179
1093
|
|
1180
|
-
if self.troubleshoot_mode:
|
1181
|
-
print("Troubleshoot mode enabled - not removing helper columns")
|
1182
|
-
sys.stdout.flush()
|
1183
|
-
|
1184
|
-
# Optionally remove temporary columns
|
1185
|
-
df.drop(
|
1186
|
-
columns=["sat_fan_mat", "sat_mat_sqrted", "combined_check"],
|
1187
|
-
inplace=True,
|
1188
|
-
)
|
1189
|
-
|
1190
1094
|
return df
|
1191
1095
|
|
1192
1096
|
except MissingColumnError as e:
|
@@ -1291,9 +1195,6 @@ class FaultConditionNine(FaultCondition):
|
|
1291
1195
|
# Ensure all required columns are present
|
1292
1196
|
self.check_required_columns(df)
|
1293
1197
|
|
1294
|
-
if self.troubleshoot_mode:
|
1295
|
-
self.troubleshoot_cols(df)
|
1296
|
-
|
1297
1198
|
# Check analog outputs [data with units of %] are floats only
|
1298
1199
|
columns_to_check = [
|
1299
1200
|
self.economizer_sig_col,
|
@@ -1301,38 +1202,27 @@ class FaultConditionNine(FaultCondition):
|
|
1301
1202
|
]
|
1302
1203
|
self.check_analog_pct(df, columns_to_check)
|
1303
1204
|
|
1304
|
-
#
|
1305
|
-
|
1306
|
-
|
1205
|
+
# Perform calculations
|
1206
|
+
oat_minus_oaterror = df[self.oat_col] - self.outdoor_degf_err_thres
|
1207
|
+
satsp_delta_saterr = (
|
1307
1208
|
df[self.sat_setpoint_col]
|
1308
1209
|
- self.delta_t_supply_fan
|
1309
1210
|
+ self.supply_degf_err_thres
|
1310
1211
|
)
|
1311
1212
|
|
1312
|
-
|
1313
|
-
(
|
1213
|
+
combined_check = (
|
1214
|
+
(oat_minus_oaterror > satsp_delta_saterr)
|
1314
1215
|
# verify AHU is in OS2 only free cooling mode
|
1315
1216
|
& (df[self.economizer_sig_col] > self.ahu_min_oa_dpr)
|
1316
1217
|
& (df[self.cooling_sig_col] < 0.1)
|
1317
1218
|
)
|
1318
1219
|
|
1319
1220
|
# Rolling sum to count consecutive trues
|
1320
|
-
rolling_sum = (
|
1321
|
-
|
1322
|
-
)
|
1221
|
+
rolling_sum = combined_check.rolling(window=self.rolling_window_size).sum()
|
1222
|
+
|
1323
1223
|
# Set flag to 1 if rolling sum equals the window size
|
1324
1224
|
df["fc9_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
|
1325
1225
|
|
1326
|
-
if self.troubleshoot_mode:
|
1327
|
-
print("Troubleshoot mode enabled - not removing helper columns")
|
1328
|
-
sys.stdout.flush()
|
1329
|
-
|
1330
|
-
# Optionally remove temporary columns
|
1331
|
-
df.drop(
|
1332
|
-
columns=["oat_minus_oaterror", "satsp_delta_saterr", "combined_check"],
|
1333
|
-
inplace=True,
|
1334
|
-
)
|
1335
|
-
|
1336
1226
|
return df
|
1337
1227
|
|
1338
1228
|
except MissingColumnError as e:
|
@@ -1433,9 +1323,6 @@ class FaultConditionTen(FaultCondition):
|
|
1433
1323
|
# Ensure all required columns are present
|
1434
1324
|
self.check_required_columns(df)
|
1435
1325
|
|
1436
|
-
if self.troubleshoot_mode:
|
1437
|
-
self.troubleshoot_cols(df)
|
1438
|
-
|
1439
1326
|
# Check analog outputs [data with units of %] are floats only
|
1440
1327
|
columns_to_check = [
|
1441
1328
|
self.economizer_sig_col,
|
@@ -1443,35 +1330,25 @@ class FaultConditionTen(FaultCondition):
|
|
1443
1330
|
]
|
1444
1331
|
self.check_analog_pct(df, columns_to_check)
|
1445
1332
|
|
1446
|
-
|
1447
|
-
df[
|
1333
|
+
# Perform calculations
|
1334
|
+
abs_mat_minus_oat = abs(df[self.mat_col] - df[self.oat_col])
|
1335
|
+
mat_oat_sqrted = np.sqrt(
|
1448
1336
|
self.mix_degf_err_thres**2 + self.outdoor_degf_err_thres**2
|
1449
1337
|
)
|
1450
1338
|
|
1451
|
-
|
1452
|
-
(
|
1453
|
-
#
|
1339
|
+
combined_check = (
|
1340
|
+
(abs_mat_minus_oat > mat_oat_sqrted)
|
1341
|
+
# Verify AHU is running in OS 3 cooling mode with minimum OA
|
1454
1342
|
& (df[self.cooling_sig_col] > 0.01)
|
1455
1343
|
& (df[self.economizer_sig_col] > 0.9)
|
1456
1344
|
)
|
1457
1345
|
|
1458
1346
|
# Rolling sum to count consecutive trues
|
1459
|
-
rolling_sum = (
|
1460
|
-
|
1461
|
-
)
|
1347
|
+
rolling_sum = combined_check.rolling(window=self.rolling_window_size).sum()
|
1348
|
+
|
1462
1349
|
# Set flag to 1 if rolling sum equals the window size
|
1463
1350
|
df["fc10_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
|
1464
1351
|
|
1465
|
-
if self.troubleshoot_mode:
|
1466
|
-
print("Troubleshoot mode enabled - not removing helper columns")
|
1467
|
-
sys.stdout.flush()
|
1468
|
-
|
1469
|
-
# Optionally remove temporary columns
|
1470
|
-
df.drop(
|
1471
|
-
columns=["abs_mat_minus_oat", "mat_oat_sqrted", "combined_check"],
|
1472
|
-
inplace=True,
|
1473
|
-
)
|
1474
|
-
|
1475
1352
|
return df
|
1476
1353
|
|
1477
1354
|
except MissingColumnError as e:
|
@@ -1575,9 +1452,6 @@ class FaultConditionEleven(FaultCondition):
|
|
1575
1452
|
# Ensure all required columns are present
|
1576
1453
|
self.check_required_columns(df)
|
1577
1454
|
|
1578
|
-
if self.troubleshoot_mode:
|
1579
|
-
self.troubleshoot_cols(df)
|
1580
|
-
|
1581
1455
|
# Check analog outputs [data with units of %] are floats only
|
1582
1456
|
columns_to_check = [
|
1583
1457
|
self.economizer_sig_col,
|
@@ -1585,37 +1459,27 @@ class FaultConditionEleven(FaultCondition):
|
|
1585
1459
|
]
|
1586
1460
|
self.check_analog_pct(df, columns_to_check)
|
1587
1461
|
|
1588
|
-
|
1589
|
-
df[
|
1462
|
+
# Perform calculations without creating DataFrame columns
|
1463
|
+
oat_plus_oaterror = df[self.oat_col] + self.outdoor_degf_err_thres
|
1464
|
+
satsp_delta_saterr = (
|
1590
1465
|
df[self.sat_setpoint_col]
|
1591
1466
|
- self.delta_t_supply_fan
|
1592
1467
|
- self.supply_degf_err_thres
|
1593
1468
|
)
|
1594
1469
|
|
1595
|
-
|
1596
|
-
(
|
1597
|
-
#
|
1470
|
+
combined_check = (
|
1471
|
+
(oat_plus_oaterror < satsp_delta_saterr)
|
1472
|
+
# Verify AHU is running in OS 3 cooling mode with 100% OA
|
1598
1473
|
& (df[self.cooling_sig_col] > 0.01)
|
1599
1474
|
& (df[self.economizer_sig_col] > 0.9)
|
1600
1475
|
)
|
1601
1476
|
|
1602
1477
|
# Rolling sum to count consecutive trues
|
1603
|
-
rolling_sum = (
|
1604
|
-
|
1605
|
-
)
|
1478
|
+
rolling_sum = combined_check.rolling(window=self.rolling_window_size).sum()
|
1479
|
+
|
1606
1480
|
# Set flag to 1 if rolling sum equals the window size
|
1607
1481
|
df["fc11_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
|
1608
1482
|
|
1609
|
-
if self.troubleshoot_mode:
|
1610
|
-
print("Troubleshoot mode enabled - not removing helper columns")
|
1611
|
-
sys.stdout.flush()
|
1612
|
-
|
1613
|
-
# Optionally remove temporary columns
|
1614
|
-
df.drop(
|
1615
|
-
columns=["oat_plus_oaterror", "satsp_delta_saterr", "combined_check"],
|
1616
|
-
inplace=True,
|
1617
|
-
)
|
1618
|
-
|
1619
1483
|
return df
|
1620
1484
|
|
1621
1485
|
except MissingColumnError as e:
|
@@ -1720,9 +1584,6 @@ class FaultConditionTwelve(FaultCondition):
|
|
1720
1584
|
# Ensure all required columns are present
|
1721
1585
|
self.check_required_columns(df)
|
1722
1586
|
|
1723
|
-
if self.troubleshoot_mode:
|
1724
|
-
self.troubleshoot_cols(df)
|
1725
|
-
|
1726
1587
|
# Check analog outputs [data with units of %] are floats only
|
1727
1588
|
columns_to_check = [
|
1728
1589
|
self.economizer_sig_col,
|
@@ -1730,45 +1591,32 @@ class FaultConditionTwelve(FaultCondition):
|
|
1730
1591
|
]
|
1731
1592
|
self.check_analog_pct(df, columns_to_check)
|
1732
1593
|
|
1733
|
-
#
|
1734
|
-
|
1594
|
+
# Perform calculations without creating DataFrame columns
|
1595
|
+
sat_minus_saterr_delta_supply_fan = (
|
1735
1596
|
df[self.sat_col] - self.supply_degf_err_thres - self.delta_t_supply_fan
|
1736
1597
|
)
|
1737
|
-
|
1598
|
+
mat_plus_materr = df[self.mat_col] + self.mix_degf_err_thres
|
1738
1599
|
|
1739
|
-
|
1740
|
-
|
1741
|
-
|
1742
|
-
|
1600
|
+
# Combined check without adding to DataFrame columns
|
1601
|
+
combined_check = operator.or_(
|
1602
|
+
# OS4 AHU state cooling @ min OA
|
1603
|
+
(sat_minus_saterr_delta_supply_fan > mat_plus_materr)
|
1604
|
+
# Verify AHU in OS4 mode
|
1743
1605
|
& (df[self.cooling_sig_col] > 0.01)
|
1744
|
-
& (df[self.economizer_sig_col] == self.ahu_min_oa_dpr),
|
1745
|
-
|
1746
|
-
|
1606
|
+
& (df[self.economizer_sig_col] == self.ahu_min_oa_dpr),
|
1607
|
+
# OR
|
1608
|
+
(sat_minus_saterr_delta_supply_fan > mat_plus_materr)
|
1609
|
+
# Verify AHU is running in OS3 cooling mode in 100% OA
|
1747
1610
|
& (df[self.cooling_sig_col] > 0.01)
|
1748
1611
|
& (df[self.economizer_sig_col] > 0.9),
|
1749
1612
|
)
|
1750
1613
|
|
1751
1614
|
# Rolling sum to count consecutive trues
|
1752
|
-
rolling_sum = (
|
1753
|
-
|
1754
|
-
)
|
1615
|
+
rolling_sum = combined_check.rolling(window=self.rolling_window_size).sum()
|
1616
|
+
|
1755
1617
|
# Set flag to 1 if rolling sum equals the window size
|
1756
1618
|
df["fc12_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
|
1757
1619
|
|
1758
|
-
if self.troubleshoot_mode:
|
1759
|
-
print("Troubleshoot mode enabled - not removing helper columns")
|
1760
|
-
sys.stdout.flush()
|
1761
|
-
|
1762
|
-
# Optionally remove temporary columns
|
1763
|
-
df.drop(
|
1764
|
-
columns=[
|
1765
|
-
"sat_minus_saterr_delta_supply_fan",
|
1766
|
-
"mat_plus_materr",
|
1767
|
-
"combined_check",
|
1768
|
-
],
|
1769
|
-
inplace=True,
|
1770
|
-
)
|
1771
|
-
|
1772
1620
|
return df
|
1773
1621
|
|
1774
1622
|
except MissingColumnError as e:
|
@@ -1869,9 +1717,6 @@ class FaultConditionThirteen(FaultCondition):
|
|
1869
1717
|
# Ensure all required columns are present
|
1870
1718
|
self.check_required_columns(df)
|
1871
1719
|
|
1872
|
-
if self.troubleshoot_mode:
|
1873
|
-
self.troubleshoot_cols(df)
|
1874
|
-
|
1875
1720
|
# Check analog outputs [data with units of %] are floats only
|
1876
1721
|
columns_to_check = [
|
1877
1722
|
self.economizer_sig_col,
|
@@ -1879,40 +1724,30 @@ class FaultConditionThirteen(FaultCondition):
|
|
1879
1724
|
]
|
1880
1725
|
self.check_analog_pct(df, columns_to_check)
|
1881
1726
|
|
1882
|
-
#
|
1883
|
-
|
1727
|
+
# Perform calculation without creating DataFrame columns
|
1728
|
+
sat_greater_than_sp_calc = (
|
1884
1729
|
df[self.sat_col]
|
1885
1730
|
> df[self.sat_setpoint_col] + self.supply_degf_err_thres
|
1886
1731
|
)
|
1887
1732
|
|
1888
|
-
|
1889
|
-
|
1890
|
-
# OS4 AHU state
|
1733
|
+
# Combined check without adding to DataFrame columns
|
1734
|
+
combined_check = operator.or_(
|
1735
|
+
# OS4 AHU state cooling @ min OA
|
1736
|
+
(sat_greater_than_sp_calc)
|
1891
1737
|
& (df[self.cooling_sig_col] > 0.01)
|
1892
|
-
& (df[self.economizer_sig_col] == self.ahu_min_oa_dpr),
|
1893
|
-
|
1894
|
-
|
1738
|
+
& (df[self.economizer_sig_col] == self.ahu_min_oa_dpr),
|
1739
|
+
# OR verify AHU is running in OS 3 cooling mode in 100% OA
|
1740
|
+
(sat_greater_than_sp_calc)
|
1895
1741
|
& (df[self.cooling_sig_col] > 0.01)
|
1896
1742
|
& (df[self.economizer_sig_col] > 0.9),
|
1897
1743
|
)
|
1898
1744
|
|
1899
1745
|
# Rolling sum to count consecutive trues
|
1900
|
-
rolling_sum = (
|
1901
|
-
|
1902
|
-
)
|
1746
|
+
rolling_sum = combined_check.rolling(window=self.rolling_window_size).sum()
|
1747
|
+
|
1903
1748
|
# Set flag to 1 if rolling sum equals the window size
|
1904
1749
|
df["fc13_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
|
1905
1750
|
|
1906
|
-
if self.troubleshoot_mode:
|
1907
|
-
print("Troubleshoot mode enabled - not removing helper columns")
|
1908
|
-
sys.stdout.flush()
|
1909
|
-
|
1910
|
-
# Optionally remove temporary columns
|
1911
|
-
df.drop(
|
1912
|
-
columns=["sat_greater_than_sp_calc", "combined_check"],
|
1913
|
-
inplace=True,
|
1914
|
-
)
|
1915
|
-
|
1916
1751
|
return df
|
1917
1752
|
|
1918
1753
|
except MissingColumnError as e:
|
@@ -2020,9 +1855,6 @@ class FaultConditionFourteen(FaultCondition):
|
|
2020
1855
|
# Ensure all required columns are present
|
2021
1856
|
self.check_required_columns(df)
|
2022
1857
|
|
2023
|
-
if self.troubleshoot_mode:
|
2024
|
-
self.troubleshoot_cols(df)
|
2025
|
-
|
2026
1858
|
# Check analog outputs [data with units of %] are floats only
|
2027
1859
|
columns_to_check = [
|
2028
1860
|
self.economizer_sig_col,
|
@@ -2032,46 +1864,33 @@ class FaultConditionFourteen(FaultCondition):
|
|
2032
1864
|
]
|
2033
1865
|
self.check_analog_pct(df, columns_to_check)
|
2034
1866
|
|
2035
|
-
#
|
2036
|
-
|
1867
|
+
# Calculate necessary checks
|
1868
|
+
clg_delta_temp = (
|
2037
1869
|
df[self.clg_coil_enter_temp_col] - df[self.clg_coil_leave_temp_col]
|
2038
1870
|
)
|
2039
|
-
|
2040
|
-
df["clg_delta_sqrted"] = (
|
1871
|
+
clg_delta_sqrted = (
|
2041
1872
|
np.sqrt(
|
2042
1873
|
self.coil_temp_enter_err_thres**2 + self.coil_temp_leav_err_thres**2
|
2043
1874
|
)
|
2044
1875
|
+ self.delta_t_supply_fan
|
2045
1876
|
)
|
2046
1877
|
|
2047
|
-
|
2048
|
-
|
2049
|
-
|
1878
|
+
# Perform combined checks without adding intermediate columns to DataFrame
|
1879
|
+
combined_check = operator.or_(
|
1880
|
+
(clg_delta_temp >= clg_delta_sqrted)
|
2050
1881
|
& (df[self.economizer_sig_col] > self.ahu_min_oa_dpr)
|
2051
1882
|
& (df[self.cooling_sig_col] < 0.1), # OR
|
2052
|
-
(
|
2053
|
-
# verify AHU is running in OS 1 at near full heat
|
1883
|
+
(clg_delta_temp >= clg_delta_sqrted)
|
2054
1884
|
& (df[self.heating_sig_col] > 0.0)
|
2055
1885
|
& (df[self.supply_vfd_speed_col] > 0.0),
|
2056
1886
|
)
|
2057
1887
|
|
2058
1888
|
# Rolling sum to count consecutive trues
|
2059
|
-
rolling_sum = (
|
2060
|
-
|
2061
|
-
)
|
1889
|
+
rolling_sum = combined_check.rolling(window=self.rolling_window_size).sum()
|
1890
|
+
|
2062
1891
|
# Set flag to 1 if rolling sum equals the window size
|
2063
1892
|
df["fc14_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
|
2064
1893
|
|
2065
|
-
if self.troubleshoot_mode:
|
2066
|
-
print("Troubleshoot mode enabled - not removing helper columns")
|
2067
|
-
sys.stdout.flush()
|
2068
|
-
|
2069
|
-
# Optionally remove temporary columns
|
2070
|
-
df.drop(
|
2071
|
-
columns=["clg_delta_temp", "clg_delta_sqrted", "combined_check"],
|
2072
|
-
inplace=True,
|
2073
|
-
)
|
2074
|
-
|
2075
1894
|
return df
|
2076
1895
|
|
2077
1896
|
except MissingColumnError as e:
|
@@ -2251,3 +2070,184 @@ class FaultConditionFifteen(FaultCondition):
|
|
2251
2070
|
print(f"Error: {e.message}")
|
2252
2071
|
sys.stdout.flush()
|
2253
2072
|
raise e
|
2073
|
+
|
2074
|
+
|
2075
|
+
class FaultConditionSixteen(FaultCondition):
|
2076
|
+
"""Class provides the definitions for Fault Condition 16.
|
2077
|
+
ERV Ineffective Process based on outdoor air temperature ranges.
|
2078
|
+
"""
|
2079
|
+
|
2080
|
+
def __init__(self, dict_):
|
2081
|
+
super().__init__()
|
2082
|
+
|
2083
|
+
# Threshold parameters for efficiency ranges based on heating and cooling
|
2084
|
+
self.erv_efficiency_min_heating = dict_.get("ERV_EFFICIENCY_MIN_HEATING", 0.7)
|
2085
|
+
self.erv_efficiency_max_heating = dict_.get("ERV_EFFICIENCY_MAX_HEATING", 0.8)
|
2086
|
+
self.erv_efficiency_min_cooling = dict_.get("ERV_EFFICIENCY_MIN_COOLING", 0.5)
|
2087
|
+
self.erv_efficiency_max_cooling = dict_.get("ERV_EFFICIENCY_MAX_COOLING", 0.6)
|
2088
|
+
|
2089
|
+
self.oat_low_threshold = dict_.get("OAT_LOW_THRES", 32.0)
|
2090
|
+
self.oat_high_threshold = dict_.get("OAT_HIGH_THRES", 80.0)
|
2091
|
+
|
2092
|
+
# Validate that threshold parameters are floats and within 0.0 and 1.0 for efficiency values
|
2093
|
+
for param, value in [
|
2094
|
+
("erv_efficiency_min_heating", self.erv_efficiency_min_heating),
|
2095
|
+
("erv_efficiency_max_heating", self.erv_efficiency_max_heating),
|
2096
|
+
("erv_efficiency_min_cooling", self.erv_efficiency_min_cooling),
|
2097
|
+
("erv_efficiency_max_cooling", self.erv_efficiency_max_cooling),
|
2098
|
+
("oat_low_threshold", self.oat_low_threshold),
|
2099
|
+
("oat_high_threshold", self.oat_high_threshold),
|
2100
|
+
]:
|
2101
|
+
if not isinstance(value, float):
|
2102
|
+
raise InvalidParameterError(
|
2103
|
+
f"The parameter '{param}' should be a float, but got {type(value).__name__}."
|
2104
|
+
)
|
2105
|
+
if "erv_efficiency" in param and not (0.0 <= value <= 1.0):
|
2106
|
+
raise InvalidParameterError(
|
2107
|
+
f"The parameter '{param}' should be a float between 0.0 and 1.0 to represent a percentage, but got {value}."
|
2108
|
+
)
|
2109
|
+
|
2110
|
+
# Other attributes
|
2111
|
+
self.erv_oat_enter_col = dict_.get("ERV_OAT_ENTER_COL", "erv_oat_enter")
|
2112
|
+
self.erv_oat_leaving_col = dict_.get("ERV_OAT_LEAVING_COL", "erv_oat_leaving")
|
2113
|
+
self.erv_eat_enter_col = dict_.get("ERV_EAT_ENTER_COL", "erv_eat_enter")
|
2114
|
+
self.erv_eat_leaving_col = dict_.get("ERV_EAT_LEAVING_COL", "erv_eat_leaving")
|
2115
|
+
self.supply_vfd_speed_col = dict_.get(
|
2116
|
+
"SUPPLY_VFD_SPEED_COL", "supply_vfd_speed"
|
2117
|
+
)
|
2118
|
+
self.rolling_window_size = dict_.get("ROLLING_WINDOW_SIZE", 1)
|
2119
|
+
self.troubleshoot_mode = dict_.get("TROUBLESHOOT_MODE", False)
|
2120
|
+
|
2121
|
+
self.equation_string = (
|
2122
|
+
"fc16_flag = 1 if temperature deltas and expected efficiency is ineffective "
|
2123
|
+
"for N consecutive values else 0 \n"
|
2124
|
+
)
|
2125
|
+
self.description_string = (
|
2126
|
+
"Fault Condition 16: ERV is an ineffective heat transfer fault. "
|
2127
|
+
"This fault occurs when the ERV's efficiency "
|
2128
|
+
"is outside the acceptable range based on the delta temperature across the "
|
2129
|
+
"ERV outside air enter temperature and ERV outside air leaving temperature, "
|
2130
|
+
"indicating poor heat transfer. "
|
2131
|
+
"It considers both heating and cooling conditions where each have acceptable "
|
2132
|
+
"ranges in percentage for expected heat transfer efficiency. The percentage needs "
|
2133
|
+
"to be a float between 0.0 and 1.0."
|
2134
|
+
)
|
2135
|
+
self.required_column_description = (
|
2136
|
+
"Required inputs are the ERV outside air entering temperature, ERV outside air leaving temperature, "
|
2137
|
+
"ERV exhaust entering temperature, ERV exhaust leaving temperature, "
|
2138
|
+
"and AHU supply fan VFD speed."
|
2139
|
+
)
|
2140
|
+
self.error_string = "One or more required columns are missing or None."
|
2141
|
+
|
2142
|
+
self.set_attributes(dict_)
|
2143
|
+
|
2144
|
+
# Set required columns specific to this fault condition
|
2145
|
+
self.required_columns = [
|
2146
|
+
self.erv_oat_enter_col,
|
2147
|
+
self.erv_oat_leaving_col,
|
2148
|
+
self.erv_eat_enter_col,
|
2149
|
+
self.erv_eat_leaving_col,
|
2150
|
+
self.supply_vfd_speed_col,
|
2151
|
+
]
|
2152
|
+
|
2153
|
+
# Check if any of the required columns are None
|
2154
|
+
if any(col is None for col in self.required_columns):
|
2155
|
+
raise MissingColumnError(
|
2156
|
+
f"{self.error_string}\n"
|
2157
|
+
f"{self.equation_string}\n"
|
2158
|
+
f"{self.description_string}\n"
|
2159
|
+
f"{self.required_column_description}\n"
|
2160
|
+
f"Missing columns: {self.required_columns}"
|
2161
|
+
)
|
2162
|
+
|
2163
|
+
# Ensure all required columns are strings
|
2164
|
+
self.required_columns = [str(col) for col in self.required_columns]
|
2165
|
+
|
2166
|
+
self.mapped_columns = (
|
2167
|
+
f"Your config dictionary is mapped as: {', '.join(self.required_columns)}"
|
2168
|
+
)
|
2169
|
+
|
2170
|
+
def get_required_columns(self) -> str:
|
2171
|
+
"""Returns a string representation of the required columns."""
|
2172
|
+
return (
|
2173
|
+
f"{self.equation_string}"
|
2174
|
+
f"{self.description_string}\n"
|
2175
|
+
f"{self.required_column_description}\n"
|
2176
|
+
f"{self.mapped_columns}"
|
2177
|
+
)
|
2178
|
+
|
2179
|
+
def calculate_erv_efficiency(self, df: pd.DataFrame) -> pd.DataFrame:
|
2180
|
+
# Calculate the temperature differences
|
2181
|
+
delta_temp_oa = df[self.erv_oat_leaving_col] - df[self.erv_oat_enter_col]
|
2182
|
+
delta_temp_ea = df[self.erv_eat_enter_col] - df[self.erv_oat_enter_col]
|
2183
|
+
|
2184
|
+
# Use the absolute value to handle both heating and cooling applications
|
2185
|
+
df["erv_efficiency_oa"] = np.abs(delta_temp_oa) / np.abs(delta_temp_ea)
|
2186
|
+
|
2187
|
+
return df
|
2188
|
+
|
2189
|
+
def apply(self, df: pd.DataFrame) -> pd.DataFrame:
|
2190
|
+
try:
|
2191
|
+
# Calculate ERV efficiency
|
2192
|
+
df = self.calculate_erv_efficiency(df)
|
2193
|
+
|
2194
|
+
# Fan must be on for a fault to be considered
|
2195
|
+
fan_on = df[self.supply_vfd_speed_col] > 0.1
|
2196
|
+
|
2197
|
+
# Combined heating and cooling checks
|
2198
|
+
cold_outside = df[self.erv_oat_enter_col] <= self.oat_low_threshold
|
2199
|
+
hot_outside = df[self.erv_oat_enter_col] >= self.oat_high_threshold
|
2200
|
+
|
2201
|
+
heating_fault = (
|
2202
|
+
(
|
2203
|
+
(df["erv_efficiency_oa"] < self.erv_efficiency_min_heating)
|
2204
|
+
| (df["erv_efficiency_oa"] > self.erv_efficiency_max_heating)
|
2205
|
+
)
|
2206
|
+
& cold_outside
|
2207
|
+
& fan_on
|
2208
|
+
)
|
2209
|
+
|
2210
|
+
cooling_fault = (
|
2211
|
+
(
|
2212
|
+
(df["erv_efficiency_oa"] < self.erv_efficiency_min_cooling)
|
2213
|
+
| (df["erv_efficiency_oa"] > self.erv_efficiency_max_cooling)
|
2214
|
+
)
|
2215
|
+
& hot_outside
|
2216
|
+
& fan_on
|
2217
|
+
)
|
2218
|
+
|
2219
|
+
df["combined_checks"] = heating_fault | cooling_fault
|
2220
|
+
|
2221
|
+
# Apply rolling sum
|
2222
|
+
df["fc16_flag"] = (
|
2223
|
+
df["combined_checks"]
|
2224
|
+
.rolling(window=self.rolling_window_size)
|
2225
|
+
.sum()
|
2226
|
+
.ge(self.rolling_window_size)
|
2227
|
+
.astype(int)
|
2228
|
+
)
|
2229
|
+
|
2230
|
+
if self.troubleshoot_mode:
|
2231
|
+
print("Troubleshoot mode enabled - not removing helper columns")
|
2232
|
+
sys.stdout.flush()
|
2233
|
+
|
2234
|
+
# Drop helper cols if not in troubleshoot mode
|
2235
|
+
if not self.troubleshoot_mode:
|
2236
|
+
df.drop(
|
2237
|
+
columns=[
|
2238
|
+
"combined_checks",
|
2239
|
+
"erv_efficiency_oa",
|
2240
|
+
],
|
2241
|
+
inplace=True,
|
2242
|
+
)
|
2243
|
+
|
2244
|
+
return df
|
2245
|
+
|
2246
|
+
except MissingColumnError as e:
|
2247
|
+
print(f"Error: {e.message}")
|
2248
|
+
sys.stdout.flush()
|
2249
|
+
raise e
|
2250
|
+
except InvalidParameterError as e:
|
2251
|
+
print(f"Error: {e.message}")
|
2252
|
+
sys.stdout.flush()
|
2253
|
+
raise e
|