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,7 +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
4
- from open_fdd.air_handling_unit.faults.helper_utils import HelperUtils
3
+ from open_fdd.air_handling_unit.faults.fault_condition import (
4
+ FaultCondition,
5
+ MissingColumnError,
6
+ )
5
7
  import sys
6
8
 
7
9
 
@@ -12,6 +14,7 @@ class FaultConditionNine(FaultCondition):
12
14
  """
13
15
 
14
16
  def __init__(self, dict_):
17
+ super().__init__()
15
18
  self.delta_t_supply_fan = float
16
19
  self.outdoor_degf_err_thres = float
17
20
  self.supply_degf_err_thres = float
@@ -23,46 +26,103 @@ class FaultConditionNine(FaultCondition):
23
26
  self.troubleshoot_mode = bool # default should be False
24
27
  self.rolling_window_size = int
25
28
 
26
- self.set_attributes(dict_)
29
+ self.equation_string = (
30
+ "fc9_flag = 1 if OAT > (SATSP - ΔT_fan + εSAT) "
31
+ "in free cooling mode for N consecutive values else 0 \n"
32
+ )
33
+ self.description_string = (
34
+ "Fault Condition 9: Outside air temperature too high in free cooling mode "
35
+ "without additional mechanical cooling in economizer mode \n"
36
+ )
37
+ self.required_column_description = (
38
+ "Required inputs are the supply air temperature setpoint, outside air temperature, "
39
+ "cooling signal, and economizer signal \n"
40
+ )
41
+ self.error_string = f"One or more required columns are missing or None \n"
27
42
 
28
- def apply(self, df: pd.DataFrame) -> pd.DataFrame:
29
- if self.troubleshoot_mode:
30
- self.troubleshoot_cols(df)
43
+ self.set_attributes(dict_)
31
44
 
32
- # Check analog outputs [data with units of %] are floats only
33
- columns_to_check = [
34
- self.economizer_sig_col,
45
+ # Set required columns specific to this fault condition
46
+ self.required_columns = [
47
+ self.sat_setpoint_col,
48
+ self.oat_col,
35
49
  self.cooling_sig_col,
50
+ self.economizer_sig_col,
36
51
  ]
37
- self.check_analog_pct(df, columns_to_check)
38
-
39
- # Create helper columns
40
- df["oat_minus_oaterror"] = df[self.oat_col] - self.outdoor_degf_err_thres
41
- df["satsp_delta_saterr"] = (
42
- df[self.sat_setpoint_col]
43
- - self.delta_t_supply_fan
44
- + self.supply_degf_err_thres
45
- )
46
52
 
47
- df["combined_check"] = (
48
- (df["oat_minus_oaterror"] > df["satsp_delta_saterr"])
49
- # verify AHU is in OS2 only free cooling mode
50
- & (df[self.economizer_sig_col] > self.ahu_min_oa_dpr)
51
- & (df[self.cooling_sig_col] < 0.1)
53
+ # Check if any of the required columns are None
54
+ if any(col is None for col in self.required_columns):
55
+ raise MissingColumnError(
56
+ f"{self.error_string}"
57
+ f"{self.equation_string}"
58
+ f"{self.description_string}"
59
+ f"{self.required_column_description}"
60
+ f"{self.required_columns}"
61
+ )
62
+
63
+ # Ensure all required columns are strings
64
+ self.required_columns = [str(col) for col in self.required_columns]
65
+
66
+ self.mapped_columns = (
67
+ f"Your config dictionary is mapped as: {', '.join(self.required_columns)}"
52
68
  )
53
69
 
54
- # Rolling sum to count consecutive trues
55
- rolling_sum = (
56
- df["combined_check"].rolling(window=self.rolling_window_size).sum()
70
+ def get_required_columns(self) -> str:
71
+ """Returns a string representation of the required columns."""
72
+ return (
73
+ f"{self.equation_string}"
74
+ f"{self.description_string}"
75
+ f"{self.required_column_description}"
76
+ f"{self.mapped_columns}"
57
77
  )
58
- # Set flag to 1 if rolling sum equals the window size
59
- df["fc9_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
60
78
 
61
- if self.troubleshoot_mode:
62
- print("Troubleshoot mode enabled - not removing helper columns")
63
- sys.stdout.flush()
64
- del df["oat_minus_oaterror"]
65
- del df["satsp_delta_saterr"]
66
- del df["combined_check"]
79
+ def apply(self, df: pd.DataFrame) -> pd.DataFrame:
80
+ try:
81
+ # Ensure all required columns are present
82
+ self.check_required_columns(df)
83
+
84
+ if self.troubleshoot_mode:
85
+ self.troubleshoot_cols(df)
86
+
87
+ # Check analog outputs [data with units of %] are floats only
88
+ columns_to_check = [
89
+ self.economizer_sig_col,
90
+ self.cooling_sig_col,
91
+ ]
92
+ self.check_analog_pct(df, columns_to_check)
93
+
94
+ # Create helper columns
95
+ df["oat_minus_oaterror"] = df[self.oat_col] - self.outdoor_degf_err_thres
96
+ df["satsp_delta_saterr"] = (
97
+ df[self.sat_setpoint_col]
98
+ - self.delta_t_supply_fan
99
+ + self.supply_degf_err_thres
100
+ )
67
101
 
68
- return df
102
+ df["combined_check"] = (
103
+ (df["oat_minus_oaterror"] > df["satsp_delta_saterr"])
104
+ # verify AHU is in OS2 only free cooling mode
105
+ & (df[self.economizer_sig_col] > self.ahu_min_oa_dpr)
106
+ & (df[self.cooling_sig_col] < 0.1)
107
+ )
108
+
109
+ # Rolling sum to count consecutive trues
110
+ rolling_sum = (
111
+ df["combined_check"].rolling(window=self.rolling_window_size).sum()
112
+ )
113
+ # Set flag to 1 if rolling sum equals the window size
114
+ df["fc9_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
115
+
116
+ if self.troubleshoot_mode:
117
+ print("Troubleshoot mode enabled - not removing helper columns")
118
+ sys.stdout.flush()
119
+ del df["oat_minus_oaterror"]
120
+ del df["satsp_delta_saterr"]
121
+ del df["combined_check"]
122
+
123
+ return df
124
+
125
+ except MissingColumnError as e:
126
+ print(f"Error: {e.message}")
127
+ sys.stdout.flush()
128
+ raise e
@@ -1,5 +1,8 @@
1
1
  import pandas as pd
2
- from open_fdd.air_handling_unit.faults.fault_condition import FaultCondition
2
+ from open_fdd.air_handling_unit.faults.fault_condition import (
3
+ FaultCondition,
4
+ MissingColumnError,
5
+ )
3
6
  import sys
4
7
 
5
8
 
@@ -9,9 +12,7 @@ class FaultConditionOne(FaultCondition):
9
12
  """
