Skip to content

Commit

Permalink
Merge pull request #1836 from NREL/fix_partial_outputs
Browse files Browse the repository at this point in the history
Prevent partial outputs when error
  • Loading branch information
shorowit authored Sep 18, 2024
2 parents 11bb6ec + 3f8efb3 commit 0d8be0f
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 56 deletions.
42 changes: 26 additions & 16 deletions HPXMLtoOpenStudio/measure.rb
Original file line number Diff line number Diff line change
Expand Up @@ -121,18 +121,11 @@ def run(model, runner, user_arguments)
# Do these once upfront for the entire HPXML object
epw_path, weather = process_weather(runner, hpxml, args)
process_whole_sfa_mf_inputs(hpxml)
hpxml_sch_map = process_defaults_schedules_emissions_files(runner, weather, hpxml, args)
hpxml_sch_map, hpxml_all_zone_loads, hpxml_all_space_loads = process_defaults_schedules_emissions_files(runner, weather, hpxml, args)

# Write updated HPXML object (w/ defaults) to file for inspection
XMLHelper.write_file(hpxml.to_doc, args[:hpxml_defaults_path])

# Write annual results output file
# This is helpful if the user wants to get these results right away (e.g.,
# they might be using the run_simulation.rb --skip-simulation argument.
results_out = []
Outputs.append_sizing_results(hpxml.buildings, results_out)
Outputs.write_results_out_to_file(results_out, args[:output_format], args[:annual_output_file_path])

# Create OpenStudio unit model(s)
hpxml_osm_map = {}
hpxml.buildings.each_with_index do |hpxml_bldg, i|
Expand All @@ -153,12 +146,27 @@ def run(model, runner, user_arguments)
Model.merge_unit_models(model, hpxml_osm_map)
end

# Create/write output
# Create EnergyPlus outputs
Outputs.apply_ems_programs(model, hpxml_osm_map, hpxml.header, args[:add_component_loads])
Outputs.apply_output_files(model, args[:debug])
Outputs.apply_output_file_controls(model, args[:debug])
Outputs.apply_additional_properties(model, hpxml, hpxml_osm_map, args[:hpxml_path], args[:building_id], args[:hpxml_defaults_path])
Outputs.write_debug_files(runner, model, args[:debug], args[:output_dir], epw_path)
# Outputs.apply_ems_debug_output(model) # Uncomment to debug EMS

# Write output files
Outputs.write_debug_files(runner, model, args[:debug], args[:output_dir], epw_path)

# Write annual results output file
# This is helpful if the user wants to get these results right away (e.g.,
# they might be using the run_simulation.rb --skip-simulation argument.
results_out = []
Outputs.append_sizing_results(hpxml.buildings, results_out)
Outputs.write_results_out_to_file(results_out, args[:output_format], args[:annual_output_file_path])

# Write design load details output file
hpxml.buildings.each do |hpxml_bldg|
HVACSizing.write_detailed_output(args[:output_format], args[:design_load_details_output_file_path],
hpxml_bldg, hpxml_all_zone_loads[hpxml_bldg], hpxml_all_space_loads[hpxml_bldg])
end
rescue Exception => e
runner.registerError("#{e.message}\n#{e.backtrace.join("\n")}")
return false
Expand Down Expand Up @@ -265,9 +273,11 @@ def process_whole_sfa_mf_inputs(hpxml)
# @param weather [WeatherFile] Weather object containing EPW information
# @param hpxml [HPXML] HPXML object
# @param args [Hash] Map of :argument_name => value
# @return [Hash] Map of HPXML Building => SchedulesFile object
# @return [Array<Hash, Hash, Hash>] Maps of HPXML Building => SchedulesFile object, HPXML Building => (Map of HPXML::Zones => DesignLoadValues object), HPXML Building => (Map of HPXML::Spaces => DesignLoadValues object)
def process_defaults_schedules_emissions_files(runner, weather, hpxml, args)
hpxml_sch_map = {}
hpxml_all_zone_loads = {}
hpxml_all_space_loads = {}
hpxml.buildings.each_with_index do |hpxml_bldg, i|
# Schedules file
Schedule.check_schedule_references(hpxml_bldg.header, args[:hpxml_path])
Expand All @@ -281,16 +291,16 @@ def process_defaults_schedules_emissions_files(runner, weather, hpxml, args)
hpxml_sch_map[hpxml_bldg] = schedules_file

