Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Mech Vent Preconditioning - Energy #763

Merged
merged 2 commits into from
May 14, 2021
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ __New Features__
- Adds an `--ep-input-format` argument to run_simulation.rb to choose epJSON as the EnergyPlus input file format instead of IDF.
- Allows `HeatingSeason` & `CoolingSeason` to be specified for defining heating and cooling equipment availability.
- Removes error-check for number of bedrooms based on conditioned floor area, per RESNET guidance.
- Revises shared mechanical ventilation preconditioning control logic to operate less often.

__Bugfixes__
- Improves ground reflectance when there is shading of windows/skylights.
Expand Down
16 changes: 8 additions & 8 deletions HPXMLtoOpenStudio/measure.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
<error>Unable to extract OpenStudio::Measure::OSMeasure object from /mnt/c/git/openstudio-hpxml/HPXMLtoOpenStudio/measure.rb. The script should contain a class that derives from OpenStudio::Measure::OSMeasure and should close with a line stating the class name followed by .new.registerWithApplication.</error>
<name>hpxm_lto_openstudio</name>
<uid>b1543b30-9465-45ff-ba04-1d1f85e763bc</uid>
<version_id>c929f134-7b14-43a4-a9ee-11bb8d27da1f</version_id>
<version_modified>20210513T171148Z</version_modified>
<version_id>9d668d73-be14-4a65-b206-764f86eca574</version_id>
<version_modified>20210514T182320Z</version_modified>
<xml_checksum>D8922A73</xml_checksum>
<class_name>HPXMLtoOpenStudio</class_name>
<display_name>HPXML to OpenStudio Translator</display_name>
Expand Down Expand Up @@ -407,12 +407,6 @@
<usage_type>test</usage_type>
<checksum>4C662AE0</checksum>
</file>
<file>
<filename>airflow.rb</filename>
<filetype>rb</filetype>
<usage_type>resource</usage_type>
<checksum>99EB6060</checksum>
</file>
<file>
<filename>geometry.rb</filename>
<filetype>rb</filetype>
Expand Down Expand Up @@ -586,5 +580,11 @@
<usage_type>resource</usage_type>
<checksum>4BBE084E</checksum>
</file>
<file>
<filename>airflow.rb</filename>
<filetype>rb</filetype>
<usage_type>resource</usage_type>
<checksum>E5C505B7</checksum>
</file>
</files>
</measure>
76 changes: 49 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')
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,11 +1519,23 @@ 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)
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)}")
Expand All @@ -1534,16 +1546,21 @@ 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_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)
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)}")
Expand All @@ -1554,18 +1571,23 @@ 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_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 +1662,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 +1671,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 +1709,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