Skip to content

Commit

Permalink
Merge acebd92 into d5a18ef
Browse files Browse the repository at this point in the history
  • Loading branch information
samuelduchesne committed Jun 24, 2020
2 parents d5a18ef + acebd92 commit 83fbd04
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 73 deletions.
217 changes: 145 additions & 72 deletions archetypal/template/conditioning.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import numpy as np
from archetypal import float_round, ReportData, log, timeit, settings
from archetypal.template import UmiBase, Unique, UmiSchedule, UniqueName
import logging as lg


class ZoneConditioning(UmiBase, metaclass=Unique):
Expand Down Expand Up @@ -338,72 +339,139 @@ def _set_economizer(self, zone):
self.EconomizerType = "DifferentialEnthalphy"

def _set_mechanical_ventilation(self, zone):
"""Iterate on 'Controller:MechanicalVentilation' objects to find the
'DesignSpecifactionOutdoorAirName' for the zone.
Todo:
- Get mechanical sizing by zone.
"""Mechanical Ventilation in UMI (or Archsim-based models) is applied to an
`ZoneHVAC:IdealLoadsAirSystem` through the `Design Specification Outdoor Air
Object Name` which in turn is a `DesignSpecification:OutdoorAir` object. It
is this last object that performs the calculation for the outdoor air
flowrate. Moreover, UMI defaults to the "sum" method, meaning that the
Outdoor Air Flow per Person {m3/s-person} and the Outdoor Air Flow per Area {
m3/s-m2} are summed to obtain the zone outdoor air flow rate. Moreover,
not all models have the `DesignSpecification:OutdoorAir` object which poses a
difficulty when trying to resolve the mechanical ventilation parameters.
Two general cases exist: 1) models with a `Zone:Sizing` object (and possibly
no `DesignSpecification:OutdoorAir`) and 2) models with
Args:
zone (Zone): The zone object.
"""
if not zone.idf.idfobjects["Controller:MechanicalVentilation".upper()]:
IsMechVentOn = False
MinFreshAirPerArea = 0 # use defaults
MinFreshAirPerPerson = 0
MechVentSchedule = UmiSchedule.constant_schedule(idf=zone.idf)
else:
design_spe_outdoor_air_name = ""
for object in zone.idf.idfobjects[
"Controller:MechanicalVentilation".upper()
]:
if zone.Name in object.fieldvalues:
indice_zone = [
k for k, s in enumerate(object.fieldvalues) if s == zone.Name
][0]
design_spe_outdoor_air_name = object.fieldvalues[indice_zone + 1]

if object.Availability_Schedule_Name != "":
MechVentSchedule = UmiSchedule(
Name=object.Availability_Schedule_Name, idf=zone.idf
)
else:
MechVentSchedule = UmiSchedule.constant_schedule(idf=zone.idf)
break
# If 'DesignSpecifactionOutdoorAirName', MechVent is ON, and gets
# the minimum fresh air (per person and area)
if design_spe_outdoor_air_name != "":
IsMechVentOn = True
design_spe_outdoor_air = zone.idf.getobject(
"DesignSpecification:OutdoorAir".upper(),
design_spe_outdoor_air_name,
)
MinFreshAirPerPerson = (
design_spe_outdoor_air.Outdoor_Air_Flow_per_Person
)
# If MinFreshAirPerPerson NOT a number, MinFreshAirPerPerson=0
try:
MinFreshAirPerPerson = float(MinFreshAirPerPerson)
except:
MinFreshAirPerPerson = 0
MinFreshAirPerArea = (
design_spe_outdoor_air.Outdoor_Air_Flow_per_Zone_Floor_Area
)
# If MinFreshAirPerArea NOT a number, MinFreshAirPerArea=0
try:
MinFreshAirPerArea = float(MinFreshAirPerArea)
except:
MinFreshAirPerArea = 0
else:
IsMechVentOn = False
MinFreshAirPerPerson = 0
MinFreshAirPerArea = 0
MechVentSchedule = UmiSchedule.constant_schedule(idf=zone.idf)
# For models with ZoneSizes
try:
try:
(
self.IsMechVentOn,
self.MinFreshAirPerArea,
self.MinFreshAirPerPerson,
self.MechVentSchedule,
) = self.fresh_air_from_zone_sizes(zone)
except ValueError:
(
self.IsMechVentOn,
self.MinFreshAirPerArea,
self.MinFreshAirPerPerson,
self.MechVentSchedule,
) = self.fresh_air_from_ideal_loads(zone)
except:
self.IsMechVentOn = False
self.MinFreshAirPerPerson = 0
self.MinFreshAirPerArea = 0
self.MechVentSchedule = UmiSchedule.constant_schedule(idf=zone.idf)

self.IsMechVentOn = IsMechVentOn
self.MinFreshAirPerArea = MinFreshAirPerArea
self.MinFreshAirPerPerson = MinFreshAirPerPerson
self.MechVentSchedule = MechVentSchedule
@staticmethod
def get_equipment_list(zone):
# get zone equipment list
connections = zone._epbunch.getreferingobjs(
iddgroups=["Zone HVAC Equipment Connections"], fields=["Zone_Name"]
)
referenced_object = next(iter(connections)).get_referenced_object(
"Zone_Conditioning_Equipment_List_Name"
)
# EquipmentList can have 18 objects. Filter out the None objects.
return filter(
None,
[
referenced_object.get_referenced_object(f"Zone_Equipment_{i}_Name")
for i in range(1, 19)
],
)

def fresh_air_from_ideal_loads(self, zone):
"""
Args:
zone:
Returns:
4-tuple: (IsMechVentOn, MinFreshAirPerArea, MinFreshAirPerPerson, MechVentSchedule)
"""
equip_list = self.get_equipment_list(zone)
equipment = next(
iter(
[
eq
for eq in equip_list
if eq.key.lower() == "ZoneHVAC:IdealLoadsAirSystem".lower()
]
)
)
oa_spec = equipment.get_referenced_object(
"Design_Specification_Outdoor_Air_Object_Name"
)
oa_area = float(oa_spec.Outdoor_Air_Flow_per_Zone_Floor_Area)
oa_person = float(oa_spec.Outdoor_Air_Flow_per_Person)
mechvent_schedule = self._mechanical_schedule_from_outdoorair_object(
oa_spec, zone
)
return True, oa_area, oa_person, mechvent_schedule

def fresh_air_from_zone_sizes(self, zone):
"""Returns the Mechanical Ventilation from the ZoneSizes Table in the sql db.
Args:
zone:
Returns:
4-tuple: (IsMechVentOn, MinFreshAirPerArea, MinFreshAirPerPerson, MechVentSchedule)
"""
import sqlite3

# create database connection with sqlite3
with sqlite3.connect(zone.idf.sql_file) as conn:
sql_query = f"""
select CalcOutsideAirFlow
from ZoneSizes
where ZoneName = "{zone.Name.upper()}";"""
oa_area = float(max(conn.execute(sql_query)) / zone.area)
designobjs = zone._epbunch.getreferingobjs(
iddgroups=["HVAC Design Objects"]
)
obj = next(iter(eq for eq in designobjs if eq.key.lower() == "sizing:zone"))
oa_spec = obj.get_referenced_object(
"Design_Specification_Outdoor_Air_Object_Name"
)
mechvent_schedule = self._mechanical_schedule_from_outdoorair_object(
oa_spec, zone
)
return True, oa_area, 0, mechvent_schedule

def _mechanical_schedule_from_outdoorair_object(self, oa_spec, zone):
try:
umi_schedule = UmiSchedule(
Name=oa_spec.Outdoor_Air_Schedule_Name, idf=zone.idf
)
log(
f"Mechanical Ventilation Schedule set as {UmiSchedule} for "
f"zone {zone.Name}",
lg.DEBUG,
)
return umi_schedule
except KeyError:
# Schedule is not specified so return a constant schedule
log(
f"No Mechanical Ventilation Schedule specified for zone "
f"{zone.Name}. Reverting to always on."
)
return UmiSchedule.constant_schedule(idf=zone.idf)

def _set_zone_cops(self, zone):
"""
Expand Down Expand Up @@ -487,9 +555,12 @@ def _set_thermostat_setpoints(self, zone):
"""
# Set Thermostat set points
# Heating and Cooling set points and schedules
self.HeatingSetpoint, self.HeatingSchedule, self.CoolingSetpoint, self.CoolingSchedule = self._get_setpoint_and_scheds(
zone
)
(
self.HeatingSetpoint,
self.HeatingSchedule,
self.CoolingSetpoint,
self.CoolingSchedule,
) = self._get_setpoint_and_scheds(zone)

# If HeatingSetpoint == nan, means there is no heat or cold input,
# therefore system is off.
Expand Down Expand Up @@ -569,9 +640,10 @@ def _set_heat_recovery(self, zone):
):
# Do HeatExchanger:AirToAir:SensibleAndLatent

HeatRecoveryEfficiencyLatent, HeatRecoveryEfficiencySensible = self._get_recoverty_effectiveness(
object, zone
)
(
HeatRecoveryEfficiencyLatent,
HeatRecoveryEfficiencySensible,
) = self._get_recoverty_effectiveness(object, zone)
HeatRecoveryType = "Enthalpy"

comment = (
Expand Down Expand Up @@ -630,8 +702,8 @@ def _get_recoverty_effectiveness(object, zone):
]
return HeatRecoveryEfficiencyLatent, HeatRecoveryEfficiencySensible

@classmethod
def _get_design_limits(cls, zone, zone_size, load_name):
@staticmethod
def _get_design_limits(zone, zone_size, load_name):
"""Gets design limits for heating and cooling systems
Args:
Expand All @@ -658,8 +730,8 @@ def _get_design_limits(cls, zone, zone_size, load_name):
LimitType = "NoLimit"
return LimitType, cap, flow

