diff --git a/HPXMLtoOpenStudio/measure.rb b/HPXMLtoOpenStudio/measure.rb index 93bbd7cd54..1c6860e4df 100644 --- a/HPXMLtoOpenStudio/measure.rb +++ b/HPXMLtoOpenStudio/measure.rb @@ -396,11 +396,15 @@ def init(model, hpxml_bldg, hpxml_header) hpxml_bldg.delete_adiabatic_subsurfaces() # EnergyPlus doesn't allow this # Hidden feature: Version of the ANSI/RESNET/ICC 301 Standard to use for equations/assumptions - if hpxml_header.eri_calculation_version.nil? - hpxml_header.eri_calculation_version = 'latest' + if hpxml_header.eri_calculation_versions.size > 1 + fail 'Only a single ERI version is supported.' end - if hpxml_header.eri_calculation_version == 'latest' - hpxml_header.eri_calculation_version = Constants::ERIVersions[-1] + + if hpxml_header.eri_calculation_versions.empty? + hpxml_header.eri_calculation_versions = ['latest'] + end + if hpxml_header.eri_calculation_versions == ['latest'] + hpxml_header.eri_calculation_versions = [Constants::ERIVersions[-1]] end # Hidden feature: Whether to override certain assumptions to better match the ASHRAE 140 specification diff --git a/HPXMLtoOpenStudio/measure.xml b/HPXMLtoOpenStudio/measure.xml index 6478eb7ed1..b2900c7b17 100644 --- a/HPXMLtoOpenStudio/measure.xml +++ b/HPXMLtoOpenStudio/measure.xml @@ -3,8 +3,8 @@ 3.1 hpxm_lto_openstudio b1543b30-9465-45ff-ba04-1d1f85e763bc - ddf8813a-f35c-400b-890b-d26121d86506 - 2025-01-17T00:21:50Z + bd5c3b3f-ccda-4dd5-b16a-1b4f7a4d69e4 + 2025-02-03T19:22:20Z D8922A73 HPXMLtoOpenStudio HPXML to OpenStudio Translator @@ -183,13 +183,13 @@ measure.rb rb script - 28965567 + 1EEA4B7B airflow.rb rb resource - 29D991BE + 23B7B4AC battery.rb @@ -327,7 +327,7 @@ defaults.rb rb resource - EB4D5A1A + CF7C5504 energyplus.rb @@ -351,13 +351,13 @@ hotwater_appliances.rb rb resource - 0B205523 + FA4DA1B3 hpxml.rb rb resource - 270F2EEB + 89899516 hpxml_schema/HPXML.xsd @@ -405,7 +405,7 @@ lighting.rb rb resource - 7B7F6D4C + 74899E6B location.rb @@ -633,7 +633,7 @@ waterheater.rb rb resource - ECEBF85F + A8560A5A weather.rb @@ -669,7 +669,7 @@ test_defaults.rb rb test - 7E9FC2FA + 45AEFA27 test_enclosure.rb diff --git a/HPXMLtoOpenStudio/resources/airflow.rb b/HPXMLtoOpenStudio/resources/airflow.rb index 4646118e59..379260d5f0 100644 --- a/HPXMLtoOpenStudio/resources/airflow.rb +++ b/HPXMLtoOpenStudio/resources/airflow.rb @@ -550,8 +550,8 @@ def self.apply_natural_ventilation_and_whole_house_fan(runner, model, spaces, hp vent_program.addLine("Set Tnvsp = (#{htg_sp_sensor.name} + #{clg_sp_sensor.name}) / 2") else # No HVAC system; use the average of defaulted heating/cooling setpoints. - htg_weekday_setpoints, htg_weekend_setpoints = Defaults.get_heating_setpoint(HPXML::HVACControlTypeManual, hpxml_header.eri_calculation_version) - clg_weekday_setpoints, clg_weekend_setpoints = Defaults.get_cooling_setpoint(HPXML::HVACControlTypeManual, hpxml_header.eri_calculation_version) + htg_weekday_setpoints, htg_weekend_setpoints = Defaults.get_heating_setpoint(HPXML::HVACControlTypeManual, hpxml_header.eri_calculation_versions[0]) + clg_weekday_setpoints, clg_weekend_setpoints = Defaults.get_cooling_setpoint(HPXML::HVACControlTypeManual, hpxml_header.eri_calculation_versions[0]) if htg_weekday_setpoints.split(', ').uniq.size == 1 && htg_weekend_setpoints.split(', ').uniq.size == 1 && htg_weekday_setpoints.split(', ').uniq == htg_weekend_setpoints.split(', ').uniq default_htg_sp = UnitConversions.convert(htg_weekend_setpoints.split(', ').uniq[0].to_f, 'F', 'C') else @@ -2391,14 +2391,14 @@ def self.apply_infiltration_adjustment_to_conditioned(runner, model, spaces, hpx infil_program.addLine('Set Qfan_with_ducts = (@Max Qexhaust Qsupply)') # Total combined air exchange - if Constants::ERIVersions.index(hpxml_header.eri_calculation_version) >= Constants::ERIVersions.index('2022') + if Constants::ERIVersions.index(hpxml_header.eri_calculation_versions[0]) >= Constants::ERIVersions.index('2022') infil_program.addLine('Set Qimb = (@Abs (Qsupply - Qexhaust))') infil_program.addLine('If Qinf + Qimb > 0') infil_program.addLine(' Set Qtot = Qfan_with_ducts + (Qinf^2) / (Qinf + Qimb)') infil_program.addLine('Else') infil_program.addLine(' Set Qtot = Qfan_with_ducts') infil_program.addLine('EndIf') - elsif Constants::ERIVersions.index(hpxml_header.eri_calculation_version) >= Constants::ERIVersions.index('2019') + elsif Constants::ERIVersions.index(hpxml_header.eri_calculation_versions[0]) >= Constants::ERIVersions.index('2019') # Follow ASHRAE 62.2-2016, Normative Appendix C equations for time-varying total airflow infil_program.addLine('If Qfan_with_ducts > 0') # Balanced system if the total supply airflow and total exhaust airflow are within 10% of their average. diff --git a/HPXMLtoOpenStudio/resources/defaults.rb b/HPXMLtoOpenStudio/resources/defaults.rb index 9fbd257932..c2e10dbc1b 100644 --- a/HPXMLtoOpenStudio/resources/defaults.rb +++ b/HPXMLtoOpenStudio/resources/defaults.rb @@ -27,9 +27,10 @@ module Defaults # @param convert_shared_systems [Boolean] Whether to convert shared systems to equivalent in-unit systems per ANSI/RESNET/ICC 301 # @return [Array] Maps of HPXML::Zones => DesignLoadValues object, HPXML::Spaces => DesignLoadValues object def self.apply(runner, hpxml, hpxml_bldg, weather, schedules_file: nil, convert_shared_systems: true) - eri_version = hpxml.header.eri_calculation_version - if eri_version.nil? + if hpxml.header.eri_calculation_versions.nil? || hpxml.header.eri_calculation_versions.empty? eri_version = 'latest' + else + eri_version = hpxml.header.eri_calculation_versions[0] end if eri_version == 'latest' eri_version = Constants::ERIVersions[-1] diff --git a/HPXMLtoOpenStudio/resources/hotwater_appliances.rb b/HPXMLtoOpenStudio/resources/hotwater_appliances.rb index 36dbfcb758..c30afdb1f7 100644 --- a/HPXMLtoOpenStudio/resources/hotwater_appliances.rb +++ b/HPXMLtoOpenStudio/resources/hotwater_appliances.rb @@ -23,7 +23,7 @@ def self.apply(runner, model, weather, spaces, hpxml_bldg, hpxml_header, schedul nbeds = hpxml_bldg.building_construction.number_of_bedrooms n_occ = hpxml_bldg.building_occupancy.number_of_residents unit_type = hpxml_bldg.building_construction.residential_facility_type - eri_version = hpxml_header.eri_calculation_version + eri_version = hpxml_header.eri_calculation_versions[0] unit_multiplier = hpxml_bldg.building_construction.number_of_units # Get appliances, etc. diff --git a/HPXMLtoOpenStudio/resources/hpxml.rb b/HPXMLtoOpenStudio/resources/hpxml.rb index b062fa4ca1..c34fa367cc 100644 --- a/HPXMLtoOpenStudio/resources/hpxml.rb +++ b/HPXMLtoOpenStudio/resources/hpxml.rb @@ -866,11 +866,11 @@ def initialize(hpxml_element, *args, **kwargs) :software_program_version, # [String] SoftwareInfo/SoftwareProgramVersion :apply_ashrae140_assumptions, # [Boolean] SoftwareInfo/extension/ApplyASHRAE140Assumptions :whole_sfa_or_mf_building_sim, # [Boolean] SoftwareInfo/extension/WholeSFAorMFBuildingSimulation - :eri_calculation_version, # [String] SoftwareInfo/extension/ERICalculation/Version - :co2index_calculation_version, # [String] SoftwareInfo/extension/CO2IndexCalculation/Version - :energystar_calculation_version, # [String] SoftwareInfo/extension/EnergyStarCalculation/Version - :iecc_eri_calculation_version, # [String] SoftwareInfo/extension/IECCERICalculation/Version - :zerh_calculation_version, # [String] SoftwareInfo/extension/ZERHCalculation/Version + :eri_calculation_versions, # [Array] SoftwareInfo/extension/ERICalculation/Version + :co2index_calculation_versions, # [Array] SoftwareInfo/extension/CO2IndexCalculation/Version + :energystar_calculation_versions, # [Array] SoftwareInfo/extension/EnergyStarCalculation/Version + :iecc_eri_calculation_versions, # [Array] SoftwareInfo/extension/IECCERICalculation/Version + :zerh_calculation_versions, # [Array] SoftwareInfo/extension/ZERHCalculation/Version :timestep, # [Integer] SoftwareInfo/extension/SimulationControl/Timestep (minutes) :sim_begin_month, # [Integer] SoftwareInfo/extension/SimulationControl/BeginMonth :sim_begin_day, # [Integer] SoftwareInfo/extension/SimulationControl/BeginDayOfMonth @@ -929,16 +929,19 @@ def to_doc(hpxml_doc) XMLHelper.add_element(software_info, 'SoftwareProgramVersion', @software_program_version, :string) unless @software_program_version.nil? XMLHelper.add_extension(software_info, 'ApplyASHRAE140Assumptions', @apply_ashrae140_assumptions, :boolean) unless @apply_ashrae140_assumptions.nil? XMLHelper.add_extension(software_info, 'WholeSFAorMFBuildingSimulation', @whole_sfa_or_mf_building_sim, :boolean) unless @whole_sfa_or_mf_building_sim.nil? - { 'ERICalculation' => @eri_calculation_version, - 'CO2IndexCalculation' => @co2index_calculation_version, - 'EnergyStarCalculation' => @energystar_calculation_version, - 'IECCERICalculation' => @iecc_eri_calculation_version, - 'ZERHCalculation' => @zerh_calculation_version }.each do |element_name, calculation_version| - next if calculation_version.nil? - - extension = XMLHelper.create_elements_as_needed(software_info, ['extension']) - calculation = XMLHelper.add_element(extension, element_name) - XMLHelper.add_element(calculation, 'Version', calculation_version, :string) + { 'ERICalculation' => @eri_calculation_versions, + 'CO2IndexCalculation' => @co2index_calculation_versions, + 'EnergyStarCalculation' => @energystar_calculation_versions, + 'IECCERICalculation' => @iecc_eri_calculation_versions, + 'ZERHCalculation' => @zerh_calculation_versions }.each do |element_name, calculation_versions| + calculation_versions = [] if calculation_versions.nil? + if not calculation_versions.empty? + extension = XMLHelper.create_elements_as_needed(software_info, ['extension']) + calculation = XMLHelper.add_element(extension, element_name) + end + calculation_versions.each do |calculation_version| + XMLHelper.add_element(calculation, 'Version', calculation_version, :string) + end end if (not @timestep.nil?) || (not @sim_begin_month.nil?) || (not @sim_begin_day.nil?) || (not @sim_end_month.nil?) || (not @sim_end_day.nil?) || (not @temperature_capacitance_multiplier.nil?) || (not @defrost_model_type.nil?) || (not @hvac_onoff_thermostat_deadband.nil?) || (not @heat_pump_backup_heating_capacity_increment.nil?) extension = XMLHelper.create_elements_as_needed(software_info, ['extension']) @@ -975,11 +978,11 @@ def from_doc(hpxml) @transaction = XMLHelper.get_value(hpxml, 'XMLTransactionHeaderInformation/Transaction', :string) @software_program_used = XMLHelper.get_value(hpxml, 'SoftwareInfo/SoftwareProgramUsed', :string) @software_program_version = XMLHelper.get_value(hpxml, 'SoftwareInfo/SoftwareProgramVersion', :string) - @eri_calculation_version = XMLHelper.get_value(hpxml, 'SoftwareInfo/extension/ERICalculation/Version', :string) - @co2index_calculation_version = XMLHelper.get_value(hpxml, 'SoftwareInfo/extension/CO2IndexCalculation/Version', :string) - @iecc_eri_calculation_version = XMLHelper.get_value(hpxml, 'SoftwareInfo/extension/IECCERICalculation/Version', :string) - @energystar_calculation_version = XMLHelper.get_value(hpxml, 'SoftwareInfo/extension/EnergyStarCalculation/Version', :string) - @zerh_calculation_version = XMLHelper.get_value(hpxml, 'SoftwareInfo/extension/ZERHCalculation/Version', :string) + @eri_calculation_versions = XMLHelper.get_values(hpxml, 'SoftwareInfo/extension/ERICalculation/Version', :string) + @co2index_calculation_versions = XMLHelper.get_values(hpxml, 'SoftwareInfo/extension/CO2IndexCalculation/Version', :string) + @iecc_eri_calculation_versions = XMLHelper.get_values(hpxml, 'SoftwareInfo/extension/IECCERICalculation/Version', :string) + @energystar_calculation_versions = XMLHelper.get_values(hpxml, 'SoftwareInfo/extension/EnergyStarCalculation/Version', :string) + @zerh_calculation_versions = XMLHelper.get_values(hpxml, 'SoftwareInfo/extension/ZERHCalculation/Version', :string) @timestep = XMLHelper.get_value(hpxml, 'SoftwareInfo/extension/SimulationControl/Timestep', :integer) @sim_begin_month = XMLHelper.get_value(hpxml, 'SoftwareInfo/extension/SimulationControl/BeginMonth', :integer) @sim_begin_day = XMLHelper.get_value(hpxml, 'SoftwareInfo/extension/SimulationControl/BeginDayOfMonth', :integer) diff --git a/HPXMLtoOpenStudio/resources/lighting.rb b/HPXMLtoOpenStudio/resources/lighting.rb index 551f48acf4..b784d91eb2 100644 --- a/HPXMLtoOpenStudio/resources/lighting.rb +++ b/HPXMLtoOpenStudio/resources/lighting.rb @@ -16,7 +16,7 @@ def self.apply(runner, model, spaces, hpxml_bldg, hpxml_header, schedules_file) lighting = hpxml_bldg.lighting unit_multiplier = hpxml_bldg.building_construction.number_of_units cfa = hpxml_bldg.building_construction.conditioned_floor_area - eri_version = hpxml_header.eri_calculation_version + eri_version = hpxml_header.eri_calculation_versions[0] n_occ = hpxml_bldg.building_occupancy.number_of_residents ltg_locns = [HPXML::LocationInterior, HPXML::LocationExterior, HPXML::LocationGarage] diff --git a/HPXMLtoOpenStudio/resources/waterheater.rb b/HPXMLtoOpenStudio/resources/waterheater.rb index b7c2d3aeaf..ec47315d48 100644 --- a/HPXMLtoOpenStudio/resources/waterheater.rb +++ b/HPXMLtoOpenStudio/resources/waterheater.rb @@ -56,7 +56,7 @@ def self.apply_tank(runner, model, spaces, hpxml_bldg, hpxml_header, water_heati unit_multiplier = hpxml_bldg.building_construction.number_of_units solar_fraction = get_water_heater_solar_fraction(water_heating_system, hpxml_bldg) t_set_c = get_t_set_c(water_heating_system.temperature, water_heating_system.water_heater_type) - plant_loop = add_plant_loop(model, t_set_c, hpxml_header.eri_calculation_version, unit_multiplier) + plant_loop = add_plant_loop(model, t_set_c, hpxml_header.eri_calculation_versions[0], unit_multiplier) act_vol = calc_storage_tank_actual_vol(water_heating_system.tank_volume, water_heating_system.fuel_type) u, ua, eta_c = disaggregate_tank_losses_and_burner_efficiency(act_vol, water_heating_system, solar_fraction, hpxml_bldg.building_construction.number_of_bedrooms) @@ -99,7 +99,7 @@ def self.apply_tankless(runner, model, spaces, hpxml_bldg, hpxml_header, water_h water_heating_system.heating_capacity = 100000000000.0 * unit_multiplier solar_fraction = get_water_heater_solar_fraction(water_heating_system, hpxml_bldg) t_set_c = get_t_set_c(water_heating_system.temperature, water_heating_system.water_heater_type) - plant_loop = add_plant_loop(model, t_set_c, hpxml_header.eri_calculation_version, unit_multiplier) + plant_loop = add_plant_loop(model, t_set_c, hpxml_header.eri_calculation_versions[0], unit_multiplier) act_vol = 1.0 * unit_multiplier _u, ua, eta_c = disaggregate_tank_losses_and_burner_efficiency(act_vol, water_heating_system, solar_fraction, hpxml_bldg.building_construction.number_of_bedrooms) @@ -142,7 +142,7 @@ def self.apply_hpwh(runner, model, spaces, hpxml_bldg, hpxml_header, water_heati obj_name = Constants::ObjectTypeWaterHeater solar_fraction = get_water_heater_solar_fraction(water_heating_system, hpxml_bldg) t_set_c = get_t_set_c(water_heating_system.temperature, water_heating_system.water_heater_type) - plant_loop = add_plant_loop(model, t_set_c, hpxml_header.eri_calculation_version, unit_multiplier) + plant_loop = add_plant_loop(model, t_set_c, hpxml_header.eri_calculation_versions[0], unit_multiplier) # Add in schedules for Tamb, RHamb, and the compressor hpwh_tamb = Model.add_schedule_constant( @@ -283,7 +283,7 @@ def self.apply_combi(runner, model, spaces, hpxml_bldg, hpxml_header, water_heat end t_set_c = get_t_set_c(water_heating_system.temperature, water_heating_system.water_heater_type) - plant_loop = add_plant_loop(model, t_set_c, hpxml_header.eri_calculation_version, unit_multiplier) + plant_loop = add_plant_loop(model, t_set_c, hpxml_header.eri_calculation_versions[0], unit_multiplier) # Create water heater water_heater = apply_water_heater(runner, model, diff --git a/HPXMLtoOpenStudio/tests/test_defaults.rb b/HPXMLtoOpenStudio/tests/test_defaults.rb index c91ae7eaeb..d954205f6f 100644 --- a/HPXMLtoOpenStudio/tests/test_defaults.rb +++ b/HPXMLtoOpenStudio/tests/test_defaults.rb @@ -3666,7 +3666,7 @@ def test_clothes_washers # Test defaults before 301-2019 Addendum A hpxml, hpxml_bldg = _create_hpxml('base.xml') - hpxml.header.eri_calculation_version = '2019' + hpxml.header.eri_calculation_versions = ['2019'] hpxml_bldg.clothes_washers[0].is_shared_appliance = nil hpxml_bldg.clothes_washers[0].location = nil hpxml_bldg.clothes_washers[0].integrated_modified_energy_factor = nil @@ -3720,7 +3720,7 @@ def test_clothes_dryers _test_default_clothes_dryer_values(default_hpxml_bldg.clothes_dryers[0], false, HPXML::LocationConditionedSpace, 3.01, 1.0, @default_schedules_csv_data[SchedulesFile::Columns[:ClothesDryer].name]['WeekdayScheduleFractions'], @default_schedules_csv_data[SchedulesFile::Columns[:ClothesDryer].name]['WeekendScheduleFractions'], @default_schedules_csv_data[SchedulesFile::Columns[:ClothesDryer].name]['MonthlyScheduleMultipliers']) # Test defaults w/ electric clothes dryer before 301-2019 Addendum A - hpxml.header.eri_calculation_version = '2019' + hpxml.header.eri_calculation_versions = ['2019'] hpxml_bldg.clothes_dryers[0].fuel_type = HPXML::FuelTypeElectricity XMLHelper.write_file(hpxml.to_doc, @tmp_hpxml_path) _default_hpxml, default_hpxml_bldg = _test_measure() @@ -3792,7 +3792,7 @@ def test_dishwashers _test_default_dishwasher_values(default_hpxml_bldg.dishwashers[0], false, HPXML::LocationConditionedSpace, 467.0, 0.12, 1.09, 33.12, 4.0, 12, 1.0, @default_schedules_csv_data[SchedulesFile::Columns[:Dishwasher].name]['WeekdayScheduleFractions'], @default_schedules_csv_data[SchedulesFile::Columns[:Dishwasher].name]['WeekendScheduleFractions'], @default_schedules_csv_data[SchedulesFile::Columns[:Dishwasher].name]['MonthlyScheduleMultipliers']) # Test defaults before 301-2019 Addendum A - hpxml.header.eri_calculation_version = '2019' + hpxml.header.eri_calculation_versions = ['2019'] XMLHelper.write_file(hpxml.to_doc, @tmp_hpxml_path) _default_hpxml, default_hpxml_bldg = _test_measure() _test_default_dishwasher_values(default_hpxml_bldg.dishwashers[0], false, HPXML::LocationConditionedSpace, 467.0, 999, 999, 999, 999, 12, 1.0, @default_schedules_csv_data[SchedulesFile::Columns[:Dishwasher].name]['WeekdayScheduleFractions'], @default_schedules_csv_data[SchedulesFile::Columns[:Dishwasher].name]['WeekendScheduleFractions'], @default_schedules_csv_data[SchedulesFile::Columns[:Dishwasher].name]['MonthlyScheduleMultipliers']) @@ -3858,7 +3858,7 @@ def test_refrigerators _test_default_refrigerator_values(default_hpxml_bldg, HPXML::LocationConditionedSpace, 727.0, 1.0, nil, nil, nil, @default_schedules_csv_data[SchedulesFile::Columns[:Refrigerator].name]['ConstantScheduleCoefficients'], @default_schedules_csv_data[SchedulesFile::Columns[:Refrigerator].name]['TemperatureScheduleCoefficients']) # Test defaults before 301-2019 Addendum A - hpxml.header.eri_calculation_version = '2019' + hpxml.header.eri_calculation_versions = ['2019'] hpxml_bldg.building_construction.number_of_bedrooms = 3 XMLHelper.write_file(hpxml.to_doc, @tmp_hpxml_path) _default_hpxml, default_hpxml_bldg = _test_measure() @@ -3988,7 +3988,7 @@ def test_cooking_ranges _test_default_cooking_range_values(default_hpxml_bldg.cooking_ranges[0], HPXML::LocationConditionedSpace, false, 1.0, @default_schedules_csv_data[SchedulesFile::Columns[:CookingRange].name]['WeekdayScheduleFractions'], @default_schedules_csv_data[SchedulesFile::Columns[:CookingRange].name]['WeekendScheduleFractions'], @default_schedules_csv_data[SchedulesFile::Columns[:CookingRange].name]['MonthlyScheduleMultipliers']) # Test defaults before 301-2019 Addendum A - hpxml.header.eri_calculation_version = '2019' + hpxml.header.eri_calculation_versions = ['2019'] XMLHelper.write_file(hpxml.to_doc, @tmp_hpxml_path) _default_hpxml, default_hpxml_bldg = _test_measure() _test_default_cooking_range_values(default_hpxml_bldg.cooking_ranges[0], HPXML::LocationConditionedSpace, false, 1.0, @default_schedules_csv_data[SchedulesFile::Columns[:CookingRange].name]['WeekdayScheduleFractions'], @default_schedules_csv_data[SchedulesFile::Columns[:CookingRange].name]['WeekendScheduleFractions'], @default_schedules_csv_data[SchedulesFile::Columns[:CookingRange].name]['MonthlyScheduleMultipliers']) @@ -4009,7 +4009,7 @@ def test_ovens _test_default_oven_values(default_hpxml_bldg.ovens[0], false) # Test defaults before 301-2019 Addendum A - hpxml.header.eri_calculation_version = '2019' + hpxml.header.eri_calculation_versions = ['2019'] XMLHelper.write_file(hpxml.to_doc, @tmp_hpxml_path) _default_hpxml, default_hpxml_bldg = _test_measure() _test_default_oven_values(default_hpxml_bldg.ovens[0], false)