Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions energytool/base/parse_results.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,9 @@ def get_output_variable(
results = eplus_res.loc[:, mask]

if drop_suffix:
new_columns = [re.sub(f":{variables}.+", "", col) for col in results.columns]
results.columns = new_columns
strip_pattern = re.compile(
":(?:" + "|".join(re.escape(v) for v in variable_names_list) + ").+"
)
results.columns = [strip_pattern.sub("", col) for col in results.columns]

return results
21 changes: 13 additions & 8 deletions energytool/system.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,9 @@ class Sensor(System):
(list of strings). Default is '*' meaning all the available variables.
"""

def __init__(self, name: str, variables: str, key_values: str | list[str] = "*"):
def __init__(
self, name: str, variables: str | list[str], key_values: str | list[str] = "*"
):
super().__init__(name=name, category=SystemCategories.SENSOR)
self.variables = variables
self.key_values = key_values
Expand All @@ -257,13 +259,16 @@ def pre_process(self, idf: IDF):
)

def post_process(self, idf: IDF = None, eplus_results: pd.DataFrame = None):
results = get_output_variable(
eplus_res=eplus_results,
key_values=self.key_values,
variables=self.variables,
)
results.columns = results.columns + f"_{self.variables}"
return results
per_variable_results = []
for variable in to_list(self.variables):
res = get_output_variable(
eplus_res=eplus_results,
key_values=self.key_values,
variables=variable,
)
res.columns = res.columns + f"_{variable}"
per_variable_results.append(res)
return pd.concat(per_variable_results, axis=1)


class SimplifiedChiller(System):
Expand Down
26 changes: 16 additions & 10 deletions tests/test_outputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,14 +127,20 @@ def test_get_output_zone_variable(self):
),
)

pd.testing.assert_frame_equal(
toy_df.iloc[:, [0, 4]],
get_output_variable(
eplus_res=toy_df,
key_values="Zone1",
variables=[
"Equipment Total Heating Energy",
"Ideal Loads Supply Air Total Heating Energy",
],
),
# Real Sensor usage passes variable names that themselves start with
# "Zone" (e.g. "Zone Ideal Loads Supply Air Total Heating Energy"), so
# this must mirror that instead of variable names with no relation to
# the column's own key prefix - otherwise the drop_suffix regex bug
# (corrupted char-class silently failing to match) goes unnoticed.
multi_variable_result = get_output_variable(
eplus_res=toy_df,
key_values="Zone1",
variables=[
"Zone Other Equipment Total Heating Energy",
"Zone Ideal Loads Supply Air Total Heating Energy",
],
)
assert list(multi_variable_result.columns) == ["ZONE1", "ZONE1"]
assert list(multi_variable_result.iloc[0]) == list(
toy_df.iloc[0, [0, 4]]
)
44 changes: 44 additions & 0 deletions tests/test_system.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,50 @@ def test_sensor(self):
rel=0.05,
)

def test_sensor_multi_variable(self):
"""A Sensor built with a list of several variables must return one
distinctly-named, non-collapsed column per variable (regression test
for the silent multi-variable column collapse bug)."""
test_build = Building(idf_path=RESOURCES_PATH / "test.idf")
test_build.add_system(
sys.Sensor(
name="IDEAL_LOADS",
variables=[
"Zone Ideal Loads Supply Air Total Heating Energy",
"Zone Ideal Loads Supply Air Total Cooling Energy",
],
key_values="*",
)
)

result = test_build.simulate(
parameter_dict={},
simulation_options={
"epw_file": (RESOURCES_PATH / "Paris_2020.epw").as_posix(),
"outputs": "SENSOR",
"verbose": "v",
},
)

heating_cols = [
c
for c in result.columns
if c.endswith("_Zone Ideal Loads Supply Air Total Heating Energy")
]
cooling_cols = [
c
for c in result.columns
if c.endswith("_Zone Ideal Loads Supply Air Total Cooling Energy")
]

assert len(heating_cols) == 4
assert len(cooling_cols) == 4
assert not set(heating_cols) & set(cooling_cols)
# the two variables must carry distinct, non-collapsed values
assert not np.allclose(
result[heating_cols].sum().to_numpy(), result[cooling_cols].sum().to_numpy()
)

def test_heater_simple(self, idf):
gas_boiler = sys.HeaterSimple(
name="Main_boiler",
Expand Down
Loading