diff --git a/BuildResidentialScheduleFile/measure.rb b/BuildResidentialScheduleFile/measure.rb
index 77e680561b..1ea6b6e0fb 100644
--- a/BuildResidentialScheduleFile/measure.rb
+++ b/BuildResidentialScheduleFile/measure.rb
@@ -68,10 +68,10 @@ def arguments(model) # rubocop:disable Lint/UnusedMethodArgument
arg.setDefaultValue(false)
args << arg
- # arg = OpenStudio::Measure::OSArgument.makeStringArgument('building_id', false)
- # arg.setDisplayName('BuildingID')
- # arg.setDescription("The ID of the HPXML Building. Only required if there are multiple Building elements in the HPXML file. Use 'ALL' to run all the HPXML Buildings (dwelling units) of a multifamily building in a single model.")
- # args << arg
+ arg = OpenStudio::Measure::OSArgument.makeStringArgument('building_id', false)
+ arg.setDisplayName('BuildingID')
+ arg.setDescription("The ID of the HPXML Building. Only required if there are multiple Building elements in the HPXML file. Use 'ALL' to apply schedules to all the HPXML Buildings (dwelling units) of a multifamily building.")
+ args << arg
return args
end
@@ -104,22 +104,27 @@ def run(model, runner, user_arguments)
hpxml = HPXML.new(hpxml_path: hpxml_path, building_id: 'ALL')
- # FIXME: Relax this constraint (using a new building_id measure argument?)
- # if hpxml.buildings.size > 1
- # runner.registerError('Cannot currently handle an HPXML with multiple Building elements.')
- # return false
- # end
- # hpxml_bldg = hpxml.buildings[0]
-
debug = false
if args[:debug].is_initialized
debug = args[:debug].get
end
args[:debug] = debug
+ if hpxml.buildings.size > 1 && !args[:building_id].is_initialized
+ fail "Argument 'building_id' required if there are multiple Building elements in the HPXML file."
+ end
+
doc = XMLHelper.parse_file(hpxml_path)
hpxml_doc = XMLHelper.get_element(doc, '/HPXML')
hpxml.buildings.each do |hpxml_bldg|
+ building_id = hpxml_bldg.building_id
+
+ if hpxml.buildings.size > 1
+ if args[:building_id].get != 'ALL'
+ next if args[:building_id].get != building_id
+ end
+ end
+
# exit if number of occupants is zero
if hpxml_bldg.building_occupancy.number_of_residents == 0
runner.registerInfo('Number of occupants set to zero; skipping generation of stochastic schedules.')
@@ -135,7 +140,7 @@ def run(model, runner, user_arguments)
return false if not success
XMLHelper.get_elements(hpxml_doc, 'Building').each do |building|
- next if XMLHelper.get_attribute_value(XMLHelper.get_element(building, 'BuildingID'), 'id') != hpxml_bldg.building_id
+ next if XMLHelper.get_attribute_value(XMLHelper.get_element(building, 'BuildingID'), 'id') != building_id
# modify the hpxml with the schedules path
extension = XMLHelper.create_elements_as_needed(building, ['BuildingDetails', 'BuildingSummary', 'extension'])
diff --git a/BuildResidentialScheduleFile/measure.xml b/BuildResidentialScheduleFile/measure.xml
index 7efb8055c9..411bc0346e 100644
--- a/BuildResidentialScheduleFile/measure.xml
+++ b/BuildResidentialScheduleFile/measure.xml
@@ -3,8 +3,8 @@
3.1
build_residential_schedule_file
f770b2db-1a9f-4e99-99a7-7f3161a594b1
- eef5a8ee-8396-415f-b0b2-04d0f3e4e108
- 2023-09-05T20:47:06Z
+ be54fce8-de7a-46b1-a1ba-ec86f5cb12a7
+ 2023-09-05T21:27:45Z
03F02484
BuildResidentialScheduleFile
Schedule File Builder
@@ -71,6 +71,14 @@
+
+ building_id
+ BuildingID
+ The ID of the HPXML Building. Only required if there are multiple Building elements in the HPXML file. Use 'ALL' to apply schedules to all the HPXML Buildings (dwelling units) of a multifamily building.
+ String
+ false
+ false
+
@@ -94,7 +102,7 @@
measure.rb
rb
script
- 50A182A0
+ A1DF0987
README.md
@@ -892,7 +900,7 @@
build_residential_schedule_file_test.rb
rb
test
- 5E289C51
+ 7BA678BD
diff --git a/BuildResidentialScheduleFile/tests/build_residential_schedule_file_test.rb b/BuildResidentialScheduleFile/tests/build_residential_schedule_file_test.rb
index 224083ff47..2648f36e7c 100644
--- a/BuildResidentialScheduleFile/tests/build_residential_schedule_file_test.rb
+++ b/BuildResidentialScheduleFile/tests/build_residential_schedule_file_test.rb
@@ -287,6 +287,7 @@ def test_multiple_buildings
XMLHelper.write_file(hpxml.to_doc, @tmp_hpxml_path)
@args_hash['output_csv_path'] = File.absolute_path(File.join(@tmp_output_path, 'occupancy-stochastic.csv'))
+ @args_hash['building_id'] = 'ALL'
model, hpxml, result = _test_measure()
info_msgs = result.info.map { |x| x.logMessage }
@@ -320,6 +321,51 @@ def test_multiple_buildings
end
end
+ def test_multiple_buildings_id
+ hpxml = _create_hpxml('base-multiple-buildings.xml')
+ XMLHelper.write_file(hpxml.to_doc, @tmp_hpxml_path)
+
+ @args_hash['output_csv_path'] = File.absolute_path(File.join(@tmp_output_path, 'occupancy-stochastic.csv'))
+ @args_hash['building_id'] = 'MyBuilding_2'
+ model, hpxml, result = _test_measure()
+
+ info_msgs = result.info.map { |x| x.logMessage }
+ assert(info_msgs.any? { |info_msg| info_msg.include?('stochastic schedule') })
+ assert(info_msgs.any? { |info_msg| info_msg.include?('SimYear=2007') })
+ assert(info_msgs.any? { |info_msg| info_msg.include?('MinutesPerStep=60') })
+ assert(info_msgs.any? { |info_msg| info_msg.include?('State=CO') })
+ assert(!info_msgs.any? { |info_msg| info_msg.include?('RandomSeed') })
+ assert(info_msgs.any? { |info_msg| info_msg.include?('GeometryNumOccupants=3.0') })
+
+ hpxml.buildings.each do |hpxml_bldg|
+ building_id = hpxml_bldg.building_id
+
+ if building_id == @args_hash['building_id']
+ sf = SchedulesFile.new(model: model,
+ schedules_paths: hpxml_bldg.schedules.schedules_filepaths,
+ year: 2007,
+ output_path: @tmp_schedule_file_path)
+
+ assert_in_epsilon(6689, sf.annual_equivalent_full_load_hrs(col_name: SchedulesFile::ColumnOccupants, schedules: sf.tmp_schedules), 0.1)
+ assert_in_epsilon(2086, sf.annual_equivalent_full_load_hrs(col_name: SchedulesFile::ColumnLightingInterior, schedules: sf.tmp_schedules), 0.1)
+ assert_in_epsilon(2086, sf.annual_equivalent_full_load_hrs(col_name: SchedulesFile::ColumnLightingGarage, schedules: sf.tmp_schedules), 0.1)
+ assert_in_epsilon(534, sf.annual_equivalent_full_load_hrs(col_name: SchedulesFile::ColumnCookingRange, schedules: sf.tmp_schedules), 0.1)
+ assert_in_epsilon(213, sf.annual_equivalent_full_load_hrs(col_name: SchedulesFile::ColumnDishwasher, schedules: sf.tmp_schedules), 0.1)
+ assert_in_epsilon(134, sf.annual_equivalent_full_load_hrs(col_name: SchedulesFile::ColumnClothesWasher, schedules: sf.tmp_schedules), 0.1)
+ assert_in_epsilon(151, sf.annual_equivalent_full_load_hrs(col_name: SchedulesFile::ColumnClothesDryer, schedules: sf.tmp_schedules), 0.1)
+ assert_in_epsilon(3250, sf.annual_equivalent_full_load_hrs(col_name: SchedulesFile::ColumnCeilingFan, schedules: sf.tmp_schedules), 0.1)
+ assert_in_epsilon(4840, sf.annual_equivalent_full_load_hrs(col_name: SchedulesFile::ColumnPlugLoadsOther, schedules: sf.tmp_schedules), 0.1)
+ assert_in_epsilon(4840, sf.annual_equivalent_full_load_hrs(col_name: SchedulesFile::ColumnPlugLoadsTV, schedules: sf.tmp_schedules), 0.1)
+ assert_in_epsilon(298, sf.annual_equivalent_full_load_hrs(col_name: SchedulesFile::ColumnHotWaterDishwasher, schedules: sf.tmp_schedules), 0.1)
+ assert_in_epsilon(325, sf.annual_equivalent_full_load_hrs(col_name: SchedulesFile::ColumnHotWaterClothesWasher, schedules: sf.tmp_schedules), 0.1)
+ assert_in_epsilon(887, sf.annual_equivalent_full_load_hrs(col_name: SchedulesFile::ColumnHotWaterFixtures, schedules: sf.tmp_schedules), 0.1)
+ assert(!sf.schedules.keys.include?(SchedulesFile::ColumnSleeping))
+ else
+ assert_empty(hpxml_bldg.schedules.schedules_filepaths)
+ end
+ end
+ end
+
def _test_measure(expect_fail: false)
# create an instance of the measure
measure = BuildResidentialScheduleFile.new