Skip to content

Commit

Permalink
fault 16 is added and example but needs closer look for desicant whee…
Browse files Browse the repository at this point in the history
…l in AHU vs typical ERV
  • Loading branch information
bbartling committed Sep 2, 2024
1 parent 8859d32 commit 720e112
Show file tree
Hide file tree
Showing 5 changed files with 1,747 additions and 19 deletions.
1,590 changes: 1,590 additions & 0 deletions examples/csv_data_source/ahu_fc16.ipynb

Large diffs are not rendered by default.

60 changes: 47 additions & 13 deletions examples/csv_data_source/ahu_individual_faults.ipynb

Large diffs are not rendered by default.

22 changes: 16 additions & 6 deletions open_fdd/air_handling_unit/faults/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -814,9 +814,10 @@ def apply(self, df: pd.DataFrame) -> pd.DataFrame:
percent_oa_calc = (df[self.mat_col] - df[self.rat_col]) / (
df[self.oat_col] - df[self.rat_col]
)
percent_oa_calc = percent_oa_calc.apply(
lambda x: x if x > 0 else 0
) # Weed out any negative values

# Replace negative values in percent_oa_calc with zero using vectorized operation
percent_oa_calc = percent_oa_calc.clip(lower=0)

perc_OAmin = self.ahu_min_oa_cfm_design / df[self.supply_fan_air_volume_col]
percent_oa_calc_minus_perc_OAmin = abs(percent_oa_calc - perc_OAmin)

Expand Down Expand Up @@ -2071,6 +2072,7 @@ def apply(self, df: pd.DataFrame) -> pd.DataFrame:
raise e