# HPXML defaults
HPXMLDefaults.apply(runner, hpxml, hpxml_bldg, weather, schedules_file: schedules_file,
design_load_details_output_file_path: args[:design_load_details_output_file_path],
output_format: args[:output_format])
all_zone_loads, all_space_loads = HPXMLDefaults.apply(runner, hpxml, hpxml_bldg, weather, schedules_file: schedules_file)
hpxml_all_zone_loads[hpxml_bldg] = all_zone_loads
hpxml_all_space_loads[hpxml_bldg] = all_space_loads
end

# Emissions files
Schedule.check_emissions_references(hpxml.header, args[:hpxml_path])
Schedule.validate_emissions_files(hpxml.header)

return hpxml_sch_map
return hpxml_sch_map, hpxml_all_zone_loads, hpxml_all_space_loads
end

# Creates a full OpenStudio model that represents the given HPXML individual dwelling by
Expand Down
12 changes: 6 additions & 6 deletions HPXMLtoOpenStudio/measure.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
<schema_version>3.1</schema_version>
<name>hpxm_lto_openstudio</name>
<uid>b1543b30-9465-45ff-ba04-1d1f85e763bc</uid>
<version_id>3818f9b2-1396-4d2f-8556-5245f035f8e2</version_id>
<version_modified>2024-09-17T21:45:12Z</version_modified>
<version_id>4aeccf55-7efe-4b55-b392-a9d5bd7ae8d2</version_id>
<version_modified>2024-09-18T00:03:05Z</version_modified>
<xml_checksum>D8922A73</xml_checksum>
<class_name>HPXMLtoOpenStudio</class_name>
<display_name>HPXML to OpenStudio Translator</display_name>
Expand Down Expand Up @@ -183,7 +183,7 @@
<filename>measure.rb</filename>
<filetype>rb</filetype>
<usage_type>script</usage_type>
<checksum>AE303996</checksum>
<checksum>1C013D19</checksum>
</file>
<file>
<filename>airflow.rb</filename>
Expand Down Expand Up @@ -357,7 +357,7 @@
<filename>hpxml_defaults.rb</filename>
<filetype>rb</filetype>
<usage_type>resource</usage_type>
<checksum>4F6E5BAE</checksum>
<checksum>87C6D4D7</checksum>
</file>
<file>
<filename>hpxml_schema/HPXML.xsd</filename>
Expand Down Expand Up @@ -393,7 +393,7 @@
<filename>hvac_sizing.rb</filename>
<filetype>rb</filetype>
<usage_type>resource</usage_type>
<checksum>9E3F6A59</checksum>
<checksum>9EDA01CC</checksum>
</file>
<file>
<filename>internal_gains.rb</filename>
Expand Down Expand Up @@ -453,7 +453,7 @@
<filename>output.rb</filename>
<filetype>rb</filetype>
<usage_type>resource</usage_type>
<checksum>67DCE0D1</checksum>
<checksum>94DB6377</checksum>
</file>
<file>
<filename>psychrometrics.rb</filename>
Expand Down
20 changes: 9 additions & 11 deletions HPXMLtoOpenStudio/resources/hpxml_defaults.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,8 @@ module HPXMLDefaults
# @param weather [WeatherFile] Weather object containing EPW information
# @param schedules_file [SchedulesFile] SchedulesFile wrapper class instance of detailed schedule files
# @param convert_shared_systems [Boolean] Whether to convert shared systems to equivalent in-unit systems per ANSI 301
# @param design_load_details_output_file_path [String] Detailed HVAC sizing output file path
# @param output_format [String] Detailed HVAC sizing output file format ('csv', 'json', or 'msgpack')
# @return [nil]
def self.apply(runner, hpxml, hpxml_bldg, weather, schedules_file: nil, convert_shared_systems: true,
design_load_details_output_file_path: nil, output_format: 'csv')
# @return [Array<Hash, Hash>] 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)
cfa = hpxml_bldg.building_construction.conditioned_floor_area
nbeds = hpxml_bldg.building_construction.number_of_bedrooms
ncfl = hpxml_bldg.building_construction.number_of_conditioned_floors
Expand Down Expand Up @@ -102,12 +99,14 @@ def self.apply(runner, hpxml, hpxml_bldg, weather, schedules_file: nil, convert_
apply_batteries(hpxml_bldg)

# Do HVAC sizing after all other defaults have been applied
apply_hvac_sizing(runner, hpxml_bldg, weather, output_format, design_load_details_output_file_path)
all_zone_loads, all_space_loads = apply_hvac_sizing(runner, hpxml_bldg, weather)

# Default detailed performance has to be after sizing to have autosized capacity information
apply_detailed_performance_data_for_var_speed_systems(hpxml_bldg)

cleanup_zones_spaces(hpxml_bldg)

return all_zone_loads, all_space_loads
end

# Returns a list of four azimuths (facing each direction). Determined based
Expand Down Expand Up @@ -3857,12 +3856,11 @@ def self.apply_fuel_loads(hpxml_bldg, cfa, schedules_file)
# @param runner [OpenStudio::Measure::OSRunner] Object typically used to display warnings
# @param hpxml_bldg [HPXML::Building] HPXML Building object representing an individual dwelling unit
# @param weather [WeatherFile] Weather object containing EPW information
# @param output_format [String] Detailed output file format ('csv', 'json', or 'msgpack')
# @param design_load_details_output_file_path [String] Detailed HVAC sizing output file path
# @return [nil]
def self.apply_hvac_sizing(runner, hpxml_bldg, weather, output_format, design_load_details_output_file_path)
# @return [Array<Hash, Hash>] Maps of HPXML::Zones => DesignLoadValues object, HPXML::Spaces => DesignLoadValues object
def self.apply_hvac_sizing(runner, hpxml_bldg, weather)
hvac_systems = HVAC.get_hpxml_hvac_systems(hpxml_bldg)
HVACSizing.calculate(runner, weather, hpxml_bldg, hvac_systems, output_format: output_format, output_file_path: design_load_details_output_file_path)
_, all_zone_loads, all_space_loads = HVACSizing.calculate(runner, weather, hpxml_bldg, hvac_systems)
return all_zone_loads, all_space_loads
end

# Converts an HPXML orientation to an HPXML azimuth.
Expand Down
40 changes: 18 additions & 22 deletions HPXMLtoOpenStudio/resources/hvac_sizing.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,8 @@ module HVACSizing
# @param hpxml_bldg [HPXML::Building] HPXML Building object representing an individual dwelling unit
# @param hvac_systems [Array<Hash>] List of HPXML HVAC (heating and/or cooling) systems
# @param update_hpxml [Boolean] Whether to update the HPXML object so that in.xml reports capacities/airflows
# @param output_format [String] Detailed output file format ('csv', 'json', or 'msgpack')
# @param output_file_path [String] Detailed output file path
# @return [Hash] Map of HVAC systems => HVACSizingValues objects
def self.calculate(runner, weather, hpxml_bldg, hvac_systems, update_hpxml: true,
output_format: 'csv', output_file_path: nil)

# @return [Array<Hash, Hash, Hash>] Maps of HVAC systems => HVACSizingValues objects, HPXML::Zones => DesignLoadValues object, HPXML::Spaces => DesignLoadValues object
def self.calculate(runner, weather, hpxml_bldg, hvac_systems, update_hpxml: true)
check_for_errors(hpxml_bldg, hvac_systems)

mj = MJValues.new
Expand Down Expand Up @@ -111,12 +107,7 @@ def self.calculate(runner, weather, hpxml_bldg, hvac_systems, update_hpxml: true
end
end

# Write detailed outputs (useful for Form J1)
if not output_file_path.nil?
write_detailed_output(output_format, output_file_path, hpxml_bldg, all_zone_loads, all_space_loads)
end

return @all_hvac_sizings
return @all_hvac_sizings, all_zone_loads, all_space_loads
end

# Checks whether we will be performing sizing calculations on the given HPXML HVAC system.
Expand Down Expand Up @@ -3162,7 +3153,7 @@ def self.process_heat_pump_adjustment(mj, runner, hvac_sizings, weather, hvac_he
# Calculate the heating load at the switchover temperature to limit unutilized capacity
temp_heat_design_temp = hpxml_bldg.header.manualj_heating_design_temp
hpxml_bldg.header.manualj_heating_design_temp = min_compressor_temp
alternate_all_hvac_sizings = calculate(runner, weather, hpxml_bldg, [hvac_system], update_hpxml: false)
alternate_all_hvac_sizings = calculate(runner, weather, hpxml_bldg, [hvac_system], update_hpxml: false)[0]
heating_load = alternate_all_hvac_sizings[hvac_system].Heat_Load
heating_temp = min_compressor_temp
hpxml_bldg.header.manualj_heating_design_temp = temp_heat_design_temp
Expand Down Expand Up @@ -4794,8 +4785,8 @@ def self.get_surfaces_with_property(obj, additional_property_type)
end

# Note: Every report name must have the HPXML BuildingID in it in case we are running a whole MF building with multiple Building elements.
if hpxml_bldg.conditioned_zones[0].id.start_with?(Constants::AutomaticallyAdded)
zone_col_names = ["#{hpxml_bldg.building_id}"] # Leave out name of automatically added zone
if hpxml_bldg.conditioned_zones.empty?
zone_col_names = ["#{hpxml_bldg.building_id}"]
else
zone_col_names = all_zone_loads.keys.map { |zone| "#{hpxml_bldg.building_id}: #{zone.id}" }
end
Expand Down Expand Up @@ -4833,27 +4824,32 @@ def self.get_surfaces_with_property(obj, additional_property_type)

# Zone results
all_zone_loads.keys.each_with_index do |zone, i|
if hpxml_bldg.conditioned_zones.empty?
zone_or_bldg = hpxml_bldg
else
zone_or_bldg = zone
end
results_out << [line_break]
results_out << ["Report: #{zone_col_names[i]}: Loads", 'Area (ft^2)', 'Length (ft)', 'Wall Area Ratio', 'Heating (Btuh)', 'Cooling Sensible (Btuh)', 'Cooling Latent (Btuh)']
get_surfaces_with_property(zone, :detailed_output_values_windows).each do |window, fj1|
get_surfaces_with_property(zone_or_bldg, :detailed_output_values_windows).each do |window, fj1|
results_out << ["Windows: #{window.id}", fj1.Area, fj1.Length, nil, fj1.Heat_Load, fj1.Cool_Load_Sens]
end
get_surfaces_with_property(zone, :detailed_output_values_skylights).each do |skylight, fj1|
get_surfaces_with_property(zone_or_bldg, :detailed_output_values_skylights).each do |skylight, fj1|
results_out << ["Skylights: #{skylight.id}", fj1.Area, fj1.Length, nil, fj1.Heat_Load, fj1.Cool_Load_Sens]
end
get_surfaces_with_property(zone, :detailed_output_values_doors).each do |door, fj1|
get_surfaces_with_property(zone_or_bldg, :detailed_output_values_doors).each do |door, fj1|
results_out << ["Doors: #{door.id}", fj1.Area, fj1.Length, nil, fj1.Heat_Load, fj1.Cool_Load_Sens]
end
get_surfaces_with_property(zone, :detailed_output_values_above_grade_walls).each do |wall, fj1|
get_surfaces_with_property(zone_or_bldg, :detailed_output_values_above_grade_walls).each do |wall, fj1|
results_out << ["Above Grade Walls: #{wall.id}", fj1.Area, fj1.Length, nil, fj1.Heat_Load, fj1.Cool_Load_Sens]
end
get_surfaces_with_property(zone, :detailed_output_values_below_grade_walls).each do |wall, fj1|
get_surfaces_with_property(zone_or_bldg, :detailed_output_values_below_grade_walls).each do |wall, fj1|
results_out << ["Below Grade Walls: #{wall.id}", fj1.Area, fj1.Length, nil, fj1.Heat_Load, fj1.Cool_Load_Sens]
end
get_surfaces_with_property(zone, :detailed_output_values_ceilings).each do |ceiling, fj1|
get_surfaces_with_property(zone_or_bldg, :detailed_output_values_ceilings).each do |ceiling, fj1|
results_out << ["Ceilings: #{ceiling.id}", fj1.Area, fj1.Length, nil, fj1.Heat_Load, fj1.Cool_Load_Sens]
end
get_surfaces_with_property(zone, :detailed_output_values_floors).each do |floor, fj1|
get_surfaces_with_property(zone_or_bldg, :detailed_output_values_floors).each do |floor, fj1|
results_out << ["Floors: #{floor.id}", fj1.Area, fj1.Length, nil, fj1.Heat_Load, fj1.Cool_Load_Sens]
end
zone_loads = all_zone_loads[zone]
Expand Down
3 changes: 2 additions & 1 deletion HPXMLtoOpenStudio/resources/output.rb
Original file line number Diff line number Diff line change
Expand Up @@ -917,7 +917,7 @@ def self.apply_total_airflows_ems_program(model, hpxml_osm_map)
# @param model [OpenStudio::Model::Model] OpenStudio Model object
# @param debug [Boolean] If true, writes in.osm, generates additional log output, and creates all E+ output files
# @return [nil]
def self.apply_output_files(model, debug)
def self.apply_output_file_controls(model, debug)
oj = model.getOutputJSON
oj.setOptionType('TimeSeriesAndTabular')
oj.setOutputJSON(debug)
Expand Down Expand Up @@ -1220,6 +1220,7 @@ def self.write_results_out_to_file(results_out, output_format, output_file_path,
require 'json'
File.open(output_file_path, mode) { |json| json.write(JSON.pretty_generate(h)) }
elsif output_format == 'msgpack'
require 'msgpack'
File.open(output_file_path, "#{mode}b") { |json| h.to_msgpack(json) }
end
end
Expand Down

0 comments on commit 0d8be0f

Please sign in to comment.