open-fdd 0.1.1__py3-none-any.whl → 0.1.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. open_fdd/air_handling_unit/faults/__init__.py +2253 -0
  2. open_fdd/air_handling_unit/faults/fault_condition.py +38 -18
  3. open_fdd/air_handling_unit/faults/fault_condition_eight.py +91 -31
  4. open_fdd/air_handling_unit/faults/fault_condition_eleven.py +93 -35
  5. open_fdd/air_handling_unit/faults/fault_condition_fifteen.py +111 -49
  6. open_fdd/air_handling_unit/faults/fault_condition_five.py +89 -34
  7. open_fdd/air_handling_unit/faults/fault_condition_four.py +136 -61
  8. open_fdd/air_handling_unit/faults/fault_condition_fourteen.py +103 -40
  9. open_fdd/air_handling_unit/faults/fault_condition_nine.py +95 -35
  10. open_fdd/air_handling_unit/faults/fault_condition_one.py +83 -31
  11. open_fdd/air_handling_unit/faults/fault_condition_seven.py +85 -26
  12. open_fdd/air_handling_unit/faults/fault_condition_six.py +134 -73
  13. open_fdd/air_handling_unit/faults/fault_condition_ten.py +91 -30
  14. open_fdd/air_handling_unit/faults/fault_condition_thirteen.py +95 -34
  15. open_fdd/air_handling_unit/faults/fault_condition_three.py +84 -29
  16. open_fdd/air_handling_unit/faults/fault_condition_twelve.py +98 -37
  17. open_fdd/air_handling_unit/faults/fault_condition_two.py +84 -32
  18. open_fdd/air_handling_unit/faults/helper_utils.py +295 -93
  19. open_fdd/air_handling_unit/images/ahu1_fc1_2024-06_1.jpg +0 -0
  20. open_fdd/air_handling_unit/images/ahu1_fc1_2024-06_2.jpg +0 -0
  21. open_fdd/air_handling_unit/images/example1.jpg +0 -0
  22. open_fdd/air_handling_unit/images/example2.jpg +0 -0
  23. open_fdd/air_handling_unit/images/fc10_definition.png +0 -0
  24. open_fdd/air_handling_unit/images/fc11_definition.png +0 -0
  25. open_fdd/air_handling_unit/images/fc12_definition.png +0 -0
  26. open_fdd/air_handling_unit/images/fc13_definition.png +0 -0
  27. open_fdd/air_handling_unit/images/fc1_definition.png +0 -0
  28. open_fdd/air_handling_unit/images/fc1_report_screenshot_all.png +0 -0
  29. open_fdd/air_handling_unit/images/fc2_definition.png +0 -0
  30. open_fdd/air_handling_unit/images/fc3_definition.png +0 -0
  31. open_fdd/air_handling_unit/images/fc4_definition.png +0 -0
  32. open_fdd/air_handling_unit/images/fc5_definition.png +0 -0
  33. open_fdd/air_handling_unit/images/fc6_definition.png +0 -0
  34. open_fdd/air_handling_unit/images/fc7_definition.png +0 -0
  35. open_fdd/air_handling_unit/images/fc8_definition.png +0 -0
  36. open_fdd/air_handling_unit/images/fc9_definition.png +0 -0
  37. open_fdd/air_handling_unit/images/latex_generator.py +175 -0
  38. open_fdd/air_handling_unit/images/params.docx +0 -0
  39. open_fdd/air_handling_unit/images/params.pdf +0 -0
  40. open_fdd/air_handling_unit/images/plot_for_repo.png +0 -0
  41. open_fdd/air_handling_unit/reports/base_report.py +47 -0
  42. open_fdd/air_handling_unit/reports/report_fc7.py +3 -1
  43. open_fdd/tests/ahu/test_ahu_fc1.py +18 -1
  44. open_fdd/tests/ahu/test_ahu_fc10.py +1 -1
  45. open_fdd/tests/ahu/test_ahu_fc11.py +1 -1
  46. open_fdd/tests/ahu/test_ahu_fc12.py +1 -1
  47. open_fdd/tests/ahu/test_ahu_fc13.py +1 -1
  48. open_fdd/tests/ahu/test_ahu_fc14.py +1 -1
  49. open_fdd/tests/ahu/test_ahu_fc15.py +1 -1
  50. open_fdd/tests/ahu/test_ahu_fc2.py +1 -1
  51. open_fdd/tests/ahu/test_ahu_fc3.py +1 -1
  52. open_fdd/tests/ahu/test_ahu_fc4.py +2 -2
  53. open_fdd/tests/ahu/test_ahu_fc5.py +1 -1
  54. open_fdd/tests/ahu/test_ahu_fc6.py +2 -2
  55. open_fdd/tests/ahu/test_ahu_fc7.py +1 -1
  56. open_fdd/tests/ahu/test_ahu_fc8.py +1 -1
  57. open_fdd/tests/ahu/test_ahu_fc9.py +1 -1
  58. {open_fdd-0.1.1.dist-info → open_fdd-0.1.4.dist-info}/METADATA +34 -5
  59. open_fdd-0.1.4.dist-info/RECORD +82 -0
  60. open_fdd-0.1.1.dist-info/RECORD +0 -59
  61. {open_fdd-0.1.1.dist-info → open_fdd-0.1.4.dist-info}/LICENSE +0 -0
  62. {open_fdd-0.1.1.dist-info → open_fdd-0.1.4.dist-info}/WHEEL +0 -0
  63. {open_fdd-0.1.1.dist-info → open_fdd-0.1.4.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,9 @@
1
1
  import pandas as pd
2
2
  import numpy as np
3
- from open_fdd.air_handling_unit.faults.fault_condition import FaultCondition
3
+ from open_fdd.air_handling_unit.faults.fault_condition import (
4
+ FaultCondition,
5
+ MissingColumnError,
6
+ )
4
7
  import sys
5
8
 
6
9
 
@@ -10,6 +13,7 @@ class FaultConditionThree(FaultCondition):
10
13
  """
11
14
 
12
15
  def __init__(self, dict_):
16
+ super().__init__()
13
17
  self.mix_degf_err_thres = float
14
18
  self.return_degf_err_thres = float
15
19
  self.outdoor_degf_err_thres = float
@@ -20,39 +24,90 @@ class FaultConditionThree(FaultCondition):
20
24
  self.troubleshoot_mode = bool # default to False
21
25
  self.rolling_window_size = int
22
26
 
27
+ self.equation_string = (
28
+ "fc3_flag = 1 if (MAT - εMAT > max(RAT + εRAT, OAT + εOAT)) and (VFDSPD > 0) "
29
+ "for N consecutive values else 0 \n"
30
+ )
31
+ self.description_string = "Fault Condition 3: Mix temperature too high; should be between outside and return air \n"
32
+ self.required_column_description = "Required inputs are the mix air temperature, return air temperature, outside air temperature, and supply fan VFD speed \n"
33
+ self.error_string = f"One or more required columns are missing or None \n"
34
+
23
35
  self.set_attributes(dict_)
24
36
 
25
- def apply(self, df: pd.DataFrame) -> pd.DataFrame:
26
- if self.troubleshoot_mode:
27
- self.troubleshoot_cols(df)
28
-
29
- # Check analog outputs [data with units of %] are floats only
30
- columns_to_check = [self.supply_vfd_speed_col]
31
- self.check_analog_pct(df, columns_to_check)
32
-
33
- # Fault condition-specific checks / flags
34
- df["mat_check"] = df[self.mat_col] - self.mix_degf_err_thres
35
- df["temp_min_check"] = np.maximum(
36
- df[self.rat_col] + self.return_degf_err_thres,
37
- df[self.oat_col] + self.outdoor_degf_err_thres,
38
- )
37
+ # Set required columns specific to this fault condition
38
+ self.required_columns = [
39
+ self.mat_col,
40
+ self.rat_col,
41
+ self.oat_col,
42
+ self.supply_vfd_speed_col,
43
+ ]
39
44
 
40
- df["combined_check"] = (df["mat_check"] > df["temp_min_check"]) & (
41
- df[self.supply_vfd_speed_col] > 0.01
45
+ # Check if any of the required columns are None
46
+ if any(col is None for col in self.required_columns):
47
+ raise MissingColumnError(
48
+ f"{self.error_string}"
49
+ f"{self.equation_string}"
50
+ f"{self.description_string}"
51
+ f"{self.required_column_description}"
52
+ f"{self.required_columns}"
53
+ )
54
+
55
+ # Ensure all required columns are strings
56
+ self.required_columns = [str(col) for col in self.required_columns]
57
+
58
+ self.mapped_columns = (
59
+ f"Your config dictionary is mapped as: {', '.join(self.required_columns)}"
42
60
  )
43
61
 
44
- # Rolling sum to count consecutive trues
45
- rolling_sum = (
46
- df["combined_check"].rolling(window=self.rolling_window_size).sum()
62
+ def get_required_columns(self) -> str:
63
+ """Returns a string representation of the required columns."""
64
+ return (
65
+ f"{self.equation_string}"
66
+ f"{self.description_string}"
67
+ f"{self.required_column_description}"
68
+ f"{self.mapped_columns}"
47
69
  )
48
- # Set flag to 1 if rolling sum equals the window size
49
- df["fc3_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
50
70
 
51
- if self.troubleshoot_mode:
52
- print("Troubleshoot mode enabled - not removing helper columns")
53
- sys.stdout.flush()
54
- del df["mat_check"]
55
- del df["temp_min_check"]
56
- del df["combined_check"]
71
+ def apply(self, df: pd.DataFrame) -> pd.DataFrame:
72
+ try:
73
+ # Ensure all required columns are present
74
+ self.check_required_columns(df)
57
75
 
58
- return df
76
+ if self.troubleshoot_mode:
77
+ self.troubleshoot_cols(df)
78
+
79
+ # Check analog outputs [data with units of %] are floats only
80
+ columns_to_check = [self.supply_vfd_speed_col]
81
+ self.check_analog_pct(df, columns_to_check)
82
+
83
+ # Fault condition-specific checks / flags
84
+ df["mat_check"] = df[self.mat_col] - self.mix_degf_err_thres
85
+ df["temp_min_check"] = np.maximum(
86
+ df[self.rat_col] + self.return_degf_err_thres,
87
+ df[self.oat_col] + self.outdoor_degf_err_thres,
88
+ )
89
+
90
+ df["combined_check"] = (df["mat_check"] > df["temp_min_check"]) & (
91
+ df[self.supply_vfd_speed_col] > 0.01
92
+ )
93
+
94
+ # Rolling sum to count consecutive trues
95
+ rolling_sum = (
96
+ df["combined_check"].rolling(window=self.rolling_window_size).sum()
97
+ )
98
+ # Set flag to 1 if rolling sum equals the window size
99
+ df["fc3_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
100
+
101
+ if self.troubleshoot_mode:
102
+ print("Troubleshoot mode enabled - not removing helper columns")
103
+ sys.stdout.flush()
104
+ del df["mat_check"]
105
+ del df["temp_min_check"]
106
+ del df["combined_check"]
107
+
108
+ return df
109
+
110
+ except MissingColumnError as e:
111
+ print(f"Error: {e.message}")
112
+ sys.stdout.flush()
113
+ raise e
@@ -1,8 +1,10 @@
1
1
  import pandas as pd
2
2
  import numpy as np
3
3
  import operator
4
- from open_fdd.air_handling_unit.faults.fault_condition import FaultCondition
5
- from open_fdd.air_handling_unit.faults.helper_utils import HelperUtils
4
+ from open_fdd.air_handling_unit.faults.fault_condition import (
5
+ FaultCondition,
6
+ MissingColumnError,
7
+ )
6
8
  import sys
7
9
 
8
10
 
@@ -13,6 +15,7 @@ class FaultConditionTwelve(FaultCondition):
13
15
  """
14
16
 
15
17
  def __init__(self, dict_):
18
+ super().__init__()
16
19
  self.delta_t_supply_fan = float
17
20
  self.mix_degf_err_thres = float
18
21
  self.supply_degf_err_thres = float
@@ -24,48 +27,106 @@ class FaultConditionTwelve(FaultCondition):
24
27
  self.troubleshoot_mode = bool # default False
25
28
  self.rolling_window_size = int
26
29
 
27
- self.set_attributes(dict_)
30
+ self.equation_string = (
31
+ "fc12_flag = 1 if SAT >= MAT + εMAT in "
32
+ "economizer + mech cooling mode for N consecutive values else 0 \n"
33
+ )
34
+ self.description_string = (
35
+ "Fault Condition 12: Supply air temperature too high; should be less than "
36
+ "mixed air temperature in economizer plus mechanical cooling mode \n"
37
+ )
38
+ self.required_column_description = (
39
+ "Required inputs are the supply air temperature, mixed air temperature, "
40
+ "cooling signal, and economizer signal \n"
41
+ )
42
+ self.error_string = f"One or more required columns are missing or None \n"
28
43
 
29
- def apply(self, df: pd.DataFrame) -> pd.DataFrame:
30
- if self.troubleshoot_mode:
31
- self.troubleshoot_cols(df)
44
+ self.set_attributes(dict_)
32
45
 
33
- # Check analog outputs [data with units of %] are floats only
34
- columns_to_check = [
35
- self.economizer_sig_col,
46
+ # Set required columns specific to this fault condition
47
+ self.required_columns = [
48
+ self.sat_col,
49
+ self.mat_col,
36
50
  self.cooling_sig_col,
51
+ self.economizer_sig_col,
37
52
  ]
38
- self.check_analog_pct(df, columns_to_check)
39
53
 
40
- # Create helper columns
41
- df["sat_minus_saterr_delta_supply_fan"] = (
42
- df[self.sat_col] - self.supply_degf_err_thres - self.delta_t_supply_fan
43
- )
44
- df["mat_plus_materr"] = df[self.mat_col] + self.mix_degf_err_thres
45
-
46
- df["combined_check"] = operator.or_(
47
- # OS4 AHU state clg @ min OA
48
- (df["sat_minus_saterr_delta_supply_fan"] > df["mat_plus_materr"])
49
- # verify AHU in OS4 mode
50
- & (df[self.cooling_sig_col] > 0.01)
51
- & (df[self.economizer_sig_col] == self.ahu_min_oa_dpr), # OR
52
- (df["sat_minus_saterr_delta_supply_fan"] > df["mat_plus_materr"])
53
- # verify ahu is running in OS 3 clg mode in 100 OA
54
- & (df[self.cooling_sig_col] > 0.01) & (df[self.economizer_sig_col] > 0.9),
54
+ # Check if any of the required columns are None
55
+ if any(col is None for col in self.required_columns):
56
+ raise MissingColumnError(
57
+ f"{self.error_string}"
58
+ f"{self.equation_string}"
59
+ f"{self.description_string}"
60
+ f"{self.required_column_description}"
61
+ f"{self.required_columns}"
62
+ )
63
+
64
+ # Ensure all required columns are strings
65
+ self.required_columns = [str(col) for col in self.required_columns]
66
+
67
+ self.mapped_columns = (
68
+ f"Your config dictionary is mapped as: {', '.join(self.required_columns)}"
55
69
  )
56
70
 
57
- # Rolling sum to count consecutive trues
58
- rolling_sum = (
59
- df["combined_check"].rolling(window=self.rolling_window_size).sum()
71
+ def get_required_columns(self) -> str:
72
+ """Returns a string representation of the required columns."""
73
+ return (
74
+ f"{self.equation_string}"
75
+ f"{self.description_string}"
76
+ f"{self.required_column_description}"
77
+ f"{self.mapped_columns}"
60
78
  )
61
- # Set flag to 1 if rolling sum equals the window size
62
- df["fc12_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
63
79
 
64
- if self.troubleshoot_mode:
65
- print("Troubleshoot mode enabled - not removing helper columns")
66
- sys.stdout.flush()
67
- del df["sat_minus_saterr_delta_supply_fan"]
68
- del df["mat_plus_materr"]
69
- del df["combined_check"]
80
+ def apply(self, df: pd.DataFrame) -> pd.DataFrame:
81
+ try:
82
+ # Ensure all required columns are present
83
+ self.check_required_columns(df)
84
+
85
+ if self.troubleshoot_mode:
86
+ self.troubleshoot_cols(df)
87
+
88
+ # Check analog outputs [data with units of %] are floats only
89
+ columns_to_check = [
90
+ self.economizer_sig_col,
91
+ self.cooling_sig_col,
92
+ ]
93
+ self.check_analog_pct(df, columns_to_check)
94
+
95
+ # Create helper columns
96
+ df["sat_minus_saterr_delta_supply_fan"] = (
97
+ df[self.sat_col] - self.supply_degf_err_thres - self.delta_t_supply_fan
98
+ )
99
+ df["mat_plus_materr"] = df[self.mat_col] + self.mix_degf_err_thres
70
100
 
71
- return df
101
+ df["combined_check"] = operator.or_(
102
+ # OS4 AHU state clg @ min OA
103
+ (df["sat_minus_saterr_delta_supply_fan"] > df["mat_plus_materr"])
104
+ # verify AHU in OS4 mode
105
+ & (df[self.cooling_sig_col] > 0.01)
106
+ & (df[self.economizer_sig_col] == self.ahu_min_oa_dpr), # OR
107
+ (df["sat_minus_saterr_delta_supply_fan"] > df["mat_plus_materr"])
108
+ # verify AHU is running in OS 3 clg mode in 100 OA
109
+ & (df[self.cooling_sig_col] > 0.01)
110
+ & (df[self.economizer_sig_col] > 0.9),
111
+ )
112
+
113
+ # Rolling sum to count consecutive trues
114
+ rolling_sum = (
115
+ df["combined_check"].rolling(window=self.rolling_window_size).sum()
116
+ )
117
+ # Set flag to 1 if rolling sum equals the window size
118
+ df["fc12_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
119
+
120
+ if self.troubleshoot_mode:
121
+ print("Troubleshoot mode enabled - not removing helper columns")
122
+ sys.stdout.flush()
123
+ del df["sat_minus_saterr_delta_supply_fan"]
124
+ del df["mat_plus_materr"]
125
+ del df["combined_check"]
126
+
127
+ return df
128
+
129
+ except MissingColumnError as e:
130
+ print(f"Error: {e.message}")
131
+ sys.stdout.flush()
132
+ raise e
@@ -1,6 +1,9 @@
1
1
  import pandas as pd
2
2
  import numpy as np
3
- from open_fdd.air_handling_unit.faults.fault_condition import FaultCondition
3
+ from open_fdd.air_handling_unit.faults.fault_condition import (
4
+ FaultCondition,
5
+ MissingColumnError,
6
+ )
4
7
  import sys
5
8
 
6
9
 
@@ -10,9 +13,7 @@ class FaultConditionTwo(FaultCondition):
10
13
  """
11
14
 
12
15
  def __init__(self, dict_):
13
- """
14
- :param dict_:
15
- """
16
+ super().__init__()
16
17
  self.mix_degf_err_thres = float
17
18
  self.return_degf_err_thres = float
18
19
  self.outdoor_degf_err_thres = float
@@ -23,39 +24,90 @@ class FaultConditionTwo(FaultCondition):
23
24
  self.troubleshoot_mode = bool # default to False
24
25
  self.rolling_window_size = int
25
26
 
27
+ self.equation_string = (
28
+ "fc2_flag = 1 if (MAT + εMAT < min(RAT - εRAT, OAT - εOAT)) and (VFDSPD > 0) "
29
+ "for N consecutive values else 0 \n"
30
+ )
31
+ self.description_string = "Fault Condition 2: Mix temperature too low; should be between outside and return air \n"
32
+ self.required_column_description = "Required inputs are the mix air temperature, return air temperature, outside air temperature, and supply fan VFD speed \n"
33
+ self.error_string = f"One or more required columns are missing or None \n"
34
+
26
35
  self.set_attributes(dict_)
27
36
 
28
- def apply(self, df: pd.DataFrame) -> pd.DataFrame:
29
- if self.troubleshoot_mode:
30
- self.troubleshoot_cols(df)
31
-
32
- # Check analog outputs [data with units of %] are floats only
33
- columns_to_check = [self.supply_vfd_speed_col]
34
- self.check_analog_pct(df, columns_to_check)
35
-
36
- # Fault condition-specific checks / flags
37
- df["mat_check"] = df[self.mat_col] + self.mix_degf_err_thres
38
- df["temp_min_check"] = np.minimum(
39
- df[self.rat_col] - self.return_degf_err_thres,
40
- df[self.oat_col] - self.outdoor_degf_err_thres,
41
- )
37
+ # Set required columns specific to this fault condition
38
+ self.required_columns = [
39
+ self.mat_col,
40
+ self.rat_col,
41
+ self.oat_col,
42
+ self.supply_vfd_speed_col,
43
+ ]
42
44
 
43
- df["combined_check"] = (df["mat_check"] < df["temp_min_check"]) & (
44
- df[self.supply_vfd_speed_col] > 0.01
45
+ # Check if any of the required columns are None
46
+ if any(col is None for col in self.required_columns):
47
+ raise MissingColumnError(
48
+ f"{self.error_string}"
49
+ f"{self.equation_string}"
50
+ f"{self.description_string}"
51
+ f"{self.required_column_description}"
52
+ f"{self.required_columns}"
53
+ )
54
+
55
+ # Ensure all required columns are strings
56
+ self.required_columns = [str(col) for col in self.required_columns]
57
+
58
+ self.mapped_columns = (
59
+ f"Your config dictionary is mapped as: {', '.join(self.required_columns)}"
45
60
  )
46
61
 
47
- # Rolling sum to count consecutive trues
48
- rolling_sum = (
49
- df["combined_check"].rolling(window=self.rolling_window_size).sum()
62
+ def get_required_columns(self) -> str:
63
+ """Returns a string representation of the required columns."""
64
+ return (
65
+ f"{self.equation_string}"
66
+ f"{self.description_string}"
67
+ f"{self.required_column_description}"
68
+ f"{self.mapped_columns}"
50
69
  )
51
- # Set flag to 1 if rolling sum equals the window size
52
- df["fc2_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
53
70
 
54
- if self.troubleshoot_mode:
55
- print("Troubleshoot mode enabled - not removing helper columns")
56
- sys.stdout.flush()
57
- del df["mat_check"]
58
- del df["temp_min_check"]
59
- del df["combined_check"]
71
+ def apply(self, df: pd.DataFrame) -> pd.DataFrame:
72
+ try:
73
+ # Ensure all required columns are present
74
+ self.check_required_columns(df)
60
75
 
61
- return df
76
+ if self.troubleshoot_mode:
77
+ self.troubleshoot_cols(df)
78
+
79
+ # Check analog outputs [data with units of %] are floats only
80
+ columns_to_check = [self.supply_vfd_speed_col]
81
+ self.check_analog_pct(df, columns_to_check)
82
+
83
+ # Fault condition-specific checks / flags
84
+ df["mat_check"] = df[self.mat_col] + self.mix_degf_err_thres
85
+ df["temp_min_check"] = np.minimum(
86
+ df[self.rat_col] - self.return_degf_err_thres,
87
+ df[self.oat_col] - self.outdoor_degf_err_thres,
88
+ )
89
+
90
+ df["combined_check"] = (df["mat_check"] < df["temp_min_check"]) & (
91
+ df[self.supply_vfd_speed_col] > 0.01
92
+ )
93
+
94
+ # Rolling sum to count consecutive trues
95
+ rolling_sum = (
96
+ df["combined_check"].rolling(window=self.rolling_window_size).sum()
97
+ )
98
+ # Set flag to 1 if rolling sum equals the window size
99
+ df["fc2_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
100
+
101
+ if self.troubleshoot_mode:
102
+ print("Troubleshoot mode enabled - not removing helper columns")
103
+ sys.stdout.flush()
104
+ del df["mat_check"]
105
+ del df["temp_min_check"]
106
+ del df["combined_check"]
107
+
108
+ return df
109
+
110
+ except MissingColumnError as e:
111
+ print(f"Error: {e.message}")
112
+ sys.stdout.flush()
113
+ raise e