open-fdd 0.1.8__tar.gz → 0.1.9__tar.gz

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-0.1.8/open_fdd.egg-info → open_fdd-0.1.9}/PKG-INFO +4 -3
  2. {open_fdd-0.1.8 → open_fdd-0.1.9}/README.md +1 -1
  3. open_fdd-0.1.9/open_fdd/air_handling_unit/faults/__init__.py +30 -0
  4. open_fdd-0.1.9/open_fdd/air_handling_unit/faults/fault_condition_eight.py +135 -0
  5. open_fdd-0.1.9/open_fdd/air_handling_unit/faults/fault_condition_eleven.py +108 -0
  6. open_fdd-0.1.9/open_fdd/air_handling_unit/faults/fault_condition_fifteen.py +189 -0
  7. open_fdd-0.1.9/open_fdd/air_handling_unit/faults/fault_condition_five.py +126 -0
  8. open_fdd-0.1.9/open_fdd/air_handling_unit/faults/fault_condition_four.py +128 -0
  9. open_fdd-0.1.9/open_fdd/air_handling_unit/faults/fault_condition_fourteen.py +177 -0
  10. open_fdd-0.1.9/open_fdd/air_handling_unit/faults/fault_condition_nine.py +140 -0
  11. open_fdd-0.1.9/open_fdd/air_handling_unit/faults/fault_condition_one.py +113 -0
  12. open_fdd-0.1.9/open_fdd/air_handling_unit/faults/fault_condition_seven.py +106 -0
  13. open_fdd-0.1.9/open_fdd/air_handling_unit/faults/fault_condition_six.py +228 -0
  14. open_fdd-0.1.9/open_fdd/air_handling_unit/faults/fault_condition_sixteen.py +196 -0
  15. open_fdd-0.1.9/open_fdd/air_handling_unit/faults/fault_condition_ten.py +119 -0
  16. open_fdd-0.1.9/open_fdd/air_handling_unit/faults/fault_condition_thirteen.py +139 -0
  17. open_fdd-0.1.9/open_fdd/air_handling_unit/faults/fault_condition_three.py +112 -0
  18. open_fdd-0.1.9/open_fdd/air_handling_unit/faults/fault_condition_twelve.py +164 -0
  19. open_fdd-0.1.9/open_fdd/air_handling_unit/faults/fault_condition_two.py +112 -0
  20. {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/air_handling_unit/faults/helper_utils.py +29 -19
  21. {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/air_handling_unit/reports/__init__.py +6 -4
  22. {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/air_handling_unit/reports/fault_report.py +3 -2
  23. open_fdd-0.1.9/open_fdd/chiller_plant/faults/__init__.py +7 -0
  24. open_fdd-0.1.9/open_fdd/chiller_plant/faults/fault_condition_one.py +113 -0
  25. open_fdd-0.1.9/open_fdd/chiller_plant/faults/fault_condition_two.py +100 -0
  26. {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/tests/ahu/test_ahu_fc1.py +2 -2
  27. {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/tests/ahu/test_ahu_fc10.py +1 -0
  28. {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/tests/ahu/test_ahu_fc11.py +3 -4
  29. {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/tests/ahu/test_ahu_fc12.py +3 -4
  30. {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/tests/ahu/test_ahu_fc13.py +3 -4
  31. {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/tests/ahu/test_ahu_fc14.py +3 -4
  32. {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/tests/ahu/test_ahu_fc15.py +3 -4
  33. {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/tests/ahu/test_ahu_fc16.py +4 -3
  34. {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/tests/ahu/test_ahu_fc2.py +1 -0
  35. {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/tests/ahu/test_ahu_fc3.py +1 -0
  36. {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/tests/ahu/test_ahu_fc4.py +3 -1
  37. {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/tests/ahu/test_ahu_fc5.py +1 -0
  38. {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/tests/ahu/test_ahu_fc6.py +1 -0
  39. {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/tests/ahu/test_ahu_fc7.py +1 -0
  40. {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/tests/ahu/test_ahu_fc8.py +1 -0
  41. {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/tests/ahu/test_ahu_fc9.py +1 -0
  42. {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/tests/chiller/test_chiller_fc1.py +2 -2
  43. {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/tests/chiller/test_chiller_fc2.py +2 -2
  44. {open_fdd-0.1.8 → open_fdd-0.1.9/open_fdd.egg-info}/PKG-INFO +4 -3
  45. {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd.egg-info/SOURCES.txt +19 -4
  46. {open_fdd-0.1.8 → open_fdd-0.1.9}/setup.py +2 -2
  47. open_fdd-0.1.8/open_fdd/air_handling_unit/faults/__init__.py +0 -1487
  48. open_fdd-0.1.8/open_fdd/air_handling_unit/faults/fault_condition.py +0 -69
  49. open_fdd-0.1.8/open_fdd/air_handling_unit/faults/shared_utils.py +0 -90
  50. open_fdd-0.1.8/open_fdd/chiller_plant/faults/__init__.py +0 -2280
  51. open_fdd-0.1.8/tests/test_base_fault.py +0 -43
  52. {open_fdd-0.1.8 → open_fdd-0.1.9}/LICENSE +0 -0
  53. {open_fdd-0.1.8 → open_fdd-0.1.9}/MANIFEST.in +0 -0
  54. {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/__init__.py +0 -0
  55. {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/air_handling_unit/__init__.py +0 -0
  56. {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/chiller_plant/__init__.py +0 -0
  57. {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/tests/__init__.py +0 -0
  58. {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/tests/ahu/__init__.py +0 -0
  59. {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/tests/chiller/__init__.py +0 -0
  60. {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd.egg-info/dependency_links.txt +0 -0
  61. {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd.egg-info/requires.txt +0 -0
  62. {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd.egg-info/top_level.txt +0 -0
  63. {open_fdd-0.1.8 → open_fdd-0.1.9}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: open_fdd
3
- Version: 0.1.8
3
+ Version: 0.1.9
4
4
  Summary: A package for fault detection and diagnosis in HVAC systems
5
5
  Home-page: https://bbartling.github.io/open-fdd/
6
6
  Author: Ben Bartling
@@ -20,6 +20,7 @@ Dynamic: classifier
20
20
  Dynamic: description
21
21
  Dynamic: description-content-type
22
22
  Dynamic: home-page
23
+ Dynamic: license-file
23
24
  Dynamic: requires-dist
24
25
  Dynamic: requires-python
25
26
  Dynamic: summary
@@ -127,7 +128,7 @@ py -3.12 -m black .
127
128
 
128
129
  【MIT License】
129
130
 
130
- Copyright 2024 Ben Bartling
131
+ Copyright 2025 Ben Bartling
131
132
 
132
133
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
133
134
 
@@ -101,7 +101,7 @@ py -3.12 -m black .
101
101
 
102
102
  【MIT License】
103
103
 
104
- Copyright 2024 Ben Bartling
104
+ Copyright 2025 Ben Bartling
105
105
 
106
106
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
107
107
 
@@ -0,0 +1,30 @@
1
+ """Base module for all fault conditions in the air handling unit (AHU) module."""
2
+
3
+ from open_fdd.air_handling_unit.faults.fault_condition_eight import FaultConditionEight
4
+ from open_fdd.air_handling_unit.faults.fault_condition_eleven import (
5
+ FaultConditionEleven,
6
+ )
7
+ from open_fdd.air_handling_unit.faults.fault_condition_fifteen import (
8
+ FaultConditionFifteen,
9
+ )
10
+ from open_fdd.air_handling_unit.faults.fault_condition_five import FaultConditionFive
11
+ from open_fdd.air_handling_unit.faults.fault_condition_four import FaultConditionFour
12
+ from open_fdd.air_handling_unit.faults.fault_condition_fourteen import (
13
+ FaultConditionFourteen,
14
+ )
15
+ from open_fdd.air_handling_unit.faults.fault_condition_nine import FaultConditionNine
16
+ from open_fdd.air_handling_unit.faults.fault_condition_one import FaultConditionOne
17
+ from open_fdd.air_handling_unit.faults.fault_condition_seven import FaultConditionSeven
18
+ from open_fdd.air_handling_unit.faults.fault_condition_six import FaultConditionSix
19
+ from open_fdd.air_handling_unit.faults.fault_condition_sixteen import (
20
+ FaultConditionSixteen,
21
+ )
22
+ from open_fdd.air_handling_unit.faults.fault_condition_ten import FaultConditionTen
23
+ from open_fdd.air_handling_unit.faults.fault_condition_thirteen import (
24
+ FaultConditionThirteen,
25
+ )
26
+ from open_fdd.air_handling_unit.faults.fault_condition_three import FaultConditionThree
27
+ from open_fdd.air_handling_unit.faults.fault_condition_twelve import (
28
+ FaultConditionTwelve,
29
+ )
30
+ from open_fdd.air_handling_unit.faults.fault_condition_two import FaultConditionTwo
@@ -0,0 +1,135 @@
1
+ import numpy as np
2
+ import pandas as pd
3
+
4
+ from open_fdd.core.base_fault import BaseFaultCondition
5
+ from open_fdd.core.components import FaultInputColumn, InstanceAttribute
6
+ from open_fdd.core.exceptions import InvalidParameterError, MissingColumnError
7
+ from open_fdd.core.mixins import FaultConditionMixin
8
+
9
+ INPUT_COLS = [
10
+ FaultInputColumn(
11
+ name="mat_col",
12
+ constant_form="MAT_COL",
13
+ description="Mixed air temperature",
14
+ unit="°F",
15
+ required=True,
16
+ type=float,
17
+ ),
18
+ FaultInputColumn(
19
+ name="sat_col",
20
+ constant_form="SAT_COL",
21
+ description="Supply air temperature",
22
+ unit="°F",
23
+ required=True,
24
+ type=float,
25
+ ),
26
+ FaultInputColumn(
27
+ name="economizer_sig_col",
28
+ constant_form="ECONOMIZER_SIG_COL",
29
+ description="Economizer signal",
30
+ unit="%",
31
+ required=True,
32
+ type=float,
33
+ ),
34
+ FaultInputColumn(
35
+ name="cooling_sig_col",
36
+ constant_form="COOLING_SIG_COL",
37
+ description="Cooling signal",
38
+ unit="%",
39
+ required=True,
40
+ type=float,
41
+ ),
42
+ ]
43
+
44
+ FAULT_PARAMS = [
45
+ InstanceAttribute(
46
+ name="delta_t_supply_fan",
47
+ constant_form="DELTA_T_SUPPLY_FAN",
48
+ description="Temperature rise across supply fan",
49
+ unit="°F",
50
+ type=float,
51
+ ),
52
+ InstanceAttribute(
53
+ name="mix_degf_err_thres",
54
+ constant_form="MIX_DEGF_ERR_THRES",
55
+ description="Mixed air temperature error threshold",
56
+ unit="°F",
57
+ type=float,
58
+ ),
59
+ InstanceAttribute(
60
+ name="supply_degf_err_thres",
61
+ constant_form="SUPPLY_DEGF_ERR_THRES",
62
+ description="Supply air temperature error threshold",
63
+ unit="°F",
64
+ type=float,
65
+ ),
66
+ InstanceAttribute(
67
+ name="ahu_min_oa_dpr",
68
+ constant_form="AHU_MIN_OA_DPR",
69
+ description="Minimum outdoor air damper position",
70
+ unit="fraction",
71
+ type=float,
72
+ ),
73
+ ]
74
+
75
+
76
+ class FaultConditionEight(BaseFaultCondition, FaultConditionMixin):
77
+ """Class provides the definitions for Fault Condition 8.
78
+ Supply air temperature and mix air temperature should
79
+ be approx equal in economizer mode.
80
+
81
+ py -3.12 -m pytest open_fdd/tests/ahu/test_ahu_fc8.py -rP -s
82
+ """
83
+
84
+ input_columns = INPUT_COLS
85
+ fault_params = FAULT_PARAMS
86
+ equation_string = (
87
+ "fc8_flag = 1 if |SAT - MAT - ΔT_fan| > √(εSAT² + εMAT²) "
88
+ "in economizer mode for N consecutive values else 0 \n"
89
+ )
90
+ description_string = (
91
+ "Fault Condition 8: Supply air temperature and mixed air temperature should "
92
+ "be approximately equal in economizer mode \n"
93
+ )
94
+ error_string = "One or more required columns are missing or None \n"
95
+
96
+ @FaultConditionMixin._handle_errors
97
+ def apply(self, df: pd.DataFrame) -> pd.DataFrame:
98
+ """Apply the fault condition to the DataFrame."""
99
+ # Apply common checks
100
+ self._apply_common_checks(df)
101
+
102
+ # Get column values using accessor methods
103
+ mat_col = self.get_input_column("mat_col")
104
+ sat_col = self.get_input_column("sat_col")
105
+ economizer_sig_col = self.get_input_column("economizer_sig_col")
106
+ cooling_sig_col = self.get_input_column("cooling_sig_col")
107
+
108
+ # Get parameter values using accessor methods
109
+ delta_t_supply_fan = self.get_param("delta_t_supply_fan")
110
+ mix_degf_err_thres = self.get_param("mix_degf_err_thres")
111
+ supply_degf_err_thres = self.get_param("supply_degf_err_thres")
112
+ ahu_min_oa_dpr = self.get_param("ahu_min_oa_dpr")
113
+
114
+ # Check analog outputs [data with units of %] are floats only
115
+ columns_to_check = [
116
+ economizer_sig_col,
117
+ cooling_sig_col,
118
+ ]
119
+ self._apply_analog_checks(df, columns_to_check, check_greater_than_one=True)
120
+
121
+ # Perform checks
122
+ sat_fan_mat = abs(df[sat_col] - delta_t_supply_fan - df[mat_col])
123
+ sat_mat_sqrted = np.sqrt(supply_degf_err_thres**2 + mix_degf_err_thres**2)
124
+
125
+ combined_check = (
126
+ (sat_fan_mat > sat_mat_sqrted)
127
+ # Verify AHU is running in OS 3 cooling mode with minimum OA
128
+ & (df[economizer_sig_col] > ahu_min_oa_dpr)
129
+ & (df[cooling_sig_col] < 0.1)
130
+ )
131
+
132
+ # Set fault flag
133
+ self._set_fault_flag(df, combined_check, "fc8_flag")
134
+
135
+ return df
@@ -0,0 +1,108 @@
1
+ import numpy as np
2
+ import pandas as pd
3
+
4
+ from open_fdd.core.base_fault import BaseFaultCondition
5
+ from open_fdd.core.components import FaultInputColumn, InstanceAttribute
6
+ from open_fdd.core.exceptions import InvalidParameterError
7
+ from open_fdd.core.mixins import FaultConditionMixin
8
+
9
+ INPUT_COLS = [
10
+ FaultInputColumn(
11
+ name="oat_col",
12
+ constant_form="OAT_COL",
13
+ description="Outside air temperature",
14
+ unit="°F",
15
+ required=True,
16
+ type=float,
17
+ ),
18
+ FaultInputColumn(
19
+ name="mat_col",
20
+ constant_form="MAT_COL",
21
+ description="Mixed air temperature",
22
+ unit="°F",
23
+ required=True,
24
+ type=float,
25
+ ),
26
+ FaultInputColumn(
27
+ name="economizer_sig_col",
28
+ constant_form="ECONOMIZER_SIG_COL",
29
+ description="Economizer signal",
30
+ unit="%",
31
+ required=True,
32
+ type=float,
33
+ ),
34
+ ]
35
+
36
+ FAULT_PARAMS = [
37
+ InstanceAttribute(
38
+ name="outdoor_degf_err_thres",
39
+ constant_form="OUTDOOR_DEGF_ERR_THRES",
40
+ description="Outdoor air temperature error threshold",
41
+ unit="°F",
42
+ type=float,
43
+ range=(0.0, 10.0),
44
+ ),
45
+ InstanceAttribute(
46
+ name="mix_degf_err_thres",
47
+ constant_form="MIX_DEGF_ERR_THRES",
48
+ description="Mixed air temperature error threshold",
49
+ unit="°F",
50
+ type=float,
51
+ range=(0.0, 10.0),
52
+ ),
53
+ ]
54
+
55
+
56
+ class FaultConditionEleven(BaseFaultCondition, FaultConditionMixin):
57
+ """Class provides the definitions for Fault Condition 11.
58
+ Outdoor air temperature and mix air temperature should
59
+ be approx equal in economizer mode.
60
+
61
+ py -3.12 -m pytest open_fdd/tests/ahu/test_ahu_fc11.py -rP -s
62
+ """
63
+
64
+ input_columns = INPUT_COLS
65
+ fault_params = FAULT_PARAMS
66
+ equation_string = (
67
+ "fc11_flag = 1 if |OAT - MAT| > √(εOAT² + εMAT²) in "
68
+ "economizer mode for N consecutive values else 0 \n"
69
+ )
70
+ description_string = (
71
+ "Fault Condition 11: Outdoor air temperature and mixed air temperature "
72
+ "should be approximately equal in economizer mode \n"
73
+ )
74
+ error_string = "One or more required columns are missing or None \n"
75
+
76
+ @FaultConditionMixin._handle_errors
77
+ def apply(self, df: pd.DataFrame) -> pd.DataFrame:
78
+ """Apply the fault condition to the DataFrame."""
79
+ # Apply common checks
80
+ self._apply_common_checks(df)
81
+
82
+ # Get column values using accessor methods
83
+ mat_col = self.get_input_column("mat_col")
84
+ oat_col = self.get_input_column("oat_col")
85
+ economizer_sig_col = self.get_input_column("economizer_sig_col")
86
+
87
+ # Get parameter values using accessor methods
88
+ mix_degf_err_thres = self.get_param("mix_degf_err_thres")
89
+ outdoor_degf_err_thres = self.get_param("outdoor_degf_err_thres")
90
+
91
+ # Check analog outputs [data with units of %] are floats only
92
+ columns_to_check = [economizer_sig_col]
93
+ self._apply_analog_checks(df, columns_to_check, check_greater_than_one=True)
94
+
95
+ # Perform calculations
96
+ abs_mat_minus_oat = abs(df[mat_col] - df[oat_col])
97
+ mat_oat_sqrted = np.sqrt(mix_degf_err_thres**2 + outdoor_degf_err_thres**2)
98
+
99
+ combined_check = (
100
+ (abs_mat_minus_oat > mat_oat_sqrted)
101
+ # Verify AHU is running in economizer mode
102
+ & (df[economizer_sig_col] > 0.9)
103
+ )
104
+
105
+ # Set fault flag
106
+ self._set_fault_flag(df, combined_check, "fc11_flag")
107
+
108
+ return df
@@ -0,0 +1,189 @@
1
+ import numpy as np
2
+ import pandas as pd
3
+
4
+ from open_fdd.core.base_fault import BaseFaultCondition
5
+ from open_fdd.core.components import FaultInputColumn, InstanceAttribute
6
+ from open_fdd.core.exceptions import InvalidParameterError
7
+ from open_fdd.core.mixins import FaultConditionMixin
8
+
9
+ INPUT_COLS = [
10
+ FaultInputColumn(
11
+ name="htg_coil_enter_temp_col",
12
+ constant_form="HTG_COIL_ENTER_TEMP_COL",
13
+ description="Heating coil entering air temperature",
14
+ unit="°F",
15
+ required=True,
16
+ type=float,
17
+ ),
18
+ FaultInputColumn(
19
+ name="htg_coil_leave_temp_col",
20
+ constant_form="HTG_COIL_LEAVE_TEMP_COL",
21
+ description="Heating coil leaving air temperature",
22
+ unit="°F",
23
+ required=True,
24
+ type=float,
25
+ ),
26
+ FaultInputColumn(
27
+ name="cooling_sig_col",
28
+ constant_form="COOLING_SIG_COL",
29
+ description="Cooling signal",
30
+ unit="%",
31
+ required=True,
32
+ type=float,
33
+ ),
34
+ FaultInputColumn(
35
+ name="heating_sig_col",
36
+ constant_form="HEATING_SIG_COL",
37
+ description="Heating signal",
38
+ unit="%",
39
+ required=True,
40
+ type=float,
41
+ ),
42
+ FaultInputColumn(
43
+ name="economizer_sig_col",
44
+ constant_form="ECONOMIZER_SIG_COL",
45
+ description="Economizer signal",
46
+ unit="%",
47
+ required=True,
48
+ type=float,
49
+ ),
50
+ FaultInputColumn(
51
+ name="supply_vfd_speed_col",
52
+ constant_form="SUPPLY_VFD_SPEED_COL",
53
+ description="Supply fan VFD speed",
54
+ unit="%",
55
+ required=True,
56
+ type=float,
57
+ ),
58
+ ]
59
+
60
+ FAULT_PARAMS = [
61
+ InstanceAttribute(
62
+ name="delta_t_supply_fan",
63
+ constant_form="DELTA_SUPPLY_FAN",
64
+ description="Temperature rise across supply fan",
65
+ unit="°F",
66
+ type=float,
67
+ range=(0.0, 5.0),
68
+ ),
69
+ InstanceAttribute(
70
+ name="coil_temp_enter_err_thres",
71
+ constant_form="COIL_TEMP_ENTER_ERR_THRES",
72
+ description="Coil entering air temperature error threshold",
73
+ unit="°F",
74
+ type=float,
75
+ range=(0.0, 10.0),
76
+ ),
77
+ InstanceAttribute(
78
+ name="coil_temp_leave_err_thres",
79
+ constant_form="COIL_TEMP_LEAV_ERR_THRES",
80
+ description="Coil leaving air temperature error threshold",
81
+ unit="°F",
82
+ type=float,
83
+ range=(0.0, 10.0),
84
+ ),
85
+ InstanceAttribute(
86
+ name="ahu_min_oa_dpr",
87
+ constant_form="AHU_MIN_OA_DPR",
88
+ description="Minimum outdoor air damper position",
89
+ unit="fraction",
90
+ type=float,
91
+ range=(0.0, 1.0),
92
+ ),
93
+ ]
94
+
95
+
96
+ class FaultConditionFifteen(BaseFaultCondition, FaultConditionMixin):
97
+ """Class provides the definitions for Fault Condition 15.
98
+ Temperature rise across inactive heating coil in OS2 (economizer),
99
+ OS3 (economizer + mechanical cooling), and OS4 (mechanical cooling only) modes.
100
+ This fault checks if there is an unexpected temperature rise across the heating coil
101
+ when it should be inactive.
102
+
103
+ py -3.12 -m pytest open_fdd/tests/ahu/test_ahu_fc15.py -rP -s
104
+ """
105
+
106
+ input_columns = INPUT_COLS
107
+ fault_params = FAULT_PARAMS
108
+ equation_string = (
109
+ "fc15_flag = 1 if (HTG_LEAVE > HTG_ENTER + √(εENTER² + εLEAVE²) + ΔTfan) "
110
+ "in OS2 (economizer), OS3 (economizer + mechanical cooling), or "
111
+ "OS4 (mechanical cooling only) modes for N consecutive values else 0 \n"
112
+ )
113
+ description_string = (
114
+ "Fault Condition 15: Temperature rise across inactive heating coil "
115
+ "in OS2 (economizer), OS3 (economizer + mechanical cooling), and "
116
+ "OS4 (mechanical cooling only) modes \n"
117
+ )
118
+ error_string = "One or more required columns are missing or None \n"
119
+
120
+ @FaultConditionMixin._handle_errors
121
+ def apply(self, df: pd.DataFrame) -> pd.DataFrame:
122
+ """Apply the fault condition to the DataFrame."""
123
+ # Apply common checks
124
+ self._apply_common_checks(df)
125
+
126
+ # Get column values using accessor methods
127
+ cooling_sig_col = self.get_input_column("cooling_sig_col")
128
+ heating_sig_col = self.get_input_column("heating_sig_col")
129
+ economizer_sig_col = self.get_input_column("economizer_sig_col")
130
+ supply_vfd_speed_col = self.get_input_column("supply_vfd_speed_col")
131
+ htg_coil_enter_temp_col = self.get_input_column("htg_coil_enter_temp_col")
132
+ htg_coil_leave_temp_col = self.get_input_column("htg_coil_leave_temp_col")
133
+
134
+ # Get parameter values using accessor methods
135
+ coil_temp_enter_err_thres = self.get_param("coil_temp_enter_err_thres")
136
+ coil_temp_leave_err_thres = self.get_param("coil_temp_leave_err_thres")
137
+ delta_t_supply_fan = self.get_param("delta_t_supply_fan")
138
+ ahu_min_oa_dpr = self.get_param("ahu_min_oa_dpr")
139
+
140
+ # Check analog outputs [data with units of %] are floats only
141
+ columns_to_check = [
142
+ cooling_sig_col,
143
+ heating_sig_col,
144
+ economizer_sig_col,
145
+ supply_vfd_speed_col,
146
+ ]
147
+ self._apply_analog_checks(df, columns_to_check, check_greater_than_one=True)
148
+
149
+ # Calculate the threshold for temperature rise, including supply fan heat
150
+ temp_rise_threshold = (
151
+ np.sqrt(coil_temp_enter_err_thres**2 + coil_temp_leave_err_thres**2)
152
+ + delta_t_supply_fan
153
+ )
154
+
155
+ # Check if there's a significant temperature rise across the heating coil
156
+ temp_rise = df[htg_coil_leave_temp_col] - df[htg_coil_enter_temp_col]
157
+ significant_temp_rise = temp_rise > temp_rise_threshold
158
+
159
+ # Check operating modes:
160
+ # OS2: Economizer mode (HTG = 0, CLG = 0, ECO > MIN_OA)
161
+ os2_mode = (
162
+ (df[heating_sig_col] == 0.0)
163
+ & (df[cooling_sig_col] == 0.0)
164
+ & (df[economizer_sig_col] > ahu_min_oa_dpr)
165
+ )
166
+
167
+ # OS3: Economizer + mechanical cooling (HTG = 0, CLG > 0, ECO > 0.9)
168
+ os3_mode = (
169
+ (df[heating_sig_col] == 0.0)
170
+ & (df[cooling_sig_col] > 0.0)
171
+ & (df[economizer_sig_col] > 0.9)
172
+ )
173
+
174
+ # OS4: Mechanical cooling only (HTG = 0, CLG > 0, ECO = MIN_OA)
175
+ os4_mode = (
176
+ (df[heating_sig_col] == 0.0)
177
+ & (df[cooling_sig_col] > 0.0)
178
+ & (df[economizer_sig_col] <= ahu_min_oa_dpr)
179
+ )
180
+
181
+ # Combine conditions:
182
+ # Fault occurs when there's a significant temperature rise across an inactive heating coil
183
+ # in OS2 (economizer), OS3 (economizer + mechanical cooling), or OS4 (mechanical cooling only) mode
184
+ combined_check = significant_temp_rise & (os2_mode | os3_mode | os4_mode)
185
+
186
+ # Set fault flag
187
+ self._set_fault_flag(df, combined_check, "fc15_flag")
188
+
189
+ return df
@@ -0,0 +1,126 @@
1
+ import pandas as pd
2
+
3
+ from open_fdd.core.base_fault import BaseFaultCondition
4
+ from open_fdd.core.components import FaultInputColumn, InstanceAttribute
5
+ from open_fdd.core.exceptions import InvalidParameterError
6
+ from open_fdd.core.mixins import FaultConditionMixin
7
+
8
+ INPUT_COLS = [
9
+ FaultInputColumn(
10
+ name="mat_col",
11
+ constant_form="MAT_COL",
12
+ description="Mixed air temperature",
13
+ unit="°F",
14
+ required=True,
15
+ type=float,
16
+ ),
17
+ FaultInputColumn(
18
+ name="sat_col",
19
+ constant_form="SAT_COL",
20
+ description="Supply air temperature",
21
+ unit="°F",
22
+ required=True,
23
+ type=float,
24
+ ),
25
+ FaultInputColumn(
26
+ name="heating_sig_col",
27
+ constant_form="HEATING_SIG_COL",
28
+ description="Heating signal",
29
+ unit="%",
30
+ required=True,
31
+ type=float,
32
+ ),
33
+ FaultInputColumn(
34
+ name="supply_vfd_speed_col",
35
+ constant_form="SUPPLY_VFD_SPEED_COL",
36
+ description="Supply fan VFD speed",
37
+ unit="%",
38
+ required=True,
39
+ type=float,
40
+ ),
41
+ ]
42
+
43
+ FAULT_PARAMS = [
44
+ InstanceAttribute(
45
+ name="mix_degf_err_thres",
46
+ constant_form="MIX_DEGF_ERR_THRES",
47
+ description="Mixed air temperature error threshold",
48
+ unit="°F",
49
+ type=float,
50
+ range=(0.0, 10.0),
51
+ ),
52
+ InstanceAttribute(
53
+ name="supply_degf_err_thres",
54
+ constant_form="SUPPLY_DEGF_ERR_THRES",
55
+ description="Supply air temperature error threshold",
56
+ unit="°F",
57
+ type=float,
58
+ range=(0.0, 10.0),
59
+ ),
60
+ InstanceAttribute(
61
+ name="delta_t_supply_fan",
62
+ constant_form="DELTA_T_SUPPLY_FAN",
63
+ description="Temperature rise across supply fan",
64
+ unit="°F",
65
+ type=float,
66
+ range=(0.0, 5.0),
67
+ ),
68
+ ]
69
+
70
+
71
+ class FaultConditionFive(BaseFaultCondition, FaultConditionMixin):
72
+ """Class provides the definitions for Fault Condition 5.
73
+ SAT too low; should be higher than MAT in HTG MODE
74
+ --Broken heating valve or other mechanical issue
75
+ related to heat valve not working as designed
76
+
77
+ py -3.12 -m pytest open_fdd/tests/ahu/test_ahu_fc5.py -rP -s
78
+ """
79
+
80
+ input_columns = INPUT_COLS
81
+ fault_params = FAULT_PARAMS
82
+ equation_string = (
83
+ "fc5_flag = 1 if (SAT + εSAT <= MAT - εMAT + ΔT_supply_fan) and "
84
+ "(heating signal > 0) and (VFDSPD > 0) for N consecutive values else 0 \n"
85
+ )
86
+ description_string = (
87
+ "Fault Condition 5: SAT too low; should be higher than MAT in HTG MODE, "
88
+ "potential broken heating valve or mechanical issue \n"
89
+ )
90
+ error_string = "One or more required columns are missing or None \n"
91
+
92
+ @FaultConditionMixin._handle_errors
93
+ def apply(self, df: pd.DataFrame) -> pd.DataFrame:
94
+ """Apply the fault condition to the DataFrame."""
95
+ # Apply common checks
96
+ self._apply_common_checks(df)
97
+
98
+ # Get column values using accessor methods
99
+ supply_vfd_speed_col = self.get_input_column("supply_vfd_speed_col")
100
+ heating_sig_col = self.get_input_column("heating_sig_col")
101
+ sat_col = self.get_input_column("sat_col")
102
+ mat_col = self.get_input_column("mat_col")
103
+
104
+ # Get parameter values using accessor methods
105
+ supply_degf_err_thres = self.get_param("supply_degf_err_thres")
106
+ mix_degf_err_thres = self.get_param("mix_degf_err_thres")
107
+ delta_t_supply_fan = self.get_param("delta_t_supply_fan")
108
+
109
+ # Check analog outputs [data with units of %] are floats only
110
+ columns_to_check = [supply_vfd_speed_col, heating_sig_col]
111
+ self._apply_analog_checks(df, columns_to_check, check_greater_than_one=True)
112
+
113
+ # Perform checks
114
+ sat_check = df[sat_col] + supply_degf_err_thres
115
+ mat_check = df[mat_col] - mix_degf_err_thres + delta_t_supply_fan
116
+
117
+ combined_check = (
118
+ (sat_check <= mat_check)
119
+ & (df[heating_sig_col] > 0.01)
120
+ & (df[supply_vfd_speed_col] > 0.01)
121
+ )
122
+
123
+ # Set fault flag
124
+ self._set_fault_flag(df, combined_check, "fc5_flag")
125
+
126
+ return df