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.
- {open_fdd-0.1.8/open_fdd.egg-info → open_fdd-0.1.9}/PKG-INFO +4 -3
- {open_fdd-0.1.8 → open_fdd-0.1.9}/README.md +1 -1
- open_fdd-0.1.9/open_fdd/air_handling_unit/faults/__init__.py +30 -0
- open_fdd-0.1.9/open_fdd/air_handling_unit/faults/fault_condition_eight.py +135 -0
- open_fdd-0.1.9/open_fdd/air_handling_unit/faults/fault_condition_eleven.py +108 -0
- open_fdd-0.1.9/open_fdd/air_handling_unit/faults/fault_condition_fifteen.py +189 -0
- open_fdd-0.1.9/open_fdd/air_handling_unit/faults/fault_condition_five.py +126 -0
- open_fdd-0.1.9/open_fdd/air_handling_unit/faults/fault_condition_four.py +128 -0
- open_fdd-0.1.9/open_fdd/air_handling_unit/faults/fault_condition_fourteen.py +177 -0
- open_fdd-0.1.9/open_fdd/air_handling_unit/faults/fault_condition_nine.py +140 -0
- open_fdd-0.1.9/open_fdd/air_handling_unit/faults/fault_condition_one.py +113 -0
- open_fdd-0.1.9/open_fdd/air_handling_unit/faults/fault_condition_seven.py +106 -0
- open_fdd-0.1.9/open_fdd/air_handling_unit/faults/fault_condition_six.py +228 -0
- open_fdd-0.1.9/open_fdd/air_handling_unit/faults/fault_condition_sixteen.py +196 -0
- open_fdd-0.1.9/open_fdd/air_handling_unit/faults/fault_condition_ten.py +119 -0
- open_fdd-0.1.9/open_fdd/air_handling_unit/faults/fault_condition_thirteen.py +139 -0
- open_fdd-0.1.9/open_fdd/air_handling_unit/faults/fault_condition_three.py +112 -0
- open_fdd-0.1.9/open_fdd/air_handling_unit/faults/fault_condition_twelve.py +164 -0
- open_fdd-0.1.9/open_fdd/air_handling_unit/faults/fault_condition_two.py +112 -0
- {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/air_handling_unit/faults/helper_utils.py +29 -19
- {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/air_handling_unit/reports/__init__.py +6 -4
- {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/air_handling_unit/reports/fault_report.py +3 -2
- open_fdd-0.1.9/open_fdd/chiller_plant/faults/__init__.py +7 -0
- open_fdd-0.1.9/open_fdd/chiller_plant/faults/fault_condition_one.py +113 -0
- open_fdd-0.1.9/open_fdd/chiller_plant/faults/fault_condition_two.py +100 -0
- {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/tests/ahu/test_ahu_fc1.py +2 -2
- {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/tests/ahu/test_ahu_fc10.py +1 -0
- {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/tests/ahu/test_ahu_fc11.py +3 -4
- {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/tests/ahu/test_ahu_fc12.py +3 -4
- {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/tests/ahu/test_ahu_fc13.py +3 -4
- {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/tests/ahu/test_ahu_fc14.py +3 -4
- {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/tests/ahu/test_ahu_fc15.py +3 -4
- {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/tests/ahu/test_ahu_fc16.py +4 -3
- {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/tests/ahu/test_ahu_fc2.py +1 -0
- {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/tests/ahu/test_ahu_fc3.py +1 -0
- {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/tests/ahu/test_ahu_fc4.py +3 -1
- {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/tests/ahu/test_ahu_fc5.py +1 -0
- {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/tests/ahu/test_ahu_fc6.py +1 -0
- {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/tests/ahu/test_ahu_fc7.py +1 -0
- {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/tests/ahu/test_ahu_fc8.py +1 -0
- {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/tests/ahu/test_ahu_fc9.py +1 -0
- {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/tests/chiller/test_chiller_fc1.py +2 -2
- {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/tests/chiller/test_chiller_fc2.py +2 -2
- {open_fdd-0.1.8 → open_fdd-0.1.9/open_fdd.egg-info}/PKG-INFO +4 -3
- {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd.egg-info/SOURCES.txt +19 -4
- {open_fdd-0.1.8 → open_fdd-0.1.9}/setup.py +2 -2
- open_fdd-0.1.8/open_fdd/air_handling_unit/faults/__init__.py +0 -1487
- open_fdd-0.1.8/open_fdd/air_handling_unit/faults/fault_condition.py +0 -69
- open_fdd-0.1.8/open_fdd/air_handling_unit/faults/shared_utils.py +0 -90
- open_fdd-0.1.8/open_fdd/chiller_plant/faults/__init__.py +0 -2280
- open_fdd-0.1.8/tests/test_base_fault.py +0 -43
- {open_fdd-0.1.8 → open_fdd-0.1.9}/LICENSE +0 -0
- {open_fdd-0.1.8 → open_fdd-0.1.9}/MANIFEST.in +0 -0
- {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/__init__.py +0 -0
- {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/air_handling_unit/__init__.py +0 -0
- {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/chiller_plant/__init__.py +0 -0
- {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/tests/__init__.py +0 -0
- {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/tests/ahu/__init__.py +0 -0
- {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd/tests/chiller/__init__.py +0 -0
- {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd.egg-info/dependency_links.txt +0 -0
- {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd.egg-info/requires.txt +0 -0
- {open_fdd-0.1.8 → open_fdd-0.1.9}/open_fdd.egg-info/top_level.txt +0 -0
- {open_fdd-0.1.8 → open_fdd-0.1.9}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: open_fdd
|
3
|
-
Version: 0.1.
|
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
|
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
|
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
|