Skip to content
Open
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 23 additions & 3 deletions HPXMLtoOpenStudio/measure.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2121,7 +2121,7 @@ def self.add_total_loads_output(runner, model, living_zone)
tot_load_sensors[:clg] = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Cooling:EnergyTransfer')
tot_load_sensors[:clg].setName('clg_load_tot')

# Need to adjusted E+ EnergyTransfer meters for dehumidifiers
# Need to adjust E+ EnergyTransfer meters for dehumidifiers
intgain_dehumidifier = nil
model.getZoneHVACDehumidifierDXs.each do |e|
next unless e.thermalZone.get.name.to_s == living_zone.name.to_s
Expand All @@ -2138,14 +2138,25 @@ def self.add_total_loads_output(runner, model, living_zone)
program.setName(Constants.ObjectNameTotalLoadsProgram)
program.addLine('Set loads_htg_tot = 0')
program.addLine('Set loads_clg_tot = 0')
# Need to adjust E+ EnergyTransfer meters for mech vent preconditioning
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Adjust total loads read from E+ output meter

# Fixme: is it possible to have cooling in hvac but heating in mech vent preconditioning? How to report load in that case?
# Fixme: Proceed reporting both
model.getEnergyManagementSystemGlobalVariables.sort.each do |ems_var|
next unless ems_var.name.to_s.start_with? 'Precond_'
if ems_var.name.to_s.start_with? 'Precond_HeatingLoad'
program.addLine("Set loads_htg_tot = loads_htg_tot + #{ems_var.name.get}")
else
program.addLine("Set loads_clg_tot = loads_clg_tot + #{ems_var.name.get}")
end
end
program.addLine("If #{liv_load_sensors[:htg].name} > 0")
s = " Set loads_htg_tot = #{tot_load_sensors[:htg].name} - #{tot_load_sensors[:clg].name}"
s = " Set loads_htg_tot = loads_htg_tot + #{tot_load_sensors[:htg].name} - #{tot_load_sensors[:clg].name}"
if not intgain_dehumidifier.nil?
s += " - #{intgain_dehumidifier.name}"
end
program.addLine(s)
program.addLine("ElseIf #{liv_load_sensors[:clg].name} > 0")
s = " Set loads_clg_tot = #{tot_load_sensors[:clg].name} - #{tot_load_sensors[:htg].name}"
s = " Set loads_clg_tot = loads_clg_tot + #{tot_load_sensors[:clg].name} - #{tot_load_sensors[:htg].name}"
if not intgain_dehumidifier.nil?
s += " + #{intgain_dehumidifier.name}"
end
Expand Down Expand Up @@ -2516,6 +2527,15 @@ def self.add_component_loads_output(runner, model, living_zone, liv_load_sensors
mechvent_sensors.each do |sensor|
program.addLine("Set hr_mechvent = hr_mechvent - #{sensor.name}")
end
# Adjust component load reporting for preconditioning
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Adjust component loads read from equipment objects (add preconditioning loads to mechvent subitem).

model.getEnergyManagementSystemGlobalVariables.sort.each do |ems_var|
next unless ems_var.name.to_s.start_with? 'Precond_'
if ems_var.name.to_s.start_with? 'Precond_HeatingLoad'
program.addLine("Set hr_mechvent = hr_mechvent + #{ems_var.name.get}")
else
program.addLine("Set hr_mechvent = hr_mechvent - #{ems_var.name.get}")
end
end
program.addLine('Set hr_ducts = 0')
ducts_sensors.each do |duct_sensors|
s = 'Set hr_ducts = hr_ducts'
Expand Down
80 changes: 53 additions & 27 deletions HPXMLtoOpenStudio/resources/airflow.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

class Airflow
def self.apply(model, runner, weather, spaces, hpxml, cfa, nbeds,
ncfl_ag, duct_systems, nv_clg_ssn_sensor, hvac_map, eri_version,
ncfl_ag, duct_systems, clg_ssn_sensor, hvac_map, eri_version,
frac_windows_operable, apply_ashrae140_assumptions)

# Global variables
Expand Down Expand Up @@ -106,10 +106,10 @@ def self.apply(model, runner, weather, spaces, hpxml, cfa, nbeds,
vented_crawl = foundation
end

apply_natural_ventilation_and_whole_house_fan(model, weather, hpxml.site, vent_fans_whf, open_window_area, nv_clg_ssn_sensor)
apply_natural_ventilation_and_whole_house_fan(model, weather, hpxml.site, vent_fans_whf, open_window_area, clg_ssn_sensor)
apply_infiltration_and_ventilation_fans(model, weather, hpxml.site, vent_fans_mech, vent_fans_kitchen, vent_fans_bath, vented_dryers,
hpxml.building_construction.has_flue_or_chimney, hpxml.air_infiltration_measurements,
vented_attic, vented_crawl, hvac_map)
vented_attic, vented_crawl, hvac_map, clg_ssn_sensor)
end

def self.get_default_fraction_of_windows_operable()
Expand Down Expand Up @@ -1486,6 +1486,8 @@ def self.calculate_fan_loads(model, infil_program, vent_mech_erv_hrv_tot, hrv_er
# Calculate mass flow rate based on outdoor air density
# Address load with flow-weighted combined effectiveness
infil_program.addLine("Set Fan_MFR = #{q_var} * OASupRho")
infil_program.addLine('Set ZoneInEnth = OASupInEnth')
infil_program.addLine('Set ZoneInTemp = OASupInTemp')
if not vent_mech_erv_hrv_tot.empty?
# ERV/HRV EMS load model
# E+ ERV model is using standard density for MFR calculation, caused discrepancy with other system types.
Expand All @@ -1502,14 +1504,12 @@ def self.calculate_fan_loads(model, infil_program, vent_mech_erv_hrv_tot, hrv_er
infil_program.addLine('Set ERVTotalHeatTrans = Fan_MFR * (ERVSupOutEnth - OASupInEnth)')
infil_program.addLine('Set ERVLatHeatTrans = ERVTotalHeatTrans - ERVSensHeatTrans')
# ERV/HRV Load calculation
infil_program.addLine('Set FanTotalToLv = Fan_MFR * (ERVSupOutEnth - ZoneAirEnth)')
infil_program.addLine('Set FanSensToLv = Fan_MFR * ZoneCp * (ERVSupOutTemp - ZoneTemp)')
infil_program.addLine('Set FanLatToLv = FanTotalToLv - FanSensToLv')
else
infil_program.addLine('Set FanTotalToLv = Fan_MFR * (OASupInEnth - ZoneAirEnth)')
infil_program.addLine('Set FanSensToLv = Fan_MFR * ZoneCp * (OASupInTemp - ZoneTemp)')
infil_program.addLine('Set FanLatToLv = FanTotalToLv - FanSensToLv')
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Some cleanups here. Also enable a single outlet temperature variable(ZoneInTemp here, any better name?) for preconditioning 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')

# Actuator,
# If preconditioned, handle actuators later in calculate_precond_loads
Expand All @@ -1519,12 +1519,25 @@ def self.calculate_fan_loads(model, infil_program, vent_mech_erv_hrv_tot, hrv_er
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, hvac_map)
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, hvac_map, 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)
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)
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 < ZoneTemp')
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.ObjectNameMechanicalVentilationPreconditioning)
htg_load_var = OpenStudio::Model::EnergyManagementSystemGlobalVariable.new(model, "Precond_HeatingLoad_#{i}")
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Global variables created here for load reporting.