10
13
 
11
14
  def __init__(self, dict_):
12
- """
13
- :param dict_:
14
- """
15
+ super().__init__()
15
16
  self.vfd_speed_percent_err_thres = float
16
17
  self.vfd_speed_percent_max = float
17
18
  self.duct_static_inches_err_thres = float
@@ -21,40 +22,91 @@ class FaultConditionOne(FaultCondition):
21
22
  self.troubleshoot_mode = bool # default should be False
22
23
  self.rolling_window_size = int
23
24
 
25
+ self.equation_string = "fc1_flag = 1 if (DSP < DPSP - εDSP) and (VFDSPD >= VFDSPD_max - εVFDSPD) for N consecutive values else 0 \n"
26
+ self.description_string = (
27
+ "Fault Condition 1: Duct static too low at fan at full speed \n"
28
+ )
29
+ self.required_column_description = "Required inputs are the duct static pressure, setpoint, and supply fan VFD speed \n"
30
+ self.error_string = f"One or more required columns are missing or None \n"
31
+
24
32
  self.set_attributes(dict_)
25
33
 
26
- def apply(self, df: pd.DataFrame) -> pd.DataFrame:
27
- if self.troubleshoot_mode:
28
- self.troubleshoot_cols(df)
34
+ # Set required columns specific to this fault condition manually
35
+ self.required_columns = [
36
+ self.duct_static_col,
37
+ self.supply_vfd_speed_col,
38
+ self.duct_static_setpoint_col,
39
+ ]
40
+
41
+ # Check if any of the required columns are None
42
+ if any(col is None for col in self.required_columns):
43
+ raise MissingColumnError(
44
+ f"{self.error_string}"
45
+ f"{self.equation_string}"
46
+ f"{self.description_string}"
47
+ f"{self.required_column_description}"
48
+ f"{self.required_columns}"
49
+ )
29
50
 
30
- # check analog outputs [data with units of %] are floats only
31
- columns_to_check = [self.supply_vfd_speed_col]
32
- self.check_analog_pct(df, columns_to_check)
51
+ # Ensure all required columns are strings
52
+ self.required_columns = [str(col) for col in self.required_columns]
33
53
 
