Skip to content

Commit

Permalink
badges added for readme
Browse files Browse the repository at this point in the history
  • Loading branch information
bbartling committed Aug 12, 2024
1 parent c039371 commit c81d407
Show file tree
Hide file tree
Showing 55 changed files with 1,984 additions and 1,198 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# open-fdd

![MIT License](https://img.shields.io/badge/license-MIT-green.svg)
![CI](https://github.com/bbartling/open-fdd/actions/workflows/ci.yml/badge.svg)
![Black](https://img.shields.io/badge/code%20style-black-000000.svg)


![Alt text](open_fdd/air_handling_unit/images/plot_for_repo.png)

This is a Python-based Fault Detection and Diagnostics (FDD) tool for running fault equations inspired by ASHRAE and NIST standards for HVAC systems across historical datasets using the Pandas computing library. The tool evaluates various fault conditions and outputs fault flags as boolean columns within typical Pandas DataFrames. These fault flags indicate the presence (True) or absence (False) of specific issues identified by the fault equations. This approach integrates seamlessly into standard data science and computer science workflows, allowing for efficient analysis, visualization, and further processing of fault conditions within familiar data structures like DataFrames.
Expand Down
24 changes: 2 additions & 22 deletions open_fdd/__init__.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@

'''
"""
open-fdd/ # Repository root
├── open_fdd/ # Python package root
│ ├── __init__.py
│ ├── air_handling_unit/
│ │ ├── __init__.py
│ │ ├── faults/
│ │ │ ├── __init__.py
│ │ │ ├── helper_utils.py
│ │ │ ├── helper_utils.pya
│ │ │ ├── fault_condition.py
│ │ │ ├── fault_condition_one.py
│ │ │ ├── fault_condition_two.py
Expand Down Expand Up @@ -37,23 +36,4 @@
├── LICENSE
└── requirements.txt
'''

"""
# test_formatting.py
import subprocess
import sys
def test_black_formatting():
# Run the Black formatter check
result = subprocess.run(
[sys.executable, "-m", "black", "--check", "."],
capture_output=True,
text=True
)
# Assert that the command was successful
assert result.returncode == 0, f"Black formatting issues found:\n{result.stdout}\n{result.stderr}"
"""
3 changes: 0 additions & 3 deletions open_fdd/air_handling_unit/faults/fault_condition.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@


class FaultCondition:

"""Parent class for Fault Conditions. Methods are inherited to all children."""

def set_attributes(self, dict_):
Expand Down Expand Up @@ -48,5 +47,3 @@ def check_analog_pct(self, df, columns):
df = helper.convert_to_float(df, col)
if df[col].max() > 1.0:
raise TypeError(helper.float_max_check_err(col))


19 changes: 13 additions & 6 deletions open_fdd/air_handling_unit/faults/fault_condition_eight.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
from open_fdd.air_handling_unit.faults.helper_utils import HelperUtils
import sys


class FaultConditionEight(FaultCondition):
""" Class provides the definitions for Fault Condition 8.
Supply air temperature and mix air temperature should
be approx equal in economizer mode.
"""Class provides the definitions for Fault Condition 8.
Supply air temperature and mix air temperature should
be approx equal in economizer mode.
"""

def __init__(self, dict_):
Expand Down Expand Up @@ -36,8 +37,12 @@ def apply(self, df: pd.DataFrame) -> pd.DataFrame:

self.check_analog_pct(df, columns_to_check)

df["sat_fan_mat"] = abs(df[self.sat_col] - self.delta_t_supply_fan - df[self.mat_col])
df["sat_mat_sqrted"] = np.sqrt(self.supply_degf_err_thres ** 2 + self.mix_degf_err_thres ** 2)
df["sat_fan_mat"] = abs(
df[self.sat_col] - self.delta_t_supply_fan - df[self.mat_col]
)
df["sat_mat_sqrted"] = np.sqrt(
self.supply_degf_err_thres**2 + self.mix_degf_err_thres**2
)

df["combined_check"] = (
(df["sat_fan_mat"] > df["sat_mat_sqrted"])
Expand All @@ -46,7 +51,9 @@ def apply(self, df: pd.DataFrame) -> pd.DataFrame:
)

# Rolling sum to count consecutive trues
rolling_sum = df["combined_check"].rolling(window=self.rolling_window_size).sum()
rolling_sum = (
df["combined_check"].rolling(window=self.rolling_window_size).sum()
)
# Set flag to 1 if rolling sum equals the window size
df["fc8_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)

Expand Down
17 changes: 11 additions & 6 deletions open_fdd/air_handling_unit/faults/fault_condition_eleven.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@
from open_fdd.air_handling_unit.faults.helper_utils import HelperUtils
import sys


class FaultConditionEleven(FaultCondition):
""" Class provides the definitions for Fault Condition 11.
Outside air temperature too low for 100% outdoor
air cooling in economizer cooling mode.
"""Class provides the definitions for Fault Condition 11.
Outside air temperature too low for 100% outdoor
air cooling in economizer cooling mode.
Economizer performance fault
Economizer performance fault
"""

def __init__(self, dict_):
Expand Down Expand Up @@ -38,7 +39,9 @@ def apply(self, df: pd.DataFrame) -> pd.DataFrame:

df["oat_plus_oaterror"] = df[self.oat_col] + self.outdoor_degf_err_thres
df["satsp_delta_saterr"] = (
df[self.sat_setpoint_col] - self.delta_t_supply_fan - self.supply_degf_err_thres
df[self.sat_setpoint_col]
- self.delta_t_supply_fan
- self.supply_degf_err_thres
)

df["combined_check"] = (
Expand All @@ -49,7 +52,9 @@ def apply(self, df: pd.DataFrame) -> pd.DataFrame:
)

# Rolling sum to count consecutive trues
rolling_sum = df["combined_check"].rolling(window=self.rolling_window_size).sum()
rolling_sum = (
df["combined_check"].rolling(window=self.rolling_window_size).sum()
)
# Set flag to 1 if rolling sum equals the window size
df["fc11_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)

Expand Down
47 changes: 27 additions & 20 deletions open_fdd/air_handling_unit/faults/fault_condition_fifteen.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
from open_fdd.air_handling_unit.faults.helper_utils import HelperUtils
import sys


class FaultConditionFifteen(FaultCondition):
""" Class provides the definitions for Fault Condition 15.
Temperature rise across inactive heating coi.
Requires coil leaving temp sensor.
"""Class provides the definitions for Fault Condition 15.
Temperature rise across inactive heating coi.
Requires coil leaving temp sensor.
"""

def __init__(self, dict_):
Expand Down Expand Up @@ -41,35 +42,41 @@ def apply(self, df: pd.DataFrame) -> pd.DataFrame:

# Create helper columns
df["htg_delta_temp"] = (
df[self.htg_coil_leave_temp_col] - df[self.htg_coil_enter_temp_col]
df[self.htg_coil_leave_temp_col] - df[self.htg_coil_enter_temp_col]
)

df["htg_delta_sqrted"] = (
np.sqrt(
self.coil_temp_enter_err_thres ** 2 + self.coil_temp_leav_err_thres ** 2
self.coil_temp_enter_err_thres**2 + self.coil_temp_leav_err_thres**2
)
+ self.delta_supply_fan
)

df["combined_check"] = (
(df["htg_delta_temp"] >= df["htg_delta_sqrted"])
# verify AHU is in OS2 only free cooling mode
& (df[self.economizer_sig_col] > self.ahu_min_oa_dpr)
& (df[self.cooling_sig_col] < 0.1)
) | (
(df["htg_delta_temp"] >= df["htg_delta_sqrted"])
# OS4 AHU state clg @ min OA
& (df[self.cooling_sig_col] > 0.01)
& (df[self.economizer_sig_col] == self.ahu_min_oa_dpr)
) | (
(df["htg_delta_temp"] >= df["htg_delta_sqrted"])
# verify AHU is running in OS 3 clg mode in 100 OA
& (df[self.cooling_sig_col] > 0.01)
& (df[self.economizer_sig_col] > 0.9)
(
(df["htg_delta_temp"] >= df["htg_delta_sqrted"])
# verify AHU is in OS2 only free cooling mode
& (df[self.economizer_sig_col] > self.ahu_min_oa_dpr)
& (df[self.cooling_sig_col] < 0.1)
)
| (
(df["htg_delta_temp"] >= df["htg_delta_sqrted"])
# OS4 AHU state clg @ min OA
& (df[self.cooling_sig_col] > 0.01)
& (df[self.economizer_sig_col] == self.ahu_min_oa_dpr)
)
| (
(df["htg_delta_temp"] >= df["htg_delta_sqrted"])
# verify AHU is running in OS 3 clg mode in 100 OA
& (df[self.cooling_sig_col] > 0.01)
& (df[self.economizer_sig_col] > 0.9)
)
)

# Rolling sum to count consecutive trues
rolling_sum = df["combined_check"].rolling(window=self.rolling_window_size).sum()
rolling_sum = (
df["combined_check"].rolling(window=self.rolling_window_size).sum()
)
# Set flag to 1 if rolling sum equals the window size
df["fc15_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)

Expand Down
29 changes: 16 additions & 13 deletions open_fdd/air_handling_unit/faults/fault_condition_five.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@
from open_fdd.air_handling_unit.faults.helper_utils import HelperUtils
import sys


class FaultConditionFive(FaultCondition):
""" Class provides the definitions for Fault Condition 5.
SAT too low; should be higher than MAT in HTG MODE
--Broken heating valve or other mechanical issue
related to heat valve not working as designed
"""Class provides the definitions for Fault Condition 5.
SAT too low; should be higher than MAT in HTG MODE
--Broken heating valve or other mechanical issue
related to heat valve not working as designed
"""

def __init__(self, dict_):
Expand All @@ -30,28 +31,30 @@ def apply(self, df: pd.DataFrame) -> pd.DataFrame:
if self.troubleshoot_mode:
self.troubleshoot_cols(df)

# check analog outputs [data with units of %] are floats only
# check analog outputs [data with units of %] are floats only
columns_to_check = [self.supply_vfd_speed_col, self.heating_sig_col]

for col in columns_to_check:
self.check_analog_pct(df, [col])

df["sat_check"] = df[self.sat_col] + self.supply_degf_err_thres
df["mat_check"] = (
df[self.mat_col] - self.mix_degf_err_thres + self.delta_t_supply_fan
df[self.mat_col] - self.mix_degf_err_thres + self.delta_t_supply_fan
)

df["combined_check"] = (
(df["sat_check"] <= df["mat_check"])
# this is to make fault only active in OS1 for htg mode only
# and fan is running. Some control programming may use htg
# vlv when AHU is off to prevent low limit freeze alarms
& (df[self.heating_sig_col] > 0.01)
& (df[self.supply_vfd_speed_col] > 0.01)
(df["sat_check"] <= df["mat_check"])
# this is to make fault only active in OS1 for htg mode only
# and fan is running. Some control programming may use htg
# vlv when AHU is off to prevent low limit freeze alarms
& (df[self.heating_sig_col] > 0.01)
& (df[self.supply_vfd_speed_col] > 0.01)
)

# Rolling sum to count consecutive trues
rolling_sum = df["combined_check"].rolling(window=self.rolling_window_size).sum()
rolling_sum = (
df["combined_check"].rolling(window=self.rolling_window_size).sum()
)
# Set flag to 1 if rolling sum equals the window size
df["fc5_flag"] = (rolling_sum == self.rolling_window_size).astype(int)

Expand Down
45 changes: 23 additions & 22 deletions open_fdd/air_handling_unit/faults/fault_condition_four.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@
from open_fdd.air_handling_unit.faults.helper_utils import HelperUtils
import sys


class FaultConditionFour(FaultCondition):
"""Class provides the definitions for Fault Condition 4.
This fault flags excessive operating states on the AHU
if its hunting between heating, econ, econ+mech, and
a mech clg modes. The code counts how many operating
changes in an hour and will throw a fault if there is
excessive OS changes to flag control sys hunting.
This fault flags excessive operating states on the AHU
if its hunting between heating, econ, econ+mech, and
a mech clg modes. The code counts how many operating
changes in an hour and will throw a fault if there is
excessive OS changes to flag control sys hunting.
"""

def __init__(self, dict_):
Expand Down Expand Up @@ -52,34 +53,34 @@ def apply(self, df: pd.DataFrame) -> pd.DataFrame:

# AHU htg only mode based on OA damper @ min oa and only htg pid/vlv modulating
df["heating_mode"] = (
(df[self.heating_sig_col] > 0)
& (df[self.cooling_sig_col] == 0)
& (df[self.supply_vfd_speed_col] > 0)
& (df[self.economizer_sig_col] == self.ahu_min_oa_dpr)
(df[self.heating_sig_col] > 0)
& (df[self.cooling_sig_col] == 0)
& (df[self.supply_vfd_speed_col] > 0)
& (df[self.economizer_sig_col] == self.ahu_min_oa_dpr)
)

# AHU econ only mode based on OA damper modulating and clg htg = zero
df["econ_only_cooling_mode"] = (
(df[self.heating_sig_col] == 0)
& (df[self.cooling_sig_col] == 0)
& (df[self.supply_vfd_speed_col] > 0)
& (df[self.economizer_sig_col] > self.ahu_min_oa_dpr)
(df[self.heating_sig_col] == 0)
& (df[self.cooling_sig_col] == 0)
& (df[self.supply_vfd_speed_col] > 0)
& (df[self.economizer_sig_col] > self.ahu_min_oa_dpr)
)

# AHU econ+mech clg mode based on OA damper modulating for cooling and clg pid/vlv modulating
df["econ_plus_mech_cooling_mode"] = (
(df[self.heating_sig_col] == 0)
& (df[self.cooling_sig_col] > 0)
& (df[self.supply_vfd_speed_col] > 0)
& (df[self.economizer_sig_col] > self.ahu_min_oa_dpr)
(df[self.heating_sig_col] == 0)
& (df[self.cooling_sig_col] > 0)
& (df[self.supply_vfd_speed_col] > 0)
& (df[self.economizer_sig_col] > self.ahu_min_oa_dpr)
)

# AHU mech mode based on OA damper @ min OA and clg pid/vlv modulating
df["mech_cooling_only_mode"] = (
(df[self.heating_sig_col] == 0)
& (df[self.cooling_sig_col] > 0)
& (df[self.supply_vfd_speed_col] > 0)
& (df[self.economizer_sig_col] == self.ahu_min_oa_dpr)
(df[self.heating_sig_col] == 0)
& (df[self.cooling_sig_col] > 0)
& (df[self.supply_vfd_speed_col] > 0)
& (df[self.economizer_sig_col] == self.ahu_min_oa_dpr)
)

# Fill non-finite values with zero or drop them
Expand Down
15 changes: 9 additions & 6 deletions open_fdd/air_handling_unit/faults/fault_condition_fourteen.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@
import operator
import sys


class FaultConditionFourteen(FaultCondition):
""" Class provides the definitions for Fault Condition 14.
Temperature drop across inactive cooling coil.
Requires coil leaving temp sensor.
"""
"""Class provides the definitions for Fault Condition 14.
Temperature drop across inactive cooling coil.
Requires coil leaving temp sensor.
"""

def __init__(self, dict_):
self.delta_t_supply_fan = float
Expand Down Expand Up @@ -47,7 +48,7 @@ def apply(self, df: pd.DataFrame) -> pd.DataFrame:

df["clg_delta_sqrted"] = (
np.sqrt(
self.coil_temp_enter_err_thres ** 2 + self.coil_temp_leav_err_thres ** 2
self.coil_temp_enter_err_thres**2 + self.coil_temp_leav_err_thres**2
)
+ self.delta_t_supply_fan
)
Expand All @@ -63,7 +64,9 @@ def apply(self, df: pd.DataFrame) -> pd.DataFrame:
)

# Rolling sum to count consecutive trues
rolling_sum = df["combined_check"].rolling(window=self.rolling_window_size).sum()
rolling_sum = (
df["combined_check"].rolling(window=self.rolling_window_size).sum()
)
# Set flag to 1 if rolling sum equals the window size
df["fc14_flag"] = (rolling_sum >= self.rolling_window_size).astype(int)

Expand Down
Loading

0 comments on commit c81d407

Please sign in to comment.