hvac_map["#{f_preheat.id}_preheat"] = [htg_energy_actuator.actuatedComponent.get]
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
Expand All @@ -1534,17 +1547,24 @@ def self.calculate_precond_loads(model, infil_program, vent_mech_preheat, vent_m
end
calculate_fan_loads(model, infil_program, vent_mech_erv_hrv_tot, hrv_erv_effectiveness_map, fan_sens_load_actuator, fan_lat_load_actuator, 'Qpreheat', true)

infil_program.addLine(" Set PreHeatingEnergy = (-FanSensToLv) * #{f_preheat.preheating_fraction_load_served}")
infil_program.addLine(" Set #{fan_sens_load_actuator.name} = #{fan_sens_load_actuator.name} + PreHeatingEnergy")
infil_program.addLine(" Set #{fan_lat_load_actuator.name} = #{fan_lat_load_actuator.name} - FanLatToLv")
infil_program.addLine(" Set #{htg_energy_actuator.name} = PreHeatingEnergy / #{f_preheat.preheating_efficiency_cop}")
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") # Fixme:Does this assumption still apply?
infil_program.addLine(' Else')
infil_program.addLine(' Set PreHeatingWatt = 0.0')
infil_program.addLine(' EndIf')
infil_program.addLine('Else')
infil_program.addLine(" Set #{htg_energy_actuator.name} = 0.0")
infil_program.addLine(' Set PreHeatingWatt = 0.0')
infil_program.addLine('EndIf')
infil_program.addLine("Set #{htg_load_var.name.get} = PreHeatingWatt * ZoneTimeStep*3600")
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 > ZoneTemp')
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.ObjectNameMechanicalVentilationPreconditioning)
clg_load_var = OpenStudio::Model::EnergyManagementSystemGlobalVariable.new(model, "Precond_CoolingLoad_#{i}")
hvac_map["#{f_precool.id}_precool"] = [clg_energy_actuator.actuatedComponent.get]
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
Expand All @@ -1554,18 +1574,24 @@ def self.calculate_precond_loads(model, infil_program, vent_mech_preheat, vent_m
end
calculate_fan_loads(model, infil_program, vent_mech_erv_hrv_tot, hrv_erv_effectiveness_map, fan_sens_load_actuator, fan_lat_load_actuator, 'Qprecool', true)

infil_program.addLine(" Set PreCoolingEnergy = FanSensToLv * #{f_precool.precooling_fraction_load_served}")
infil_program.addLine(" Set #{fan_sens_load_actuator.name} = #{fan_sens_load_actuator.name} - PreCoolingEnergy")
infil_program.addLine(" Set #{fan_lat_load_actuator.name} = #{fan_lat_load_actuator.name} - FanLatToLv")
infil_program.addLine(" Set #{clg_energy_actuator.name} = PreCoolingEnergy / #{f_precool.precooling_efficiency_cop}")
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(' Else')
infil_program.addLine(' Set PreCoolingWatt = 0.0')
infil_program.addLine(' EndIf')
infil_program.addLine('Else')
infil_program.addLine(" Set #{clg_energy_actuator.name} = 0.0")
infil_program.addLine(' Set PreCoolingWatt = 0.0')
infil_program.addLine('EndIf')
infil_program.addLine("Set #{clg_load_var.name.get} = PreCoolingWatt * ZoneTimeStep*3600")
infil_program.addLine("Set #{clg_energy_actuator.name} = PreCoolingWatt / #{f_precool.precooling_efficiency_cop}")
end
end

def self.apply_infiltration_and_mechanical_ventilation(model, site, vent_fans_mech, living_ach50, living_const_ach, weather, vent_fans_kitchen, vent_fans_bath, vented_dryers,
range_sch_sensors_map, bath_sch_sensors_map, dryer_exhaust_sch_sensors_map, has_flue_chimney, hvac_map)
range_sch_sensors_map, bath_sch_sensors_map, dryer_exhaust_sch_sensors_map, has_flue_chimney, hvac_map, clg_ssn_sensor)
# 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?) }
Expand Down Expand Up @@ -1640,7 +1666,7 @@ def self.apply_infiltration_and_mechanical_ventilation(model, site, vent_fans_me
calculate_fan_loads(model, infil_program, vent_mech_erv_hrv_tot, hrv_erv_effectiveness_map, fan_sens_load_actuator, fan_lat_load_actuator, 'Qload')

# Address preconditioning
calculate_precond_loads(model, infil_program, vent_mech_preheat, vent_mech_precool, hrv_erv_effectiveness_map, fan_sens_load_actuator, fan_lat_load_actuator, hvac_map)
calculate_precond_loads(model, infil_program, vent_mech_preheat, vent_mech_precool, hrv_erv_effectiveness_map, fan_sens_load_actuator, fan_lat_load_actuator, hvac_map, clg_ssn_sensor)

program_calling_manager = OpenStudio::Model::EnergyManagementSystemProgramCallingManager.new(model)
program_calling_manager.setName("#{infil_program.name} calling manager")
Expand All @@ -1649,7 +1675,7 @@ def self.apply_infiltration_and_mechanical_ventilation(model, site, vent_fans_me
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, air_infils, vented_attic, vented_crawl, hvac_map)
has_flue_chimney, air_infils, vented_attic, vented_crawl, hvac_map, clg_ssn_sensor)
# Get living space infiltration
living_ach50 = nil
living_const_ach = nil
Expand Down Expand Up @@ -1687,7 +1713,7 @@ def self.apply_infiltration_and_ventilation_fans(model, weather, site, vent_fans

# Get mechanical ventilation
apply_infiltration_and_mechanical_ventilation(model, site, vent_fans_mech, living_ach50, living_const_ach, weather, vent_fans_kitchen, vent_fans_bath, vented_dryers,
range_sch_sensors_map, bath_sch_sensors_map, dryer_exhaust_sch_sensors_map, has_flue_chimney, hvac_map)
range_sch_sensors_map, bath_sch_sensors_map, dryer_exhaust_sch_sensors_map, has_flue_chimney, hvac_map, clg_ssn_sensor)
end

def self.apply_infiltration_to_living(site, living_ach50, living_const_ach, infil_program, weather, has_flue_chimney)
Expand Down
Loading