example_files/resources/hpxml-measures/HPXMLtoOpenStudio/resources/airflow.rb in urbanopt-cli-0.10.0 vs example_files/resources/hpxml-measures/HPXMLtoOpenStudio/resources/airflow.rb in urbanopt-cli-0.11.0
- old
+ new
@@ -1,30 +1,35 @@
+# *********************************************************************************
+# URBANopt (tm), Copyright (c) Alliance for Sustainable Energy, LLC.
+# See also https://github.com/urbanopt/urbanopt-cli/blob/develop/LICENSE.md
+# *********************************************************************************
+
# frozen_string_literal: true
class Airflow
# Constants
InfilPressureExponent = 0.65
- def self.apply(model, runner, weather, spaces, hpxml, cfa, nbeds,
+ def self.apply(model, runner, weather, spaces, hpxml_header, hpxml_bldg, cfa, nbeds,
ncfl_ag, duct_systems, airloop_map, clg_ssn_sensor, eri_version,
frac_windows_operable, apply_ashrae140_assumptions, schedules_file,
unavailable_periods, hvac_availability_sensor)
# Global variables
@runner = runner
@spaces = spaces
- @year = hpxml.header.sim_calendar_year
- @living_space = spaces[HPXML::LocationLivingSpace]
- @living_zone = @living_space.thermalZone.get
+ @year = hpxml_header.sim_calendar_year
+ @conditioned_space = spaces[HPXML::LocationConditionedSpace]
+ @conditioned_zone = @conditioned_space.thermalZone.get
@nbeds = nbeds
@ncfl_ag = ncfl_ag
@eri_version = eri_version
@apply_ashrae140_assumptions = apply_ashrae140_assumptions
@cfa = cfa
- @cooking_range_in_cond_space = hpxml.cooking_ranges.empty? ? true : HPXML::conditioned_locations_this_unit.include?(hpxml.cooking_ranges[0].location)
- @clothes_dryer_in_cond_space = hpxml.clothes_dryers.empty? ? true : HPXML::conditioned_locations_this_unit.include?(hpxml.clothes_dryers[0].location)
+ @cooking_range_in_cond_space = hpxml_bldg.cooking_ranges.empty? ? true : HPXML::conditioned_locations_this_unit.include?(hpxml_bldg.cooking_ranges[0].location)
+ @clothes_dryer_in_cond_space = hpxml_bldg.clothes_dryers.empty? ? true : HPXML::conditioned_locations_this_unit.include?(hpxml_bldg.clothes_dryers[0].location)
@hvac_availability_sensor = hvac_availability_sensor
# Global sensors
@pbar_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Site Outdoor Air Barometric Pressure')
@@ -32,33 +37,33 @@
@wout_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Site Outdoor Air Humidity Ratio')
@wout_sensor.setName('out wt s')
@win_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Zone Air Humidity Ratio')
- @win_sensor.setName("#{Constants.ObjectNameAirflow} win s")
- @win_sensor.setKeyName(@living_zone.name.to_s)
+ @win_sensor.setName('win s')
+ @win_sensor.setKeyName(@conditioned_zone.name.to_s)
@vwind_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Site Wind Speed')
@vwind_sensor.setName('site vw s')
@tin_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Zone Mean Air Temperature')
- @tin_sensor.setName("#{Constants.ObjectNameAirflow} tin s")
- @tin_sensor.setKeyName(@living_zone.name.to_s)
+ @tin_sensor.setName('tin s')
+ @tin_sensor.setKeyName(@conditioned_zone.name.to_s)
@tout_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Zone Outdoor Air Drybulb Temperature')
- @tout_sensor.setName("#{Constants.ObjectNameAirflow} tt s")
- @tout_sensor.setKeyName(@living_zone.name.to_s)
+ @tout_sensor.setName('tout s')
+ @tout_sensor.setKeyName(@conditioned_zone.name.to_s)
@adiabatic_const = nil
# Ventilation fans
vent_fans_mech = []
vent_fans_kitchen = []
vent_fans_bath = []
vent_fans_whf = []
vent_fans_cfis_suppl = []
- hpxml.ventilation_fans.each do |vent_fan|
+ hpxml_bldg.ventilation_fans.each do |vent_fan|
next unless vent_fan.hours_in_operation.nil? || vent_fan.hours_in_operation > 0
if vent_fan.used_for_whole_building_ventilation
if not vent_fan.is_cfis_supplemental_fan?
vent_fans_mech << vent_fan
@@ -75,11 +80,11 @@
end
end
end
# Vented clothes dryers
- vented_dryers = hpxml.clothes_dryers.select { |cd| cd.is_vented && cd.vented_flow_rate.to_f > 0 }
+ vented_dryers = hpxml_bldg.clothes_dryers.select { |cd| cd.is_vented && cd.vented_flow_rate.to_f > 0 }
# Initialization
initialize_cfis(model, vent_fans_mech, airloop_map, unavailable_periods)
model.getAirLoopHVACs.each do |air_loop|
initialize_fan_objects(model, air_loop)
@@ -89,35 +94,35 @@
end
# Apply ducts
duct_systems.each do |ducts, object|
- apply_ducts(model, ducts, object, vent_fans_mech)
+ apply_ducts(model, ducts, object, vent_fans_mech, hpxml_bldg.building_construction.number_of_units)
end
# Apply infiltration/ventilation
- set_wind_speed_correction(model, hpxml.site)
- window_area = hpxml.windows.map { |w| w.area }.sum(0.0)
+ set_wind_speed_correction(model, hpxml_bldg.site)
+ window_area = hpxml_bldg.windows.map { |w| w.area }.sum(0.0)
open_window_area = window_area * frac_windows_operable * 0.5 * 0.2 # Assume A) 50% of the area of an operable window can be open, and B) 20% of openable window area is actually open
- vented_attic = hpxml.attics.find { |attic| attic.attic_type == HPXML::AtticTypeVented }
- vented_crawl = hpxml.foundations.find { |foundation| foundation.foundation_type == HPXML::FoundationTypeCrawlspaceVented }
+ vented_attic = hpxml_bldg.attics.find { |attic| attic.attic_type == HPXML::AtticTypeVented }
+ vented_crawl = hpxml_bldg.foundations.find { |foundation| foundation.foundation_type == HPXML::FoundationTypeCrawlspaceVented }
- _sla, living_ach50, nach, infil_volume, infil_height, a_ext = get_values_from_air_infiltration_measurements(hpxml, cfa, weather)
+ _sla, conditioned_ach50, nach, infil_volume, infil_height, a_ext = get_values_from_air_infiltration_measurements(hpxml_bldg, cfa, weather)
if @apply_ashrae140_assumptions
- living_const_ach = nach
- living_ach50 = nil
+ conditioned_const_ach = nach
+ conditioned_ach50 = nil
end
- living_const_ach *= a_ext unless living_const_ach.nil?
- living_ach50 *= a_ext unless living_ach50.nil?
- has_flue_chimney_in_cond_space = hpxml.air_infiltration.has_flue_or_chimney_in_conditioned_space
+ conditioned_const_ach *= a_ext unless conditioned_const_ach.nil?
+ conditioned_ach50 *= a_ext unless conditioned_ach50.nil?
+ has_flue_chimney_in_cond_space = hpxml_bldg.air_infiltration.has_flue_or_chimney_in_conditioned_space
- apply_natural_ventilation_and_whole_house_fan(model, hpxml.site, vent_fans_whf, open_window_area, clg_ssn_sensor, hpxml.header.natvent_days_per_week,
+ apply_natural_ventilation_and_whole_house_fan(model, hpxml_bldg.site, vent_fans_whf, open_window_area, clg_ssn_sensor, hpxml_bldg.header.natvent_days_per_week,
infil_volume, infil_height, unavailable_periods)
- apply_infiltration_and_ventilation_fans(model, weather, hpxml.site, vent_fans_mech, vent_fans_kitchen, vent_fans_bath, vented_dryers,
- has_flue_chimney_in_cond_space, living_ach50, living_const_ach, infil_volume, infil_height,
+ apply_infiltration_and_ventilation_fans(model, weather, hpxml_bldg.site, vent_fans_mech, vent_fans_kitchen, vent_fans_bath, vented_dryers,
+ has_flue_chimney_in_cond_space, conditioned_ach50, conditioned_const_ach, infil_volume, infil_height,
vented_attic, vented_crawl, clg_ssn_sensor, schedules_file, vent_fans_cfis_suppl, unavailable_periods)
end
def self.get_default_fraction_of_windows_operable()
# Combining the value below with the assumption that 50% of
@@ -169,17 +174,17 @@
end
end
fail 'Unexpected error.'
end
- def self.get_values_from_air_infiltration_measurements(hpxml, cfa, weather)
- measurement = get_infiltration_measurement_of_interest(hpxml.air_infiltration_measurements)
+ def self.get_values_from_air_infiltration_measurements(hpxml_bldg, cfa, weather)
+ measurement = get_infiltration_measurement_of_interest(hpxml_bldg.air_infiltration_measurements)
volume = measurement.infiltration_volume
height = measurement.infiltration_height
if height.nil?
- height = hpxml.inferred_infiltration_height(volume)
+ height = hpxml_bldg.inferred_infiltration_height(volume)
end
sla, ach50, nach = nil
if [HPXML::UnitsACH, HPXML::UnitsCFM].include?(measurement.unit_of_measure)
if measurement.unit_of_measure == HPXML::UnitsACH
@@ -212,13 +217,13 @@
a_ext = 1.0 if a_ext.nil?
return sla, ach50, nach, volume, height, a_ext
end
- def self.get_default_mech_vent_flow_rate(hpxml, vent_fan, weather, cfa, nbeds)
+ def self.get_default_mech_vent_flow_rate(hpxml_bldg, vent_fan, weather, cfa, nbeds)
# Calculates Qfan cfm requirement per ASHRAE 62.2-2019
- sla, _ach50, _nach, _volume, height, a_ext = get_values_from_air_infiltration_measurements(hpxml, cfa, weather)
+ sla, _ach50, _nach, _volume, height, a_ext = get_values_from_air_infiltration_measurements(hpxml_bldg, cfa, weather)
nl = get_infiltration_NL_from_SLA(sla, height)
q_inf = nl * weather.data.WSF * cfa / 7.3 # Effective annual average infiltration rate, cfm, eq. 4.5a
q_tot = get_mech_vent_qtot_cfm(nbeds, cfa)
@@ -339,12 +344,12 @@
whf_avail_sensor.setKeyName(whf_avail_sch.name.to_s)
whf_avail_sensors[vent_whf.id] = whf_avail_sensor
end
# Sensors
- if @living_zone.thermostatSetpointDualSetpoint.is_initialized
- thermostat = @living_zone.thermostatSetpointDualSetpoint.get
+ if @conditioned_zone.thermostatSetpointDualSetpoint.is_initialized
+ thermostat = @conditioned_zone.thermostatSetpointDualSetpoint.get
htg_sp_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Schedule Value')
htg_sp_sensor.setName('htg sp s')
htg_sp_sensor.setKeyName(thermostat.heatingSetpointTemperatureSchedule.get.name.to_s)
@@ -355,27 +360,29 @@
# Actuators
nv_flow = OpenStudio::Model::SpaceInfiltrationDesignFlowRate.new(model)
nv_flow.setName(Constants.ObjectNameNaturalVentilation + ' flow')
nv_flow.setSchedule(model.alwaysOnDiscreteSchedule)
- nv_flow.setSpace(@living_space)
+ nv_flow.setSpace(@conditioned_space)
nv_flow_actuator = OpenStudio::Model::EnergyManagementSystemActuator.new(nv_flow, *EPlus::EMSActuatorZoneInfiltrationFlowRate)
nv_flow_actuator.setName("#{nv_flow.name} act")
+ nv_flow.additionalProperties.setFeature('ObjectType', Constants.ObjectNameNaturalVentilation)
whf_flow = OpenStudio::Model::SpaceInfiltrationDesignFlowRate.new(model)
whf_flow.setName(Constants.ObjectNameWholeHouseFan + ' flow')
whf_flow.setSchedule(model.alwaysOnDiscreteSchedule)
- whf_flow.setSpace(@living_space)
+ whf_flow.setSpace(@conditioned_space)
whf_flow_actuator = OpenStudio::Model::EnergyManagementSystemActuator.new(whf_flow, *EPlus::EMSActuatorZoneInfiltrationFlowRate)
whf_flow_actuator.setName("#{whf_flow.name} act")
+ whf_flow.additionalProperties.setFeature('ObjectType', Constants.ObjectNameWholeHouseFan)
# Electric Equipment (for whole house fan electricity consumption)
whf_equip_def = OpenStudio::Model::ElectricEquipmentDefinition.new(model)
whf_equip_def.setName(Constants.ObjectNameWholeHouseFan)
whf_equip = OpenStudio::Model::ElectricEquipment.new(whf_equip_def)
whf_equip.setName(Constants.ObjectNameWholeHouseFan)
- whf_equip.setSpace(@living_space) # no heat gain, so assign the equipment to an arbitrary space
+ whf_equip.setSpace(@conditioned_space) # no heat gain, so assign the equipment to an arbitrary space
whf_equip_def.setFractionRadiant(0)
whf_equip_def.setFractionLatent(0)
whf_equip_def.setFractionLost(1)
whf_equip.setSchedule(model.alwaysOnDiscreteSchedule)
whf_equip.setEndUseSubcategory(Constants.ObjectNameWholeHouseFan)
@@ -388,29 +395,30 @@
whf_zone = @spaces[HPXML::LocationAtticVented].thermalZone.get
elsif not @spaces[HPXML::LocationAtticUnvented].nil?
whf_zone = @spaces[HPXML::LocationAtticUnvented].thermalZone.get
end
if not whf_zone.nil?
- # Air from living to WHF zone (attic)
+ # Air from conditioned space to WHF zone (attic)
zone_mixing = OpenStudio::Model::ZoneMixing.new(whf_zone)
zone_mixing.setName("#{Constants.ObjectNameWholeHouseFan} mix")
- zone_mixing.setSourceZone(@living_zone)
- liv_to_zone_flow_rate_actuator = OpenStudio::Model::EnergyManagementSystemActuator.new(zone_mixing, *EPlus::EMSActuatorZoneMixingFlowRate)
- liv_to_zone_flow_rate_actuator.setName("#{zone_mixing.name} act")
+ zone_mixing.setSourceZone(@conditioned_zone)
+ cond_to_zone_flow_rate_actuator = OpenStudio::Model::EnergyManagementSystemActuator.new(zone_mixing, *EPlus::EMSActuatorZoneMixingFlowRate)
+ cond_to_zone_flow_rate_actuator.setName("#{zone_mixing.name} act")
end
area = 0.6 * open_window_area # ft^2, for Sherman-Grimsrud
max_rate = 20.0 # Air Changes per hour
max_flow_rate = max_rate * infil_volume / UnitConversions.convert(1.0, 'hr', 'min')
neutral_level = 0.5
hor_lk_frac = 0.0
- c_w, c_s = calc_wind_stack_coeffs(site, hor_lk_frac, neutral_level, @living_space, infil_height)
+ c_w, c_s = calc_wind_stack_coeffs(site, hor_lk_frac, neutral_level, @conditioned_space, infil_height)
max_oa_hr = 0.0115 # From BA HSP
max_oa_rh = 0.7 # From BA HSP
# Program
vent_program = OpenStudio::Model::EnergyManagementSystemProgram.new(model)
+ vent_program.additionalProperties.setFeature('ObjectType', Constants.ObjectNameNaturalVentilation)
vent_program.setName(Constants.ObjectNameNaturalVentilation + ' program')
vent_program.addLine("Set Tin = #{@tin_sensor.name}")
vent_program.addLine("Set Tout = #{@tout_sensor.name}")
vent_program.addLine("Set Wout = #{@wout_sensor.name}")
vent_program.addLine("Set Pbar = #{@pbar_sensor.name}")
@@ -428,11 +436,11 @@
end
vent_program.addLine("Set NVavail = #{nv_avail_sensor.name}")
vent_program.addLine("Set ClgSsnAvail = #{nv_clg_ssn_sensor.name}")
vent_program.addLine("Set #{nv_flow_actuator.name} = 0") # Init
vent_program.addLine("Set #{whf_flow_actuator.name} = 0") # Init
- vent_program.addLine("Set #{liv_to_zone_flow_rate_actuator.name} = 0") unless whf_zone.nil? # Init
+ vent_program.addLine("Set #{cond_to_zone_flow_rate_actuator.name} = 0") unless whf_zone.nil? # Init
vent_program.addLine("Set #{whf_elec_actuator.name} = 0") # Init
infil_constraints = 'If ((Wout < MaxHR) && (Phiout < MaxRH) && (Tin > Tout) && (Tin > Tnvsp) && (ClgSsnAvail > 0))'
if not @hvac_availability_sensor.nil?
# We are using the availability schedule, but we also constrain the window opening based on temperatures and humidity.
# We're assuming that if the HVAC is not available, you'd ignore the humidity constraints we normally put on window opening per the old HSP guidance (RH < 70% and w < 0.015).
@@ -447,11 +455,11 @@
vent_program.addLine(' Set Adj = (Tin-Tnvsp)/(Tin-Tout)')
vent_program.addLine(' Set Adj = (@Min Adj 1)')
vent_program.addLine(' Set Adj = (@Max Adj 0)')
vent_program.addLine(' If (WHF_Flow > 0)') # If available, prioritize whole house fan
vent_program.addLine(" Set #{whf_flow_actuator.name} = WHF_Flow*Adj")
- vent_program.addLine(" Set #{liv_to_zone_flow_rate_actuator.name} = WHF_Flow*Adj") unless whf_zone.nil?
+ vent_program.addLine(" Set #{cond_to_zone_flow_rate_actuator.name} = WHF_Flow*Adj") unless whf_zone.nil?
vent_program.addLine(' Set WHF_W = 0')
vent_fans_whf.each do |vent_whf|
vent_program.addLine(" Set WHF_W = WHF_W + #{vent_whf.fan_power} * #{whf_avail_sensors[vent_whf.id].name}")
end
vent_program.addLine(" Set #{whf_elec_actuator.name} = WHF_W*Adj")
@@ -470,12 +478,25 @@
manager = OpenStudio::Model::EnergyManagementSystemProgramCallingManager.new(model)
manager.setName("#{vent_program.name} calling manager")
manager.setCallingPoint('BeginZoneTimestepAfterInitHeatBalance')
manager.addProgram(vent_program)
+
+ create_timeseries_flowrate_ems_output_var(model, nv_flow_actuator.name.to_s, vent_program)
+ create_timeseries_flowrate_ems_output_var(model, whf_flow_actuator.name.to_s, vent_program)
end
+ def self.create_timeseries_flowrate_ems_output_var(model, ems_var_name, ems_program)
+ # This is only used to report timeseries flow rates when requested
+ ems_output_var = OpenStudio::Model::EnergyManagementSystemOutputVariable.new(model, ems_var_name)
+ ems_output_var.setName("#{ems_var_name}_timeseries_outvar")
+ ems_output_var.setTypeOfDataInVariable('Averaged')
+ ems_output_var.setUpdateFrequency('ZoneTimestep')
+ ems_output_var.setEMSProgramOrSubroutineName(ems_program)
+ ems_output_var.setUnits('m^/s')
+ end
+
def self.create_nv_and_whf_avail_sch(model, obj_name, num_days_per_week, unavailable_periods = [])
avail_sch = OpenStudio::Model::ScheduleRuleset.new(model)
sch_name = "#{obj_name} schedule"
avail_sch.setName(sch_name)
avail_sch.defaultDaySchedule.setName("#{sch_name} default day")
@@ -497,13 +518,14 @@
year = model.getYearDescription.assumedYear
Schedule.set_unavailable_periods(avail_sch, sch_name, unavailable_periods, year)
return avail_sch
end
- def self.create_return_air_duct_zone(model, loop_name)
+ def self.create_return_air_duct_zone(model, loop_name, unit_multiplier)
# Create the return air plenum zone, space
ra_duct_zone = OpenStudio::Model::ThermalZone.new(model)
+ ra_duct_zone.setMultiplier(unit_multiplier)
ra_duct_zone.setName(loop_name + ' ret air zone')
ra_duct_zone.setVolume(1.0)
ra_duct_polygon = OpenStudio::Point3dVector.new
ra_duct_polygon << OpenStudio::Point3d.new(0, 0, 0)
@@ -537,11 +559,11 @@
end
return ra_duct_zone
end
- def self.create_other_equipment_object_and_actuator(model:, name:, space:, frac_lat:, frac_lost:, hpxml_fuel_type: nil, end_use: nil, is_duct_load_for_report: nil)
+ def self.create_other_equipment_object_and_actuator(model:, name:, space:, frac_lat:, frac_lost:, hpxml_fuel_type: nil, end_use: nil)
other_equip_def = OpenStudio::Model::OtherEquipmentDefinition.new(model)
other_equip_def.setName("#{name} equip")
other_equip = OpenStudio::Model::OtherEquipment.new(other_equip_def)
other_equip.setName(other_equip_def.name.to_s)
if hpxml_fuel_type.nil?
@@ -557,13 +579,10 @@
other_equip_def.setFractionLost(frac_lost)
other_equip_def.setFractionLatent(frac_lat)
other_equip_def.setFractionRadiant(0.0)
actuator = OpenStudio::Model::EnergyManagementSystemActuator.new(other_equip, *EPlus::EMSActuatorOtherEquipmentPower, other_equip.space.get)
actuator.setName("#{other_equip.name} act")
- if not is_duct_load_for_report.nil?
- other_equip.additionalProperties.setFeature(Constants.IsDuctLoadForReport, is_duct_load_for_report)
- end
return actuator
end
def self.initialize_cfis(model, vent_fans_mech, airloop_map, unavailable_periods)
# Get AirLoop associated with CFIS
@@ -652,11 +671,11 @@
else
fail "Unexpected fan: #{supply_fan.name}"
end
end
- def self.apply_ducts(model, ducts, object, vent_fans_mech)
+ def self.apply_ducts(model, ducts, object, vent_fans_mech, unit_multiplier)
ducts.each do |duct|
if not duct.loc_schedule.nil?
# Pass MF space temperature schedule name
duct.location = duct.loc_schedule.name.to_s
elsif not duct.loc_space.nil?
@@ -672,20 +691,20 @@
if object.is_a? OpenStudio::Model::AirLoopHVAC
# Most system types
# Set the return plenum
- ra_duct_zone = create_return_air_duct_zone(model, object.name.to_s)
+ ra_duct_zone = create_return_air_duct_zone(model, object.name.to_s, unit_multiplier)
ra_duct_space = ra_duct_zone.spaces[0]
- @living_zone.setReturnPlenum(ra_duct_zone, object)
+ @conditioned_zone.setReturnPlenum(ra_duct_zone, object)
inlet_node = object.demandInletNode
elsif object.is_a? OpenStudio::Model::ZoneHVACFourPipeFanCoil
# Ducted fan coil
# No return plenum
- ra_duct_space = @living_space
+ ra_duct_space = @conditioned_space
inlet_node = object.inletNode.get
end
# -- Sensors --
@@ -712,45 +731,45 @@
ah_wout_var = OpenStudio::Model::EnergyManagementSystemGlobalVariable.new(model, "#{object.name} AH Wout".gsub(' ', '_'))
ah_wout_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'System Node Humidity Ratio')
ah_wout_sensor.setName("#{ah_wout_var.name} s")
ah_wout_sensor.setKeyName(inlet_node.name.to_s)
- living_zone_return_air_node = nil
- @living_zone.returnAirModelObjects.each do |return_air_model_obj|
+ conditioned_zone_return_air_node = nil
+ @conditioned_zone.returnAirModelObjects.each do |return_air_model_obj|
next if return_air_model_obj.to_Node.get.airLoopHVAC.get != object
- living_zone_return_air_node = return_air_model_obj
+ conditioned_zone_return_air_node = return_air_model_obj
end
# Return air temperature
ra_t_var = OpenStudio::Model::EnergyManagementSystemGlobalVariable.new(model, "#{object.name} RA T".gsub(' ', '_'))
- if not living_zone_return_air_node.nil?
+ if not conditioned_zone_return_air_node.nil?
ra_t_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'System Node Temperature')
ra_t_sensor.setName("#{ra_t_var.name} s")
- ra_t_sensor.setKeyName(living_zone_return_air_node.name.to_s)
+ ra_t_sensor.setKeyName(conditioned_zone_return_air_node.name.to_s)
else
ra_t_sensor = @tin_sensor
end
# Return air humidity ratio
ra_w_var = OpenStudio::Model::EnergyManagementSystemGlobalVariable.new(model, "#{object.name} Ra W".gsub(' ', '_'))
- if not living_zone_return_air_node.nil?
+ if not conditioned_zone_return_air_node.nil?
ra_w_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'System Node Humidity Ratio')
ra_w_sensor.setName("#{ra_w_var.name} s")
- ra_w_sensor.setKeyName(living_zone_return_air_node.name.to_s)
+ ra_w_sensor.setKeyName(conditioned_zone_return_air_node.name.to_s)
else
ra_w_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Zone Mean Air Humidity Ratio')
ra_w_sensor.setName("#{ra_w_var.name} s")
- ra_w_sensor.setKeyName(@living_zone.name.to_s)
+ ra_w_sensor.setKeyName(@conditioned_zone.name.to_s)
end
# Get duct located zone or ambient temperature schedule objects
duct_locations = ducts.map { |duct| if duct.zone.nil? then duct.loc_schedule else duct.zone end }.uniq
# Create one duct program for each duct location zone
duct_locations.each_with_index do |duct_location, i|
- next if (not duct_location.nil?) && (duct_location.name.to_s == @living_zone.name.to_s)
+ next if (not duct_location.nil?) && (duct_location.name.to_s == @conditioned_zone.name.to_s)
object_name_idx = "#{object.name}_#{i}"
# -- Sensors --
@@ -782,19 +801,19 @@
dz_w_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Site Outdoor Air Humidity Ratio')
dz_w_sensor.setName("#{dz_w_var.name} s")
dz_w = "#{dz_w_sensor.name}"
elsif duct_location.name.to_s == HPXML::LocationOtherHousingUnit
dz_w_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Zone Mean Air Humidity Ratio')
- dz_w_sensor.setKeyName(@living_zone.name.to_s)
+ dz_w_sensor.setKeyName(@conditioned_zone.name.to_s)
dz_w_sensor.setName("#{dz_w_var.name} s")
dz_w = "#{dz_w_sensor.name}"
else
dz_w_sensor1 = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Site Outdoor Air Humidity Ratio')
dz_w_sensor1.setName("#{dz_w_var.name} s 1")
dz_w_sensor2 = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Zone Mean Air Humidity Ratio')
dz_w_sensor2.setName("#{dz_w_var.name} s 2")
- dz_w_sensor2.setKeyName(@living_zone.name.to_s)
+ dz_w_sensor2.setKeyName(@conditioned_zone.name.to_s)
dz_w = "(#{dz_w_sensor1.name} + #{dz_w_sensor2.name}) / 2"
end
else
dz_w_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Site Outdoor Air Humidity Ratio')
dz_w_sensor.setName("#{dz_w_var.name} s")
@@ -812,51 +831,51 @@
else
f_regain = 0.0
end
# Other equipment objects to cancel out the supply air leakage directly into the return plenum
- equip_act_infos << ['supply_sens_lk_to_liv', 'SupSensLkToLv', true, @living_space, 0.0, f_regain]
- equip_act_infos << ['supply_lat_lk_to_liv', 'SupLatLkToLv', true, @living_space, 1.0 - f_regain, f_regain]
+ equip_act_infos << ['supply_sens_lk_to_cond', 'SupSensLkToCond', Constants.ObjectNameDuctLoad, @conditioned_space, 0.0, f_regain]
+ equip_act_infos << ['supply_lat_lk_to_cond', 'SupLatLkToCond', Constants.ObjectNameDuctLoad, @conditioned_space, 1.0 - f_regain, f_regain]
- # Supply duct conduction load added to the living space
- equip_act_infos << ['supply_cond_to_liv', 'SupCondToLv', true, @living_space, 0.0, f_regain]
+ # Supply duct conduction load added to the conditioned space
+ equip_act_infos << ['supply_cond_to_cond', 'SupCondToLv', Constants.ObjectNameDuctLoad, @conditioned_space, 0.0, f_regain]
# Return duct conduction load added to the return plenum zone
- equip_act_infos << ['return_cond_to_rp', 'RetCondToRP', true, ra_duct_space, 0.0, f_regain]
+ equip_act_infos << ['return_cond_to_rp', 'RetCondToRP', Constants.ObjectNameDuctLoad, ra_duct_space, 0.0, f_regain]
# Return duct sensible leakage impact on the return plenum
- equip_act_infos << ['return_sens_lk_to_rp', 'RetSensLkToRP', true, ra_duct_space, 0.0, f_regain]
+ equip_act_infos << ['return_sens_lk_to_rp', 'RetSensLkToRP', Constants.ObjectNameDuctLoad, ra_duct_space, 0.0, f_regain]
# Return duct latent leakage impact on the return plenum
- equip_act_infos << ['return_lat_lk_to_rp', 'RetLatLkToRP', true, ra_duct_space, 1.0 - f_regain, f_regain]
+ equip_act_infos << ['return_lat_lk_to_rp', 'RetLatLkToRP', Constants.ObjectNameDuctLoad, ra_duct_space, 1.0 - f_regain, f_regain]
# Supply duct conduction impact on the duct zone
if not duct_location.is_a? OpenStudio::Model::ThermalZone # Outside or scheduled temperature
- equip_act_infos << ['supply_cond_to_dz', 'SupCondToDZ', false, @living_space, 0.0, 1.0] # Arbitrary space, all heat lost
+ equip_act_infos << ['supply_cond_to_dz', 'SupCondToDZ', nil, @conditioned_space, 0.0, 1.0] # Arbitrary space, all heat lost
else
- equip_act_infos << ['supply_cond_to_dz', 'SupCondToDZ', false, duct_location.spaces[0], 0.0, 0.0]
+ equip_act_infos << ['supply_cond_to_dz', 'SupCondToDZ', nil, duct_location.spaces[0], 0.0, 0.0]
end
# Return duct conduction impact on the duct zone
if not duct_location.is_a? OpenStudio::Model::ThermalZone # Outside or scheduled temperature
- equip_act_infos << ['return_cond_to_dz', 'RetCondToDZ', false, @living_space, 0.0, 1.0] # Arbitrary space, all heat lost
+ equip_act_infos << ['return_cond_to_dz', 'RetCondToDZ', nil, @conditioned_space, 0.0, 1.0] # Arbitrary space, all heat lost
else
- equip_act_infos << ['return_cond_to_dz', 'RetCondToDZ', false, duct_location.spaces[0], 0.0, 0.0]
+ equip_act_infos << ['return_cond_to_dz', 'RetCondToDZ', nil, duct_location.spaces[0], 0.0, 0.0]
end
# Supply duct sensible leakage impact on the duct zone
if not duct_location.is_a? OpenStudio::Model::ThermalZone # Outside or scheduled temperature
- equip_act_infos << ['supply_sens_lk_to_dz', 'SupSensLkToDZ', false, @living_space, 0.0, 1.0] # Arbitrary space, all heat lost
+ equip_act_infos << ['supply_sens_lk_to_dz', 'SupSensLkToDZ', nil, @conditioned_space, 0.0, 1.0] # Arbitrary space, all heat lost
else
- equip_act_infos << ['supply_sens_lk_to_dz', 'SupSensLkToDZ', false, duct_location.spaces[0], 0.0, 0.0]
+ equip_act_infos << ['supply_sens_lk_to_dz', 'SupSensLkToDZ', nil, duct_location.spaces[0], 0.0, 0.0]
end
# Supply duct latent leakage impact on the duct zone
if not duct_location.is_a? OpenStudio::Model::ThermalZone # Outside or scheduled temperature
- equip_act_infos << ['supply_lat_lk_to_dz', 'SupLatLkToDZ', false, @living_space, 0.0, 1.0] # Arbitrary space, all heat lost
+ equip_act_infos << ['supply_lat_lk_to_dz', 'SupLatLkToDZ', nil, @conditioned_space, 0.0, 1.0] # Arbitrary space, all heat lost
else
- equip_act_infos << ['supply_lat_lk_to_dz', 'SupLatLkToDZ', false, duct_location.spaces[0], 1.0, 0.0]
+ equip_act_infos << ['supply_lat_lk_to_dz', 'SupLatLkToDZ', nil, duct_location.spaces[0], 1.0, 0.0]
end
duct_vars = {}
duct_actuators = {}
[false, true].each do |is_cfis|
@@ -868,38 +887,38 @@
prefix = ''
end
equip_act_infos.each do |act_info|
var_name = "#{prefix}#{act_info[0]}"
object_name = "#{object_name_idx} #{prefix}#{act_info[1]}".gsub(' ', '_')
- is_load_for_report = act_info[2]
+ end_use = act_info[2]
space = act_info[3]
if is_cfis && (space == ra_duct_space)
# Move all CFIS return duct losses to the conditioned space so as to avoid extreme plenum temperatures
# due to mismatch between return plenum duct loads and airloop airflow rate (which does not actually
# increase due to the presence of CFIS).
- space = @living_space
+ space = @conditioned_space
end
frac_lat = act_info[4]
frac_lost = act_info[5]
if not is_cfis
duct_vars[var_name] = OpenStudio::Model::EnergyManagementSystemGlobalVariable.new(model, object_name)
end
- duct_actuators[var_name] = create_other_equipment_object_and_actuator(model: model, name: object_name, space: space, frac_lat: frac_lat, frac_lost: frac_lost, is_duct_load_for_report: is_load_for_report)
+ duct_actuators[var_name] = create_other_equipment_object_and_actuator(model: model, name: object_name, space: space, frac_lat: frac_lat, frac_lost: frac_lost, end_use: end_use)
end
end
- # Two objects are required to model the air exchange between the duct zone and the living space since
+ # Two objects are required to model the air exchange between the duct zone and the conditioned space since
# ZoneMixing objects can not account for direction of air flow (both are controlled by EMS)
# List of: [Var name, object name, space, frac load latent, frac load outside]
mix_act_infos = []
if duct_location.is_a? OpenStudio::Model::ThermalZone
- # Accounts for leaks from the duct zone to the living zone
- mix_act_infos << ['dz_to_liv_flow_rate', 'ZoneMixDZToLv', @living_zone, duct_location]
- # Accounts for leaks from the living zone to the duct zone
- mix_act_infos << ['liv_to_dz_flow_rate', 'ZoneMixLvToDZ', duct_location, @living_zone]
+ # Accounts for leaks from the duct zone to the conditioned zone
+ mix_act_infos << ['dz_to_cond_flow_rate', 'ZoneMixDZToCond', @conditioned_zone, duct_location]
+ # Accounts for leaks from the conditioned zone to the duct zone
+ mix_act_infos << ['cond_to_dz_flow_rate', 'ZoneMixCondToDZ', duct_location, @conditioned_zone]
end
[false, true].each do |is_cfis|
if is_cfis
next unless @cfis_airloop.values.include? object
@@ -920,10 +939,11 @@
zone_mixing = OpenStudio::Model::ZoneMixing.new(dest_zone)
zone_mixing.setName("#{object_name} mix")
zone_mixing.setSourceZone(source_zone)
duct_actuators[var_name] = OpenStudio::Model::EnergyManagementSystemActuator.new(zone_mixing, *EPlus::EMSActuatorZoneMixingFlowRate)
duct_actuators[var_name].setName("#{zone_mixing.name} act")
+ zone_mixing.additionalProperties.setFeature('ObjectType', Constants.ObjectNameDuctLoad)
end
end
# -- Global Variables --
@@ -963,54 +983,54 @@
# Duct Subroutine
duct_subroutine = OpenStudio::Model::EnergyManagementSystemSubroutine.new(model)
duct_subroutine.setName("#{object_name_idx} duct subroutine")
- duct_subroutine.addLine("Set AH_MFR = #{ah_mfr_var.name}")
+ duct_subroutine.addLine("Set AH_MFR = #{ah_mfr_var.name} / #{unit_multiplier}")
duct_subroutine.addLine('If AH_MFR>0')
duct_subroutine.addLine(" Set AH_Tout = #{ah_tout_var.name}")
duct_subroutine.addLine(" Set AH_Wout = #{ah_wout_var.name}")
duct_subroutine.addLine(" Set RA_T = #{ra_t_var.name}")
duct_subroutine.addLine(" Set RA_W = #{ra_w_var.name}")
duct_subroutine.addLine(" Set Fan_RTF = #{@fan_rtf_var[object].name}")
duct_subroutine.addLine(" Set DZ_T = #{dz_t_var.name}")
duct_subroutine.addLine(" Set DZ_W = #{dz_w_var.name}")
- duct_subroutine.addLine(" Set AH_VFR = #{ah_vfr_var.name}")
+ duct_subroutine.addLine(" Set AH_VFR = #{ah_vfr_var.name} / #{unit_multiplier}")
duct_subroutine.addLine(' Set h_SA = (@HFnTdbW AH_Tout AH_Wout)') # J/kg
duct_subroutine.addLine(' Set h_RA = (@HFnTdbW RA_T RA_W)') # J/kg
duct_subroutine.addLine(' Set h_fg = (@HfgAirFnWTdb AH_Wout AH_Tout)') # J/kg
duct_subroutine.addLine(' Set h_DZ = (@HFnTdbW DZ_T DZ_W)') # J/kg
duct_subroutine.addLine(' Set air_cp = 1006.0') # J/kg-C
if not leakage_fracs[HPXML::DuctTypeSupply].nil?
duct_subroutine.addLine(" Set f_sup = #{leakage_fracs[HPXML::DuctTypeSupply]}") # frac
elsif not leakage_cfm25s[HPXML::DuctTypeSupply].nil?
- duct_subroutine.addLine(" Set f_sup = #{UnitConversions.convert(leakage_cfm25s[HPXML::DuctTypeSupply], 'cfm', 'm^3/s').round(6)} / (#{@fan_mfr_max_var[object].name} * 1.0135)") # frac
+ duct_subroutine.addLine(" Set f_sup = #{UnitConversions.convert(leakage_cfm25s[HPXML::DuctTypeSupply], 'cfm', 'm^3/s').round(6)} / (#{@fan_mfr_max_var[object].name}/#{unit_multiplier} * 1.0135)") # frac
else
duct_subroutine.addLine(' Set f_sup = 0.0') # frac
end
if not leakage_fracs[HPXML::DuctTypeReturn].nil?
duct_subroutine.addLine(" Set f_ret = #{leakage_fracs[HPXML::DuctTypeReturn]}") # frac
elsif not leakage_cfm25s[HPXML::DuctTypeReturn].nil?
- duct_subroutine.addLine(" Set f_ret = #{UnitConversions.convert(leakage_cfm25s[HPXML::DuctTypeReturn], 'cfm', 'm^3/s').round(6)} / (#{@fan_mfr_max_var[object].name} * 1.0135)") # frac
+ duct_subroutine.addLine(" Set f_ret = #{UnitConversions.convert(leakage_cfm25s[HPXML::DuctTypeReturn], 'cfm', 'm^3/s').round(6)} / (#{@fan_mfr_max_var[object].name}/#{unit_multiplier} * 1.0135)") # frac
else
duct_subroutine.addLine(' Set f_ret = 0.0') # frac
end
duct_subroutine.addLine(' Set sup_lk_mfr = f_sup * AH_MFR') # kg/s
duct_subroutine.addLine(' Set ret_lk_mfr = f_ret * AH_MFR') # kg/s
- # Supply leakage to living
- duct_subroutine.addLine(' Set SupTotLkToLiv = sup_lk_mfr*(h_RA - h_SA)') # W
- duct_subroutine.addLine(' Set SupLatLkToLv = sup_lk_mfr*h_fg*(RA_W-AH_Wout)') # W
- duct_subroutine.addLine(' Set SupSensLkToLv = SupTotLkToLiv-SupLatLkToLv') # W
+ # Supply leakage to conditioned space
+ duct_subroutine.addLine(' Set SupTotLkToCond = sup_lk_mfr*(h_RA - h_SA)') # W
+ duct_subroutine.addLine(' Set SupLatLkToCond = sup_lk_mfr*h_fg*(RA_W-AH_Wout)') # W
+ duct_subroutine.addLine(' Set SupSensLkToCond = SupTotLkToCond-SupLatLkToCond') # W
# Supply conduction
duct_subroutine.addLine(" Set supply_ua = #{UnitConversions.convert(ua_values[HPXML::DuctTypeSupply], 'Btu/(hr*F)', 'W/K').round(3)}")
duct_subroutine.addLine(' Set eTm = 0-((Fan_RTF/(AH_MFR*air_cp))*supply_ua)')
duct_subroutine.addLine(' Set t_sup = DZ_T+((AH_Tout-DZ_T)*(@Exp eTm))') # deg-C
- duct_subroutine.addLine(' Set SupCondToLv = AH_MFR*air_cp*(t_sup-AH_Tout)') # W
- duct_subroutine.addLine(' Set SupCondToDZ = 0-SupCondToLv') # W
+ duct_subroutine.addLine(' Set SupCondToCond = AH_MFR*air_cp*(t_sup-AH_Tout)') # W
+ duct_subroutine.addLine(' Set SupCondToDZ = 0-SupCondToCond') # W
# Return conduction
duct_subroutine.addLine(" Set return_ua = #{UnitConversions.convert(ua_values[HPXML::DuctTypeReturn], 'Btu/(hr*F)', 'W/K').round(3)}")
duct_subroutine.addLine(' Set eTm = 0-((Fan_RTF/(AH_MFR*air_cp))*return_ua)')
duct_subroutine.addLine(' Set t_ret = DZ_T+((RA_T-DZ_T)*(@Exp eTm))') # deg-C
@@ -1020,57 +1040,57 @@
# Return leakage to return plenum
duct_subroutine.addLine(' Set RetLatLkToRP = 0') # W
duct_subroutine.addLine(' Set RetSensLkToRP = ret_lk_mfr*air_cp*(DZ_T-RA_T)') # W
# Supply leakage to duct zone
- # The below terms are not the same as SupLatLkToLv and SupSensLkToLv.
+ # The below terms are not the same as SupLatLkToCond and SupSensLkToCond.
# To understand why, suppose the AHzone temperature equals the supply air temperature. In this case, the terms below
- # should be zero while SupLatLkToLv and SupSensLkToLv should still be non-zero.
+ # should be zero while SupLatLkToCond and SupSensLkToCond should still be non-zero.
duct_subroutine.addLine(' Set SupTotLkToDZ = sup_lk_mfr*(h_SA-h_DZ)') # W
duct_subroutine.addLine(' Set SupLatLkToDZ = sup_lk_mfr*h_fg*(AH_Wout-DZ_W)') # W
duct_subroutine.addLine(' Set SupSensLkToDZ = SupTotLkToDZ-SupLatLkToDZ') # W
duct_subroutine.addLine(' Set f_imbalance = f_sup-f_ret') # frac
duct_subroutine.addLine(" Set oa_vfr = #{f_oa} * f_imbalance * AH_VFR") # m3/s
duct_subroutine.addLine(' Set sup_lk_vfr = f_sup * AH_VFR') # m3/s
duct_subroutine.addLine(' Set ret_lk_vfr = f_ret * AH_VFR') # m3/s
- duct_subroutine.addLine(' If f_sup > f_ret') # Living zone is depressurized relative to duct zone
- duct_subroutine.addLine(' Set ZoneMixLvToDZ = 0') # m3/s
- duct_subroutine.addLine(' Set ZoneMixDZToLv = (sup_lk_vfr-ret_lk_vfr)-oa_vfr') # m3/s
- duct_subroutine.addLine(' Else') # Living zone is pressurized relative to duct zone
- duct_subroutine.addLine(' Set ZoneMixLvToDZ = (ret_lk_vfr-sup_lk_vfr)+oa_vfr') # m3/s
- duct_subroutine.addLine(' Set ZoneMixDZToLv = 0') # m3/s
+ duct_subroutine.addLine(' If f_sup > f_ret') # Conditioned zone is depressurized relative to duct zone
+ duct_subroutine.addLine(' Set ZoneMixCondToDZ = 0') # m3/s
+ duct_subroutine.addLine(' Set ZoneMixDZToCond = (sup_lk_vfr-ret_lk_vfr)-oa_vfr') # m3/s
+ duct_subroutine.addLine(' Else') # Conditioned zone is pressurized relative to duct zone
+ duct_subroutine.addLine(' Set ZoneMixCondToDZ = (ret_lk_vfr-sup_lk_vfr)+oa_vfr') # m3/s
+ duct_subroutine.addLine(' Set ZoneMixDZToCond = 0') # m3/s
duct_subroutine.addLine(' EndIf')
duct_subroutine.addLine('Else') # No air handler flow rate
- duct_subroutine.addLine(' Set SupLatLkToLv = 0')
- duct_subroutine.addLine(' Set SupSensLkToLv = 0')
- duct_subroutine.addLine(' Set SupCondToLv = 0')
+ duct_subroutine.addLine(' Set SupLatLkToCond = 0')
+ duct_subroutine.addLine(' Set SupSensLkToCond = 0')
+ duct_subroutine.addLine(' Set SupCondToCond = 0')
duct_subroutine.addLine(' Set RetCondToRP = 0')
duct_subroutine.addLine(' Set RetLatLkToRP = 0')
duct_subroutine.addLine(' Set RetSensLkToRP = 0')
duct_subroutine.addLine(' Set RetCondToDZ = 0')
duct_subroutine.addLine(' Set SupCondToDZ = 0')
duct_subroutine.addLine(' Set SupLatLkToDZ = 0')
duct_subroutine.addLine(' Set SupSensLkToDZ = 0')
- duct_subroutine.addLine(' Set ZoneMixLvToDZ = 0') # m3/s
- duct_subroutine.addLine(' Set ZoneMixDZToLv = 0') # m3/s
+ duct_subroutine.addLine(' Set ZoneMixCondToDZ = 0') # m3/s
+ duct_subroutine.addLine(' Set ZoneMixDZToCond = 0') # m3/s
duct_subroutine.addLine('EndIf')
- duct_subroutine.addLine("Set #{duct_vars['supply_lat_lk_to_liv'].name} = SupLatLkToLv")
- duct_subroutine.addLine("Set #{duct_vars['supply_sens_lk_to_liv'].name} = SupSensLkToLv")
- duct_subroutine.addLine("Set #{duct_vars['supply_cond_to_liv'].name} = SupCondToLv")
+ duct_subroutine.addLine("Set #{duct_vars['supply_lat_lk_to_cond'].name} = SupLatLkToCond")
+ duct_subroutine.addLine("Set #{duct_vars['supply_sens_lk_to_cond'].name} = SupSensLkToCond")
+ duct_subroutine.addLine("Set #{duct_vars['supply_cond_to_cond'].name} = SupCondToCond")
duct_subroutine.addLine("Set #{duct_vars['return_cond_to_rp'].name} = RetCondToRP")
duct_subroutine.addLine("Set #{duct_vars['return_lat_lk_to_rp'].name} = RetLatLkToRP")
duct_subroutine.addLine("Set #{duct_vars['return_sens_lk_to_rp'].name} = RetSensLkToRP")
duct_subroutine.addLine("Set #{duct_vars['return_cond_to_dz'].name} = RetCondToDZ")
duct_subroutine.addLine("Set #{duct_vars['supply_cond_to_dz'].name} = SupCondToDZ")
duct_subroutine.addLine("Set #{duct_vars['supply_lat_lk_to_dz'].name} = SupLatLkToDZ")
duct_subroutine.addLine("Set #{duct_vars['supply_sens_lk_to_dz'].name} = SupSensLkToDZ")
- if not duct_actuators['liv_to_dz_flow_rate'].nil?
- duct_subroutine.addLine("Set #{duct_vars['liv_to_dz_flow_rate'].name} = ZoneMixLvToDZ")
+ if not duct_actuators['cond_to_dz_flow_rate'].nil?
+ duct_subroutine.addLine("Set #{duct_vars['cond_to_dz_flow_rate'].name} = ZoneMixCondToDZ")
end
- if not duct_actuators['dz_to_liv_flow_rate'].nil?
- duct_subroutine.addLine("Set #{duct_vars['dz_to_liv_flow_rate'].name} = ZoneMixDZToLv")
+ if not duct_actuators['dz_to_cond_flow_rate'].nil?
+ duct_subroutine.addLine("Set #{duct_vars['dz_to_cond_flow_rate'].name} = ZoneMixDZToCond")
end
# Duct Program
duct_program = OpenStudio::Model::EnergyManagementSystemProgram.new(model)
@@ -1086,25 +1106,25 @@
duct_program.addLine("Set #{ra_t_var.name} = #{ra_t_sensor.name}")
duct_program.addLine("Set #{ra_w_var.name} = #{ra_w_sensor.name}")
duct_program.addLine("Set #{dz_t_var.name} = #{dz_t_sensor.name}")
duct_program.addLine("Set #{dz_w_var.name} = #{dz_w}")
duct_program.addLine("Run #{duct_subroutine.name}")
- duct_program.addLine("Set #{duct_actuators['supply_sens_lk_to_liv'].name} = #{duct_vars['supply_sens_lk_to_liv'].name}")
- duct_program.addLine("Set #{duct_actuators['supply_lat_lk_to_liv'].name} = #{duct_vars['supply_lat_lk_to_liv'].name}")
- duct_program.addLine("Set #{duct_actuators['supply_cond_to_liv'].name} = #{duct_vars['supply_cond_to_liv'].name}")
+ duct_program.addLine("Set #{duct_actuators['supply_sens_lk_to_cond'].name} = #{duct_vars['supply_sens_lk_to_cond'].name}")
+ duct_program.addLine("Set #{duct_actuators['supply_lat_lk_to_cond'].name} = #{duct_vars['supply_lat_lk_to_cond'].name}")
+ duct_program.addLine("Set #{duct_actuators['supply_cond_to_cond'].name} = #{duct_vars['supply_cond_to_cond'].name}")
duct_program.addLine("Set #{duct_actuators['return_sens_lk_to_rp'].name} = #{duct_vars['return_sens_lk_to_rp'].name}")
duct_program.addLine("Set #{duct_actuators['return_lat_lk_to_rp'].name} = #{duct_vars['return_lat_lk_to_rp'].name}")
duct_program.addLine("Set #{duct_actuators['return_cond_to_rp'].name} = #{duct_vars['return_cond_to_rp'].name}")
duct_program.addLine("Set #{duct_actuators['return_cond_to_dz'].name} = #{duct_vars['return_cond_to_dz'].name}")
duct_program.addLine("Set #{duct_actuators['supply_cond_to_dz'].name} = #{duct_vars['supply_cond_to_dz'].name}")
duct_program.addLine("Set #{duct_actuators['supply_sens_lk_to_dz'].name} = #{duct_vars['supply_sens_lk_to_dz'].name}")
duct_program.addLine("Set #{duct_actuators['supply_lat_lk_to_dz'].name} = #{duct_vars['supply_lat_lk_to_dz'].name}")
- if not duct_actuators['dz_to_liv_flow_rate'].nil?
- duct_program.addLine("Set #{duct_actuators['dz_to_liv_flow_rate'].name} = #{duct_vars['dz_to_liv_flow_rate'].name}")
+ if not duct_actuators['dz_to_cond_flow_rate'].nil?
+ duct_program.addLine("Set #{duct_actuators['dz_to_cond_flow_rate'].name} = #{duct_vars['dz_to_cond_flow_rate'].name}")
end
- if not duct_actuators['liv_to_dz_flow_rate'].nil?
- duct_program.addLine("Set #{duct_actuators['liv_to_dz_flow_rate'].name} = #{duct_vars['liv_to_dz_flow_rate'].name}")
+ if not duct_actuators['cond_to_dz_flow_rate'].nil?
+ duct_program.addLine("Set #{duct_actuators['cond_to_dz_flow_rate'].name} = #{duct_vars['cond_to_dz_flow_rate'].name}")
end
if @cfis_airloop.values.include? object
cfis_id = @cfis_airloop.key(object)
@@ -1122,43 +1142,43 @@
duct_program.addLine(" Set #{ah_tout_var.name} = #{ra_t_sensor.name}")
duct_program.addLine(" Set #{ah_wout_var.name} = #{ra_w_sensor.name}")
duct_program.addLine(" Set #{ra_t_var.name} = #{ra_t_sensor.name}")
duct_program.addLine(" Set #{ra_w_var.name} = #{ra_w_sensor.name}")
duct_program.addLine(" Run #{duct_subroutine.name}")
- duct_program.addLine(" Set #{duct_actuators['cfis_supply_sens_lk_to_liv'].name} = #{duct_vars['supply_sens_lk_to_liv'].name}")
- duct_program.addLine(" Set #{duct_actuators['cfis_supply_lat_lk_to_liv'].name} = #{duct_vars['supply_lat_lk_to_liv'].name}")
- duct_program.addLine(" Set #{duct_actuators['cfis_supply_cond_to_liv'].name} = #{duct_vars['supply_cond_to_liv'].name}")
+ duct_program.addLine(" Set #{duct_actuators['cfis_supply_sens_lk_to_cond'].name} = #{duct_vars['supply_sens_lk_to_cond'].name}")
+ duct_program.addLine(" Set #{duct_actuators['cfis_supply_lat_lk_to_cond'].name} = #{duct_vars['supply_lat_lk_to_cond'].name}")
+ duct_program.addLine(" Set #{duct_actuators['cfis_supply_cond_to_cond'].name} = #{duct_vars['supply_cond_to_cond'].name}")
duct_program.addLine(" Set #{duct_actuators['cfis_return_sens_lk_to_rp'].name} = #{duct_vars['return_sens_lk_to_rp'].name}")
duct_program.addLine(" Set #{duct_actuators['cfis_return_lat_lk_to_rp'].name} = #{duct_vars['return_lat_lk_to_rp'].name}")
duct_program.addLine(" Set #{duct_actuators['cfis_return_cond_to_rp'].name} = #{duct_vars['return_cond_to_rp'].name}")
duct_program.addLine(" Set #{duct_actuators['cfis_return_cond_to_dz'].name} = #{duct_vars['return_cond_to_dz'].name}")
duct_program.addLine(" Set #{duct_actuators['cfis_supply_cond_to_dz'].name} = #{duct_vars['supply_cond_to_dz'].name}")
duct_program.addLine(" Set #{duct_actuators['cfis_supply_sens_lk_to_dz'].name} = #{duct_vars['supply_sens_lk_to_dz'].name}")
duct_program.addLine(" Set #{duct_actuators['cfis_supply_lat_lk_to_dz'].name} = #{duct_vars['supply_lat_lk_to_dz'].name}")
- if not duct_actuators['dz_to_liv_flow_rate'].nil?
- duct_program.addLine(" Set #{duct_actuators['cfis_dz_to_liv_flow_rate'].name} = #{duct_vars['dz_to_liv_flow_rate'].name}")
+ if not duct_actuators['dz_to_cond_flow_rate'].nil?
+ duct_program.addLine(" Set #{duct_actuators['cfis_dz_to_cond_flow_rate'].name} = #{duct_vars['dz_to_cond_flow_rate'].name}")
end
- if not duct_actuators['liv_to_dz_flow_rate'].nil?
- duct_program.addLine(" Set #{duct_actuators['cfis_liv_to_dz_flow_rate'].name} = #{duct_vars['liv_to_dz_flow_rate'].name}")
+ if not duct_actuators['cond_to_dz_flow_rate'].nil?
+ duct_program.addLine(" Set #{duct_actuators['cfis_cond_to_dz_flow_rate'].name} = #{duct_vars['cond_to_dz_flow_rate'].name}")
end
duct_program.addLine('Else')
end
- duct_program.addLine(" Set #{duct_actuators['cfis_supply_sens_lk_to_liv'].name} = 0")
- duct_program.addLine(" Set #{duct_actuators['cfis_supply_lat_lk_to_liv'].name} = 0")
- duct_program.addLine(" Set #{duct_actuators['cfis_supply_cond_to_liv'].name} = 0")
+ duct_program.addLine(" Set #{duct_actuators['cfis_supply_sens_lk_to_cond'].name} = 0")
+ duct_program.addLine(" Set #{duct_actuators['cfis_supply_lat_lk_to_cond'].name} = 0")
+ duct_program.addLine(" Set #{duct_actuators['cfis_supply_cond_to_cond'].name} = 0")
duct_program.addLine(" Set #{duct_actuators['cfis_return_sens_lk_to_rp'].name} = 0")
duct_program.addLine(" Set #{duct_actuators['cfis_return_lat_lk_to_rp'].name} = 0")
duct_program.addLine(" Set #{duct_actuators['cfis_return_cond_to_rp'].name} = 0")
duct_program.addLine(" Set #{duct_actuators['cfis_return_cond_to_dz'].name} = 0")
duct_program.addLine(" Set #{duct_actuators['cfis_supply_cond_to_dz'].name} = 0")
duct_program.addLine(" Set #{duct_actuators['cfis_supply_sens_lk_to_dz'].name} = 0")
duct_program.addLine(" Set #{duct_actuators['cfis_supply_lat_lk_to_dz'].name} = 0")
- if not duct_actuators['dz_to_liv_flow_rate'].nil?
- duct_program.addLine(" Set #{duct_actuators['cfis_dz_to_liv_flow_rate'].name} = 0")
+ if not duct_actuators['dz_to_cond_flow_rate'].nil?
+ duct_program.addLine(" Set #{duct_actuators['cfis_dz_to_cond_flow_rate'].name} = 0")
end
- if not duct_actuators['liv_to_dz_flow_rate'].nil?
- duct_program.addLine(" Set #{duct_actuators['cfis_liv_to_dz_flow_rate'].name} = 0")
+ if not duct_actuators['cond_to_dz_flow_rate'].nil?
+ duct_program.addLine(" Set #{duct_actuators['cfis_cond_to_dz_flow_rate'].name} = 0")
end
if add_cfis_duct_losses
duct_program.addLine('EndIf')
end
@@ -1270,11 +1290,11 @@
equip_def = OpenStudio::Model::ElectricEquipmentDefinition.new(model)
equip_def.setName(obj_name)
equip = OpenStudio::Model::ElectricEquipment.new(equip_def)
equip.setName(obj_name)
- equip.setSpace(@living_space) # no heat gain, so assign the equipment to an arbitrary space
+ equip.setSpace(@conditioned_space) # no heat gain, so assign the equipment to an arbitrary space
equip_def.setDesignLevel(vent_object.fan_power * vent_object.count)
equip_def.setFractionRadiant(0)
equip_def.setFractionLatent(0)
equip_def.setFractionLost(1)
equip.setSchedule(obj_sch.schedule)
@@ -1282,24 +1302,24 @@
return obj_sch_sensor
end
def self.apply_dryer_exhaust(model, vented_dryer, schedules_file, index, unavailable_periods)
- obj_name = "#{Constants.ObjectNameClothesDryerExhaust} #{index}"
+ obj_name = "#{Constants.ObjectNameClothesDryer} exhaust #{index}"
# Create schedule
obj_sch = nil
if not schedules_file.nil?
obj_sch_name = SchedulesFile::ColumnClothesDryer
- obj_sch = schedules_file.create_schedule_file(col_name: obj_sch_name)
+ obj_sch = schedules_file.create_schedule_file(model, col_name: obj_sch_name)
full_load_hrs = schedules_file.annual_equivalent_full_load_hrs(col_name: obj_sch_name)
end
if obj_sch.nil?
cd_weekday_sch = vented_dryer.weekday_fractions
cd_weekend_sch = vented_dryer.weekend_fractions
cd_monthly_sch = vented_dryer.monthly_multipliers
- obj_sch = MonthWeekdayWeekendSchedule.new(model, Constants.ObjectNameClothesDryer, cd_weekday_sch, cd_weekend_sch, cd_monthly_sch, Constants.ScheduleTypeLimitsFraction, unavailable_periods: unavailable_periods)
+ obj_sch = MonthWeekdayWeekendSchedule.new(model, obj_name + ' schedule', cd_weekday_sch, cd_weekend_sch, cd_monthly_sch, Constants.ScheduleTypeLimitsFraction, unavailable_periods: unavailable_periods)
obj_sch = obj_sch.schedule
obj_sch_name = obj_sch.name.to_s
full_load_hrs = Schedule.annual_equivalent_full_load_hrs(@year, obj_sch)
end
@@ -1316,10 +1336,11 @@
end
def self.calc_hrv_erv_effectiveness(vent_mech_fans)
# Create the mapping between mech vent instance and the effectiveness results
hrv_erv_effectiveness_map = {}
+ p_atm = UnitConversions.convert(1.0, 'atm', 'psi')
vent_mech_fans.each do |vent_mech|
hrv_erv_effectiveness_map[vent_mech] = {}
vent_mech_cfm = vent_mech.average_oa_unit_flow_rate
if (vent_mech_cfm > 0)
@@ -1329,11 +1350,11 @@
t_exh_in = 22.0
# w_exh_in = 0.0065
cp_a = 1006.0
p_fan = vent_mech.average_unit_fan_power # Watts
- m_fan = UnitConversions.convert(vent_mech_cfm, 'cfm', 'm^3/s') * 16.02 * Psychrometrics.rhoD_fT_w_P(UnitConversions.convert(t_sup_in, 'C', 'F'), w_sup_in, 14.7) # kg/s
+ m_fan = UnitConversions.convert(vent_mech_cfm, 'cfm', 'm^3/s') * UnitConversions.convert(Psychrometrics.rhoD_fT_w_P(UnitConversions.convert(t_sup_in, 'C', 'F'), w_sup_in, p_atm), 'lbm/ft^3', 'kg/m^3') # kg/s
if not vent_mech.sensible_recovery_efficiency.nil?
# The following is derived from CSA 439, Clause 9.3.3.1, Eq. 12:
# E_SHR = (m_sup,fan * Cp * (Tsup,out - Tsup,in) - P_sup,fan) / (m_exh,fan * Cp * (Texh,in - Tsup,in) + P_exh,fan)
t_sup_out = t_sup_in + (vent_mech.sensible_recovery_efficiency * (m_fan * cp_a * (t_exh_in - t_sup_in) + p_fan) + p_fan) / (m_fan * cp_a)
@@ -1365,11 +1386,11 @@
t_sup_in = 35.0
w_sup_in = 0.0178
t_exh_in = 24.0
w_exh_in = 0.0092
- m_fan = UnitConversions.convert(vent_mech_cfm, 'cfm', 'm^3/s') * UnitConversions.convert(Psychrometrics.rhoD_fT_w_P(UnitConversions.convert(t_sup_in, 'C', 'F'), w_sup_in, 14.7), 'lbm/ft^3', 'kg/m^3') # kg/s
+ m_fan = UnitConversions.convert(vent_mech_cfm, 'cfm', 'm^3/s') * UnitConversions.convert(Psychrometrics.rhoD_fT_w_P(UnitConversions.convert(t_sup_in, 'C', 'F'), w_sup_in, p_atm), 'lbm/ft^3', 'kg/m^3') # kg/s
t_sup_out_gross = t_sup_in - vent_mech_sens_eff * (t_sup_in - t_exh_in)
t_sup_out = t_sup_out_gross + p_fan / (m_fan * cp_a)
h_sup_in = Psychrometrics.h_fT_w_SI(t_sup_in, w_sup_in)
@@ -1517,11 +1538,11 @@
equip_def = OpenStudio::Model::ElectricEquipmentDefinition.new(model)
equip_def.setName(obj_name)
equip = OpenStudio::Model::ElectricEquipment.new(equip_def)
equip.setName(obj_name)
- equip.setSpace(@living_space)
+ equip.setSpace(@conditioned_space)
equip_def.setFractionRadiant(0)
equip_def.setFractionLatent(0)
equip.setSchedule(avail_sch)
equip.setEndUseSubcategory(Constants.ObjectNameMechanicalVentilation)
equip_def.setFractionLost(fan_heat_lost_fraction)
@@ -1539,13 +1560,13 @@
end
def self.setup_mech_vent_vars_actuators(model:, program:)
# Actuators for mech vent fan
sens_name = "#{Constants.ObjectNameMechanicalVentilationHouseFan} sensible load"
- fan_sens_load_actuator = create_other_equipment_object_and_actuator(model: model, name: sens_name, space: @living_space, frac_lat: 0.0, frac_lost: 0.0)
+ fan_sens_load_actuator = create_other_equipment_object_and_actuator(model: model, name: sens_name, space: @conditioned_space, frac_lat: 0.0, frac_lost: 0.0, end_use: Constants.ObjectNameMechanicalVentilationHouseFan)
lat_name = "#{Constants.ObjectNameMechanicalVentilationHouseFan} latent load"
- fan_lat_load_actuator = create_other_equipment_object_and_actuator(model: model, name: lat_name, space: @living_space, frac_lat: 1.0, frac_lost: 0.0)
+ fan_lat_load_actuator = create_other_equipment_object_and_actuator(model: model, name: lat_name, space: @conditioned_space, frac_lat: 1.0, frac_lost: 0.0, end_use: Constants.ObjectNameMechanicalVentilationHouseFan)
program.addLine("Set #{fan_sens_load_actuator.name} = 0.0")
program.addLine("Set #{fan_lat_load_actuator.name} = 0.0")
# Air property at inlet nodes on both sides
program.addLine("Set OASupInPb = #{@pbar_sensor.name}") # oa barometric pressure
program.addLine("Set OASupInTemp = #{@tout_sensor.name}") # oa db temperature
@@ -1625,10 +1646,13 @@
infil_program.addLine('Set Qb = Qfan - Qu') # Balanced flow
infil_program.addLine('Set Qtot = (((Qu^2) + (Qinf^2)) ^ 0.5) + Qb')
infil_program.addLine('Set Qinf_adj = Qtot - Qu - Qb')
end
infil_program.addLine("Set #{infil_flow_actuator.name} = Qinf_adj")
+
+ create_timeseries_flowrate_ems_output_var(model, 'Qfan', infil_program)
+ create_timeseries_flowrate_ems_output_var(model, infil_flow_actuator.name.to_s, infil_program)
end
def self.calculate_fan_loads(infil_program, vent_mech_erv_hrv_tot, hrv_erv_effectiveness_map, fan_sens_load_actuator, fan_lat_load_actuator, q_var, preconditioned = false)
# Variables for combined effectiveness
infil_program.addLine('Set Effectiveness_Sens = 0.0')
@@ -1661,40 +1685,41 @@
infil_program.addLine('Set ERVLatHeatTrans = ERVTotalHeatTrans - ERVSensHeatTrans')
# ERV/HRV Load calculation
infil_program.addLine('Set ZoneInEnth = ERVSupOutEnth')
infil_program.addLine('Set ZoneInTemp = ERVSupOutTemp')
end
- infil_program.addLine('Set FanTotalToLv = Fan_MFR * (ZoneInEnth - ZoneAirEnth)')
- infil_program.addLine('Set FanSensToLv = Fan_MFR * ZoneCp * (ZoneInTemp - ZoneTemp)')
- infil_program.addLine('Set FanLatToLv = FanTotalToLv - FanSensToLv')
+ infil_program.addLine('Set FanTotalToCond = Fan_MFR * (ZoneInEnth - ZoneAirEnth)')
+ infil_program.addLine('Set FanSensToCond = Fan_MFR * ZoneCp * (ZoneInTemp - ZoneTemp)')
+ infil_program.addLine('Set FanLatToCond = FanTotalToCond - FanSensToCond')
# Actuator,
# If preconditioned, handle actuators later in calculate_precond_loads
if not preconditioned
- infil_program.addLine("Set #{fan_sens_load_actuator.name} = #{fan_sens_load_actuator.name} + FanSensToLv")
- infil_program.addLine("Set #{fan_lat_load_actuator.name} = #{fan_lat_load_actuator.name} + FanLatToLv")
+ infil_program.addLine("Set #{fan_sens_load_actuator.name} = #{fan_sens_load_actuator.name} + FanSensToCond")
+ infil_program.addLine("Set #{fan_lat_load_actuator.name} = #{fan_lat_load_actuator.name} + FanLatToCond")
end
end
def self.calculate_precond_loads(model, infil_program, vent_mech_preheat, vent_mech_precool, hrv_erv_effectiveness_map, fan_sens_load_actuator, fan_lat_load_actuator, clg_ssn_sensor)
# Preconditioning
# Assume introducing no sensible loads to zone if preconditioned
if not vent_mech_preheat.empty?
htg_stp_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Zone Thermostat Heating Setpoint Temperature')
- htg_stp_sensor.setName("#{Constants.ObjectNameAirflow} htg stp s")
- htg_stp_sensor.setKeyName(@living_zone.name.to_s)
+ htg_stp_sensor.setName('htg stp s')
+ htg_stp_sensor.setKeyName(@conditioned_zone.name.to_s)
infil_program.addLine("Set HtgStp = #{htg_stp_sensor.name}") # heating thermostat setpoint
end
if not vent_mech_precool.empty?
clg_stp_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Zone Thermostat Cooling Setpoint Temperature')
- clg_stp_sensor.setName("#{Constants.ObjectNameAirflow} clg stp s")
- clg_stp_sensor.setKeyName(@living_zone.name.to_s)
+ clg_stp_sensor.setName('clg stp s')
+ clg_stp_sensor.setKeyName(@conditioned_zone.name.to_s)
infil_program.addLine("Set ClgStp = #{clg_stp_sensor.name}") # cooling thermostat setpoint
end
vent_mech_preheat.each_with_index do |f_preheat, i|
infil_program.addLine("If (OASupInTemp < HtgStp) && (#{clg_ssn_sensor.name} < 1)")
- htg_energy_actuator = create_other_equipment_object_and_actuator(model: model, name: "shared mech vent preheating energy #{i}", space: @living_space, frac_lat: 0.0, frac_lost: 1.0, hpxml_fuel_type: f_preheat.preheating_fuel, end_use: Constants.ObjectNameMechanicalVentilationPreheating)
+ cnt = model.getOtherEquipments.select { |e| e.endUseSubcategory.start_with? Constants.ObjectNameMechanicalVentilationPreheating }.size # Ensure unique meter for each preheating system
+ htg_energy_actuator = create_other_equipment_object_and_actuator(model: model, name: "shared mech vent preheating energy #{i}", space: @conditioned_space, frac_lat: 0.0, frac_lost: 1.0, hpxml_fuel_type: f_preheat.preheating_fuel, end_use: "#{Constants.ObjectNameMechanicalVentilationPreheating}#{cnt + 1}")
htg_energy_actuator.actuatedComponent.get.additionalProperties.setFeature('HPXML_ID', f_preheat.id) # Used by reporting measure
infil_program.addLine(" Set Qpreheat = #{UnitConversions.convert(f_preheat.average_oa_unit_flow_rate, 'cfm', 'm^3/s').round(4)}")
if [HPXML::MechVentTypeERV, HPXML::MechVentTypeHRV].include? f_preheat.fan_type
vent_mech_erv_hrv_tot = [f_preheat]
else
@@ -1704,22 +1729,23 @@
infil_program.addLine(' If ZoneInTemp < HtgStp')
infil_program.addLine(' Set FanSensToSpt = Fan_MFR * ZoneCp * (ZoneInTemp - HtgStp)')
infil_program.addLine(" Set PreHeatingWatt = (-FanSensToSpt) * #{f_preheat.preheating_fraction_load_served}")
infil_program.addLine(" Set #{fan_sens_load_actuator.name} = #{fan_sens_load_actuator.name} + PreHeatingWatt")
- infil_program.addLine(" Set #{fan_lat_load_actuator.name} = #{fan_lat_load_actuator.name} - FanLatToLv")
+ infil_program.addLine(" Set #{fan_lat_load_actuator.name} = #{fan_lat_load_actuator.name} - FanLatToCond")
infil_program.addLine(' Else')
infil_program.addLine(' Set PreHeatingWatt = 0.0')
infil_program.addLine(' EndIf')
infil_program.addLine('Else')
infil_program.addLine(' Set PreHeatingWatt = 0.0')
infil_program.addLine('EndIf')
infil_program.addLine("Set #{htg_energy_actuator.name} = PreHeatingWatt / #{f_preheat.preheating_efficiency_cop}")
end
vent_mech_precool.each_with_index do |f_precool, i|
infil_program.addLine("If (OASupInTemp > ClgStp) && (#{clg_ssn_sensor.name} > 0)")
- clg_energy_actuator = create_other_equipment_object_and_actuator(model: model, name: "shared mech vent precooling energy #{i}", space: @living_space, frac_lat: 0.0, frac_lost: 1.0, hpxml_fuel_type: f_precool.precooling_fuel, end_use: Constants.ObjectNameMechanicalVentilationPrecooling)
+ cnt = model.getOtherEquipments.select { |e| e.endUseSubcategory.start_with? Constants.ObjectNameMechanicalVentilationPrecooling }.size # Ensure unique meter for each precooling system
+ clg_energy_actuator = create_other_equipment_object_and_actuator(model: model, name: "shared mech vent precooling energy #{i}", space: @conditioned_space, frac_lat: 0.0, frac_lost: 1.0, hpxml_fuel_type: f_precool.precooling_fuel, end_use: "#{Constants.ObjectNameMechanicalVentilationPrecooling}#{cnt + 1}")
clg_energy_actuator.actuatedComponent.get.additionalProperties.setFeature('HPXML_ID', f_precool.id) # Used by reporting measure
infil_program.addLine(" Set Qprecool = #{UnitConversions.convert(f_precool.average_oa_unit_flow_rate, 'cfm', 'm^3/s').round(4)}")
if [HPXML::MechVentTypeERV, HPXML::MechVentTypeHRV].include? f_precool.fan_type
vent_mech_erv_hrv_tot = [f_precool]
else
@@ -1729,22 +1755,22 @@
infil_program.addLine(' If ZoneInTemp > ClgStp')
infil_program.addLine(' Set FanSensToSpt = Fan_MFR * ZoneCp * (ZoneInTemp - ClgStp)')
infil_program.addLine(" Set PreCoolingWatt = FanSensToSpt * #{f_precool.precooling_fraction_load_served}")
infil_program.addLine(" Set #{fan_sens_load_actuator.name} = #{fan_sens_load_actuator.name} - PreCoolingWatt")
- infil_program.addLine(" Set #{fan_lat_load_actuator.name} = #{fan_lat_load_actuator.name} - FanLatToLv") # Fixme:Does this assumption still apply?
+ infil_program.addLine(" Set #{fan_lat_load_actuator.name} = #{fan_lat_load_actuator.name} - FanLatToCond") # Fixme:Does this assumption still apply?
infil_program.addLine(' Else')
infil_program.addLine(' Set PreCoolingWatt = 0.0')
infil_program.addLine(' EndIf')
infil_program.addLine('Else')
infil_program.addLine(' Set PreCoolingWatt = 0.0')
infil_program.addLine('EndIf')
infil_program.addLine("Set #{clg_energy_actuator.name} = PreCoolingWatt / #{f_precool.precooling_efficiency_cop}")
end
end
- def self.apply_infiltration_ventilation_to_conditioned(model, site, vent_fans_mech, living_ach50, living_const_ach, infil_volume, infil_height, weather,
+ def self.apply_infiltration_ventilation_to_conditioned(model, site, vent_fans_mech, conditioned_ach50, conditioned_const_ach, infil_volume, infil_height, weather,
vent_fans_kitchen, vent_fans_bath, vented_dryers, has_flue_chimney_in_cond_space, clg_ssn_sensor, schedules_file,
vent_fans_cfis_suppl, unavailable_periods)
# Categorize fans into different types
vent_mech_preheat = vent_fans_mech.select { |vent_mech| (not vent_mech.preheating_efficiency_cop.nil?) }
vent_mech_precool = vent_fans_mech.select { |vent_mech| (not vent_mech.precooling_efficiency_cop.nil?) }
@@ -1777,20 +1803,22 @@
hrv_erv_effectiveness_map = calc_hrv_erv_effectiveness(vent_mech_erv_hrv_tot)
infil_flow = OpenStudio::Model::SpaceInfiltrationDesignFlowRate.new(model)
infil_flow.setName(Constants.ObjectNameInfiltration + ' flow')
infil_flow.setSchedule(model.alwaysOnDiscreteSchedule)
- infil_flow.setSpace(@living_space)
+ infil_flow.setSpace(@conditioned_space)
infil_flow_actuator = OpenStudio::Model::EnergyManagementSystemActuator.new(infil_flow, *EPlus::EMSActuatorZoneInfiltrationFlowRate)
infil_flow_actuator.setName("#{infil_flow.name} act")
+ infil_flow.additionalProperties.setFeature('ObjectType', Constants.ObjectNameInfiltration)
- # Living Space Infiltration Calculation/Program
+ # Conditioned Space Infiltration Calculation/Program
infil_program = OpenStudio::Model::EnergyManagementSystemProgram.new(model)
+ infil_program.additionalProperties.setFeature('ObjectType', Constants.ObjectNameInfiltration)
infil_program.setName(Constants.ObjectNameInfiltration + ' program')
# Calculate infiltration without adjustment by ventilation
- apply_infiltration_to_conditioned(site, living_ach50, living_const_ach, infil_program, weather, has_flue_chimney_in_cond_space, infil_volume, infil_height)
+ apply_infiltration_to_conditioned(site, conditioned_ach50, conditioned_const_ach, infil_program, weather, has_flue_chimney_in_cond_space, infil_volume, infil_height)
# Common variable and load actuators across multiple mech vent calculations, create only once
fan_sens_load_actuator, fan_lat_load_actuator = setup_mech_vent_vars_actuators(model: model, program: infil_program)
# Apply CFIS
@@ -1823,39 +1851,39 @@
program_calling_manager.setCallingPoint('BeginZoneTimestepAfterInitHeatBalance')
program_calling_manager.addProgram(infil_program)
end
def self.apply_infiltration_and_ventilation_fans(model, weather, site, vent_fans_mech, vent_fans_kitchen, vent_fans_bath, vented_dryers,
- has_flue_chimney_in_cond_space, living_ach50, living_const_ach, infil_volume, infil_height, vented_attic,
+ has_flue_chimney_in_cond_space, conditioned_ach50, conditioned_const_ach, infil_volume, infil_height, vented_attic,
vented_crawl, clg_ssn_sensor, schedules_file, vent_fans_cfis_suppl, unavailable_periods)
# Infiltration for unconditioned spaces
- apply_infiltration_to_garage(model, site, living_ach50)
+ apply_infiltration_to_garage(model, site, conditioned_ach50)
apply_infiltration_to_unconditioned_basement(model)
apply_infiltration_to_vented_crawlspace(model, weather, vented_crawl)
apply_infiltration_to_unvented_crawlspace(model)
apply_infiltration_to_vented_attic(model, weather, site, vented_attic)
apply_infiltration_to_unvented_attic(model)
# Infiltration/ventilation for conditioned space
- apply_infiltration_ventilation_to_conditioned(model, site, vent_fans_mech, living_ach50, living_const_ach, infil_volume, infil_height, weather,
+ apply_infiltration_ventilation_to_conditioned(model, site, vent_fans_mech, conditioned_ach50, conditioned_const_ach, infil_volume, infil_height, weather,
vent_fans_kitchen, vent_fans_bath, vented_dryers, has_flue_chimney_in_cond_space, clg_ssn_sensor, schedules_file,
vent_fans_cfis_suppl, unavailable_periods)
end
- def self.apply_infiltration_to_conditioned(site, living_ach50, living_const_ach, infil_program, weather, has_flue_chimney_in_cond_space, infil_volume, infil_height)
+ def self.apply_infiltration_to_conditioned(site, conditioned_ach50, conditioned_const_ach, infil_program, weather, has_flue_chimney_in_cond_space, infil_volume, infil_height)
site_ap = site.additional_properties
- if living_ach50.to_f > 0
+ if conditioned_ach50.to_f > 0
# Based on "Field Validation of Algebraic Equations for Stack and
# Wind Driven Air Infiltration Calculations" by Walker and Wilson (1998)
- outside_air_density = UnitConversions.convert(weather.header.LocalPressure, 'atm', 'Btu/ft^3') / (Gas.Air.r * (weather.data.AnnualAvgDrybulb + 460.0))
+ outside_air_density = UnitConversions.convert(weather.header.LocalPressure, 'atm', 'Btu/ft^3') / (Gas.Air.r * UnitConversions.convert(weather.data.AnnualAvgDrybulb, 'F', 'R'))
n_i = InfilPressureExponent
- living_sla = get_infiltration_SLA_from_ACH50(living_ach50, n_i, @cfa, infil_volume) # Calculate SLA
- a_o = living_sla * @cfa # Effective Leakage Area (ft^2)
+ conditioned_sla = get_infiltration_SLA_from_ACH50(conditioned_ach50, n_i, @cfa, infil_volume) # Calculate SLA
+ a_o = conditioned_sla * @cfa # Effective Leakage Area (ft^2)
# Flow Coefficient (cfm/inH2O^n) (based on ASHRAE HoF)
inf_conv_factor = 776.25 # [ft/min]/[inH2O^(1/2)*ft^(3/2)/lbm^(1/2)]
delta_pref = 0.016 # inH2O
c_i = a_o * (2.0 / outside_air_density)**0.5 * delta_pref**(0.5 - n_i) * inf_conv_factor
@@ -1901,11 +1929,11 @@
f_i = n_i * y_i * (z_f - 1.0)**((3.0 * n_i - 1.0) / 3.0) * (1.0 - (3.0 * (x_c - x_i)**2.0 * r_i**(1 - n_i)) / (2.0 * (z_f + 1.0))) # Additive flue function, Eq. 12
else
f_i = 0.0 # Additive flue function (eq. 12)
end
f_s = ((1.0 + n_i * r_i) / (n_i + 1.0)) * (0.5 - 0.5 * m_i**1.2)**(n_i + 1.0) + f_i
- stack_coef = f_s * (UnitConversions.convert(outside_air_density * Constants.g * infil_height, 'lbm/(ft*s^2)', 'inH2O') / (Constants.AssumedInsideTemp + 460.0))**n_i # inH2O^n/R^n
+ stack_coef = f_s * (UnitConversions.convert(outside_air_density * Constants.g * infil_height, 'lbm/(ft*s^2)', 'inH2O') / UnitConversions.convert(Constants.AssumedInsideTemp, 'F', 'R'))**n_i # inH2O^n/R^n
# Calculate wind coefficient
if not @spaces[HPXML::LocationCrawlspaceVented].nil?
if x_i > 1.0 - 2.0 * y_i
# Critical floor to ceiling difference above which f_w does not change (eq. 25)
@@ -1938,13 +1966,12 @@
infil_program.addLine("Set sft = (f_t*#{(site_ap.aim2_shelter_coeff * (1.0 - y_i)) + (s_wflue * (1.5 * y_i))})")
infil_program.addLine("Set temp1 = ((c*Cw)*((sft*#{@vwind_sensor.name})^(2*n)))^2")
infil_program.addLine('Set Qinf = (((c*Cs*(dT^n))^2)+temp1)^0.5')
infil_program.addLine('Set Qinf = (@Max Qinf 0)')
- elsif living_const_ach.to_f > 0
- living_ach = living_const_ach
- infil_program.addLine("Set Qinf = #{living_ach * UnitConversions.convert(infil_volume, 'ft^3', 'm^3') / UnitConversions.convert(1.0, 'hr', 's')}")
+ elsif conditioned_const_ach.to_f > 0
+ infil_program.addLine("Set Qinf = #{conditioned_const_ach * UnitConversions.convert(infil_volume, 'ft^3', 'm^3') / UnitConversions.convert(1.0, 'hr', 's')}")
else
infil_program.addLine('Set Qinf = 0')
end
end
@@ -1955,10 +1982,10 @@
end
coord_z = Geometry.get_z_origin_for_zone(space.thermalZone.get)
f_t_SG = site_ap.site_terrain_multiplier * ((space_height + coord_z) / 32.8)**site_ap.site_terrain_exponent / (site_ap.terrain_multiplier * (site_ap.height / 32.8)**site_ap.terrain_exponent)
f_s_SG = 2.0 / 3.0 * (1 + hor_lk_frac / 2.0) * (2.0 * neutral_level * (1.0 - neutral_level))**0.5 / (neutral_level**0.5 + (1.0 - neutral_level)**0.5)
f_w_SG = site_ap.s_g_shielding_coef * (1.0 - hor_lk_frac)**(1.0 / 3.0) * f_t_SG
- c_s_SG = f_s_SG**2.0 * Constants.g * space_height / (Constants.AssumedInsideTemp + 460.0)
+ c_s_SG = f_s_SG**2.0 * Constants.g * space_height / UnitConversions.convert(Constants.AssumedInsideTemp, 'F', 'R')
c_w_SG = f_w_SG**2.0
return c_w_SG, c_s_SG
end
def self.get_infiltration_NL_from_SLA(sla, infil_height)