Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wrap ZoneHVAC:EvaporativeCoolerUnit #215

Open
wants to merge 11 commits into
base: develop
Choose a base branch
from
7 changes: 6 additions & 1 deletion model/simulationtests/autosize_hvac.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1215,9 +1215,14 @@ def make_tes_coil(model)
term.addToThermalZone(zn)
vrf.addTerminal(term)

when 44
zoneHVACEvaporativeCoolerUnit = OpenStudio::Model::ZoneHVACEvaporativeCoolerUnit.new(model)
zoneHVACEvaporativeCoolerUnit.autosizeDesignSupplyAirFlowRate
zoneHVACEvaporativeCoolerUnit.addToThermalZone(zn)

Comment on lines +1218 to +1222
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added to the autosize_hvac! Which told me the method was not implemented correctly so did NREL/OpenStudio@c969110

when 26, 27, 28, 29, 30, 31, 32, 33, 38, 40, 43
# Previously used for the unitary systems, dehum, etc
when 0, 44
when 0
# This wasn't assigned yet and is for grabs
puts "Nothing added to #{zn.name}, index #{zone_index}"
else
Expand Down
122 changes: 122 additions & 0 deletions model/simulationtests/zone_hvac_evaporative_cooler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
from itertools import product

import openstudio
from lib.baseline_model import BaselineModel

model = BaselineModel()

# make a 8 stories, 100m X 50m, 8 zones building
model.add_geometry(length=100, width=50, num_floors=8, floor_to_floor_height=4, plenum_height=0, perimeter_zone_depth=0)

# add windows at a 40% window-to-wall ratio
model.add_windows(wwr=0.4, offset=1, application_type="Above Floor")

# assign constructions from a local library to the walls/windows/etc. in the model
model.set_constructions()

# set whole building space type; simplified 90.1-2004 Large Office Whole Building
model.set_space_type()

# add design days to the model (Chicago)
model.add_design_days()

# add thermostats
model.add_thermostats(heating_setpoint=19, cooling_setpoint=26)


def make_zone_hvac_cooler(
z: openstudio.model.ThermalZone,
direct_is_first: bool = False,
add_secondary: bool = True,
fan_placement: str = "BlowThrough",
):
model = z.model()
indirect_evap = None
direct_evap = None
if direct_is_first:
# There is a default ctor
zoneHVACEvaporativeCoolerUnit = openstudio.model.ZoneHVACEvaporativeCoolerUnit(model)
direct_evap = zoneHVACEvaporativeCoolerUnit.firstEvaporativeCooler()
supplyAirFan = zoneHVACEvaporativeCoolerUnit.supplyAirFan()

fan_system = openstudio.model.FanSystemModel(model)
zoneHVACEvaporativeCoolerUnit.setSupplyAirFan(fan_system)
supplyAirFan.remove()
supplyAirFan = fan_system
# An optional Second EvaporativeCooler
if add_secondary:
indirect_evap = openstudio.model.EvaporativeCoolerIndirectResearchSpecial(model)
zoneHVACEvaporativeCoolerUnit.setSecondEvaporativeCooler(indirect_evap)
else:
# And an explicit Ctor:
# ZoneHVACEvaporativeCoolerUnit(const Model& model, Schedule& availabilitySchedule, HVACComponent& supplyAirFan, HVACComponent& firstEvaporativeCooler);
supplyAirFan = openstudio.model.FanSystemModel(model)

indirect_evap = openstudio.model.EvaporativeCoolerIndirectResearchSpecial(model)
zoneHVACEvaporativeCoolerUnit = openstudio.model.ZoneHVACEvaporativeCoolerUnit(
model, model.alwaysOnDiscreteSchedule(), supplyAirFan, indirect_evap
)
# An optional Second EvaporativeCooler
if add_secondary:
direct_evap = openstudio.model.EvaporativeCoolerDirectResearchSpecial(
model, model.alwaysOnDiscreteSchedule()
)
zoneHVACEvaporativeCoolerUnit.setSecondEvaporativeCooler(direct_evap)

if indirect_evap is not None:
indirect_evap.setName(f"{z.nameString()} Indirect Evaporative Cooler")
if direct_evap is not None:
direct_evap.setName(f"{z.nameString()} Direct Evaporative Cooler")

supplyAirFan.setName(f"{z.nameString()} Supply Fan")
zoneHVACEvaporativeCoolerUnit.setName(f"{z.nameString()} Evap Unit")
# zoneHVACEvaporativeCoolerUnit.resetSecondEvaporativeCooler()
#
# Redoing what the default constructor does for demonstration purposes
# zoneHVACEvaporativeCoolerUnit.setDesignSupplyAirFlowRate(1.0)
zoneHVACEvaporativeCoolerUnit.autosizeDesignSupplyAirFlowRate()
zoneHVACEvaporativeCoolerUnit.setAvailabilitySchedule(model.alwaysOnDiscreteSchedule())
zoneHVACEvaporativeCoolerUnit.setFanPlacement(fan_placement)
zoneHVACEvaporativeCoolerUnit.setCoolerUnitControlMethod("ZoneCoolingLoadVariableSpeedFan")
zoneHVACEvaporativeCoolerUnit.setThrottlingRangeTemperatureDifference(1.1)
zoneHVACEvaporativeCoolerUnit.setCoolingLoadControlThresholdHeatTransferRate(100.0)
zoneHVACEvaporativeCoolerUnit.setShutOffRelativeHumidity(100.0)