@classmethod
def _get_cop(cls, zone, energy_in_list, energy_out_variable_name):
@staticmethod
def _get_cop(zone, energy_in_list, energy_out_variable_name):
"""Calculates COP for heating or cooling systems
Args:
Expand Down Expand Up @@ -699,7 +771,8 @@ def _get_cop(cls, zone, energy_in_list, energy_out_variable_name):

return cop

def _get_setpoint_and_scheds(self, zone):
@staticmethod
def _get_setpoint_and_scheds(zone):
"""Gets temperature set points from sql EnergyPlus output and associated
schedules.
Expand Down
2 changes: 1 addition & 1 deletion archetypal/template/opaque_construction.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ def heat_capacity_per_unit_wall_area(self):
"""
return sum(
[
layer.Material.Density * layer.Material.SpecificHeat * layer.Thickness
layer.heat_capacity
for layer in self.Layers
]
)
Expand Down
5 changes: 5 additions & 0 deletions archetypal/template/umi_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,11 @@ def u_value(self):
"""
return 1 / self.r_value

@property
def heat_capacity(self):
"""float: The Material Layer's heat capacity J/m2-k"""
return self.Material.Density * self.Material.SpecificHeat * self.Thickness

@property
def specific_heat(self):
"""float: The Material's specific heat J/kg-K"""
Expand Down

0 comments on commit 83fbd04

Please sign in to comment.