class FaultConditionSixteen(FaultCondition):
"""Class provides the definitions for Fault Condition 16.
ERV Ineffective Process based on outdoor air temperature ranges.
Expand Down Expand Up @@ -2175,12 +2177,20 @@ def get_required_columns(self) -> str:
f"{self.mapped_columns}"
)

def calculate_erv_efficiency(self, df: pd.DataFrame) -> pd.DataFrame:
# Calculate the temperature differences
delta_temp_oa = df[self.erv_oat_leaving_col] - df[self.erv_oat_enter_col]
delta_temp_ea = df[self.erv_eat_enter_col] - df[self.erv_oat_enter_col]

# Use the absolute value to handle both heating and cooling applications
df["erv_efficiency_oa"] = np.abs(delta_temp_oa) / np.abs(delta_temp_ea)

return df

def apply(self, df: pd.DataFrame) -> pd.DataFrame:
try:
# Calculate ERV efficiency
df["erv_efficiency_oa"] = (
df[self.erv_oat_leaving_col] - df[self.erv_oat_enter_col]
) / (df[self.erv_eat_enter_col] - df[self.erv_oat_enter_col])
df = self.calculate_erv_efficiency(df)

# Fan must be on for a fault to be considered
fan_on = df[self.supply_vfd_speed_col] > 0.1
Expand Down
93 changes: 93 additions & 0 deletions open_fdd/air_handling_unit/reports/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import matplotlib.pyplot as plt
from open_fdd.air_handling_unit.reports.fault_report import BaseFaultReport
from open_fdd.air_handling_unit.faults import FaultConditionSixteen
import pandas as pd
import numpy as np
import sys


class FaultCodeOneReport(BaseFaultReport):
Expand Down Expand Up @@ -892,3 +895,93 @@ def summarize_fault_times(self, df: pd.DataFrame) -> dict:
),
}
return summary


class FaultCodeSixteenReport(BaseFaultReport):
def __init__(self, config):
super().__init__(config, "fc16_flag")

self.supply_vfd_speed_col = config["SUPPLY_VFD_SPEED_COL"]
self.erv_oat_enter_col = config["ERV_OAT_ENTER_COL"]
self.erv_oat_leaving_col = config["ERV_OAT_LEAVING_COL"]
self.erv_eat_enter_col = config["ERV_EAT_ENTER_COL"]
self.erv_eat_leaving_col = config["ERV_EAT_LEAVING_COL"]

# Instantiate FaultConditionSixteen to access its methods
self.fc16 = FaultConditionSixteen(config)

def create_plot(self, df: pd.DataFrame):
# Calculate the efficiency before plotting using FaultConditionSixteen method
df = self.fc16.calculate_erv_efficiency(df)

print("=" * 50)
print("Info: ERV calculated efficiency ")
print("summary statistics ")
print(df["erv_efficiency_oa"].describe())
print("=" * 50)

sys.stdout.flush()

# Create the plot with four subplots
fig, (ax1, ax2, ax3, ax4) = plt.subplots(4, 1, figsize=(25, 10))
fig.suptitle("Fault Conditions 16 Plot")

# Plot ERV Outdoor Air Side Temps
ax1.plot(df.index, df[self.erv_oat_enter_col], label="Enter", color="blue")
ax1.plot(df.index, df[self.erv_oat_leaving_col], label="Leaving", color="green")
ax1.legend(loc="best")
ax1.set_ylabel("ERV Outdoor Air Side Temps °F")

# Plot ERV Exhaust Air Side Temps
ax2.plot(df.index, df[self.erv_eat_enter_col], label="Enter", color="red")
ax2.plot(df.index, df[self.erv_eat_leaving_col], label="Leaving", color="purple")
ax2.legend(loc="best")
ax2.set_ylabel("ERV Exhaust Air Side Temps °F")


# Plot ERV Efficiency
ax3.plot(df.index, df["erv_efficiency_oa"], label="ERV Efficiency OA", color="b")
ax3.legend(loc="best")
ax3.set_ylabel("ERV Efficiency OA")

# Plot Fault Flags
ax4.plot(df.index, df[self.fault_col], label="Fault", color="k")
ax4.set_xlabel("Date")
ax4.set_ylabel("Fault Flags")
ax4.legend(loc="best")

plt.tight_layout(rect=[0, 0.03, 1, 0.95])
plt.show()
plt.close()

def summarize_fault_times(self, df: pd.DataFrame) -> dict:
delta = df.index.to_series().diff()
summary = {
"total_days": round(delta.sum() / pd.Timedelta(days=1), 2),
"total_hours": round(delta.sum() / pd.Timedelta(hours=1)),
"hours_fc16_mode": round(
(delta * df[self.fault_col]).sum() / pd.Timedelta(hours=1)
),
"percent_true": round(df[self.fault_col].mean() * 100, 2),
"percent_false": round((100 - df[self.fault_col].mean() * 100), 2),
"flag_true_erv_oat_enter_temp": round(
df[self.erv_oat_enter_col].where(df[self.fault_col] == 1).mean(), 2
),
"flag_true_erv_oat_leave_temp": round(
df[self.erv_oat_leaving_col].where(df[self.fault_col] == 1).mean(), 2
),

"flag_true_erv_eat_enter_temp": round(
df[self.erv_eat_enter_col].where(df[self.fault_col] == 1).mean(), 2
),
"flag_true_erv_eat_leave_temp": round(
df[self.erv_eat_leaving_col].where(df[self.fault_col] == 1).mean(), 2
),

"hours_motor_runtime": round(
(delta * df[self.supply_vfd_speed_col].gt(0.01).astype(int)).sum()
/ pd.Timedelta(hours=1),
2,
),
}
return summary
1 change: 1 addition & 0 deletions open_fdd/air_handling_unit/reports/fault_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ def display_report_in_ipython(self, df: pd.DataFrame):
sys.stdout.flush()

if df[self.fault_col].max() != 0:
self.create_plot(df)
self.create_hist_plot(df)
else:
print("NO FAULTS FOUND - Skipping time-of-day Histogram plot")
Expand Down

0 comments on commit 720e112

Please sign in to comment.