34
- df["static_check_"] = (
35
- df[self.duct_static_col]
36
- < df[self.duct_static_setpoint_col] - self.duct_static_inches_err_thres
54
+ self.mapped_columns = (
55
+ f"Your config dictionary is mapped as: {', '.join(self.required_columns)}"
37
56
  )
38
- df["fan_check_"] = (
39
- df[self.supply_vfd_speed_col]
40
- >= self.vfd_speed_percent_max - self.vfd_speed_percent_err_thres
57
+
58
+ def get_required_columns(self) -> str:
59
+ """called from IPython to print out"""
60
+ return (
61
+ f"{self.equation_string}"
62
+ f"{self.description_string}"
63
+ f"{self.required_column_description}"
64
+ f"{self.mapped_columns}"
41
65
  )
42
66
 
43
- # Combined condition check
44
- df["combined_check"] = df["static_check_"] & df["fan_check_"]
67
+ def apply(self, df: pd.DataFrame) -> pd.DataFrame:
68
+ try:
69
+ # Ensure all required columns are present
70
+ self.check_required_columns(df)
45
71
 
46
- # Rolling sum to count consecutive trues
47
- rolling_sum = (
48
- df["combined_check"].rolling(window=self.rolling_window_size).sum()
49
- )
50
- # Set flag to 1 if rolling sum equals the window size
51
- df["fc1_flag"] = (rolling_sum == self.rolling_window_size).astype(int)
72
+ if self.troubleshoot_mode:
73
+ self.troubleshoot_cols(df)
52
74
 
53
- if self.troubleshoot_mode:
54
- print("Troubleshoot mode enabled - not removing helper columns")
55
- sys.stdout.flush()
56
- del df["static_check_"]
57
- del df["fan_check_"]
58
- del df["combined_check"]
75
+ # Check analog outputs [data with units of %] are floats only
76
+ columns_to_check = [self.supply_vfd_speed_col]
77
+ self.check_analog_pct(df, columns_to_check)
59
78
 
60
- return df
79
+ df["static_check_"] = (
80
+ df[self.duct_static_col]
81
+ < df[self.duct_static_setpoint_col] - self.duct_static_inches_err_thres
82
+ )
83
+ df["fan_check_"] = (
84
+ df[self.supply_vfd_speed_col]
85
+ >= self.vfd_speed_percent_max - self.vfd_speed_percent_err_thres
86
+ )
87
+
88
+ # Combined condition check
89
+ df["combined_check"] = df["static_check_"] & df["fan_check_"]
90
+
91
+ # Rolling sum to count consecutive trues
92
+ rolling_sum = (
93
+ df["combined_check"].rolling(window=self.rolling_window_size).sum()
94
+ )
95
+ # Set flag to 1 if rolling sum equals the window size
96
+ df["fc1_flag"] = (rolling_sum == self.rolling_window_size).astype(int)
97
+
98
+ if self.troubleshoot_mode:
99
+ print("Troubleshoot mode enabled - not removing helper columns")
100
+ sys.stdout.flush()
101
+
102
+ # Optionally remove temporary columns
103
+ df.drop(
104
+ columns=["static_check_", "fan_check_", "combined_check"], inplace=True
105
+ )
106
+
107
+ return df
108
+
109
+ except MissingColumnError as e:
110
+ print(f"Error: {e.message}")
111
+ sys.stdout.flush()
112
+ raise e
@@ -1,7 +1,8 @@
1
1
  import pandas as pd
2
- import numpy as np
3
- from open_fdd.air_handling_unit.faults.fault_condition import FaultCondition
4
- from open_fdd.air_handling_unit.faults.helper_utils import HelperUtils
2
+ from open_fdd.air_handling_unit.faults.fault_condition import (
3
+ FaultCondition,
4
+ MissingColumnError,
5
+ )
5
6
  import sys
6
7
 
7
8
 
@@ -12,6 +13,7 @@ class FaultConditionSeven(FaultCondition):
12
13
  """
13
14
 
14
15
  def __init__(self, dict_):
16
+ super().__init__()
15
17
  self.supply_degf_err_thres = float
16
18
  self.sat_col = str
17
19
  self.sat_setpoint_col = str
@@ -20,36 +22,93 @@ class FaultConditionSeven(FaultCondition):
20
22
  self.troubleshoot_mode = bool # default to False
21
23
  self.rolling_window_size = int
22
24
 
25
+ self.equation_string = (
26
+ "fc7_flag = 1 if SAT < (SATSP - εSAT) in full heating mode "
27
+ "and VFD speed > 0 for N consecutive values else 0 \n"
28
+ )
29
+ self.description_string = (
30
+ "Fault Condition 7: Supply air temperature too low in full heating mode "
31
+ "with heating valve fully open \n"
32
+ )
33
+ self.required_column_description = (
34
+ "Required inputs are the supply air temperature, supply air temperature setpoint, "
35
+ "heating signal, and supply fan VFD speed \n"
36
+ )
37
+ self.error_string = f"One or more required columns are missing or None \n"
38
+
23
39
  self.set_attributes(dict_)
24
40
 
25
- def apply(self, df: pd.DataFrame) -> pd.DataFrame:
26
- if self.troubleshoot_mode:
27
- self.troubleshoot_cols(df)
41
+ # Set required columns specific to this fault condition
42
+ self.required_columns = [
43
+ self.sat_col,
44
+ self.sat_setpoint_col,
45
+ self.heating_sig_col,
46
+ self.supply_vfd_speed_col,
47
+ ]
28
48
 
29
- # Check analog outputs [data with units of %] are floats only
30
- columns_to_check = [self.supply_vfd_speed_col, self.heating_sig_col]
31
- self.check_analog_pct(df, columns_to_check)
49
+ # Check if any of the required columns are None
50
+ if any(col is None for col in self.required_columns):
51
+ raise MissingColumnError(
52
+ f"{self.error_string}"
53
+ f"{self.equation_string}"
54
+ f"{self.description_string}"
55
+ f"{self.required_column_description}"
56
+ f"{self.required_columns}"
57
+ )
32
58
 
33
- # Fault condition-specific checks / flags
34
- df["sat_check"] = df[self.sat_setpoint_col] - self.supply_degf_err_thres
59
+ # Ensure all required columns are strings
60
+ self.required_columns = [str(col) for col in self.required_columns]
35
61
 
36
- df["combined_check"] = (
37
- (df[self.sat_col] < df["sat_check"])
38
- & (df[self.heating_sig_col] > 0.9)
39
- & (df[self.supply_vfd_speed_col] > 0)
62
+ self.mapped_columns = (
63
+ f"Your config dictionary is mapped as: {', '.join(self.required_columns)}"
40
64
  )
41
65
 
42
- # Rolling sum to count consecutive trues
43
- rolling_sum = (
44
- df["combined_check"].rolling(window=self.rolling_window_size).sum()
66
+ def get_required_columns(self) -> str:
67
+ """Returns a string representation of the required columns."""
68
+ return (
69
+ f"{self.equation_string}"
70
+ f"{self.description_string}"
71
+ f"{self.required_column_description}"
72
+ f"{self.mapped_columns}"
45
73
  )
46
- # Set flag to 1 if rolling sum equals the window size
47
- df["fc7_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
48
74
 
49
- if self.troubleshoot_mode:
50
- print("Troubleshoot mode enabled - not removing helper columns")
51
- sys.stdout.flush()
52
- del df["sat_check"]
53
- del df["combined_check"]
75
+ def apply(self, df: pd.DataFrame) -> pd.DataFrame:
76
+ try:
77
+ # Ensure all required columns are present
78
+ self.check_required_columns(df)
79
+
80
+ if self.troubleshoot_mode:
81
+ self.troubleshoot_cols(df)
54
82
 
55
- return df
83
+ # Check analog outputs [data with units of %] are floats only
84
+ columns_to_check = [self.supply_vfd_speed_col, self.heating_sig_col]
85
+ self.check_analog_pct(df, columns_to_check)
86
+
87
+ # Fault condition-specific checks / flags
88
+ df["sat_check"] = df[self.sat_setpoint_col] - self.supply_degf_err_thres
89
+
90
+ df["combined_check"] = (
91
+ (df[self.sat_col] < df["sat_check"])
92
+ & (df[self.heating_sig_col] > 0.9)
93
+ & (df[self.supply_vfd_speed_col] > 0)
94
+ )
95
+
96
+ # Rolling sum to count consecutive trues
97
+ rolling_sum = (
98
+ df["combined_check"].rolling(window=self.rolling_window_size).sum()
99
+ )
100
+ # Set flag to 1 if rolling sum equals the window size
101
+ df["fc7_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)
102
+
103
+ if self.troubleshoot_mode:
104
+ print("Troubleshoot mode enabled - not removing helper columns")
105
+ sys.stdout.flush()
106
+ del df["sat_check"]
107
+ del df["combined_check"]
108
+
109
+ return df
110
+
111
+ except MissingColumnError as e:
112
+ print(f"Error: {e.message}")
113
+ sys.stdout.flush()
114
+ raise e