# ******************************************************************************* # OpenStudio(R), Copyright (c) 2008-2021, Alliance for Sustainable Energy, LLC. # All rights reserved. # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # (1) Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # # (2) Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # # (3) Neither the name of the copyright holder nor the names of any contributors # may be used to endorse or promote products derived from this software without # specific prior written permission from the respective party. # # (4) Other than as required in clauses (1) and (2), distributions in any form # of modifications or other derivative works may not use the "OpenStudio" # trademark, "OS", "os", or any other confusingly similar designation without # specific prior written permission from Alliance for Sustainable Energy, LLC. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND ANY CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S), ANY CONTRIBUTORS, THE # UNITED STATES GOVERNMENT, OR THE UNITED STATES DEPARTMENT OF ENERGY, NOR ANY OF # THEIR EMPLOYEES, BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT # OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # ******************************************************************************* module OsLib_HVAC_zedg_fan_coil_air_cooled # do something def self.doSomething(input) # do something output = input result = output return result end # validate and make plenum zones def self.validateAndAddPlenumZonesToSystem(model, runner, options = {}) # set defaults to use if user inputs not passed in defaults = { 'zonesPlenum' => nil, 'zonesPrimary' => nil, 'type' => 'ceilingReturn' } # merge user inputs with defaults options = defaults.merge(options) # array of valid ceiling plenums zoneSurfaceHash = {} zonePlenumHash = {} if options['zonesPlenum'].nil? runner.registerWarning('No plenum zones were passed in, validateAndAddPlenumZonesToSystem will not alter the model.') else options['zonesPlenum'].each do |zone| # get spaces in zone spaces = zone.spaces # get adjacent spaces spaces.each do |space| # get surfaces surfaces = space.surfaces # loop through surfaces looking for floors with surface boundary condition, grab zone that surface's parent space is in. surfaces.each do |surface| if (surface.outsideBoundaryCondition == 'Surface') && (surface.surfaceType == 'Floor') next unless surface.adjacentSurface.is_initialized adjacentSurface = surface.adjacentSurface.get next unless adjacentSurface.space.is_initialized adjacentSurfaceSpace = adjacentSurface.space.get next unless adjacentSurfaceSpace.thermalZone.is_initialized adjacentSurfaceSpaceZone = adjacentSurfaceSpace.thermalZone.get if options['zonesPrimary'].include? adjacentSurfaceSpaceZone if zoneSurfaceHash[adjacentSurfaceSpaceZone].nil? || (surface.grossArea > zoneSurfaceHash[adjacentSurfaceSpaceZone]) adjacentSurfaceSpaceZone.setReturnPlenum(zone) zoneSurfaceHash[adjacentSurfaceSpaceZone] = surface.grossArea zonePlenumHash[adjacentSurfaceSpaceZone] = zone end end end end end end end # report out results of zone-plenum hash zonePlenumHash.each do |zone, plenum| runner.registerInfo("#{plenum.name} has been set as a return air plenum for #{zone.name}.") end # pass back zone-plenum hash result = zonePlenumHash return result end def self.sortZones(model, runner, options = {}) # set defaults to use if user inputs not passed in defaults = { 'standardBuildingTypeTest' => nil, # not used for now 'secondarySpaceTypeTest' => nil, 'ceilingReturnPlenumSpaceType' => nil } # merge user inputs with defaults options = defaults.merge(options) # set up zone type arrays zonesPrimary = [] zonesSecondary = [] zonesPlenum = [] zonesUnconditioned = [] # get thermal zones zones = model.getThermalZones zones.each do |zone| # assign appropriate zones to zonesPlenum or zonesUnconditioned (those that don't have thermostats or zone HVAC equipment) # if not conditioned then add to zonesPlenum or zonesUnconditioned if zone.thermostatSetpointDualSetpoint.is_initialized || !zone.equipment.empty? # zone is conditioned. check if its space type is secondary or primary spaces = zone.spaces spaces.each do |space| # if a zone has already been assigned as secondary, skip next if zonesSecondary.include? zone # get space type if it exists next unless space.spaceType.is_initialized spaceType = space.spaceType.get # get standards information # for now skip standardsBuildingType and just rely on the standardsSpaceType. Seems like enough. next unless spaceType.standardsSpaceType.is_initialized standardSpaceType = spaceType.standardsSpaceType.get # test space type against secondary space type array # if any space type in zone is secondary, assign zone as secondary if options['secondarySpaceTypeTest'].include? standardSpaceType zonesSecondary << zone end end # if zone not assigned as secondary, assign as primary unless zonesSecondary.include? zone zonesPrimary << zone end else # determine if zone is a plenum zone or general unconditioned zone # assume it is a plenum if it has at least one planum space zone.spaces.each do |space| # if a zone has already been assigned as a plenum, skip next if zonesPlenum.include? zone # if zone not assigned as a plenum, get space type if it exists # compare to plenum space type if it has been assigned if space.spaceType.is_initialized && (options['ceilingReturnPlenumSpaceType'].nil? == false) spaceType = space.spaceType.get if spaceType == options['ceilingReturnPlenumSpaceType'] zonesPlenum << zone # zone has a plenum space; assign it as a plenum end end end # if zone not assigned as a plenum, assign it as unconditioned unless zonesPlenum.include? zone zonesUnconditioned << zone end end end zonesSorted = { 'zonesPrimary' => zonesPrimary, 'zonesSecondary' => zonesSecondary, 'zonesPlenum' => zonesPlenum, 'zonesUnconditioned' => zonesUnconditioned } # pass back zonesSorted hash result = zonesSorted return result end def self.reportConditions(model, runner, condition, extra_string = '') airloops = model.getAirLoopHVACs plantLoops = model.getPlantLoops zones = model.getThermalZones # count up zone equipment (not counting zone exhaust fans) zoneHasEquip = false zonesWithEquipCounter = 0 zones.each do |zone| if !zone.equipment.empty? zone.equipment.each do |equip| unless equip.to_FanZoneExhaust.is_initialized zonesWithEquipCounter += 1 break end end end end if condition == 'initial' runner.registerInitialCondition("The building started with #{airloops.size} air loops and #{plantLoops.size} plant loops. #{zonesWithEquipCounter} zones were conditioned with zone equipment.") elsif condition == 'final' runner.registerFinalCondition("The building finished with #{airloops.size} air loops and #{plantLoops.size} plant loops. #{zonesWithEquipCounter} zones are conditioned with zone equipment. #{extra_string}") end end def self.removeEquipment(model, runner) airloops = model.getAirLoopHVACs plantLoops = model.getPlantLoops zones = model.getThermalZones # remove all airloops airloops.each(&:remove) # remove all zone equipment except zone exhaust fans zones.each do |zone| zone.equipment.each do |equip| if equip.to_FanZoneExhaust.is_initialized else equip.remove end end end # remove plant loops plantLoops.each do |plantLoop| # get the demand components and see if water use connection, then save it # notify user with info statement if supply side of plant loop had heat exchanger for refrigeration usedForSHWorRefrigeration = false plantLoop.demandComponents.each do |comp| # AP code to check your comments above if comp.to_WaterUseConnections.is_initialized || comp.to_CoilWaterHeatingDesuperheater.is_initialized usedForSHWorRefrigeration = true end end if usedForSHWorRefrigeration == false plantLoop.remove else runner.registerWarning("#{plantLoop.name} is used for SHW or refrigeration heat reclaim. Loop will not be deleted") end end end def self.assignHVACSchedules(model, runner, options = {}) schedulesHVAC = {} airloops = model.getAirLoopHVACs # find airloop with most primary spaces max_primary_spaces = 0 representative_airloop = false building_HVAC_schedule = false building_ventilation_schedule = false unless options['remake_schedules'] # if remake schedules not selected, get relevant schedules from model if they exist airloops.each do |air_loop| primary_spaces = 0 air_loop.thermalZones.each do |thermal_zone| thermal_zone.spaces.each do |space| if space.spaceType.is_initialized if space.spaceType.get.name.is_initialized if space.spaceType.get.name.get.include? options['primarySpaceType'] primary_spaces += 1 end end end end end if primary_spaces > max_primary_spaces max_primary_spaces = primary_spaces representative_airloop = air_loop end end end if representative_airloop building_HVAC_schedule = representative_airloop.availabilitySchedule if representative_airloop.airLoopHVACOutdoorAirSystem.is_initialized building_ventilation_schedule_optional = representative_airloop.airLoopHVACOutdoorAirSystem.get.getControllerOutdoorAir.maximumFractionofOutdoorAirSchedule if building_ventilation_schedule_optional.is_initialized building_ventilation_schedule = building_ventilation_schedule.get end end end # build new airloop schedules if existing model doesn't have them if options['primarySpaceType'] == 'Classroom' # ventilation schedule unless building_ventilation_schedule ruleset_name = 'AEDG K-12 Ventilation Schedule' winter_design_day = [[24, 1]] summer_design_day = [[24, 1]] default_day = ['Weekday', [6, 0], [18, 1], [24, 0]] rules = [] rules << ['Weekend', '1/1-12/31', 'Sat/Sun', [24, 0]] rules << ['Summer Weekday', '7/1-8/31', 'Mon/Tue/Wed/Thu/Fri', [8, 0], [13, 1], [24, 0]] options_ventilation = { 'name' => ruleset_name, 'winter_design_day' => winter_design_day, 'summer_design_day' => summer_design_day, 'default_day' => default_day, 'rules' => rules } building_ventilation_schedule = OsLib_Schedules.createComplexSchedule(model, options_ventilation) end # HVAC availability schedule unless building_HVAC_schedule ruleset_name = 'AEDG K-12 HVAC Availability Schedule' winter_design_day = [[24, 1]] summer_design_day = [[24, 1]] default_day = ['Weekday', [6, 0], [18, 1], [24, 0]] rules = [] rules << ['Weekend', '1/1-12/31', 'Sat/Sun', [24, 0]] rules << ['Summer Weekday', '7/1-8/31', 'Mon/Tue/Wed/Thu/Fri', [8, 0], [13, 1], [24, 0]] options_hvac = { 'name' => ruleset_name, 'winter_design_day' => winter_design_day, 'summer_design_day' => summer_design_day, 'default_day' => default_day, 'rules' => rules } building_HVAC_schedule = OsLib_Schedules.createComplexSchedule(model, options_hvac) end elsif options['primarySpaceType'] == 'Office' # ventilation schedule unless building_ventilation_schedule ruleset_name = 'AEDG Office Ventilation Schedule' winter_design_day = [[24, 1]] # ML These are not always on in PNNL model summer_design_day = [[24, 1]] # ML These are not always on in PNNL model default_day = ['Weekday', [7, 0], [22, 1], [24, 0]] # ML PNNL has a one hour ventilation offset rules = [] rules << ['Saturday', '1/1-12/31', 'Sat', [7, 0], [18, 1], [24, 0]] # ML PNNL has a one hour ventilation offset rules << ['Sunday', '1/1-12/31', 'Sun', [24, 0]] options_ventilation = { 'name' => ruleset_name, 'winter_design_day' => winter_design_day, 'summer_design_day' => summer_design_day, 'default_day' => default_day, 'rules' => rules } building_ventilation_schedule = OsLib_Schedules.createComplexSchedule(model, options_ventilation) end # HVAC availability schedule unless building_HVAC_schedule ruleset_name = 'AEDG Office HVAC Availability Schedule' winter_design_day = [[24, 1]] # ML These are not always on in PNNL model summer_design_day = [[24, 1]] # ML These are not always on in PNNL model default_day = ['Weekday', [6, 0], [22, 1], [24, 0]] # ML PNNL has a one hour ventilation offset rules = [] rules << ['Saturday', '1/1-12/31', 'Sat', [6, 0], [18, 1], [24, 0]] # ML PNNL has a one hour ventilation offset rules << ['Sunday', '1/1-12/31', 'Sun', [24, 0]] options_hvac = { 'name' => ruleset_name, 'winter_design_day' => winter_design_day, 'summer_design_day' => summer_design_day, 'default_day' => default_day, 'rules' => rules } building_HVAC_schedule = OsLib_Schedules.createComplexSchedule(model, options_hvac) end # special loops for radiant system (different temperature setpoints) if options['allHVAC']['zone'] == 'Radiant' # create hot water schedule for radiant heating loop schedulesHVAC['radiant_hot_water'] = OsLib_Schedules.createComplexSchedule(model, 'name' => 'AEDG HW-Radiant-Loop-Temp-Schedule', 'default_day' => ['All Days', [24, 45.0]]) # create hot water schedule for radiant cooling loop schedulesHVAC['radiant_chilled_water'] = OsLib_Schedules.createComplexSchedule(model, 'name' => 'AEDG CW-Radiant-Loop-Temp-Schedule', 'default_day' => ['All Days', [24, 15.0]]) # create mean radiant heating and cooling setpoint schedules # ML ideally, should grab schedules tied to zone thermostat and make modified versions that follow the setback pattern # for now, create new ones that match the recommended HVAC schedule # mean radiant heating setpoint schedule (PNNL values) ruleset_name = 'AEDG Office Mean Radiant Heating Setpoint Schedule' winter_design_day = [[24, 18.8]] summer_design_day = [[6, 18.3], [22, 18.8], [24, 18.3]] default_day = ['Weekday', [6, 18.3], [22, 18.8], [24, 18.3]] rules = [] rules << ['Saturday', '1/1-12/31', 'Sat', [6, 18.3], [18, 18.8], [24, 18.3]] rules << ['Sunday', '1/1-12/31', 'Sun', [24, 18.3]] options_radiant_heating = { 'name' => ruleset_name, 'winter_design_day' => winter_design_day, 'summer_design_day' => summer_design_day, 'default_day' => default_day, 'rules' => rules } mean_radiant_heating_schedule = OsLib_Schedules.createComplexSchedule(model, options_radiant_heating) schedulesHVAC['mean_radiant_heating'] = mean_radiant_heating_schedule # mean radiant cooling setpoint schedule (PNNL values) ruleset_name = 'AEDG Office Mean Radiant Cooling Setpoint Schedule' winter_design_day = [[6, 26.7], [22, 24.0], [24, 26.7]] summer_design_day = [[24, 24.0]] default_day = ['Weekday', [6, 26.7], [22, 24.0], [24, 26.7]] rules = [] rules << ['Saturday', '1/1-12/31', 'Sat', [6, 26.7], [18, 24.0], [24, 26.7]] rules << ['Sunday', '1/1-12/31', 'Sun', [24, 26.7]] options_radiant_cooling = { 'name' => ruleset_name, 'winter_design_day' => winter_design_day, 'summer_design_day' => summer_design_day, 'default_day' => default_day, 'rules' => rules } mean_radiant_cooling_schedule = OsLib_Schedules.createComplexSchedule(model, options_radiant_cooling) schedulesHVAC['mean_radiant_cooling'] = mean_radiant_cooling_schedule end end # SAT schedule if options['allHVAC']['primary']['doas'] # primary airloop is DOAS schedulesHVAC['primary_sat'] = sch_ruleset_DOAS_setpoint = OsLib_Schedules.createComplexSchedule(model, 'name' => 'AEDG DOAS Temperature Setpoint Schedule', 'default_day' => ['All Days', [24, 20.0]]) else # primary airloop is multizone VAV that cools schedulesHVAC['primary_sat'] = sch_ruleset_DOAS_setpoint = OsLib_Schedules.createComplexSchedule(model, 'name' => 'AEDG Cold Deck Temperature Setpoint Schedule', 'default_day' => ['All Days', [24, 12.8]]) end schedulesHVAC['ventilation'] = building_ventilation_schedule schedulesHVAC['hvac'] = building_HVAC_schedule # build new plant schedules as needed zoneHVACHotWaterPlant = ['FanCoil', 'DualDuct', 'Baseboard'] # dual duct has fan coil and baseboard zoneHVACChilledWaterPlant = ['FanCoil', 'DualDuct'] # dual duct has fan coil # hot water if (options['allHVAC']['primary']['heat'] == 'Water') || (options['allHVAC']['secondary']['heat'] == 'Water') || zoneHVACHotWaterPlant.include?(options['allHVAC']['zone']) schedulesHVAC['hot_water'] = OsLib_Schedules.createComplexSchedule(model, 'name' => 'AEDG HW-Loop-Temp-Schedule', 'default_day' => ['All Days', [24, 67.0]]) end # chilled water if (options['allHVAC']['primary']['cool'] == 'Water') || (options['allHVAC']['secondary']['cool'] == 'Water') || zoneHVACChilledWaterPlant.include?(options['allHVAC']['zone']) schedulesHVAC['chilled_water'] = OsLib_Schedules.createComplexSchedule(model, 'name' => 'AEDG CW-Loop-Temp-Schedule', 'default_day' => ['All Days', [24, 6.7]]) end # heat pump condenser loop schedules if options['allHVAC']['zone'] == 'GSHP' # there will be a heat pump condenser loop # loop setpoint schedule schedulesHVAC['hp_loop'] = OsLib_Schedules.createComplexSchedule(model, 'name' => 'AEDG HP-Loop-Temp-Schedule', 'default_day' => ['All Days', [24, 21]]) # cooling component schedule (#ML won't need this if a ground loop is actually modeled) schedulesHVAC['hp_loop_cooling'] = OsLib_Schedules.createComplexSchedule(model, 'name' => 'AEDG HP-Loop-Clg-Temp-Schedule', 'default_day' => ['All Days', [24, 21]]) # heating component schedule schedulesHVAC['hp_loop_heating'] = OsLib_Schedules.createComplexSchedule(model, 'name' => 'AEDG HP-Loop-Htg-Temp-Schedule', 'default_day' => ['All Days', [24, 5]]) end if options['allHVAC']['zone'] == 'WSHP' # there will be a heat pump condenser loop # loop setpoint schedule schedulesHVAC['hp_loop'] = OsLib_Schedules.createComplexSchedule(model, 'name' => 'AEDG HP-Loop-Temp-Schedule', 'default_day' => ['All Days', [24, 30]]) # PNNL # cooling component schedule (#ML won't need this if a ground loop is actually modeled) schedulesHVAC['hp_loop_cooling'] = OsLib_Schedules.createComplexSchedule(model, 'name' => 'AEDG HP-Loop-Clg-Temp-Schedule', 'default_day' => ['All Days', [24, 30]]) # PNNL # heating component schedule schedulesHVAC['hp_loop_heating'] = OsLib_Schedules.createComplexSchedule(model, 'name' => 'AEDG HP-Loop-Htg-Temp-Schedule', 'default_day' => ['All Days', [24, 20]]) # PNNL end # pass back schedulesHVAC hash result = schedulesHVAC return result end def self.createHotWaterPlant(model, runner, hot_water_setpoint_schedule, loop_type) hot_water_plant = OpenStudio::Model::PlantLoop.new(model) hot_water_plant.setName("AEDG #{loop_type} Loop") hot_water_plant.setMaximumLoopTemperature(100) hot_water_plant.setMinimumLoopTemperature(10) loop_sizing = hot_water_plant.sizingPlant loop_sizing.setLoopType('Heating') if loop_type == 'Hot Water' loop_sizing.setDesignLoopExitTemperature(60) # changed from 82C to 60C elsif loop_type == 'Radiant Hot Water' loop_sizing.setDesignLoopExitTemperature(60) # ML follows convention of sizing temp being larger than supplu temp end loop_sizing.setLoopDesignTemperatureDifference(16.67) # changed from 11K to 16.67K # create a pump pump = OpenStudio::Model::PumpVariableSpeed.new(model) pump.setRatedPumpHead(119563) # Pa pump.setMotorEfficiency(0.92) # changed from 0.9 to 0.92 pump.setCoefficient1ofthePartLoadPerformanceCurve(0) pump.setCoefficient2ofthePartLoadPerformanceCurve(0.0216) pump.setCoefficient3ofthePartLoadPerformanceCurve(-0.0325) pump.setCoefficient4ofthePartLoadPerformanceCurve(1.0095) # create a boiler boiler = OpenStudio::Model::BoilerHotWater.new(model) boiler.setNominalThermalEfficiency(0.9) boiler.setBoilerFlowMode('LeavingSetpointModulated') # create a scheduled setpoint manager setpoint_manager_scheduled = OpenStudio::Model::SetpointManagerScheduled.new(model, hot_water_setpoint_schedule) # create a supply bypass pipe pipe_supply_bypass = OpenStudio::Model::PipeAdiabatic.new(model) # create a supply outlet pipe pipe_supply_outlet = OpenStudio::Model::PipeAdiabatic.new(model) # create a demand bypass pipe pipe_demand_bypass = OpenStudio::Model::PipeAdiabatic.new(model) # create a demand inlet pipe pipe_demand_inlet = OpenStudio::Model::PipeAdiabatic.new(model) # create a demand outlet pipe pipe_demand_outlet = OpenStudio::Model::PipeAdiabatic.new(model) # connect components to plant loop # supply side components hot_water_plant.addSupplyBranchForComponent(boiler) hot_water_plant.addSupplyBranchForComponent(pipe_supply_bypass) pump.addToNode(hot_water_plant.supplyInletNode) pipe_supply_outlet.addToNode(hot_water_plant.supplyOutletNode) setpoint_manager_scheduled.addToNode(hot_water_plant.supplyOutletNode) # demand side components (water coils are added as they are added to airloops and zoneHVAC) hot_water_plant.addDemandBranchForComponent(pipe_demand_bypass) pipe_demand_inlet.addToNode(hot_water_plant.demandInletNode) pipe_demand_outlet.addToNode(hot_water_plant.demandOutletNode) # pass back hot water plant result = hot_water_plant return result end def self.createChilledWaterPlant(model, runner, chilled_water_setpoint_schedule, loop_type, chillerType) # chilled water plant chilled_water_plant = OpenStudio::Model::PlantLoop.new(model) chilled_water_plant.setName("AEDG #{loop_type} Loop") chilled_water_plant.setMaximumLoopTemperature(98) chilled_water_plant.setMinimumLoopTemperature(1) loop_sizing = chilled_water_plant.sizingPlant loop_sizing.setLoopType('Cooling') if loop_type == 'Chilled Water' loop_sizing.setDesignLoopExitTemperature(6.7) elsif loop_type == 'Radiant Chilled Water' loop_sizing.setDesignLoopExitTemperature(15) end loop_sizing.setLoopDesignTemperatureDifference(6.7) # create a pump pump = OpenStudio::Model::PumpVariableSpeed.new(model) pump.setRatedPumpHead(149453) # Pa pump.setMotorEfficiency(0.9) pump.setCoefficient1ofthePartLoadPerformanceCurve(0) pump.setCoefficient2ofthePartLoadPerformanceCurve(0.0216) pump.setCoefficient3ofthePartLoadPerformanceCurve(-0.0325) pump.setCoefficient4ofthePartLoadPerformanceCurve(1.0095) # create a chiller if chillerType == 'WaterCooled' # create clgCapFuncTempCurve clgCapFuncTempCurve = OpenStudio::Model::CurveBiquadratic.new(model) clgCapFuncTempCurve.setCoefficient1Constant(1.07E+00) clgCapFuncTempCurve.setCoefficient2x(4.29E-02) clgCapFuncTempCurve.setCoefficient3xPOW2(4.17E-04) clgCapFuncTempCurve.setCoefficient4y(-8.10E-03) clgCapFuncTempCurve.setCoefficient5yPOW2(-4.02E-05) clgCapFuncTempCurve.setCoefficient6xTIMESY(-3.86E-04) clgCapFuncTempCurve.setMinimumValueofx(0) clgCapFuncTempCurve.setMaximumValueofx(20) clgCapFuncTempCurve.setMinimumValueofy(0) clgCapFuncTempCurve.setMaximumValueofy(50) # create eirFuncTempCurve eirFuncTempCurve = OpenStudio::Model::CurveBiquadratic.new(model) eirFuncTempCurve.setCoefficient1Constant(4.68E-01) eirFuncTempCurve.setCoefficient2x(-1.38E-02) eirFuncTempCurve.setCoefficient3xPOW2(6.98E-04) eirFuncTempCurve.setCoefficient4y(1.09E-02) eirFuncTempCurve.setCoefficient5yPOW2(4.62E-04) eirFuncTempCurve.setCoefficient6xTIMESY(-6.82E-04) eirFuncTempCurve.setMinimumValueofx(0) eirFuncTempCurve.setMaximumValueofx(20) eirFuncTempCurve.setMinimumValueofy(0) eirFuncTempCurve.setMaximumValueofy(50) # create eirFuncPlrCurve eirFuncPlrCurve = OpenStudio::Model::CurveQuadratic.new(model) eirFuncPlrCurve.setCoefficient1Constant(1.41E-01) eirFuncPlrCurve.setCoefficient2x(6.55E-01) eirFuncPlrCurve.setCoefficient3xPOW2(2.03E-01) eirFuncPlrCurve.setMinimumValueofx(0) eirFuncPlrCurve.setMaximumValueofx(1.2) # construct chiller chiller = OpenStudio::Model::ChillerElectricEIR.new(model, clgCapFuncTempCurve, eirFuncTempCurve, eirFuncPlrCurve) chiller.setReferenceCOP(6.1) chiller.setCondenserType('WaterCooled') chiller.setChillerFlowMode('LeavingSetpointModulated') elsif chillerType == 'AirCooled' # create clgCapFuncTempCurve clgCapFuncTempCurve = OpenStudio::Model::CurveBiquadratic.new(model) clgCapFuncTempCurve.setCoefficient1Constant(1.05E+00) clgCapFuncTempCurve.setCoefficient2x(3.36E-02) clgCapFuncTempCurve.setCoefficient3xPOW2(2.15E-04) clgCapFuncTempCurve.setCoefficient4y(-5.18E-03) clgCapFuncTempCurve.setCoefficient5yPOW2(-4.42E-05) clgCapFuncTempCurve.setCoefficient6xTIMESY(-2.15E-04) clgCapFuncTempCurve.setMinimumValueofx(0) clgCapFuncTempCurve.setMaximumValueofx(20) clgCapFuncTempCurve.setMinimumValueofy(0) clgCapFuncTempCurve.setMaximumValueofy(50) # create eirFuncTempCurve eirFuncTempCurve = OpenStudio::Model::CurveBiquadratic.new(model) eirFuncTempCurve.setCoefficient1Constant(5.83E-01) eirFuncTempCurve.setCoefficient2x(-4.04E-03) eirFuncTempCurve.setCoefficient3xPOW2(4.68E-04) eirFuncTempCurve.setCoefficient4y(-2.24E-04) eirFuncTempCurve.setCoefficient5yPOW2(4.81E-04) eirFuncTempCurve.setCoefficient6xTIMESY(-6.82E-04) eirFuncTempCurve.setMinimumValueofx(0) eirFuncTempCurve.setMaximumValueofx(20) eirFuncTempCurve.setMinimumValueofy(0) eirFuncTempCurve.setMaximumValueofy(50) # create eirFuncPlrCurve eirFuncPlrCurve = OpenStudio::Model::CurveQuadratic.new(model) eirFuncPlrCurve.setCoefficient1Constant(4.19E-02) eirFuncPlrCurve.setCoefficient2x(6.25E-01) eirFuncPlrCurve.setCoefficient3xPOW2(3.23E-01) eirFuncPlrCurve.setMinimumValueofx(0) eirFuncPlrCurve.setMaximumValueofx(1.2) # construct chiller chiller = OpenStudio::Model::ChillerElectricEIR.new(model, clgCapFuncTempCurve, eirFuncTempCurve, eirFuncPlrCurve) chiller.setReferenceCOP(2.85) chiller.setCondenserType('AirCooled') chiller.setChillerFlowMode('ConstantFlow') end # create a scheduled setpoint manager setpoint_manager_scheduled = OpenStudio::Model::SetpointManagerScheduled.new(model, chilled_water_setpoint_schedule) # create a supply bypass pipe pipe_supply_bypass = OpenStudio::Model::PipeAdiabatic.new(model) # create a supply outlet pipe pipe_supply_outlet = OpenStudio::Model::PipeAdiabatic.new(model) # create a demand bypass pipe pipe_demand_bypass = OpenStudio::Model::PipeAdiabatic.new(model) # create a demand inlet pipe pipe_demand_inlet = OpenStudio::Model::PipeAdiabatic.new(model) # create a demand outlet pipe pipe_demand_outlet = OpenStudio::Model::PipeAdiabatic.new(model) # connect components to plant loop # supply side components chilled_water_plant.addSupplyBranchForComponent(chiller) chilled_water_plant.addSupplyBranchForComponent(pipe_supply_bypass) pump.addToNode(chilled_water_plant.supplyInletNode) pipe_supply_outlet.addToNode(chilled_water_plant.supplyOutletNode) setpoint_manager_scheduled.addToNode(chilled_water_plant.supplyOutletNode) # demand side components (water coils are added as they are added to airloops and ZoneHVAC) chilled_water_plant.addDemandBranchForComponent(pipe_demand_bypass) pipe_demand_inlet.addToNode(chilled_water_plant.demandInletNode) pipe_demand_outlet.addToNode(chilled_water_plant.demandOutletNode) # pass back chilled water plant result = chilled_water_plant return result end def self.createCondenserLoop(model, runner, options) condenserLoops = {} # check for water-cooled chillers waterCooledChiller = false model.getChillerElectricEIRs.each do |chiller| next if waterCooledChiller == true if chiller.condenserType == 'WaterCooled' waterCooledChiller = true end end # create condenser loop for water-cooled chillers if waterCooledChiller # create condenser loop for water-cooled chiller(s) condenser_loop = OpenStudio::Model::PlantLoop.new(model) condenser_loop.setName('AEDG Condenser Loop') condenser_loop.setMaximumLoopTemperature(80) condenser_loop.setMinimumLoopTemperature(5) loop_sizing = condenser_loop.sizingPlant loop_sizing.setLoopType('Condenser') loop_sizing.setDesignLoopExitTemperature(29.4) loop_sizing.setLoopDesignTemperatureDifference(5.6) # create a pump pump = OpenStudio::Model::PumpVariableSpeed.new(model) pump.setRatedPumpHead(134508) # Pa pump.setMotorEfficiency(0.9) pump.setCoefficient1ofthePartLoadPerformanceCurve(0) pump.setCoefficient2ofthePartLoadPerformanceCurve(0.0216) pump.setCoefficient3ofthePartLoadPerformanceCurve(-0.0325) pump.setCoefficient4ofthePartLoadPerformanceCurve(1.0095) # create a cooling tower tower = OpenStudio::Model::CoolingTowerVariableSpeed.new(model) # create a supply bypass pipe pipe_supply_bypass = OpenStudio::Model::PipeAdiabatic.new(model) # create a supply outlet pipe pipe_supply_outlet = OpenStudio::Model::PipeAdiabatic.new(model) # create a demand bypass pipe pipe_demand_bypass = OpenStudio::Model::PipeAdiabatic.new(model) # create a demand inlet pipe pipe_demand_inlet = OpenStudio::Model::PipeAdiabatic.new(model) # create a demand outlet pipe pipe_demand_outlet = OpenStudio::Model::PipeAdiabatic.new(model) # create a setpoint manager setpoint_manager_follow_oa = OpenStudio::Model::SetpointManagerFollowOutdoorAirTemperature.new(model) setpoint_manager_follow_oa.setOffsetTemperatureDifference(0) setpoint_manager_follow_oa.setMaximumSetpointTemperature(80) setpoint_manager_follow_oa.setMinimumSetpointTemperature(5) # connect components to plant loop # supply side components condenser_loop.addSupplyBranchForComponent(tower) condenser_loop.addSupplyBranchForComponent(pipe_supply_bypass) pump.addToNode(condenser_loop.supplyInletNode) pipe_supply_outlet.addToNode(condenser_loop.supplyOutletNode) setpoint_manager_follow_oa.addToNode(condenser_loop.supplyOutletNode) # demand side components model.getChillerElectricEIRs.each do |chiller| if chiller.condenserType == 'WaterCooled' # works only if chillers not already connected to condenser loop(s) condenser_loop.addDemandBranchForComponent(chiller) end end condenser_loop.addDemandBranchForComponent(pipe_demand_bypass) pipe_demand_inlet.addToNode(condenser_loop.demandInletNode) pipe_demand_outlet.addToNode(condenser_loop.demandOutletNode) condenserLoops['condenser_loop'] = condenser_loop end if (options['zoneHVAC'] == 'WSHP') || (options['zoneHVAC'] == 'GSHP') # create condenser loop for heat pumps condenser_loop = OpenStudio::Model::PlantLoop.new(model) condenser_loop.setName('AEDG Heat Pump Loop') condenser_loop.setMaximumLoopTemperature(80) condenser_loop.setMinimumLoopTemperature(1) loop_sizing = condenser_loop.sizingPlant loop_sizing.setLoopType('Condenser') if options['zoneHVAC'] == 'GSHP' loop_sizing.setDesignLoopExitTemperature(21) loop_sizing.setLoopDesignTemperatureDifference(5) elsif options['zoneHVAC'] == 'WSHP' loop_sizing.setDesignLoopExitTemperature(30) # PNNL loop_sizing.setLoopDesignTemperatureDifference(20) # PNNL end # create a pump pump = OpenStudio::Model::PumpVariableSpeed.new(model) pump.setRatedPumpHead(134508) # Pa pump.setMotorEfficiency(0.9) pump.setCoefficient1ofthePartLoadPerformanceCurve(0) pump.setCoefficient2ofthePartLoadPerformanceCurve(0.0216) pump.setCoefficient3ofthePartLoadPerformanceCurve(-0.0325) pump.setCoefficient4ofthePartLoadPerformanceCurve(1.0095) # create a supply bypass pipe pipe_supply_bypass = OpenStudio::Model::PipeAdiabatic.new(model) # create a supply outlet pipe pipe_supply_outlet = OpenStudio::Model::PipeAdiabatic.new(model) # create a demand bypass pipe pipe_demand_bypass = OpenStudio::Model::PipeAdiabatic.new(model) # create a demand inlet pipe pipe_demand_inlet = OpenStudio::Model::PipeAdiabatic.new(model) # create a demand outlet pipe pipe_demand_outlet = OpenStudio::Model::PipeAdiabatic.new(model) # create setpoint managers setpoint_manager_scheduled_loop = OpenStudio::Model::SetpointManagerScheduled.new(model, options['loop_setpoint_schedule']) setpoint_manager_scheduled_cooling = OpenStudio::Model::SetpointManagerScheduled.new(model, options['cooling_setpoint_schedule']) setpoint_manager_scheduled_heating = OpenStudio::Model::SetpointManagerScheduled.new(model, options['heating_setpoint_schedule']) # connect components to plant loop # supply side components condenser_loop.addSupplyBranchForComponent(pipe_supply_bypass) pump.addToNode(condenser_loop.supplyInletNode) pipe_supply_outlet.addToNode(condenser_loop.supplyOutletNode) setpoint_manager_scheduled_loop.addToNode(condenser_loop.supplyOutletNode) # demand side components condenser_loop.addDemandBranchForComponent(pipe_demand_bypass) pipe_demand_inlet.addToNode(condenser_loop.demandInletNode) pipe_demand_outlet.addToNode(condenser_loop.demandOutletNode) # add additional components according to specific system type if options['zoneHVAC'] == 'GSHP' # add district cooling and heating to supply side district_cooling = OpenStudio::Model::DistrictCooling.new(model) district_cooling.setNominalCapacity(1000000000000) # large number; no autosizing condenser_loop.addSupplyBranchForComponent(district_cooling) setpoint_manager_scheduled_cooling.addToNode(district_cooling.outletModelObject.get.to_Node.get) district_heating = OpenStudio::Model::DistrictHeating.new(model) district_heating.setNominalCapacity(1000000000000) # large number; no autosizing district_heating.addToNode(district_cooling.outletModelObject.get.to_Node.get) setpoint_manager_scheduled_heating.addToNode(district_heating.outletModelObject.get.to_Node.get) # add heat pumps to demand side after they get created elsif options['zoneHVAC'] == 'WSHP' # add a boiler and cooling tower to supply side # create a boiler boiler = OpenStudio::Model::BoilerHotWater.new(model) boiler.setNominalThermalEfficiency(0.9) condenser_loop.addSupplyBranchForComponent(boiler) setpoint_manager_scheduled_heating.addToNode(boiler.outletModelObject.get.to_Node.get) # create a cooling tower tower = OpenStudio::Model::CoolingTowerSingleSpeed.new(model) tower.addToNode(boiler.outletModelObject.get.to_Node.get) setpoint_manager_scheduled_cooling.addToNode(tower.outletModelObject.get.to_Node.get) end condenserLoops['heat_pump_loop'] = condenser_loop end # pass back condenser loop(s) result = condenserLoops return result end def self.createPrimaryAirLoops(model, runner, options) primary_airloops = [] # create primary airloop for each story assignedThermalZones = [] model.getBuildingStorys.sort.each do |building_story| # ML stories need to be reordered from the ground up thermalZonesToAdd = [] building_story.spaces.each do |space| # make sure spaces are assigned to thermal zones # otherwise might want to send a warning if space.thermalZone.is_initialized thermal_zone = space.thermalZone.get # grab primary zones if options['zonesPrimary'].include? thermal_zone # make sure zone was not already assigned to another air loop unless assignedThermalZones.include? thermal_zone # make sure thermal zones are not duplicated (spaces can share thermal zones) unless thermalZonesToAdd.include? thermal_zone thermalZonesToAdd << thermal_zone end end end end end # make sure thermal zones don't get added to more than one air loop assignedThermalZones << thermalZonesToAdd # create new air loop if story contains primary zones unless thermalZonesToAdd.empty? airloop_primary = OpenStudio::Model::AirLoopHVAC.new(model) airloop_primary.setName("AEDG Air Loop HVAC #{building_story.name}") # modify system sizing properties sizing_system = airloop_primary.sizingSystem # set central heating and cooling temperatures for sizing sizing_system.setCentralCoolingDesignSupplyAirTemperature(12.8) sizing_system.setCentralHeatingDesignSupplyAirTemperature(40) # ML OS default is 16.7 # load specification sizing_system.setSystemOutdoorAirMethod('VentilationRateProcedure') # ML OS default is ZoneSum if options['primaryHVAC']['doas'] sizing_system.setTypeofLoadtoSizeOn('VentilationRequirement') # DOAS sizing_system.setAllOutdoorAirinCooling(true) # DOAS sizing_system.setAllOutdoorAirinHeating(true) # DOAS else sizing_system.setTypeofLoadtoSizeOn('Sensible') # VAV sizing_system.setAllOutdoorAirinCooling(false) # VAV sizing_system.setAllOutdoorAirinHeating(false) # VAV end air_loop_comps = [] # set availability schedule airloop_primary.setAvailabilitySchedule(options['hvac_schedule']) # create air loop fan if options['primaryHVAC']['fan'] == 'Variable' # create variable speed fan and set system sizing accordingly sizing_system.setCentralHeatingMaximumSystemAirFlowRatio(0.3) # DCV # variable speed fan fan = OpenStudio::Model::FanVariableVolume.new(model, model.alwaysOnDiscreteSchedule) fan.setFanEfficiency(0.6) fan.setPressureRise(1120) # Pa fan.autosizeMaximumFlowRate fan.setFanPowerMinimumFlowFraction(0.6) fan.setMotorEfficiency(0.85) fan.setMotorInAirstreamFraction(1.0) air_loop_comps << fan else sizing_system.setCentralHeatingMaximumSystemAirFlowRatio(1.0) # No DCV # constant speed fan fan = OpenStudio::Model::FanConstantVolume.new(model, model.alwaysOnDiscreteSchedule) fan.setFanEfficiency(0.6) fan.setPressureRise(500) # Pa fan.autosizeMaximumFlowRate fan.setMotorEfficiency(0.9) fan.setMotorInAirstreamFraction(1.0) air_loop_comps << fan end # create heating coil if options['primaryHVAC']['heat'] == 'Water' # water coil heating_coil = OpenStudio::Model::CoilHeatingWater.new(model, model.alwaysOnDiscreteSchedule) air_loop_comps << heating_coil else # gas coil heating_coil = OpenStudio::Model::CoilHeatingGas.new(model, model.alwaysOnDiscreteSchedule) air_loop_comps << heating_coil end # create cooling coil if options['primaryHVAC']['cool'] == 'Water' # water coil cooling_coil = OpenStudio::Model::CoilCoolingWater.new(model, model.alwaysOnDiscreteSchedule) air_loop_comps << cooling_coil elsif options['primaryHVAC']['cool'] == 'SingleDX' # single speed DX coil # create cooling coil # create clgCapFuncTempCurve clgCapFuncTempCurve = OpenStudio::Model::CurveBiquadratic.new(model) clgCapFuncTempCurve.setCoefficient1Constant(0.42415) clgCapFuncTempCurve.setCoefficient2x(0.04426) clgCapFuncTempCurve.setCoefficient3xPOW2(-0.00042) clgCapFuncTempCurve.setCoefficient4y(0.00333) clgCapFuncTempCurve.setCoefficient5yPOW2(-0.00008) clgCapFuncTempCurve.setCoefficient6xTIMESY(-0.00021) clgCapFuncTempCurve.setMinimumValueofx(17) clgCapFuncTempCurve.setMaximumValueofx(22) clgCapFuncTempCurve.setMinimumValueofy(13) clgCapFuncTempCurve.setMaximumValueofy(46) # create clgCapFuncFlowFracCurve clgCapFuncFlowFracCurve = OpenStudio::Model::CurveQuadratic.new(model) clgCapFuncFlowFracCurve.setCoefficient1Constant(0.77136) clgCapFuncFlowFracCurve.setCoefficient2x(0.34053) clgCapFuncFlowFracCurve.setCoefficient3xPOW2(-0.11088) clgCapFuncFlowFracCurve.setMinimumValueofx(0.75918) clgCapFuncFlowFracCurve.setMaximumValueofx(1.13877) # create clgEirFuncTempCurve clgEirFuncTempCurve = OpenStudio::Model::CurveBiquadratic.new(model) clgEirFuncTempCurve.setCoefficient1Constant(1.23649) clgEirFuncTempCurve.setCoefficient2x(-0.02431) clgEirFuncTempCurve.setCoefficient3xPOW2(0.00057) clgEirFuncTempCurve.setCoefficient4y(-0.01434) clgEirFuncTempCurve.setCoefficient5yPOW2(0.00063) clgEirFuncTempCurve.setCoefficient6xTIMESY(-0.00038) clgEirFuncTempCurve.setMinimumValueofx(17) clgEirFuncTempCurve.setMaximumValueofx(22) clgEirFuncTempCurve.setMinimumValueofy(13) clgEirFuncTempCurve.setMaximumValueofy(46) # create clgEirFuncFlowFracCurve clgEirFuncFlowFracCurve = OpenStudio::Model::CurveQuadratic.new(model) clgEirFuncFlowFracCurve.setCoefficient1Constant(1.20550) clgEirFuncFlowFracCurve.setCoefficient2x(-0.32953) clgEirFuncFlowFracCurve.setCoefficient3xPOW2(0.12308) clgEirFuncFlowFracCurve.setMinimumValueofx(0.75918) clgEirFuncFlowFracCurve.setMaximumValueofx(1.13877) # create clgPlrCurve clgPlrCurve = OpenStudio::Model::CurveQuadratic.new(model) clgPlrCurve.setCoefficient1Constant(0.77100) clgPlrCurve.setCoefficient2x(0.22900) clgPlrCurve.setCoefficient3xPOW2(0.0) clgPlrCurve.setMinimumValueofx(0.0) clgPlrCurve.setMaximumValueofx(1.0) # cooling coil cooling_coil = OpenStudio::Model::CoilCoolingDXSingleSpeed.new(model, model.alwaysOnDiscreteSchedule, clgCapFuncTempCurve, clgCapFuncFlowFracCurve, clgEirFuncTempCurve, clgEirFuncFlowFracCurve, clgPlrCurve) cooling_coil.setRatedCOP(OpenStudio::OptionalDouble.new(4)) air_loop_comps << cooling_coil else # two speed DX coil (PNNL curves) # create cooling coil # create clgCapFuncTempCurve clgCapFuncTempCurve = OpenStudio::Model::CurveBiquadratic.new(model) clgCapFuncTempCurve.setCoefficient1Constant(1.39072) clgCapFuncTempCurve.setCoefficient2x(-0.0529058) clgCapFuncTempCurve.setCoefficient3xPOW2(0.0018423) clgCapFuncTempCurve.setCoefficient4y(0.00058267) clgCapFuncTempCurve.setCoefficient5yPOW2(-0.000186814) clgCapFuncTempCurve.setCoefficient6xTIMESY(0.000265159) clgCapFuncTempCurve.setMinimumValueofx(16.5556) clgCapFuncTempCurve.setMaximumValueofx(22.1111) clgCapFuncTempCurve.setMinimumValueofy(23.7778) clgCapFuncTempCurve.setMaximumValueofy(47.66) # create clgCapFuncFlowFracCurve clgCapFuncFlowFracCurve = OpenStudio::Model::CurveQuadratic.new(model) clgCapFuncFlowFracCurve.setCoefficient1Constant(0.718954) clgCapFuncFlowFracCurve.setCoefficient2x(0.435436) clgCapFuncFlowFracCurve.setCoefficient3xPOW2(-0.154193) clgCapFuncFlowFracCurve.setMinimumValueofx(0.75) clgCapFuncFlowFracCurve.setMaximumValueofx(1.25) # create clgEirFuncTempCurve clgEirFuncTempCurve = OpenStudio::Model::CurveBiquadratic.new(model) clgEirFuncTempCurve.setCoefficient1Constant(-0.536161) clgEirFuncTempCurve.setCoefficient2x(0.105138) clgEirFuncTempCurve.setCoefficient3xPOW2(-0.00172659) clgEirFuncTempCurve.setCoefficient4y(0.0149848) clgEirFuncTempCurve.setCoefficient5yPOW2(0.000659948) clgEirFuncTempCurve.setCoefficient6xTIMESY(-0.0017385) clgEirFuncTempCurve.setMinimumValueofx(16.5556) clgEirFuncTempCurve.setMaximumValueofx(22.1111) clgEirFuncTempCurve.setMinimumValueofy(23.7778) clgEirFuncTempCurve.setMaximumValueofy(47.66) # create clgEirFuncFlowFracCurve clgEirFuncFlowFracCurve = OpenStudio::Model::CurveQuadratic.new(model) clgEirFuncFlowFracCurve.setCoefficient1Constant(1.19525) clgEirFuncFlowFracCurve.setCoefficient2x(-0.306138) clgEirFuncFlowFracCurve.setCoefficient3xPOW2(0.110973) clgEirFuncFlowFracCurve.setMinimumValueofx(0.75) clgEirFuncFlowFracCurve.setMaximumValueofx(1.25) # create clgPlrCurve clgPlrCurve = OpenStudio::Model::CurveQuadratic.new(model) clgPlrCurve.setCoefficient1Constant(0.77100) clgPlrCurve.setCoefficient2x(0.22900) clgPlrCurve.setCoefficient3xPOW2(0.0) clgPlrCurve.setMinimumValueofx(0.0) clgPlrCurve.setMaximumValueofx(1.0) # cooling coil cooling_coil = OpenStudio::Model::CoilCoolingDXTwoSpeed.new(model, model.alwaysOnDiscreteSchedule, clgCapFuncTempCurve, clgCapFuncFlowFracCurve, clgEirFuncTempCurve, clgEirFuncFlowFracCurve, clgPlrCurve, clgCapFuncTempCurve, clgEirFuncTempCurve) cooling_coil.setRatedHighSpeedCOP(4) cooling_coil.setRatedLowSpeedCOP(4) air_loop_comps << cooling_coil end unless options['zoneHVAC'] == 'DualDuct' # create controller outdoor air controller_OA = OpenStudio::Model::ControllerOutdoorAir.new(model) controller_OA.autosizeMinimumOutdoorAirFlowRate controller_OA.autosizeMaximumOutdoorAirFlowRate # create ventilation schedules and assign to OA controller if options['primaryHVAC']['doas'] controller_OA.setMinimumFractionofOutdoorAirSchedule(model.alwaysOnDiscreteSchedule) controller_OA.setMaximumFractionofOutdoorAirSchedule(model.alwaysOnDiscreteSchedule) else # multizone VAV that ventilates controller_OA.setMaximumFractionofOutdoorAirSchedule(options['ventilation_schedule']) controller_OA.setEconomizerControlType('DifferentialEnthalpy') # add night cycling (ML would people actually do this for a VAV system?)) airloop_primary.setNightCycleControlType('CycleOnAny') # ML Does this work with variable speed fans? end controller_OA.setHeatRecoveryBypassControlType('BypassWhenOAFlowGreaterThanMinimum') # create outdoor air system system_OA = OpenStudio::Model::AirLoopHVACOutdoorAirSystem.new(model, controller_OA) air_loop_comps << system_OA # create ERV heat_exchanger = OpenStudio::Model::HeatExchangerAirToAirSensibleAndLatent.new(model) heat_exchanger.setAvailabilitySchedule(model.alwaysOnDiscreteSchedule) sensible_eff = 0.75 latent_eff = 0.69 heat_exchanger.setSensibleEffectivenessat100CoolingAirFlow(sensible_eff) heat_exchanger.setSensibleEffectivenessat100HeatingAirFlow(sensible_eff) heat_exchanger.setSensibleEffectivenessat75CoolingAirFlow(sensible_eff) heat_exchanger.setSensibleEffectivenessat75HeatingAirFlow(sensible_eff) heat_exchanger.setLatentEffectivenessat100CoolingAirFlow(latent_eff) heat_exchanger.setLatentEffectivenessat100HeatingAirFlow(latent_eff) heat_exchanger.setLatentEffectivenessat75CoolingAirFlow(latent_eff) heat_exchanger.setLatentEffectivenessat75HeatingAirFlow(latent_eff) heat_exchanger.setFrostControlType('ExhaustOnly') heat_exchanger.setThresholdTemperature(-12.2) heat_exchanger.setInitialDefrostTimeFraction(0.1670) heat_exchanger.setRateofDefrostTimeFractionIncrease(0.0240) heat_exchanger.setEconomizerLockout(false) end # create scheduled setpoint manager for airloop if options['primaryHVAC']['doas'] || (options['zoneHVAC'] == 'DualDuct') # DOAS or VAV for cooling and not ventilation setpoint_manager = OpenStudio::Model::SetpointManagerScheduled.new(model, options['primary_sat_schedule']) else # VAV for cooling and ventilation setpoint_manager = OpenStudio::Model::SetpointManagerOutdoorAirReset.new(model) setpoint_manager.setSetpointatOutdoorLowTemperature(15.6) setpoint_manager.setOutdoorLowTemperature(14.4) setpoint_manager.setSetpointatOutdoorHighTemperature(12.8) setpoint_manager.setOutdoorHighTemperature(21.1) end # connect components to airloop # find the supply inlet node of the airloop airloop_supply_inlet = airloop_primary.supplyInletNode # add the components to the airloop air_loop_comps.each do |comp| comp.addToNode(airloop_supply_inlet) if comp.to_CoilHeatingWater.is_initialized options['hot_water_plant'].addDemandBranchForComponent(comp) comp.controllerWaterCoil.get.setMinimumActuatedFlow(0) elsif comp.to_CoilCoolingWater.is_initialized options['chilled_water_plant'].addDemandBranchForComponent(comp) comp.controllerWaterCoil.get.setMinimumActuatedFlow(0) end end # add erv to outdoor air system unless options['zoneHVAC'] == 'DualDuct' heat_exchanger.addToNode(system_OA.outboardOANode.get) end # add setpoint manager to supply equipment outlet node setpoint_manager.addToNode(airloop_primary.supplyOutletNode) # add thermal zones to airloop thermalZonesToAdd.each do |zone| # make an air terminal for the zone if options['primaryHVAC']['fan'] == 'Variable' air_terminal = OpenStudio::Model::AirTerminalSingleDuctVAVNoReheat.new(model, model.alwaysOnDiscreteSchedule) else air_terminal = OpenStudio::Model::AirTerminalSingleDuctUncontrolled.new(model, model.alwaysOnDiscreteSchedule) end # attach new terminal to the zone and to the airloop airloop_primary.addBranchForZone(zone, air_terminal.to_StraightComponent) end primary_airloops << airloop_primary end end # pass back primary airloops result = primary_airloops return result end def self.createSecondaryAirLoops(model, runner, options) secondary_airloops = [] # create secondary airloop for each secondary zone model.getThermalZones.each do |zone| if options['zonesSecondary'].include? zone # create secondary airloop airloop_secondary = OpenStudio::Model::AirLoopHVAC.new(model) airloop_secondary.setName("AEDG Air Loop HVAC #{zone.name}") # modify system sizing properties sizing_system = airloop_secondary.sizingSystem # set central heating and cooling temperatures for sizing sizing_system.setCentralCoolingDesignSupplyAirTemperature(12.8) sizing_system.setCentralHeatingDesignSupplyAirTemperature(40) # ML OS default is 16.7 # load specification sizing_system.setSystemOutdoorAirMethod('VentilationRateProcedure') # ML OS default is ZoneSum sizing_system.setTypeofLoadtoSizeOn('Sensible') # PSZ sizing_system.setAllOutdoorAirinCooling(false) # PSZ sizing_system.setAllOutdoorAirinHeating(false) # PSZ sizing_system.setCentralHeatingMaximumSystemAirFlowRatio(1.0) # Constant volume fan air_loop_comps = [] # set availability schedule (HVAC operation schedule) airloop_secondary.setAvailabilitySchedule(options['hvac_schedule']) if options['secondaryHVAC']['fan'] == 'Variable' # create variable speed fan and set system sizing accordingly sizing_system.setCentralHeatingMaximumSystemAirFlowRatio(0.3) # DCV # variable speed fan fan = OpenStudio::Model::FanVariableVolume.new(model, model.alwaysOnDiscreteSchedule) fan.setFanEfficiency(0.6) fan.setPressureRise(498) # Pa fan.autosizeMaximumFlowRate fan.setFanPowerMinimumFlowFraction(0.6) fan.setMotorEfficiency(0.85) fan.setMotorInAirstreamFraction(1.0) air_loop_comps << fan else sizing_system.setCentralHeatingMaximumSystemAirFlowRatio(1.0) # No DCV # constant speed fan fan = OpenStudio::Model::FanConstantVolume.new(model, model.alwaysOnDiscreteSchedule) fan.setFanEfficiency(0.6) fan.setPressureRise(498) # Pa fan.autosizeMaximumFlowRate fan.setMotorEfficiency(0.85) fan.setMotorInAirstreamFraction(1.0) air_loop_comps << fan end # create cooling coil if options['secondaryHVAC']['cool'] == 'Water' # water coil cooling_coil = OpenStudio::Model::CoilCoolingWater.new(model, model.alwaysOnDiscreteSchedule) air_loop_comps << cooling_coil elsif options['secondaryHVAC']['cool'] == 'SingleDX' # single speed DX coil # create cooling coil # create clgCapFuncTempCurve clgCapFuncTempCurve = OpenStudio::Model::CurveBiquadratic.new(model) clgCapFuncTempCurve.setCoefficient1Constant(0.42415) clgCapFuncTempCurve.setCoefficient2x(0.04426) clgCapFuncTempCurve.setCoefficient3xPOW2(-0.00042) clgCapFuncTempCurve.setCoefficient4y(0.00333) clgCapFuncTempCurve.setCoefficient5yPOW2(-0.00008) clgCapFuncTempCurve.setCoefficient6xTIMESY(-0.00021) clgCapFuncTempCurve.setMinimumValueofx(17) clgCapFuncTempCurve.setMaximumValueofx(22) clgCapFuncTempCurve.setMinimumValueofy(13) clgCapFuncTempCurve.setMaximumValueofy(46) # create clgCapFuncFlowFracCurve clgCapFuncFlowFracCurve = OpenStudio::Model::CurveQuadratic.new(model) clgCapFuncFlowFracCurve.setCoefficient1Constant(0.77136) clgCapFuncFlowFracCurve.setCoefficient2x(0.34053) clgCapFuncFlowFracCurve.setCoefficient3xPOW2(-0.11088) clgCapFuncFlowFracCurve.setMinimumValueofx(0.75918) clgCapFuncFlowFracCurve.setMaximumValueofx(1.13877) # create clgEirFuncTempCurve clgEirFuncTempCurve = OpenStudio::Model::CurveBiquadratic.new(model) clgEirFuncTempCurve.setCoefficient1Constant(1.23649) clgEirFuncTempCurve.setCoefficient2x(-0.02431) clgEirFuncTempCurve.setCoefficient3xPOW2(0.00057) clgEirFuncTempCurve.setCoefficient4y(-0.01434) clgEirFuncTempCurve.setCoefficient5yPOW2(0.00063) clgEirFuncTempCurve.setCoefficient6xTIMESY(-0.00038) clgEirFuncTempCurve.setMinimumValueofx(17) clgEirFuncTempCurve.setMaximumValueofx(22) clgEirFuncTempCurve.setMinimumValueofy(13) clgEirFuncTempCurve.setMaximumValueofy(46) # create clgEirFuncFlowFracCurve clgEirFuncFlowFracCurve = OpenStudio::Model::CurveQuadratic.new(model) clgEirFuncFlowFracCurve.setCoefficient1Constant(1.20550) clgEirFuncFlowFracCurve.setCoefficient2x(-0.32953) clgEirFuncFlowFracCurve.setCoefficient3xPOW2(0.12308) clgEirFuncFlowFracCurve.setMinimumValueofx(0.75918) clgEirFuncFlowFracCurve.setMaximumValueofx(1.13877) # create clgPlrCurve clgPlrCurve = OpenStudio::Model::CurveQuadratic.new(model) clgPlrCurve.setCoefficient1Constant(0.77100) clgPlrCurve.setCoefficient2x(0.22900) clgPlrCurve.setCoefficient3xPOW2(0.0) clgPlrCurve.setMinimumValueofx(0.0) clgPlrCurve.setMaximumValueofx(1.0) # cooling coil cooling_coil = OpenStudio::Model::CoilCoolingDXSingleSpeed.new(model, model.alwaysOnDiscreteSchedule, clgCapFuncTempCurve, clgCapFuncFlowFracCurve, clgEirFuncTempCurve, clgEirFuncFlowFracCurve, clgPlrCurve) cooling_coil.setRatedCOP(OpenStudio::OptionalDouble.new(4)) air_loop_comps << cooling_coil else # two speed DX coil (PNNL curves) # create cooling coil # create clgCapFuncTempCurve clgCapFuncTempCurve = OpenStudio::Model::CurveBiquadratic.new(model) clgCapFuncTempCurve.setCoefficient1Constant(1.39072) clgCapFuncTempCurve.setCoefficient2x(-0.0529058) clgCapFuncTempCurve.setCoefficient3xPOW2(0.0018423) clgCapFuncTempCurve.setCoefficient4y(0.00058267) clgCapFuncTempCurve.setCoefficient5yPOW2(-0.000186814) clgCapFuncTempCurve.setCoefficient6xTIMESY(0.000265159) clgCapFuncTempCurve.setMinimumValueofx(16.5556) clgCapFuncTempCurve.setMaximumValueofx(22.1111) clgCapFuncTempCurve.setMinimumValueofy(23.7778) clgCapFuncTempCurve.setMaximumValueofy(47.66) # create clgCapFuncFlowFracCurve clgCapFuncFlowFracCurve = OpenStudio::Model::CurveQuadratic.new(model) clgCapFuncFlowFracCurve.setCoefficient1Constant(0.718954) clgCapFuncFlowFracCurve.setCoefficient2x(0.435436) clgCapFuncFlowFracCurve.setCoefficient3xPOW2(-0.154193) clgCapFuncFlowFracCurve.setMinimumValueofx(0.75) clgCapFuncFlowFracCurve.setMaximumValueofx(1.25) # create clgEirFuncTempCurve clgEirFuncTempCurve = OpenStudio::Model::CurveBiquadratic.new(model) clgEirFuncTempCurve.setCoefficient1Constant(-0.536161) clgEirFuncTempCurve.setCoefficient2x(0.105138) clgEirFuncTempCurve.setCoefficient3xPOW2(-0.00172659) clgEirFuncTempCurve.setCoefficient4y(0.0149848) clgEirFuncTempCurve.setCoefficient5yPOW2(0.000659948) clgEirFuncTempCurve.setCoefficient6xTIMESY(-0.0017385) clgEirFuncTempCurve.setMinimumValueofx(16.5556) clgEirFuncTempCurve.setMaximumValueofx(22.1111) clgEirFuncTempCurve.setMinimumValueofy(23.7778) clgEirFuncTempCurve.setMaximumValueofy(47.66) # create clgEirFuncFlowFracCurve clgEirFuncFlowFracCurve = OpenStudio::Model::CurveQuadratic.new(model) clgEirFuncFlowFracCurve.setCoefficient1Constant(1.19525) clgEirFuncFlowFracCurve.setCoefficient2x(-0.306138) clgEirFuncFlowFracCurve.setCoefficient3xPOW2(0.110973) clgEirFuncFlowFracCurve.setMinimumValueofx(0.75) clgEirFuncFlowFracCurve.setMaximumValueofx(1.25) # create clgPlrCurve clgPlrCurve = OpenStudio::Model::CurveQuadratic.new(model) clgPlrCurve.setCoefficient1Constant(0.77100) clgPlrCurve.setCoefficient2x(0.22900) clgPlrCurve.setCoefficient3xPOW2(0.0) clgPlrCurve.setMinimumValueofx(0.0) clgPlrCurve.setMaximumValueofx(1.0) # cooling coil cooling_coil = OpenStudio::Model::CoilCoolingDXTwoSpeed.new(model, model.alwaysOnDiscreteSchedule, clgCapFuncTempCurve, clgCapFuncFlowFracCurve, clgEirFuncTempCurve, clgEirFuncFlowFracCurve, clgPlrCurve, clgCapFuncTempCurve, clgEirFuncTempCurve) cooling_coil.setRatedHighSpeedCOP(4) cooling_coil.setRatedLowSpeedCOP(4) air_loop_comps << cooling_coil end if options['secondaryHVAC']['heat'] == 'Water' # water coil heating_coil = OpenStudio::Model::CoilHeatingWater.new(model, model.alwaysOnDiscreteSchedule) air_loop_comps << heating_coil else # gas coil heating_coil = OpenStudio::Model::CoilHeatingGas.new(model, model.alwaysOnDiscreteSchedule) air_loop_comps << heating_coil end # create controller outdoor air controller_OA = OpenStudio::Model::ControllerOutdoorAir.new(model) controller_OA.autosizeMinimumOutdoorAirFlowRate controller_OA.autosizeMaximumOutdoorAirFlowRate controller_OA.setEconomizerControlType('DifferentialEnthalpy') controller_OA.setMaximumFractionofOutdoorAirSchedule(options['ventilation_schedule']) controller_OA.setHeatRecoveryBypassControlType('BypassWhenOAFlowGreaterThanMinimum') # create outdoor air system system_OA = OpenStudio::Model::AirLoopHVACOutdoorAirSystem.new(model, controller_OA) air_loop_comps << system_OA # create ERV heat_exchanger = OpenStudio::Model::HeatExchangerAirToAirSensibleAndLatent.new(model) heat_exchanger.setAvailabilitySchedule(model.alwaysOnDiscreteSchedule) sensible_eff = 0.75 latent_eff = 0.69 heat_exchanger.setSensibleEffectivenessat100CoolingAirFlow(sensible_eff) heat_exchanger.setSensibleEffectivenessat100HeatingAirFlow(sensible_eff) heat_exchanger.setSensibleEffectivenessat75CoolingAirFlow(sensible_eff) heat_exchanger.setSensibleEffectivenessat75HeatingAirFlow(sensible_eff) heat_exchanger.setLatentEffectivenessat100CoolingAirFlow(latent_eff) heat_exchanger.setLatentEffectivenessat100HeatingAirFlow(latent_eff) heat_exchanger.setLatentEffectivenessat75CoolingAirFlow(latent_eff) heat_exchanger.setLatentEffectivenessat75HeatingAirFlow(latent_eff) heat_exchanger.setFrostControlType('ExhaustOnly') heat_exchanger.setThresholdTemperature(-12.2) heat_exchanger.setInitialDefrostTimeFraction(0.1670) heat_exchanger.setRateofDefrostTimeFractionIncrease(0.0240) heat_exchanger.setEconomizerLockout(false) # create setpoint manager for airloop setpoint_manager = OpenStudio::Model::SetpointManagerSingleZoneReheat.new(model) setpoint_manager.setMinimumSupplyAirTemperature(10) setpoint_manager.setMaximumSupplyAirTemperature(50) setpoint_manager.setControlZone(zone) # connect components to airloop # find the supply inlet node of the airloop airloop_supply_inlet = airloop_secondary.supplyInletNode # add the components to the airloop air_loop_comps.each do |comp| comp.addToNode(airloop_supply_inlet) if comp.to_CoilHeatingWater.is_initialized options['hot_water_plant'].addDemandBranchForComponent(comp) comp.controllerWaterCoil.get.setMinimumActuatedFlow(0) elsif comp.to_CoilCoolingWater.is_initialized options['chilled_water_plant'].addDemandBranchForComponent(comp) comp.controllerWaterCoil.get.setMinimumActuatedFlow(0) end end # add erv to outdoor air system heat_exchanger.addToNode(system_OA.outboardOANode.get) # add setpoint manager to supply equipment outlet node setpoint_manager.addToNode(airloop_secondary.supplyOutletNode) # add thermal zone to airloop if options['secondaryHVAC']['fan'] == 'Variable' air_terminal = OpenStudio::Model::AirTerminalSingleDuctVAVNoReheat.new(model, model.alwaysOnDiscreteSchedule) else air_terminal = OpenStudio::Model::AirTerminalSingleDuctUncontrolled.new(model, model.alwaysOnDiscreteSchedule) end # attach new terminal to the zone and to the airloop airloop_secondary.addBranchForZone(zone, air_terminal.to_StraightComponent) # add night cycling airloop_secondary.setNightCycleControlType('CycleOnAny') # ML Does this work with variable speed fans? secondary_airloops << airloop_secondary end end # pass back secondary airloops result = secondary_airloops return result end def self.createPrimaryZoneEquipment(model, runner, options) model.getThermalZones.each do |zone| if options['zonesPrimary'].include? zone if options['zoneHVAC'] == 'FanCoil' # create fan coil # create fan fan = OpenStudio::Model::FanOnOff.new(model, model.alwaysOnDiscreteSchedule) fan.setFanEfficiency(0.6) fan.setPressureRise(299) # Pa fan.autosizeMaximumFlowRate fan.setMotorEfficiency(0.85) fan.setMotorInAirstreamFraction(1.0) # create cooling coil and connect to chilled water plant cooling_coil = OpenStudio::Model::CoilCoolingWater.new(model, model.alwaysOnDiscreteSchedule) options['chilled_water_plant'].addDemandBranchForComponent(cooling_coil) cooling_coil.controllerWaterCoil.get.setMinimumActuatedFlow(0) # create heating coil and connect to hot water plant heating_coil = OpenStudio::Model::CoilHeatingWater.new(model, model.alwaysOnDiscreteSchedule) options['hot_water_plant'].addDemandBranchForComponent(heating_coil) heating_coil.controllerWaterCoil.get.setMinimumActuatedFlow(0) # construct fan coil fan_coil = OpenStudio::Model::ZoneHVACFourPipeFanCoil.new(model, model.alwaysOnDiscreteSchedule, fan, cooling_coil, heating_coil) fan_coil.setMaximumOutdoorAirFlowRate(0) # add fan coil to thermal zone fan_coil.addToThermalZone(zone) elsif (options['zoneHVAC'] == 'WSHP') || (options['zoneHVAC'] == 'GSHP') # create water source heat pump and attach to heat pump loop # create fan fan = OpenStudio::Model::FanOnOff.new(model, model.alwaysOnDiscreteSchedule) fan.setFanEfficiency(0.5) fan.setPressureRise(75) # Pa fan.autosizeMaximumFlowRate fan.setMotorEfficiency(0.9) fan.setMotorInAirstreamFraction(1.0) # create cooling coil and connect to heat pump loop cooling_coil = OpenStudio::Model::CoilCoolingWaterToAirHeatPumpEquationFit.new(model) cooling_coil.setRatedCoolingCoefficientofPerformance(6.45) cooling_coil.setTotalCoolingCapacityCoefficient1(-9.149069561) cooling_coil.setTotalCoolingCapacityCoefficient2(10.87814026) cooling_coil.setTotalCoolingCapacityCoefficient3(-1.718780157) cooling_coil.setTotalCoolingCapacityCoefficient4(0.746414818) cooling_coil.setTotalCoolingCapacityCoefficient5(0.0) cooling_coil.setSensibleCoolingCapacityCoefficient1(-5.462690012) cooling_coil.setSensibleCoolingCapacityCoefficient2(17.95968138) cooling_coil.setSensibleCoolingCapacityCoefficient3(-11.87818402) cooling_coil.setSensibleCoolingCapacityCoefficient4(-0.980163419) cooling_coil.setSensibleCoolingCapacityCoefficient5(0.767285761) cooling_coil.setSensibleCoolingCapacityCoefficient6(0.0) cooling_coil.setCoolingPowerConsumptionCoefficient1(-3.205409884) cooling_coil.setCoolingPowerConsumptionCoefficient2(-0.976409399) cooling_coil.setCoolingPowerConsumptionCoefficient3(3.97892546) cooling_coil.setCoolingPowerConsumptionCoefficient4(0.938181818) cooling_coil.setCoolingPowerConsumptionCoefficient5(0.0) options['heat_pump_loop'].addDemandBranchForComponent(cooling_coil) # create heating coil and connect to heat pump loop heating_coil = OpenStudio::Model::CoilHeatingWaterToAirHeatPumpEquationFit.new(model) heating_coil.setRatedHeatingCoefficientofPerformance(4.0) heating_coil.setHeatingCapacityCoefficient1(-1.361311959) heating_coil.setHeatingCapacityCoefficient2(-2.471798046) heating_coil.setHeatingCapacityCoefficient3(4.173164514) heating_coil.setHeatingCapacityCoefficient4(0.640757401) heating_coil.setHeatingCapacityCoefficient5(0.0) heating_coil.setHeatingPowerConsumptionCoefficient1(-2.176941116) heating_coil.setHeatingPowerConsumptionCoefficient2(0.832114286) heating_coil.setHeatingPowerConsumptionCoefficient3(1.570743399) heating_coil.setHeatingPowerConsumptionCoefficient4(0.690793651) heating_coil.setHeatingPowerConsumptionCoefficient5(0.0) options['heat_pump_loop'].addDemandBranchForComponent(heating_coil) # create supplemental heating coil supplemental_heating_coil = OpenStudio::Model::CoilHeatingElectric.new(model, model.alwaysOnDiscreteSchedule) # construct heat pump heat_pump = OpenStudio::Model::ZoneHVACWaterToAirHeatPump.new(model, model.alwaysOnDiscreteSchedule, fan, heating_coil, cooling_coil, supplemental_heating_coil) heat_pump.setSupplyAirFlowRateWhenNoCoolingorHeatingisNeeded(OpenStudio::OptionalDouble.new(0)) heat_pump.setOutdoorAirFlowRateDuringCoolingOperation(OpenStudio::OptionalDouble.new(0)) heat_pump.setOutdoorAirFlowRateDuringHeatingOperation(OpenStudio::OptionalDouble.new(0)) heat_pump.setOutdoorAirFlowRateWhenNoCoolingorHeatingisNeeded(OpenStudio::OptionalDouble.new(0)) # add heat pump to thermal zone heat_pump.addToThermalZone(zone) elsif options['zoneHVAC'] == 'ASHP' # create air source heat pump # create fan fan = OpenStudio::Model::FanOnOff.new(model, model.alwaysOnDiscreteSchedule) fan.setFanEfficiency(0.5) fan.setPressureRise(75) # Pa fan.autosizeMaximumFlowRate fan.setMotorEfficiency(0.9) fan.setMotorInAirstreamFraction(1.0) # create heating coil # create htgCapFuncTempCurve htgCapFuncTempCurve = OpenStudio::Model::CurveCubic.new(model) htgCapFuncTempCurve.setCoefficient1Constant(0.758746) htgCapFuncTempCurve.setCoefficient2x(0.027626) htgCapFuncTempCurve.setCoefficient3xPOW2(0.000148716) htgCapFuncTempCurve.setCoefficient4xPOW3(0.0000034992) htgCapFuncTempCurve.setMinimumValueofx(-20) htgCapFuncTempCurve.setMaximumValueofx(20) # create htgCapFuncFlowFracCurve htgCapFuncFlowFracCurve = OpenStudio::Model::CurveCubic.new(model) htgCapFuncFlowFracCurve.setCoefficient1Constant(0.84) htgCapFuncFlowFracCurve.setCoefficient2x(0.16) htgCapFuncFlowFracCurve.setCoefficient3xPOW2(0) htgCapFuncFlowFracCurve.setCoefficient4xPOW3(0) htgCapFuncFlowFracCurve.setMinimumValueofx(0.5) htgCapFuncFlowFracCurve.setMaximumValueofx(1.5) # create htgEirFuncTempCurve htgEirFuncTempCurve = OpenStudio::Model::CurveCubic.new(model) htgEirFuncTempCurve.setCoefficient1Constant(1.19248) htgEirFuncTempCurve.setCoefficient2x(-0.0300438) htgEirFuncTempCurve.setCoefficient3xPOW2(0.00103745) htgEirFuncTempCurve.setCoefficient4xPOW3(-0.000023328) htgEirFuncTempCurve.setMinimumValueofx(-20) htgEirFuncTempCurve.setMaximumValueofx(20) # create htgEirFuncFlowFracCurve htgEirFuncFlowFracCurve = OpenStudio::Model::CurveQuadratic.new(model) htgEirFuncFlowFracCurve.setCoefficient1Constant(1.3824) htgEirFuncFlowFracCurve.setCoefficient2x(-0.4336) htgEirFuncFlowFracCurve.setCoefficient3xPOW2(0.0512) htgEirFuncFlowFracCurve.setMinimumValueofx(0) htgEirFuncFlowFracCurve.setMaximumValueofx(1) # create htgPlrCurve htgPlrCurve = OpenStudio::Model::CurveQuadratic.new(model) htgPlrCurve.setCoefficient1Constant(0.75) htgPlrCurve.setCoefficient2x(0.25) htgPlrCurve.setCoefficient3xPOW2(0.0) htgPlrCurve.setMinimumValueofx(0.0) htgPlrCurve.setMaximumValueofx(1.0) # heating coil heating_coil = OpenStudio::Model::CoilHeatingDXSingleSpeed.new(model, model.alwaysOnDiscreteSchedule, htgCapFuncTempCurve, htgCapFuncFlowFracCurve, htgEirFuncTempCurve, htgEirFuncFlowFracCurve, htgPlrCurve) heating_coil.setRatedCOP(3.4) heating_coil.setCrankcaseHeaterCapacity(200) heating_coil.setMaximumOutdoorDryBulbTemperatureforCrankcaseHeaterOperation(8) heating_coil.autosizeResistiveDefrostHeaterCapacity # create cooling coil # create clgCapFuncTempCurve clgCapFuncTempCurve = OpenStudio::Model::CurveBiquadratic.new(model) clgCapFuncTempCurve.setCoefficient1Constant(0.942587793) clgCapFuncTempCurve.setCoefficient2x(0.009543347) clgCapFuncTempCurve.setCoefficient3xPOW2(0.0018423) clgCapFuncTempCurve.setCoefficient4y(-0.011042676) clgCapFuncTempCurve.setCoefficient5yPOW2(0.000005249) clgCapFuncTempCurve.setCoefficient6xTIMESY(-0.000009720) clgCapFuncTempCurve.setMinimumValueofx(17) clgCapFuncTempCurve.setMaximumValueofx(22) clgCapFuncTempCurve.setMinimumValueofy(13) clgCapFuncTempCurve.setMaximumValueofy(46) # create clgCapFuncFlowFracCurve clgCapFuncFlowFracCurve = OpenStudio::Model::CurveQuadratic.new(model) clgCapFuncFlowFracCurve.setCoefficient1Constant(0.718954) clgCapFuncFlowFracCurve.setCoefficient2x(0.435436) clgCapFuncFlowFracCurve.setCoefficient3xPOW2(-0.154193) clgCapFuncFlowFracCurve.setMinimumValueofx(0.75) clgCapFuncFlowFracCurve.setMaximumValueofx(1.25) # create clgEirFuncTempCurve clgEirFuncTempCurve = OpenStudio::Model::CurveBiquadratic.new(model) clgEirFuncTempCurve.setCoefficient1Constant(0.342414409) clgEirFuncTempCurve.setCoefficient2x(0.034885008) clgEirFuncTempCurve.setCoefficient3xPOW2(-0.000623700) clgEirFuncTempCurve.setCoefficient4y(0.004977216) clgEirFuncTempCurve.setCoefficient5yPOW2(0.000437951) clgEirFuncTempCurve.setCoefficient6xTIMESY(-0.000728028) clgEirFuncTempCurve.setMinimumValueofx(17) clgEirFuncTempCurve.setMaximumValueofx(22) clgEirFuncTempCurve.setMinimumValueofy(13) clgEirFuncTempCurve.setMaximumValueofy(46) # create clgEirFuncFlowFracCurve clgEirFuncFlowFracCurve = OpenStudio::Model::CurveQuadratic.new(model) clgEirFuncFlowFracCurve.setCoefficient1Constant(1.1552) clgEirFuncFlowFracCurve.setCoefficient2x(-0.1808) clgEirFuncFlowFracCurve.setCoefficient3xPOW2(0.0256) clgEirFuncFlowFracCurve.setMinimumValueofx(0.5) clgEirFuncFlowFracCurve.setMaximumValueofx(1.5) # create clgPlrCurve clgPlrCurve = OpenStudio::Model::CurveQuadratic.new(model) clgPlrCurve.setCoefficient1Constant(0.75) clgPlrCurve.setCoefficient2x(0.25) clgPlrCurve.setCoefficient3xPOW2(0.0) clgPlrCurve.setMinimumValueofx(0.0) clgPlrCurve.setMaximumValueofx(1.0) # cooling coil cooling_coil = OpenStudio::Model::CoilCoolingDXSingleSpeed.new(model, model.alwaysOnDiscreteSchedule, clgCapFuncTempCurve, clgCapFuncFlowFracCurve, clgEirFuncTempCurve, clgEirFuncFlowFracCurve, clgPlrCurve) cooling_coil.setRatedCOP(OpenStudio::OptionalDouble.new(4)) # create supplemental heating coil supplemental_heating_coil = OpenStudio::Model::CoilHeatingElectric.new(model, model.alwaysOnDiscreteSchedule) # construct heat pump heat_pump = OpenStudio::Model::ZoneHVACPackagedTerminalHeatPump.new(model, model.alwaysOnDiscreteSchedule, fan, heating_coil, cooling_coil, supplemental_heating_coil) heat_pump.setSupplyAirFlowRateWhenNoCoolingorHeatingisNeeded(0) heat_pump.setOutdoorAirFlowRateDuringCoolingOperation(0) heat_pump.setOutdoorAirFlowRateDuringHeatingOperation(0) heat_pump.setOutdoorAirFlowRateWhenNoCoolingorHeatingisNeeded(0) # add heat pump to thermal zone heat_pump.addToThermalZone(zone) elsif options['zoneHVAC'] == 'Baseboard' # create baseboard heater add add to thermal zone and hot water loop baseboard_coil = OpenStudio::Model::CoilHeatingWaterBaseboard.new(model) baseboard_heater = OpenStudio::Model::ZoneHVACBaseboardConvectiveWater.new(model, model.alwaysOnDiscreteSchedule, baseboard_coil) baseboard_heater.addToThermalZone(zone) options['hot_water_plant'].addDemandBranchForComponent(baseboard_coil) elsif options['zoneHVAC'] == 'Radiant' # create low temperature radiant object and add to thermal zone and radiant plant loops # create hot water coil and attach to radiant hot water loop heating_coil = OpenStudio::Model::CoilHeatingLowTempRadiantVarFlow.new(model, options['mean_radiant_heating_setpoint_schedule']) options['radiant_hot_water_plant'].addDemandBranchForComponent(heating_coil) # create chilled water coil and attach to radiant chilled water loop cooling_coil = OpenStudio::Model::CoilCoolingLowTempRadiantVarFlow.new(model, options['mean_radiant_cooling_setpoint_schedule']) options['radiant_chilled_water_plant'].addDemandBranchForComponent(cooling_coil) low_temp_radiant = OpenStudio::Model::ZoneHVACLowTempRadiantVarFlow.new(model, model.alwaysOnDiscreteSchedule, heating_coil, cooling_coil) low_temp_radiant.setRadiantSurfaceType('Floors') low_temp_radiant.setHydronicTubingInsideDiameter(0.012) low_temp_radiant.setTemperatureControlType('MeanRadiantTemperature') low_temp_radiant.addToThermalZone(zone) # create radiant floor construction and substitute for existing floor (interior or exterior) constructions # create materials for radiant floor construction layers = [] # ignore layer below insulation, which will depend on boundary condition layers << rigid_insulation_1in = OpenStudio::Model::StandardOpaqueMaterial.new(model, 'Rough', 0.0254, 0.02, 56.06, 1210) layers << concrete_2in = OpenStudio::Model::StandardOpaqueMaterial.new(model, 'MediumRough', 0.0508, 2.31, 2322, 832) layers << concrete_2in # create radiant floor construction from materials radiant_floor = OpenStudio::Model::ConstructionWithInternalSource.new(layers) radiant_floor.setSourcePresentAfterLayerNumber(2) radiant_floor.setSourcePresentAfterLayerNumber(2) # assign radiant construction to zone floor zone.spaces.each do |space| space.surfaces.each do |surface| if surface.surfaceType == 'Floor' surface.setConstruction(radiant_floor) end end end elsif options['zoneHVAC'] == 'DualDuct' # create baseboard heater add add to thermal zone and hot water loop baseboard_coil = OpenStudio::Model::CoilHeatingWaterBaseboard.new(model) baseboard_heater = OpenStudio::Model::ZoneHVACBaseboardConvectiveWater.new(model, model.alwaysOnDiscreteSchedule, baseboard_coil) baseboard_heater.addToThermalZone(zone) options['hot_water_plant'].addDemandBranchForComponent(baseboard_coil) # create fan coil (to mimic functionality of DOAS) # variable speed fan fan = OpenStudio::Model::FanVariableVolume.new(model, model.alwaysOnDiscreteSchedule) fan.setFanEfficiency(0.69) fan.setPressureRise(75) # Pa #ML This number is a guess; zone equipment pretending to be a DOAS fan.autosizeMaximumFlowRate fan.setFanPowerMinimumFlowFraction(0.6) fan.setMotorEfficiency(0.9) fan.setMotorInAirstreamFraction(1.0) # create chilled water coil and attach to chilled water loop cooling_coil = OpenStudio::Model::CoilCoolingWater.new(model, model.alwaysOnDiscreteSchedule) options['chilled_water_plant'].addDemandBranchForComponent(cooling_coil) cooling_coil.controllerWaterCoil.get.setMinimumActuatedFlow(0) # create hot water coil and attach to hot water loop heating_coil = OpenStudio::Model::CoilHeatingWater.new(model, model.alwaysOnDiscreteSchedule) options['hot_water_plant'].addDemandBranchForComponent(heating_coil) heating_coil.controllerWaterCoil.get.setMinimumActuatedFlow(0) # construct fan coil (DOAS) and attach to thermal zone fan_coil_doas = OpenStudio::Model::ZoneHVACFourPipeFanCoil.new(model, options['ventilation_schedule'], fan, cooling_coil, heating_coil) fan_coil_doas.setCapacityControlMethod('VariableFanVariableFlow') fan_coil_doas.addToThermalZone(zone) end end end end def self.addDCV(model, runner, options) options['primary_airloops']&.each do |airloop| if options['allHVAC']['primary']['fan'] == 'Variable' if airloop.airLoopHVACOutdoorAirSystem.is_initialized controller_mv = airloop.airLoopHVACOutdoorAirSystem.get.getControllerOutdoorAir.controllerMechanicalVentilation controller_mv.setDemandControlledVentilation(true) runner.registerInfo("Enabling demand control ventilation for #{airloop.name}") end end end options['secondary_airloops']&.each do |airloop| if options['allHVAC']['secondary']['fan'] == 'Variable' if airloop.airLoopHVACOutdoorAirSystem.is_initialized controller_mv = airloop.airLoopHVACOutdoorAirSystem.get.getControllerOutdoorAir.controllerMechanicalVentilation controller_mv.setDemandControlledVentilation(true) runner.registerInfo("Enabling demand control ventilation for #{airloop.name}") end end end end end