# Add it to a ThermalZone
zoneHVACEvaporativeCoolerUnit.addToThermalZone(z)

# Rename nodes for clarity
z.zoneAirNode().setName(f"{z.nameString()} Zone Air Node")
# z.returnAirModelObjects.modelObjects()[0].setName("{z.nameString()} Zone Return Air Node")
z.inletPortList().modelObjects()[0].setName(f"{z.nameString()} Zone Air Inlet Node")
z.exhaustPortList().modelObjects()[0].setName(f"{z.nameString()} Zone Air Exhaust Node")

return zoneHVACEvaporativeCoolerUnit


# In order to produce more consistent results between different runs,
# we sort the zones by names
zones = sorted(model.getThermalZones(), key=lambda z: z.nameString())

configs = [
{"zone_name": "DirectFirst", "direct_is_first": True, "add_secondary": True},
# Mimic SMStore8 from StripMallZoneEvapCoolerAutosized.idf
# https://github.com/NREL/EnergyPlus/blob/31e3c33467c5873371bf48b12a7318215971c315/testfiles/StripMallZoneEvapCoolerAutosized.idf#L4767-L4784
{"zone_name": "IndirectFirst", "direct_is_first": False, "add_secondary": True},
{"zone_name": "DirectOnly", "direct_is_first": True, "add_secondary": False},
{"zone_name": "IndirectOnly", "direct_is_first": False, "add_secondary": False},
]
fan_placements = ["BlowThrough", "DrawThrough"]
assert len(configs) * len(fan_placements) == len(zones)

for (config, fan_placement), z in zip(product(configs, fan_placements), zones):
z.setName(f"{config['zone_name']} {fan_placement} Zn")
make_zone_hvac_cooler(
z=z,
direct_is_first=config["direct_is_first"],
add_secondary=config["add_secondary"],
fan_placement=fan_placement,
)

# save the OpenStudio model (.osm)
model.save_openstudio_osm(osm_save_directory=None, osm_name="in.osm")
125 changes: 125 additions & 0 deletions model/simulationtests/zone_hvac_evaporative_cooler.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
# frozen_string_literal: true

require 'openstudio'
require_relative 'lib/baseline_model'

model = BaselineModel.new

# make a 8 stories, 100m X 50m, 8 zone building
model.add_geometry({ 'length' => 100,
'width' => 50,
'num_floors' => 8,
'floor_to_floor_height' => 4,
'plenum_height' => 0,
'perimeter_zone_depth' => 0 })
Comment on lines +8 to +14
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because I found the node connections to be broken, I'm using 8 zones...


# add windows at a 40% window-to-wall ratio
model.add_windows({ 'wwr' => 0.4,
'offset' => 1,
'application_type' => 'Above Floor' })

# assign constructions from a local library to the walls/windows/etc. in the model
model.set_constructions

# set whole building space type; simplified 90.1-2004 Large Office Whole Building
model.set_space_type

# add design days to the model (Chicago)
model.add_design_days

# add thermostats
model.add_thermostats({ 'heating_setpoint' => 24,
'cooling_setpoint' => 28 })

def make_zone_hvac_cooler(z, direct_is_first: false, add_secondary: true, fan_placement: 'BlowThrough')
model = z.model
indirect_evap = nil
direct_evap = nil
if direct_is_first
# There is a default ctor
zoneHVACEvaporativeCoolerUnit = OpenStudio::Model::ZoneHVACEvaporativeCoolerUnit.new(model)
direct_evap = zoneHVACEvaporativeCoolerUnit.firstEvaporativeCooler
supplyAirFan = zoneHVACEvaporativeCoolerUnit.supplyAirFan

fan_system = OpenStudio::Model::FanSystemModel.new(model)
zoneHVACEvaporativeCoolerUnit.setSupplyAirFan(fan_system)
supplyAirFan.remove
supplyAirFan = fan_system
# An optional Second EvaporativeCooler
if add_secondary
indirect_evap = OpenStudio::Model::EvaporativeCoolerIndirectResearchSpecial.new(model)
zoneHVACEvaporativeCoolerUnit.setSecondEvaporativeCooler(indirect_evap)
end
else
# And an explicit Ctor:
# ZoneHVACEvaporativeCoolerUnit(const Model& model, Schedule& availabilitySchedule, HVACComponent& supplyAirFan, HVACComponent& firstEvaporativeCooler);
supplyAirFan = OpenStudio::Model::FanSystemModel.new(model)

