Skip to content

Commit

Permalink
Added reporting_frequency parameter to IDF class (#422)
Browse files Browse the repository at this point in the history
* Added reporting_frequency parameter to IDF class

* Implements frequency in Outputs

* Fix flake8

* Fix tests
  • Loading branch information
samuelduchesne committed Dec 1, 2022
1 parent 9121d65 commit bfec4cc
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 42 deletions.
28 changes: 25 additions & 3 deletions archetypal/idfclass/idf.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
from io import IOBase, StringIO
from itertools import chain
from math import isclose
from typing import IO, Iterable, Optional, Tuple, Union
from typing import IO, Iterable, Optional, Tuple, Union, Literal

ReportingFrequency = Literal["Annual", "Monthly", "Daily", "Hourly", "Timestep"]

import eppy
import pandas as pd
Expand Down Expand Up @@ -116,6 +118,7 @@ class IDF(GeomIDF):
"design_day",
"readvars",
"as_version",
"reporting_frequency",
],
"variables": [
"idfobjects",
Expand All @@ -124,6 +127,7 @@ class IDF(GeomIDF):
"design_day",
"readvars",
"as_version",
"reporting_frequency",
],
"sim_id": [
"idfobjects",
Expand Down Expand Up @@ -196,6 +200,7 @@ def __init__(
output_directory=None,
outputtype="standard",
iddname: Optional[Union[str, IO, Path]] = None,
reporting_frequency: ReportingFrequency = "Monthly",
**kwargs,
):
"""Initialize an IDF object.
Expand All @@ -218,6 +223,9 @@ def __init__(
convert (bool): If True, only convert IDF->epJSON or epJSON->IDF.
outputtype (str): Specifies the idf string representation of the model.
Choices are: "standard", "nocomment1", "nocomment2", "compressed".
reporting_frequency (str): Choice of "Annual", "Monthly", "Daily",
"Hourly", "Timestep". Defaults to "Monthly". Is used in the
initialization of the self.Outputs object.
EnergyPlus args:
tmp_dir=None,
Expand All @@ -226,7 +234,7 @@ def __init__(
include=None,
keep_original=True,
"""
# Set independents to there original values
# Set independents to their original values
if include is None:
include = []
self.idfname = idfname
Expand All @@ -249,6 +257,7 @@ def __init__(
self._position = position
self._translated = False
self._rotated = False
self._reporting_frequency = reporting_frequency
self.output_prefix = None
self.name = (
name
Expand Down Expand Up @@ -299,7 +308,12 @@ def __init__(
self.upgrade(to_version=self.as_version, overwrite=False)
finally:
# Set model outputs
self._outputs = Outputs(idf=self, include_html=False, include_sqlite=False)
self._outputs = Outputs(
idf=self,
include_html=False,
include_sqlite=False,
reporting_frequency=reporting_frequency,
)
if self.prep_outputs:
self._outputs.include_html = True
self._outputs.include_sqlite = True
Expand Down Expand Up @@ -2651,6 +2665,14 @@ def rotated(self):
def rotated(self, value):
self._rotated = bool(value)

@property
def reporting_frequency(self):
return self._reporting_frequency

@reporting_frequency.setter
def reporting_frequency(self, value):
self._reporting_frequency = value

def getsiteshadingsurfaces(self, surface_type=""):
site_shading_types = self.idd_index["ref2names"][
"AllShadingSurfNames"
Expand Down
130 changes: 93 additions & 37 deletions archetypal/idfclass/outputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,10 @@ class Outputs:
def __init__(
self,
idf,
variables=None,
meters=None,
variables=(),
meters=(),
outputs=None,
reporting_frequency="Hourly",
reporting_frequency="Monthly",
include_sqlite=True,
include_html=True,
unit_conversion=None,
Expand All @@ -114,17 +114,19 @@ def __init__(
idf (IDF): the IDF object for wich this outputs object is created.
"""
self.idf = idf
self.reporting_frequency = reporting_frequency
self.output_variables = set(
a.Variable_Name for a in idf.idfobjects["Output:Variable".upper()]
)
self.output_meters = set(
get_name_attribute(a) for a in idf.idfobjects["Output:Meter".upper()]
(get_name_attribute(a), a.Reporting_Frequency)
for a in idf.idfobjects["Output:Meter".upper()]
)
self.other_outputs = outputs
self.output_variables += tuple(variables or ())
self.output_meters += tuple(meters or ())

self.output_variables += tuple((v, reporting_frequency) for v in variables)
self.output_meters += tuple((m, reporting_frequency) for m in meters)
self.other_outputs += tuple(outputs or ())
self.reporting_frequency = reporting_frequency
self.include_sqlite = include_sqlite
self.include_html = include_html
self.unit_conversion = unit_conversion
Expand Down Expand Up @@ -186,11 +188,15 @@ def output_variables(self, value):
value, (str, bytes)
), f"Expected list or tuple. Got {type(value)}."
values = []
# for each element
for output in value:
values.append(str(output))
if isinstance(output, tuple):
values.append(output)
else:
values.append((output, self.reporting_frequency))
value = set(values)
else:
value = set()
value = ()
self._output_variables = value

@property
Expand All @@ -206,7 +212,10 @@ def output_meters(self, value):
), f"Expected list or tuple. Got {type(value)}."
values = []
for output in value:
values.append(str(output))
if isinstance(output, tuple):
values.append(output)
else:
values.append((output, self.reporting_frequency))
value = set(values)
else:
value = set()
Expand Down Expand Up @@ -276,9 +285,13 @@ def add_custom(self, outputs):
assert isinstance(outputs, Iterable), "outputs must be some sort of iterable"
for output in outputs:
if "meter" in output["key"].lower():
self._output_meters.add(output["Key_Name"])
self._output_meters.add(
(output["Key_Name"], output["Reporting_Frequency"].title())
)
elif "variable" in output["key"].lower():
self._output_variables.add(output["Variable_Name"])
self._output_variables.add(
(output["Variable_Name"], output["Reporting_Frequency"].title())
)
else:
self._other_outputs.append(output)
return self
Expand Down Expand Up @@ -422,7 +435,7 @@ def add_umi_template_outputs(self):
"Zone Thermostat Heating Setpoint Temperature",
]
for output in variables:
self._output_variables.add(output)
self._output_variables.add((output, self.reporting_frequency))

meters = [
"Baseboard:EnergyTransfer",
Expand All @@ -446,7 +459,7 @@ def add_umi_template_outputs(self):
"WaterSystems:EnergyTransfer",
]
for meter in meters:
self._output_meters.add(meter)
self._output_meters.add((meter, self.reporting_frequency))
return self

def add_dxf(self):
Expand All @@ -473,7 +486,7 @@ def add_umi_outputs(self):
"Water Heater Heating Energy",
]
for output in outputs:
self._output_variables.add(output)
self._output_variables.add((output, self.reporting_frequency))
return self

def add_sensible_heat_gain_summary_components(self):
Expand Down Expand Up @@ -524,33 +537,76 @@ def add_sensible_heat_gain_summary_components(self):
"Zone Air Heat Balance Outdoor Air Transfer Rate"
]

tuple(map(self._output_variables.add, hvac_input_sensible_air_heating))
tuple(map(self._output_variables.add, hvac_input_sensible_air_cooling))
tuple(map(self._output_variables.add, hvac_input_heated_surface_heating))
tuple(map(self._output_variables.add, hvac_input_cooled_surface_cooling))
tuple(map(self._output_variables.add, people_sensible_heat_addition))
tuple(map(self._output_variables.add, lights_sensible_heat_addition))
tuple(
map(
self._output_variables.add,
equipment_sensible_heat_addition_and_equipment_sensible_heat_removal,
zip(hvac_input_sensible_air_heating, (self.reporting_frequency,)),
)
)
tuple(
map(
self._output_variables.add, window_heat_addition_and_window_heat_removal
self._output_variables.add,
zip(hvac_input_sensible_air_cooling, (self.reporting_frequency,)),
)
)
tuple(
map(
self._output_variables.add,
interzone_air_transfer_heat_addition_and_interzone_air_transfer_heat_removal,
zip(hvac_input_heated_surface_heating, (self.reporting_frequency,)),
)
)
tuple(
map(
self._output_variables.add,
infiltration_heat_addition_and_infiltration_heat_removal,
zip(hvac_input_cooled_surface_cooling, (self.reporting_frequency,)),
)
)
tuple(
map(
self._output_variables.add,
zip(people_sensible_heat_addition, (self.reporting_frequency,)),
)
)
tuple(
map(
self._output_variables.add,
zip(lights_sensible_heat_addition, (self.reporting_frequency,)),
)
)
tuple(
map(
self._output_variables.add,
zip(
equipment_sensible_heat_addition_and_equipment_sensible_heat_removal,
(self.reporting_frequency,),
),
)
)
tuple(
map(
self._output_variables.add,
zip(
window_heat_addition_and_window_heat_removal,
(self.reporting_frequency,),
),
)
)
tuple(
map(
self._output_variables.add,
zip(
interzone_air_transfer_heat_addition_and_interzone_air_transfer_heat_removal,
(self.reporting_frequency,),
),
)
)
tuple(
map(
self._output_variables.add,
zip(
infiltration_heat_addition_and_infiltration_heat_removal,
(self.reporting_frequency,),
),
)
)

Expand Down Expand Up @@ -583,7 +639,7 @@ def add_end_use_balance_components(self):
EndUseBalance.AIR_SYSTEM,
]:
for item in group:
self._output_variables.add(item)
self._output_variables.add((item, "Hourly"))
return self

def add_load_balance_components(self):
Expand All @@ -608,7 +664,7 @@ def add_load_balance_components(self):
self.WINDOW_GAIN,
]:
for item in group:
self._output_variables.add(item)
self._output_variables.add((item, "Hourly"))

def add_profile_gas_elect_outputs(self):
"""Adds the following meters: Electricity:Facility, Gas:Facility,
Expand All @@ -623,7 +679,7 @@ def add_profile_gas_elect_outputs(self):
"Cooling:Electricity",
]
for output in outputs:
self._output_meters.add(output)
self._output_meters.add((output, self.reporting_frequency))
return self

def add_hvac_energy_use(self):
Expand Down Expand Up @@ -659,22 +715,20 @@ def add_hvac_energy_use(self):
"Zone VRF Air Terminal Heating Electricity Energy",
]
for output in outputs:
self._output_variables.add(output)
self._output_variables.add((output, self.reporting_frequency))

def apply(self):
"""Applies the outputs to the idf model. Modifies the model by calling
:meth:`~archetypal.idfclass.idf.IDF.newidfobject`"""
for output in self.output_variables:
for (variable, reporting_frequency) in self.output_variables:
self.idf.newidfobject(
key="Output:Variable".upper(),
**dict(
Variable_Name=output, Reporting_Frequency=self.reporting_frequency
),
**dict(Variable_Name=variable, Reporting_Frequency=reporting_frequency),
)
for meter in self.output_meters:
for (meter, reporting_frequency) in self.output_meters:
self.idf.newidfobject(
key="Output:Meter".upper(),
**dict(Key_Name=meter, Reporting_Frequency=self.reporting_frequency),
**dict(Key_Name=meter, Reporting_Frequency=reporting_frequency),
)
for output in self.other_outputs:
key = output.pop("key", None)
Expand All @@ -684,8 +738,10 @@ def apply(self):
return self

def __repr__(self):
variables = "OutputVariables:\n {}".format("\n ".join(self.output_variables))
meters = "OutputMeters:\n {}".format("\n ".join(self.output_meters))
variables = "OutputVariables:\n {}".format(
"\n ".join(map(str, self.output_variables))
)
meters = "OutputMeters:\n {}".format("\n ".join(map(str, self.output_meters)))
outputs = "Outputs:\n {}".format(
"\n ".join((a["key"] for a in self.other_outputs))
)
Expand Down
4 changes: 2 additions & 2 deletions tests/test_outputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def test_output_init(self, idf):
outputs.add_umi_template_outputs()
assert len(outputs.output_variables) > 1
assert len(outputs.output_meters) > 1
assert outputs.reporting_frequency == "Hourly"
assert outputs.reporting_frequency == "Monthly"
assert outputs.include_sqlite
assert outputs.include_html

Expand All @@ -32,7 +32,7 @@ def test_output_properties(self, idf):

outputs.output_variables = ["Air System Outdoor Air Minimum Flow Fraction"]
assert outputs.output_variables == (
"Air System Outdoor Air Minimum Flow Fraction",
("Air System Outdoor Air Minimum Flow Fraction", "Monthly"),
)
outputs.reporting_frequency = "daily" # lower case
assert outputs.reporting_frequency == "Daily" # should be upper case
Expand Down

0 comments on commit bfec4cc

Please sign in to comment.