Skip to content
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
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