indirect_evap = OpenStudio::Model::EvaporativeCoolerIndirectResearchSpecial.new(model)
zoneHVACEvaporativeCoolerUnit = OpenStudio::Model::ZoneHVACEvaporativeCoolerUnit.new(
model, model.alwaysOnDiscreteSchedule, supplyAirFan, indirect_evap
)
# An optional Second EvaporativeCooler
if add_secondary
direct_evap = OpenStudio::Model::EvaporativeCoolerDirectResearchSpecial.new(model, model.alwaysOnDiscreteSchedule)
zoneHVACEvaporativeCoolerUnit.setSecondEvaporativeCooler(direct_evap)
end
end
indirect_evap&.setName("#{z.nameString} Indirect Evaporative Cooler")
direct_evap&.setName("#{z.nameString} Direct Evaporative Cooler")
supplyAirFan.setName("#{z.nameString} Supply Fan")
zoneHVACEvaporativeCoolerUnit.setName("#{z.nameString} Evap Unit")
# zoneHVACEvaporativeCoolerUnit.resetSecondEvaporativeCooler
#
# Redoing what the default constructor does for demonstration purposes
# zoneHVACEvaporativeCoolerUnit.setDesignSupplyAirFlowRate(1.0)
zoneHVACEvaporativeCoolerUnit.autosizeDesignSupplyAirFlowRate
zoneHVACEvaporativeCoolerUnit.setAvailabilitySchedule(model.alwaysOnDiscreteSchedule)
zoneHVACEvaporativeCoolerUnit.setSupplyAirFan(zoneHVACEvaporativeCoolerUnit.supplyAirFan)
zoneHVACEvaporativeCoolerUnit.setFanPlacement(fan_placement)
zoneHVACEvaporativeCoolerUnit.setCoolerUnitControlMethod('ZoneCoolingLoadVariableSpeedFan')
zoneHVACEvaporativeCoolerUnit.setThrottlingRangeTemperatureDifference(1.1)
zoneHVACEvaporativeCoolerUnit.setCoolingLoadControlThresholdHeatTransferRate(100.0)
zoneHVACEvaporativeCoolerUnit.setShutOffRelativeHumidity(100.0)

# Add it to a ThermalZone
zoneHVACEvaporativeCoolerUnit.addToThermalZone(z)

# Rename nodes for clarity
z.zoneAirNode.setName("#{z.nameString} Zone Air Node")
# z.returnAirModelObjects.modelObjects[0].setName("#{z.nameString} Zone Return Air Node")
z.inletPortList.modelObjects[0].setName("#{z.nameString} Zone Air Inlet Node")
z.exhaustPortList.modelObjects[0].setName("#{z.nameString} Zone Air Exhaust Node")
end
Comment on lines +34 to +93
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Helper to create a specific configuration


# In order to produce more consistent results between different runs,
# we sort the zones by names
zones = model.getThermalZones.sort_by { |z| z.name.to_s }

configs = [
{ zone_name: 'DirectFirst', direct_is_first: true, add_secondary: true },

# Mimic SMStore8 from StripMallZoneEvapCoolerAutosized.idf
# https://github.com/NREL/EnergyPlus/blob/31e3c33467c5873371bf48b12a7318215971c315/testfiles/StripMallZoneEvapCoolerAutosized.idf#L4767-L4784
{ zone_name: 'IndirectFirst', direct_is_first: false, add_secondary: true },

{ zone_name: 'DirectOnly', direct_is_first: true, add_secondary: false },

{ zone_name: 'IndirectOnly', direct_is_first: false, add_secondary: false }
]
fan_placements = ['BlowThrough', 'DrawThrough']
raise 'Mismatch' unless configs.size * fan_placements.size == zones.size

configs.product(fan_placements).zip(zones).each do |(config, fan_placement), z|
z.setName("#{config[:zone_name]} #{fan_placement} Zn")
make_zone_hvac_cooler(
z,
direct_is_first: config[:direct_is_first],
add_secondary: config[:add_secondary],
fan_placement: fan_placement
)
end
Comment on lines +95 to +121
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Testing 8 combinations: for each of these four, I test BlowThrough + DrawThrough

  • DirectOnly
  • IndirectOnly
  • Direct then Indirect
  • Indirect then Direct


# save the OpenStudio model (.osm)
model.save_openstudio_osm({ 'osm_save_directory' => Dir.pwd,
'osm_name' => 'in.osm' })
13 changes: 13 additions & 0 deletions model_tests.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2147,6 +2147,19 @@ def test_zone_hvac_cooling_panel_osm
result = sim_test('zone_hvac_cooling_panel.osm')
end

def test_zone_hvac_evaporative_cooler_rb
result = sim_test('zone_hvac_evaporative_cooler.rb')
end

def test_zone_hvac_evaporative_cooler_py
result = sim_test('zone_hvac_evaporative_cooler.py')
end

# TODO: To be added in the next official release after: 3.9.0
# def test_zone_hvac_evaporative_cooler_osm
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MISSING the # TODO: To be added in the next official release after: 3.9.0

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

# result = sim_test('zone_hvac_evaporative_cooler.osm')
# end

def test_zone_hvac_equipment_list_rb
result = sim_test('zone_hvac_equipment_list.rb')
end
Expand Down
Loading