open-fdd 0.1.5__py3-none-any.whl → 0.1.7__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 +331 -300
- open_fdd/air_handling_unit/faults/helper_utils.py +3 -0
- open_fdd/air_handling_unit/faults/shared_utils.py +16 -1
- open_fdd/air_handling_unit/reports/__init__.py +107 -0
- open_fdd/air_handling_unit/reports/fault_report.py +1 -0
- open_fdd/tests/ahu/test_ahu_fc1.py +1 -0
- open_fdd/tests/ahu/test_ahu_fc16.py +205 -0
- {open_fdd-0.1.5.dist-info → open_fdd-0.1.7.dist-info}/METADATA +4 -3
- open_fdd-0.1.7.dist-info/RECORD +31 -0
- {open_fdd-0.1.5.dist-info → open_fdd-0.1.7.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.5.dist-info/RECORD +0 -83
- {open_fdd-0.1.5.dist-info → open_fdd-0.1.7.dist-info}/LICENSE +0 -0
- {open_fdd-0.1.5.dist-info → open_fdd-0.1.7.dist-info}/top_level.txt +0 -0
@@ -5,6 +5,7 @@ from open_fdd.air_handling_unit.faults.fault_condition import (
|
|
5
5
|
MissingColumnError,
|
6
6
|
InvalidParameterError,
|
7
7
|
)
|
8
|
+
from open_fdd.air_handling_unit.faults.helper_utils import SharedUtils
|
8
9
|
import operator
|
9
10
|
import sys
|
10
11
|
|
@@ -12,6 +13,8 @@ import sys
|
|
12
13
|
class FaultConditionOne(FaultCondition):
|
13
14
|
"""Class provides the definitions for Fault Condition 1.
|
14
15
|
AHU low duct static pressure fan fault.
|
16
|
+
|
17
|
+
py -3.12 -m pytest open_fdd/tests/ahu/test_ahu_fc1.py -rP -s
|
15
18
|
"""
|
16
19
|
|
17
20
|
def __init__(self, dict_):
|
@@ -101,34 +104,25 @@ class FaultConditionOne(FaultCondition):
|
|
101
104
|
columns_to_check = [self.supply_vfd_speed_col]
|
102
105
|
self.check_analog_pct(df, columns_to_check)
|
103
106
|
|
104
|
-
|
107
|
+
# Perform checks
|
108
|
+
static_check = (
|
105
109
|
df[self.duct_static_col]
|
106
110
|
< df[self.duct_static_setpoint_col] - self.duct_static_inches_err_thres
|
107
111
|
)
|
108
|
-
|
112
|
+
fan_check = (
|
109
113
|
df[self.supply_vfd_speed_col]
|
110
114
|
>= self.vfd_speed_percent_max - self.vfd_speed_percent_err_thres
|
111
115
|
)
|
112
116
|
|
113
117
|
# Combined condition check
|
114
|
-
|
118
|
+
combined_check = static_check & fan_check
|
115
119
|
|
116
120
|
# Rolling sum to count consecutive trues
|
117
|
-
rolling_sum = (
|
118
|
-
|
119
|
-
)
|
121
|
+
rolling_sum = combined_check.rolling(window=self.rolling_window_size).sum()
|
122
|
+
|
120
123
|
# Set flag to 1 if rolling sum equals the window size
|
121
124
|
df["fc1_flag"] = (rolling_sum == self.rolling_window_size).astype(int)
|
122
125
|
|
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
126
|
return df
|
133
127
|
|
134
128
|
except MissingColumnError as e:
|
@@ -232,33 +226,23 @@ class FaultConditionTwo(FaultCondition):
|
|
232
226
|
columns_to_check = [self.supply_vfd_speed_col]
|
233
227
|
self.check_analog_pct(df, columns_to_check)
|
234
228
|
|
235
|
-
#
|
236
|
-
|
237
|
-
|
229
|
+
# Perform checks
|
230
|
+
mat_check = df[self.mat_col] + self.mix_degf_err_thres
|
231
|
+
temp_min_check = np.minimum(
|
238
232
|
df[self.rat_col] - self.return_degf_err_thres,
|
239
233
|
df[self.oat_col] - self.outdoor_degf_err_thres,
|
240
234
|
)
|
241
235
|
|
242
|
-
|
236
|
+
combined_check = (mat_check < temp_min_check) & (
|
243
237
|
df[self.supply_vfd_speed_col] > 0.01
|
244
238
|
)
|
245
239
|
|
246
240
|
# Rolling sum to count consecutive trues
|
247
|
-
rolling_sum = (
|
248
|
-
|
249
|
-
)
|
241
|
+
rolling_sum = combined_check.rolling(window=self.rolling_window_size).sum()
|
242
|
+
|
250
243
|
# Set flag to 1 if rolling sum equals the window size
|
251
244
|
df["fc2_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
|
252
245
|
|
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
246
|
return df
|
263
247
|
|
264
248
|
except MissingColumnError as e:
|
@@ -362,33 +346,23 @@ class FaultConditionThree(FaultCondition):
|
|
362
346
|
columns_to_check = [self.supply_vfd_speed_col]
|
363
347
|
self.check_analog_pct(df, columns_to_check)
|
364
348
|
|
365
|
-
#
|
366
|
-
|
367
|
-
|
349
|
+
# Perform checks
|
350
|
+
mat_check = df[self.mat_col] - self.mix_degf_err_thres
|
351
|
+
temp_max_check = np.maximum(
|
368
352
|
df[self.rat_col] + self.return_degf_err_thres,
|
369
353
|
df[self.oat_col] + self.outdoor_degf_err_thres,
|
370
354
|
)
|
371
355
|
|
372
|
-
|
356
|
+
combined_check = (mat_check > temp_max_check) & (
|
373
357
|
df[self.supply_vfd_speed_col] > 0.01
|
374
358
|
)
|
375
359
|
|
376
360
|
# Rolling sum to count consecutive trues
|
377
|
-
rolling_sum = (
|
378
|
-
|
379
|
-
)
|
361
|
+
rolling_sum = combined_check.rolling(window=self.rolling_window_size).sum()
|
362
|
+
|
380
363
|
# Set flag to 1 if rolling sum equals the window size
|
381
364
|
df["fc3_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
|
382
365
|
|
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
366
|
return df
|
393
367
|
|
394
368
|
except MissingColumnError as e:
|
@@ -675,40 +649,28 @@ class FaultConditionFive(FaultCondition):
|
|
675
649
|
# Ensure all required columns are present
|
676
650
|
self.check_required_columns(df)
|
677
651
|
|
678
|
-
if self.troubleshoot_mode:
|
679
|
-
self.troubleshoot_cols(df)
|
680
|
-
|
681
652
|
# Check analog outputs [data with units of %] are floats only
|
682
653
|
columns_to_check = [self.supply_vfd_speed_col, self.heating_sig_col]
|
654
|
+
self.check_analog_pct(df, columns_to_check)
|
683
655
|
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
df["sat_check"] = df[self.sat_col] + self.supply_degf_err_thres
|
688
|
-
df["mat_check"] = (
|
656
|
+
# Perform checks
|
657
|
+
sat_check = df[self.sat_col] + self.supply_degf_err_thres
|
658
|
+
mat_check = (
|
689
659
|
df[self.mat_col] - self.mix_degf_err_thres + self.delta_t_supply_fan
|
690
660
|
)
|
691
661
|
|
692
|
-
|
693
|
-
(
|
662
|
+
combined_check = (
|
663
|
+
(sat_check <= mat_check)
|
694
664
|
& (df[self.heating_sig_col] > 0.01)
|
695
665
|
& (df[self.supply_vfd_speed_col] > 0.01)
|
696
666
|
)
|
697
667
|
|
698
668
|
# Rolling sum to count consecutive trues
|
699
|
-
rolling_sum = (
|
700
|
-
|
701
|
-
)
|
669
|
+
rolling_sum = combined_check.rolling(window=self.rolling_window_size).sum()
|
670
|
+
|
702
671
|
# Set flag to 1 if rolling sum equals the window size
|
703
672
|
df["fc5_flag"] = (rolling_sum == self.rolling_window_size).astype(int)
|
704
673
|
|
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
674
|
return df
|
713
675
|
|
714
676
|
except MissingColumnError as e:
|
@@ -839,8 +801,12 @@ class FaultConditionSix(FaultCondition):
|
|
839
801
|
# Ensure all required columns are present
|
840
802
|
self.check_required_columns(df)
|
841
803
|
|
842
|
-
|
843
|
-
|
804
|
+
# Check for zeros in the columns that could lead to division by zero errors
|
805
|
+
cols_to_check = [self.rat_col, self.oat_col, self.supply_fan_air_volume_col]
|
806
|
+
if df[cols_to_check].eq(0).any().any():
|
807
|
+
print(f"Warning: Zero values found in columns: {cols_to_check}")
|
808
|
+
print("This may cause division by zero errors.")
|
809
|
+
sys.stdout.flush()
|
844
810
|
|
845
811
|
# Check analog outputs [data with units of %] are floats only
|
846
812
|
columns_to_check = [
|
@@ -849,75 +815,45 @@ class FaultConditionSix(FaultCondition):
|
|
849
815
|
self.heating_sig_col,
|
850
816
|
self.cooling_sig_col,
|
851
817
|
]
|
818
|
+
self.check_analog_pct(df, columns_to_check)
|
852
819
|
|
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]) / (
|
820
|
+
# Calculate intermediate values
|
821
|
+
rat_minus_oat = abs(df[self.rat_col] - df[self.oat_col])
|
822
|
+
percent_oa_calc = (df[self.mat_col] - df[self.rat_col]) / (
|
859
823
|
df[self.oat_col] - df[self.rat_col]
|
860
824
|
)
|
861
825
|
|
862
|
-
#
|
863
|
-
|
864
|
-
lambda x: x if x > 0 else 0
|
865
|
-
)
|
826
|
+
# Replace negative values in percent_oa_calc with zero using vectorized operation
|
827
|
+
percent_oa_calc = percent_oa_calc.clip(lower=0)
|
866
828
|
|
867
|
-
|
868
|
-
|
869
|
-
)
|
829
|
+
perc_OAmin = self.ahu_min_oa_cfm_design / df[self.supply_fan_air_volume_col]
|
830
|
+
percent_oa_calc_minus_perc_OAmin = abs(percent_oa_calc - perc_OAmin)
|
870
831
|
|
871
|
-
|
872
|
-
|
832
|
+
# Combined checks for OS 1 and OS 4 modes
|
833
|
+
os1_htg_mode_check = (
|
834
|
+
(rat_minus_oat >= self.oat_rat_delta_min)
|
835
|
+
& (percent_oa_calc_minus_perc_OAmin > self.airflow_err_thres)
|
836
|
+
& (df[self.heating_sig_col] > 0.0)
|
837
|
+
& (df[self.supply_vfd_speed_col] > 0.0)
|
873
838
|
)
|
874
839
|
|
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
|
840
|
+
os4_clg_mode_check = (
|
841
|
+
(rat_minus_oat >= self.oat_rat_delta_min)
|
842
|
+
& (percent_oa_calc_minus_perc_OAmin > self.airflow_err_thres)
|
892
843
|
& (df[self.heating_sig_col] == 0.0)
|
893
844
|
& (df[self.cooling_sig_col] > 0.0)
|
894
845
|
& (df[self.supply_vfd_speed_col] > 0.0)
|
895
|
-
& (df[self.economizer_sig_col] == self.ahu_min_oa_dpr)
|
846
|
+
& (df[self.economizer_sig_col] == self.ahu_min_oa_dpr)
|
896
847
|
)
|
897
848
|
|
849
|
+
combined_check = os1_htg_mode_check | os4_clg_mode_check
|
850
|
+
|
898
851
|
# Rolling sum to count consecutive trues
|
899
|
-
rolling_sum = (
|
900
|
-
|
901
|
-
)
|
852
|
+
rolling_sum = combined_check.rolling(window=self.rolling_window_size).sum()
|
853
|
+
|
902
854
|
# Set flag to 1 if rolling sum equals the window size
|
903
855
|
df["fc6_flag"] = (rolling_sum == self.rolling_window_size).astype(int)
|
904
856
|
|
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
857
|
return df
|
922
858
|
|
923
859
|
except MissingColumnError as e:
|
@@ -1013,36 +949,25 @@ class FaultConditionSeven(FaultCondition):
|
|
1013
949
|
# Ensure all required columns are present
|
1014
950
|
self.check_required_columns(df)
|
1015
951
|
|
1016
|
-
if self.troubleshoot_mode:
|
1017
|
-
self.troubleshoot_cols(df)
|
1018
|
-
|
1019
952
|
# Check analog outputs [data with units of %] are floats only
|
1020
953
|
columns_to_check = [self.supply_vfd_speed_col, self.heating_sig_col]
|
1021
954
|
self.check_analog_pct(df, columns_to_check)
|
1022
955
|
|
1023
|
-
#
|
1024
|
-
|
956
|
+
# Perform checks
|
957
|
+
sat_check = df[self.sat_setpoint_col] - self.supply_degf_err_thres
|
1025
958
|
|
1026
|
-
|
1027
|
-
(df[self.sat_col] <
|
959
|
+
combined_check = (
|
960
|
+
(df[self.sat_col] < sat_check)
|
1028
961
|
& (df[self.heating_sig_col] > 0.9)
|
1029
962
|
& (df[self.supply_vfd_speed_col] > 0)
|
1030
963
|
)
|
1031
964
|
|
1032
965
|
# Rolling sum to count consecutive trues
|
1033
|
-
rolling_sum = (
|
1034
|
-
|
1035
|
-
)
|
966
|
+
rolling_sum = combined_check.rolling(window=self.rolling_window_size).sum()
|
967
|
+
|
1036
968
|
# Set flag to 1 if rolling sum equals the window size
|
1037
969
|
df["fc7_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
|
1038
970
|
|
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
971
|
return df
|
1047
972
|
|
1048
973
|
except MissingColumnError as e:
|
@@ -1147,9 +1072,6 @@ class FaultConditionEight(FaultCondition):
|
|
1147
1072
|
# Ensure all required columns are present
|
1148
1073
|
self.check_required_columns(df)
|
1149
1074
|
|
1150
|
-
if self.troubleshoot_mode:
|
1151
|
-
self.troubleshoot_cols(df)
|
1152
|
-
|
1153
1075
|
# Check analog outputs [data with units of %] are floats only
|
1154
1076
|
columns_to_check = [
|
1155
1077
|
self.economizer_sig_col,
|
@@ -1157,36 +1079,26 @@ class FaultConditionEight(FaultCondition):
|
|
1157
1079
|
]
|
1158
1080
|
self.check_analog_pct(df, columns_to_check)
|
1159
1081
|
|
1160
|
-
|
1082
|
+
# Perform checks
|
1083
|
+
sat_fan_mat = abs(
|
1161
1084
|
df[self.sat_col] - self.delta_t_supply_fan - df[self.mat_col]
|
1162
1085
|
)
|
1163
|
-
|
1086
|
+
sat_mat_sqrted = np.sqrt(
|
1164
1087
|
self.supply_degf_err_thres**2 + self.mix_degf_err_thres**2
|
1165
1088
|
)
|
1166
1089
|
|
1167
|
-
|
1168
|
-
(
|
1090
|
+
combined_check = (
|
1091
|
+
(sat_fan_mat > sat_mat_sqrted)
|
1169
1092
|
& (df[self.economizer_sig_col] > self.ahu_min_oa_dpr)
|
1170
1093
|
& (df[self.cooling_sig_col] < 0.1)
|
1171
1094
|
)
|
1172
1095
|
|
1173
1096
|
# Rolling sum to count consecutive trues
|
1174
|
-
rolling_sum = (
|
1175
|
-
|
1176
|
-
)
|
1097
|
+
rolling_sum = combined_check.rolling(window=self.rolling_window_size).sum()
|
1098
|
+
|
1177
1099
|
# Set flag to 1 if rolling sum equals the window size
|
1178
1100
|
df["fc8_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
|
1179
1101
|
|
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
1102
|
return df
|
1191
1103
|
|
1192
1104
|
except MissingColumnError as e:
|
@@ -1291,9 +1203,6 @@ class FaultConditionNine(FaultCondition):
|
|
1291
1203
|
# Ensure all required columns are present
|
1292
1204
|
self.check_required_columns(df)
|
1293
1205
|
|
1294
|
-
if self.troubleshoot_mode:
|
1295
|
-
self.troubleshoot_cols(df)
|
1296
|
-
|
1297
1206
|
# Check analog outputs [data with units of %] are floats only
|
1298
1207
|
columns_to_check = [
|
1299
1208
|
self.economizer_sig_col,
|
@@ -1301,38 +1210,27 @@ class FaultConditionNine(FaultCondition):
|
|
1301
1210
|
]
|
1302
1211
|
self.check_analog_pct(df, columns_to_check)
|
1303
1212
|
|
1304
|
-
#
|
1305
|
-
|
1306
|
-
|
1213
|
+
# Perform calculations
|
1214
|
+
oat_minus_oaterror = df[self.oat_col] - self.outdoor_degf_err_thres
|
1215
|
+
satsp_delta_saterr = (
|
1307
1216
|
df[self.sat_setpoint_col]
|
1308
1217
|
- self.delta_t_supply_fan
|
1309
1218
|
+ self.supply_degf_err_thres
|
1310
1219
|
)
|
1311
1220
|
|
1312
|
-
|
1313
|
-
(
|
1221
|
+
combined_check = (
|
1222
|
+
(oat_minus_oaterror > satsp_delta_saterr)
|
1314
1223
|
# verify AHU is in OS2 only free cooling mode
|
1315
1224
|
& (df[self.economizer_sig_col] > self.ahu_min_oa_dpr)
|
1316
1225
|
& (df[self.cooling_sig_col] < 0.1)
|
1317
1226
|
)
|
1318
1227
|
|
1319
1228
|
# Rolling sum to count consecutive trues
|
1320
|
-
rolling_sum = (
|
1321
|
-
|
1322
|
-
)
|
1229
|
+
rolling_sum = combined_check.rolling(window=self.rolling_window_size).sum()
|
1230
|
+
|
1323
1231
|
# Set flag to 1 if rolling sum equals the window size
|
1324
1232
|
df["fc9_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
|
1325
1233
|
|
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
1234
|
return df
|
1337
1235
|
|
1338
1236
|
except MissingColumnError as e:
|
@@ -1433,9 +1331,6 @@ class FaultConditionTen(FaultCondition):
|
|
1433
1331
|
# Ensure all required columns are present
|
1434
1332
|
self.check_required_columns(df)
|
1435
1333
|
|
1436
|
-
if self.troubleshoot_mode:
|
1437
|
-
self.troubleshoot_cols(df)
|
1438
|
-
|
1439
1334
|
# Check analog outputs [data with units of %] are floats only
|
1440
1335
|
columns_to_check = [
|
1441
1336
|
self.economizer_sig_col,
|
@@ -1443,35 +1338,25 @@ class FaultConditionTen(FaultCondition):
|
|
1443
1338
|
]
|
1444
1339
|
self.check_analog_pct(df, columns_to_check)
|
1445
1340
|
|
1446
|
-
|
1447
|
-
df[
|
1341
|
+
# Perform calculations
|
1342
|
+
abs_mat_minus_oat = abs(df[self.mat_col] - df[self.oat_col])
|
1343
|
+
mat_oat_sqrted = np.sqrt(
|
1448
1344
|
self.mix_degf_err_thres**2 + self.outdoor_degf_err_thres**2
|
1449
1345
|
)
|
1450
1346
|
|
1451
|
-
|
1452
|
-
(
|
1453
|
-
#
|
1347
|
+
combined_check = (
|
1348
|
+
(abs_mat_minus_oat > mat_oat_sqrted)
|
1349
|
+
# Verify AHU is running in OS 3 cooling mode with minimum OA
|
1454
1350
|
& (df[self.cooling_sig_col] > 0.01)
|
1455
1351
|
& (df[self.economizer_sig_col] > 0.9)
|
1456
1352
|
)
|
1457
1353
|
|
1458
1354
|
# Rolling sum to count consecutive trues
|
1459
|
-
rolling_sum = (
|
1460
|
-
|
1461
|
-
)
|
1355
|
+
rolling_sum = combined_check.rolling(window=self.rolling_window_size).sum()
|
1356
|
+
|
1462
1357
|
# Set flag to 1 if rolling sum equals the window size
|
1463
1358
|
df["fc10_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
|
1464
1359
|
|
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
1360
|
return df
|
1476
1361
|
|
1477
1362
|
except MissingColumnError as e:
|
@@ -1575,9 +1460,6 @@ class FaultConditionEleven(FaultCondition):
|
|
1575
1460
|
# Ensure all required columns are present
|
1576
1461
|
self.check_required_columns(df)
|
1577
1462
|
|
1578
|
-
if self.troubleshoot_mode:
|
1579
|
-
self.troubleshoot_cols(df)
|
1580
|
-
|
1581
1463
|
# Check analog outputs [data with units of %] are floats only
|
1582
1464
|
columns_to_check = [
|
1583
1465
|
self.economizer_sig_col,
|
@@ -1585,37 +1467,27 @@ class FaultConditionEleven(FaultCondition):
|
|
1585
1467
|
]
|
1586
1468
|
self.check_analog_pct(df, columns_to_check)
|
1587
1469
|
|
1588
|
-
|
1589
|
-
df[
|
1470
|
+
# Perform calculations without creating DataFrame columns
|
1471
|
+
oat_plus_oaterror = df[self.oat_col] + self.outdoor_degf_err_thres
|
1472
|
+
satsp_delta_saterr = (
|
1590
1473
|
df[self.sat_setpoint_col]
|
1591
1474
|
- self.delta_t_supply_fan
|
1592
1475
|
- self.supply_degf_err_thres
|
1593
1476
|
)
|
1594
1477
|
|
1595
|
-
|
1596
|
-
(
|
1597
|
-
#
|
1478
|
+
combined_check = (
|
1479
|
+
(oat_plus_oaterror < satsp_delta_saterr)
|
1480
|
+
# Verify AHU is running in OS 3 cooling mode with 100% OA
|
1598
1481
|
& (df[self.cooling_sig_col] > 0.01)
|
1599
1482
|
& (df[self.economizer_sig_col] > 0.9)
|
1600
1483
|
)
|
1601
1484
|
|
1602
1485
|
# Rolling sum to count consecutive trues
|
1603
|
-
rolling_sum = (
|
1604
|
-
|
1605
|
-
)
|
1486
|
+
rolling_sum = combined_check.rolling(window=self.rolling_window_size).sum()
|
1487
|
+
|
1606
1488
|
# Set flag to 1 if rolling sum equals the window size
|
1607
1489
|
df["fc11_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
|
1608
1490
|
|
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
1491
|
return df
|
1620
1492
|
|
1621
1493
|
except MissingColumnError as e:
|
@@ -1720,9 +1592,6 @@ class FaultConditionTwelve(FaultCondition):
|
|
1720
1592
|
# Ensure all required columns are present
|
1721
1593
|
self.check_required_columns(df)
|
1722
1594
|
|
1723
|
-
if self.troubleshoot_mode:
|
1724
|
-
self.troubleshoot_cols(df)
|
1725
|
-
|
1726
1595
|
# Check analog outputs [data with units of %] are floats only
|
1727
1596
|
columns_to_check = [
|
1728
1597
|
self.economizer_sig_col,
|
@@ -1730,45 +1599,32 @@ class FaultConditionTwelve(FaultCondition):
|
|
1730
1599
|
]
|
1731
1600
|
self.check_analog_pct(df, columns_to_check)
|
1732
1601
|
|
1733
|
-
#
|
1734
|
-
|
1602
|
+
# Perform calculations without creating DataFrame columns
|
1603
|
+
sat_minus_saterr_delta_supply_fan = (
|
1735
1604
|
df[self.sat_col] - self.supply_degf_err_thres - self.delta_t_supply_fan
|
1736
1605
|
)
|
1737
|
-
|
1606
|
+
mat_plus_materr = df[self.mat_col] + self.mix_degf_err_thres
|
1738
1607
|
|
1739
|
-
|
1740
|
-
|
1741
|
-
|
1742
|
-
|
1608
|
+
# Combined check without adding to DataFrame columns
|
1609
|
+
combined_check = operator.or_(
|
1610
|
+
# OS4 AHU state cooling @ min OA
|
1611
|
+
(sat_minus_saterr_delta_supply_fan > mat_plus_materr)
|
1612
|
+
# Verify AHU in OS4 mode
|
1743
1613
|
& (df[self.cooling_sig_col] > 0.01)
|
1744
|
-
& (df[self.economizer_sig_col] == self.ahu_min_oa_dpr),
|
1745
|
-
|
1746
|
-
|
1614
|
+
& (df[self.economizer_sig_col] == self.ahu_min_oa_dpr),
|
1615
|
+
# OR
|
1616
|
+
(sat_minus_saterr_delta_supply_fan > mat_plus_materr)
|
1617
|
+
# Verify AHU is running in OS3 cooling mode in 100% OA
|
1747
1618
|
& (df[self.cooling_sig_col] > 0.01)
|
1748
1619
|
& (df[self.economizer_sig_col] > 0.9),
|
1749
1620
|
)
|
1750
1621
|
|
1751
1622
|
# Rolling sum to count consecutive trues
|
1752
|
-
rolling_sum = (
|
1753
|
-
|
1754
|
-
)
|
1623
|
+
rolling_sum = combined_check.rolling(window=self.rolling_window_size).sum()
|
1624
|
+
|
1755
1625
|
# Set flag to 1 if rolling sum equals the window size
|
1756
1626
|
df["fc12_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
|
1757
1627
|
|
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
1628
|
return df
|
1773
1629
|
|
1774
1630
|
except MissingColumnError as e:
|
@@ -1869,9 +1725,6 @@ class FaultConditionThirteen(FaultCondition):
|
|
1869
1725
|
# Ensure all required columns are present
|
1870
1726
|
self.check_required_columns(df)
|
1871
1727
|
|
1872
|
-
if self.troubleshoot_mode:
|
1873
|
-
self.troubleshoot_cols(df)
|
1874
|
-
|
1875
1728
|
# Check analog outputs [data with units of %] are floats only
|
1876
1729
|
columns_to_check = [
|
1877
1730
|
self.economizer_sig_col,
|
@@ -1879,40 +1732,30 @@ class FaultConditionThirteen(FaultCondition):
|
|
1879
1732
|
]
|
1880
1733
|
self.check_analog_pct(df, columns_to_check)
|
1881
1734
|
|
1882
|
-
#
|
1883
|
-
|
1735
|
+
# Perform calculation without creating DataFrame columns
|
1736
|
+
sat_greater_than_sp_calc = (
|
1884
1737
|
df[self.sat_col]
|
1885
1738
|
> df[self.sat_setpoint_col] + self.supply_degf_err_thres
|
1886
1739
|
)
|
1887
1740
|
|
1888
|
-
|
1889
|
-
|
1890
|
-
# OS4 AHU state
|
1741
|
+
# Combined check without adding to DataFrame columns
|
1742
|
+
combined_check = operator.or_(
|
1743
|
+
# OS4 AHU state cooling @ min OA
|
1744
|
+
(sat_greater_than_sp_calc)
|
1891
1745
|
& (df[self.cooling_sig_col] > 0.01)
|
1892
|
-
& (df[self.economizer_sig_col] == self.ahu_min_oa_dpr),
|
1893
|
-
|
1894
|
-
|
1746
|
+
& (df[self.economizer_sig_col] == self.ahu_min_oa_dpr),
|
1747
|
+
# OR verify AHU is running in OS 3 cooling mode in 100% OA
|
1748
|
+
(sat_greater_than_sp_calc)
|
1895
1749
|
& (df[self.cooling_sig_col] > 0.01)
|
1896
1750
|
& (df[self.economizer_sig_col] > 0.9),
|
1897
1751
|
)
|
1898
1752
|
|
1899
1753
|
# Rolling sum to count consecutive trues
|
1900
|
-
rolling_sum = (
|
1901
|
-
|
1902
|
-
)
|
1754
|
+
rolling_sum = combined_check.rolling(window=self.rolling_window_size).sum()
|
1755
|
+
|
1903
1756
|
# Set flag to 1 if rolling sum equals the window size
|
1904
1757
|
df["fc13_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
|
1905
1758
|
|
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
1759
|
return df
|
1917
1760
|
|
1918
1761
|
except MissingColumnError as e:
|
@@ -2020,9 +1863,6 @@ class FaultConditionFourteen(FaultCondition):
|
|
2020
1863
|
# Ensure all required columns are present
|
2021
1864
|
self.check_required_columns(df)
|
2022
1865
|
|
2023
|
-
if self.troubleshoot_mode:
|
2024
|
-
self.troubleshoot_cols(df)
|
2025
|
-
|
2026
1866
|
# Check analog outputs [data with units of %] are floats only
|
2027
1867
|
columns_to_check = [
|
2028
1868
|
self.economizer_sig_col,
|
@@ -2032,46 +1872,33 @@ class FaultConditionFourteen(FaultCondition):
|
|
2032
1872
|
]
|
2033
1873
|
self.check_analog_pct(df, columns_to_check)
|
2034
1874
|
|
2035
|
-
#
|
2036
|
-
|
1875
|
+
# Calculate necessary checks
|
1876
|
+
clg_delta_temp = (
|
2037
1877
|
df[self.clg_coil_enter_temp_col] - df[self.clg_coil_leave_temp_col]
|
2038
1878
|
)
|
2039
|
-
|
2040
|
-
df["clg_delta_sqrted"] = (
|
1879
|
+
clg_delta_sqrted = (
|
2041
1880
|
np.sqrt(
|
2042
1881
|
self.coil_temp_enter_err_thres**2 + self.coil_temp_leav_err_thres**2
|
2043
1882
|
)
|
2044
1883
|
+ self.delta_t_supply_fan
|
2045
1884
|
)
|
2046
1885
|
|
2047
|
-
|
2048
|
-
|
2049
|
-
|
1886
|
+
# Perform combined checks without adding intermediate columns to DataFrame
|
1887
|
+
combined_check = operator.or_(
|
1888
|
+
(clg_delta_temp >= clg_delta_sqrted)
|
2050
1889
|
& (df[self.economizer_sig_col] > self.ahu_min_oa_dpr)
|
2051
1890
|
& (df[self.cooling_sig_col] < 0.1), # OR
|
2052
|
-
(
|
2053
|
-
# verify AHU is running in OS 1 at near full heat
|
1891
|
+
(clg_delta_temp >= clg_delta_sqrted)
|
2054
1892
|
& (df[self.heating_sig_col] > 0.0)
|
2055
1893
|
& (df[self.supply_vfd_speed_col] > 0.0),
|
2056
1894
|
)
|
2057
1895
|
|
2058
1896
|
# Rolling sum to count consecutive trues
|
2059
|
-
rolling_sum = (
|
2060
|
-
|
2061
|
-
)
|
1897
|
+
rolling_sum = combined_check.rolling(window=self.rolling_window_size).sum()
|
1898
|
+
|
2062
1899
|
# Set flag to 1 if rolling sum equals the window size
|
2063
1900
|
df["fc14_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
|
2064
1901
|
|
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
1902
|
return df
|
2076
1903
|
|
2077
1904
|
except MissingColumnError as e:
|
@@ -2251,3 +2078,207 @@ class FaultConditionFifteen(FaultCondition):
|
|
2251
2078
|
print(f"Error: {e.message}")
|
2252
2079
|
sys.stdout.flush()
|
2253
2080
|
raise e
|
2081
|
+
|
2082
|
+
|
2083
|
+
class FaultConditionSixteen(FaultCondition):
|
2084
|
+
"""Class provides the definitions for Fault Condition 16.
|
2085
|
+
ERV Ineffective Process based on outdoor air temperature ranges.
|
2086
|
+
"""
|
2087
|
+
|
2088
|
+
def __init__(self, dict_):
|
2089
|
+
super().__init__()
|
2090
|
+
|
2091
|
+
# Threshold parameters for efficiency ranges based on heating and cooling
|
2092
|
+
self.erv_efficiency_min_heating = dict_.get("ERV_EFFICIENCY_MIN_HEATING", 0.7)
|
2093
|
+
self.erv_efficiency_max_heating = dict_.get("ERV_EFFICIENCY_MAX_HEATING", 0.8)
|
2094
|
+
self.erv_efficiency_min_cooling = dict_.get("ERV_EFFICIENCY_MIN_COOLING", 0.5)
|
2095
|
+
self.erv_efficiency_max_cooling = dict_.get("ERV_EFFICIENCY_MAX_COOLING", 0.6)
|
2096
|
+
|
2097
|
+
self.oat_low_threshold = dict_.get("OAT_LOW_THRES", 32.0)
|
2098
|
+
self.oat_high_threshold = dict_.get("OAT_HIGH_THRES", 80.0)
|
2099
|
+
self.oat_rat_delta_min = dict_.get("OAT_RAT_DELTA_MIN", None)
|
2100
|
+
|
2101
|
+
# Validate that threshold parameters are floats and within 0.0 and 1.0 for efficiency values
|
2102
|
+
for param, value in [
|
2103
|
+
("erv_efficiency_min_heating", self.erv_efficiency_min_heating),
|
2104
|
+
("erv_efficiency_max_heating", self.erv_efficiency_max_heating),
|
2105
|
+
("erv_efficiency_min_cooling", self.erv_efficiency_min_cooling),
|
2106
|
+
("erv_efficiency_max_cooling", self.erv_efficiency_max_cooling),
|
2107
|
+
("oat_low_threshold", self.oat_low_threshold),
|
2108
|
+
("oat_high_threshold", self.oat_high_threshold),
|
2109
|
+
("oat_rat_delta_min", self.oat_rat_delta_min),
|
2110
|
+
]:
|
2111
|
+
if not isinstance(value, float):
|
2112
|
+
raise InvalidParameterError(
|
2113
|
+
f"The parameter '{param}' should be a float, but got {type(value).__name__}."
|
2114
|
+
)
|
2115
|
+
if "erv_efficiency" in param and not (0.0 <= value <= 1.0):
|
2116
|
+
raise InvalidParameterError(
|
2117
|
+
f"The parameter '{param}' should be a float between 0.0 and 1.0 to represent a percentage, but got {value}."
|
2118
|
+
)
|
2119
|
+
|
2120
|
+
# Other attributes
|
2121
|
+
self.erv_oat_enter_col = dict_.get("ERV_OAT_ENTER_COL", "erv_oat_enter")
|
2122
|
+
self.erv_oat_leaving_col = dict_.get("ERV_OAT_LEAVING_COL", "erv_oat_leaving")
|
2123
|
+
self.erv_eat_enter_col = dict_.get("ERV_EAT_ENTER_COL", "erv_eat_enter")
|
2124
|
+
self.erv_eat_leaving_col = dict_.get("ERV_EAT_LEAVING_COL", "erv_eat_leaving")
|
2125
|
+
self.supply_vfd_speed_col = dict_.get(
|
2126
|
+
"SUPPLY_VFD_SPEED_COL", "supply_vfd_speed"
|
2127
|
+
)
|
2128
|
+
self.rolling_window_size = dict_.get("ROLLING_WINDOW_SIZE", 1)
|
2129
|
+
self.troubleshoot_mode = dict_.get("TROUBLESHOOT_MODE", False)
|
2130
|
+
|
2131
|
+
self.equation_string = (
|
2132
|
+
"fc16_flag = 1 if temperature deltas and expected efficiency is ineffective "
|
2133
|
+
"for N consecutive values else 0 \n"
|
2134
|
+
)
|
2135
|
+
self.description_string = (
|
2136
|
+
"Fault Condition 16: ERV is an ineffective heat transfer fault. "
|
2137
|
+
"This fault occurs when the ERV's efficiency "
|
2138
|
+
"is outside the acceptable range based on the delta temperature across the "
|
2139
|
+
"ERV outside air enter temperature and ERV outside air leaving temperature, "
|
2140
|
+
"indicating poor heat transfer. "
|
2141
|
+
"It considers both heating and cooling conditions where each have acceptable "
|
2142
|
+
"ranges in percentage for expected heat transfer efficiency. The percentage needs "
|
2143
|
+
"to be a float between 0.0 and 1.0."
|
2144
|
+
)
|
2145
|
+
self.required_column_description = (
|
2146
|
+
"Required inputs are the ERV outside air entering temperature, ERV outside air leaving temperature, "
|
2147
|
+
"ERV exhaust entering temperature, ERV exhaust leaving temperature, "
|
2148
|
+
"and AHU supply fan VFD speed."
|
2149
|
+
)
|
2150
|
+
self.error_string = "One or more required columns are missing or None."
|
2151
|
+
|
2152
|
+
self.set_attributes(dict_)
|
2153
|
+
|
2154
|
+
# Set required columns specific to this fault condition
|
2155
|
+
self.required_columns = [
|
2156
|
+
self.erv_oat_enter_col,
|
2157
|
+
self.erv_oat_leaving_col,
|
2158
|
+
self.erv_eat_enter_col,
|
2159
|
+
self.erv_eat_leaving_col,
|
2160
|
+
self.supply_vfd_speed_col,
|
2161
|
+
]
|
2162
|
+
|
2163
|
+
# Check if any of the required columns are None
|
2164
|
+
if any(col is None for col in self.required_columns):
|
2165
|
+
raise MissingColumnError(
|
2166
|
+
f"{self.error_string}\n"
|
2167
|
+
f"{self.equation_string}\n"
|
2168
|
+
f"{self.description_string}\n"
|
2169
|
+
f"{self.required_column_description}\n"
|
2170
|
+
f"Missing columns: {self.required_columns}"
|
2171
|
+
)
|
2172
|
+
|
2173
|
+
# Ensure all required columns are strings
|
2174
|
+
self.required_columns = [str(col) for col in self.required_columns]
|
2175
|
+
|
2176
|
+
self.mapped_columns = (
|
2177
|
+
f"Your config dictionary is mapped as: {', '.join(self.required_columns)}"
|
2178
|
+
)
|
2179
|
+
|
2180
|
+
def get_required_columns(self) -> str:
|
2181
|
+
"""Returns a string representation of the required columns."""
|
2182
|
+
return (
|
2183
|
+
f"{self.equation_string}"
|
2184
|
+
f"{self.description_string}\n"
|
2185
|
+
f"{self.required_column_description}\n"
|
2186
|
+
f"{self.mapped_columns}"
|
2187
|
+
)
|
2188
|
+
|
2189
|
+
def calculate_erv_efficiency(self, df: pd.DataFrame) -> pd.DataFrame:
|
2190
|
+
|
2191
|
+
df = SharedUtils.clean_nan_values(df)
|
2192
|
+
|
2193
|
+
cols_to_check = [self.erv_eat_enter_col, self.erv_oat_enter_col]
|
2194
|
+
if df[cols_to_check].eq(0).any().any():
|
2195
|
+
print(f"Warning: Zero values found in columns: {cols_to_check}")
|
2196
|
+
print(f"This may cause division by zero errors.")
|
2197
|
+
sys.stdout.flush()
|
2198
|
+
|
2199
|
+
# Calculate the temperature differences
|
2200
|
+
delta_temp_oa = df[self.erv_oat_leaving_col] - df[self.erv_oat_enter_col]
|
2201
|
+
delta_temp_ea = df[self.erv_eat_enter_col] - df[self.erv_oat_enter_col]
|
2202
|
+
|
2203
|
+
# Use the absolute value to handle both heating and cooling applications
|
2204
|
+
df["erv_efficiency_oa"] = np.abs(delta_temp_oa) / np.abs(delta_temp_ea)
|
2205
|
+
|
2206
|
+
return df
|
2207
|
+
|
2208
|
+
def apply(self, df: pd.DataFrame) -> pd.DataFrame:
|
2209
|
+
try:
|
2210
|
+
# Calculate ERV efficiency
|
2211
|
+
df = self.calculate_erv_efficiency(df)
|
2212
|
+
|
2213
|
+
# Fan must be on for a fault to be considered
|
2214
|
+
fan_on = df[self.supply_vfd_speed_col] > 0.1
|
2215
|
+
|
2216
|
+
# Determine if the conditions are for heating or cooling based on OAT
|
2217
|
+
cold_outside = df[self.erv_oat_enter_col] <= self.oat_low_threshold
|
2218
|
+
hot_outside = df[self.erv_oat_enter_col] >= self.oat_high_threshold
|
2219
|
+
|
2220
|
+
# Calculate the temperature difference between the exhaust air entering and outside air entering
|
2221
|
+
rat_minus_oat = abs(df[self.erv_eat_enter_col] - df[self.erv_oat_enter_col])
|
2222
|
+
good_delta_check = rat_minus_oat >= self.oat_rat_delta_min
|
2223
|
+
|
2224
|
+
# Initialize the fault condition to False (no fault)
|
2225
|
+
df["fc16_flag"] = 0
|
2226
|
+
|
2227
|
+
# Apply heating fault logic
|
2228
|
+
heating_fault = (
|
2229
|
+
(
|
2230
|
+
(df["erv_efficiency_oa"] < self.erv_efficiency_min_heating)
|
2231
|
+
| (df["erv_efficiency_oa"] > self.erv_efficiency_max_heating)
|
2232
|
+
)
|
2233
|
+
& cold_outside
|
2234
|
+
& good_delta_check
|
2235
|
+
& fan_on
|
2236
|
+
)
|
2237
|
+
|
2238
|
+
# Apply cooling fault logic
|
2239
|
+
cooling_fault = (
|
2240
|
+
(
|
2241
|
+
(df["erv_efficiency_oa"] < self.erv_efficiency_min_cooling)
|
2242
|
+
| (df["erv_efficiency_oa"] > self.erv_efficiency_max_cooling)
|
2243
|
+
)
|
2244
|
+
& hot_outside
|
2245
|
+
& good_delta_check
|
2246
|
+
& fan_on
|
2247
|
+
)
|
2248
|
+
|
2249
|
+
# Combine the faults
|
2250
|
+
df["combined_checks"] = heating_fault | cooling_fault
|
2251
|
+
|
2252
|
+
# Apply rolling sum to combined checks to account for rolling window
|
2253
|
+
df["fc16_flag"] = (
|
2254
|
+
df["combined_checks"]
|
2255
|
+
.rolling(window=self.rolling_window_size)
|
2256
|
+
.sum()
|
2257
|
+
.ge(self.rolling_window_size)
|
2258
|
+
.astype(int)
|
2259
|
+
)
|
2260
|
+
|
2261
|
+
if self.troubleshoot_mode:
|
2262
|
+
print("Troubleshoot mode enabled - not removing helper columns")
|
2263
|
+
sys.stdout.flush()
|
2264
|
+
|
2265
|
+
# Drop helper cols if not in troubleshoot mode
|
2266
|
+
if not self.troubleshoot_mode:
|
2267
|
+
df.drop(
|
2268
|
+
columns=[
|
2269
|
+
"combined_checks",
|
2270
|
+
"erv_efficiency_oa",
|
2271
|
+
],
|
2272
|
+
inplace=True,
|
2273
|
+
)
|
2274
|
+
|
2275
|
+
return df
|
2276
|
+
|
2277
|
+
except MissingColumnError as e:
|
2278
|
+
print(f"Error: {e.message}")
|
2279
|
+
sys.stdout.flush()
|
2280
|
+
raise e
|
2281
|
+
except InvalidParameterError as e:
|
2282
|
+
print(f"Error: {e.message}")
|
2283
|
+
sys.stdout.flush()
|
2284
|
+
raise e
|