diff --git a/BuildResidentialHPXML/measure.xml b/BuildResidentialHPXML/measure.xml index 265fdb1e9c..e412739e6e 100644 --- a/BuildResidentialHPXML/measure.xml +++ b/BuildResidentialHPXML/measure.xml @@ -3,8 +3,8 @@ 3.0 build_residential_hpxml a13a8983-2b01-4930-8af2-42030b6e4233 - 235c0e9c-de56-46b1-8712-83df120d39b8 - 20230501T221652Z + 7e14b99a-8028-4045-a389-bc04982c8bdf + 20230503T221424Z 2C38F48B BuildResidentialHPXML HPXML Builder @@ -6583,18 +6583,18 @@ - - geometry.rb - rb - resource - D9E75C2E - build_residential_hpxml_test.rb rb test 7F6A3D85 + + geometry.rb + rb + resource + 5184329E + OpenStudio diff --git a/BuildResidentialHPXML/resources/geometry.rb b/BuildResidentialHPXML/resources/geometry.rb index e9b35673ba..b10393ee8d 100644 --- a/BuildResidentialHPXML/resources/geometry.rb +++ b/BuildResidentialHPXML/resources/geometry.rb @@ -19,7 +19,7 @@ def self.get_abs_azimuth(relative_azimuth, building_orientation) def self.get_absolute_tilt(tilt_str, roof_pitch, epw_file) tilt_str = tilt_str.downcase if tilt_str.start_with? 'roofpitch' - roof_angle = Math.atan(roof_pitch / 12.0) * 180.0 / Math::PI + roof_angle = UnitConversions.convert(Math.atan(roof_pitch / 12.0), 'rad', 'deg') return Float(eval(tilt_str.gsub('roofpitch', roof_angle.to_s))) elsif tilt_str.start_with? 'latitude' return Float(eval(tilt_str.gsub('latitude', epw_file.latitude.to_s))) diff --git a/HPXMLtoOpenStudio/measure.rb b/HPXMLtoOpenStudio/measure.rb index 0309ca0f55..7b50d53615 100644 --- a/HPXMLtoOpenStudio/measure.rb +++ b/HPXMLtoOpenStudio/measure.rb @@ -399,72 +399,80 @@ def self.add_roofs(runner, model, spaces) next if surfaces.empty? # Apply construction - has_radiant_barrier = roof.radiant_barrier - if has_radiant_barrier - radiant_barrier_grade = roof.radiant_barrier_grade - end - # FUTURE: Create Constructions.get_air_film(surface) method; use in measure.rb and hpxml_translator_test.rb - inside_film = Material.AirFilmRoof(Geometry.get_roof_pitch([surfaces[0]])) - outside_film = Material.AirFilmOutside - mat_roofing = Material.RoofMaterial(roof.roof_type) - if @apply_ashrae140_assumptions - inside_film = Material.AirFilmRoofASHRAE140 - outside_film = Material.AirFilmOutsideASHRAE140 - end - mat_int_finish = Material.InteriorFinishMaterial(roof.interior_finish_type, roof.interior_finish_thickness) - if mat_int_finish.nil? - fallback_mat_int_finish = nil - else - fallback_mat_int_finish = Material.InteriorFinishMaterial(mat_int_finish.name, 0.1) # Try thin material - end - install_grade = 1 - assembly_r = roof.insulation_assembly_r_value + inside_film = roof.additional_properties.inside_film + outside_film = roof.additional_properties.outside_film - if not mat_int_finish.nil? - # Closed cavity - constr_sets = [ - WoodStudConstructionSet.new(Material.Stud2x(8.0), 0.07, 20.0, 0.75, mat_int_finish, mat_roofing), # 2x8, 24" o.c. + R20 - WoodStudConstructionSet.new(Material.Stud2x(8.0), 0.07, 10.0, 0.75, mat_int_finish, mat_roofing), # 2x8, 24" o.c. + R10 - WoodStudConstructionSet.new(Material.Stud2x(8.0), 0.07, 0.0, 0.75, mat_int_finish, mat_roofing), # 2x8, 24" o.c. - WoodStudConstructionSet.new(Material.Stud2x6, 0.07, 0.0, 0.75, mat_int_finish, mat_roofing), # 2x6, 24" o.c. - WoodStudConstructionSet.new(Material.Stud2x4, 0.07, 0.0, 0.5, mat_int_finish, mat_roofing), # 2x4, 16" o.c. - WoodStudConstructionSet.new(Material.Stud2x4, 0.01, 0.0, 0.0, fallback_mat_int_finish, mat_roofing), # Fallback - ] - match, constr_set, cavity_r = Constructions.pick_wood_stud_construction_set(assembly_r, constr_sets, inside_film, outside_film) + if roof.has_detailed_construction + # Layer-by-layer construction + + Constructions.apply_detailed_construction(model, surfaces, roof.detailed_construction, inside_film, + outside_film, roof.solar_absorptance, roof.emittance) - Constructions.apply_closed_cavity_roof(model, surfaces, "#{roof.id} construction", - cavity_r, install_grade, - constr_set.stud.thick_in, - true, constr_set.framing_factor, - constr_set.mat_int_finish, - constr_set.osb_thick_in, constr_set.rigid_r, - constr_set.mat_ext_finish, has_radiant_barrier, - inside_film, outside_film, radiant_barrier_grade, - roof.solar_absorptance, roof.emittance) else - # Open cavity - constr_sets = [ - GenericConstructionSet.new(10.0, 0.5, nil, mat_roofing), # w/R-10 rigid - GenericConstructionSet.new(0.0, 0.5, nil, mat_roofing), # Standard - GenericConstructionSet.new(0.0, 0.0, nil, mat_roofing), # Fallback - ] - match, constr_set, layer_r = Constructions.pick_generic_construction_set(assembly_r, constr_sets, inside_film, outside_film) + # Assembly R-value - cavity_r = 0 - cavity_ins_thick_in = 0 - framing_factor = 0 - framing_thick_in = 0 + has_radiant_barrier = roof.radiant_barrier + if has_radiant_barrier + radiant_barrier_grade = roof.radiant_barrier_grade + end + # FUTURE: Create Constructions.get_air_film(surface) method; use in measure.rb and hpxml_translator_test.rb + mat_roofing = Material.RoofMaterial(roof.roof_type) + mat_int_finish = Material.InteriorFinishMaterial(roof.interior_finish_type, roof.interior_finish_thickness) + if mat_int_finish.nil? + fallback_mat_int_finish = nil + else + fallback_mat_int_finish = Material.InteriorFinishMaterial(mat_int_finish.name, 0.1) # Try thin material + end - Constructions.apply_open_cavity_roof(model, surfaces, "#{roof.id} construction", - cavity_r, install_grade, cavity_ins_thick_in, - framing_factor, framing_thick_in, - constr_set.osb_thick_in, layer_r + constr_set.rigid_r, - constr_set.mat_ext_finish, has_radiant_barrier, - inside_film, outside_film, radiant_barrier_grade, - roof.solar_absorptance, roof.emittance) + install_grade = 1 + assembly_r = roof.insulation_assembly_r_value + + if not mat_int_finish.nil? + # Closed cavity + constr_sets = [ + WoodStudConstructionSet.new(Material.Stud2x(8.0), 0.07, 20.0, 0.75, mat_int_finish, mat_roofing), # 2x8, 24" o.c. + R20 + WoodStudConstructionSet.new(Material.Stud2x(8.0), 0.07, 10.0, 0.75, mat_int_finish, mat_roofing), # 2x8, 24" o.c. + R10 + WoodStudConstructionSet.new(Material.Stud2x(8.0), 0.07, 0.0, 0.75, mat_int_finish, mat_roofing), # 2x8, 24" o.c. + WoodStudConstructionSet.new(Material.Stud2x6, 0.07, 0.0, 0.75, mat_int_finish, mat_roofing), # 2x6, 24" o.c. + WoodStudConstructionSet.new(Material.Stud2x4, 0.07, 0.0, 0.5, mat_int_finish, mat_roofing), # 2x4, 16" o.c. + WoodStudConstructionSet.new(Material.Stud2x4, 0.01, 0.0, 0.0, fallback_mat_int_finish, mat_roofing), # Fallback + ] + match, constr_set, cavity_r = Constructions.pick_wood_stud_construction_set(assembly_r, constr_sets, inside_film, outside_film) + + Constructions.apply_closed_cavity_roof(model, surfaces, "#{roof.id} construction", + cavity_r, install_grade, + constr_set.stud.thick_in, + true, constr_set.framing_factor, + constr_set.mat_int_finish, + constr_set.osb_thick_in, constr_set.rigid_r, + constr_set.mat_ext_finish, has_radiant_barrier, + inside_film, outside_film, radiant_barrier_grade, + roof.solar_absorptance, roof.emittance) + else + # Open cavity + constr_sets = [ + GenericConstructionSet.new(10.0, 0.5, nil, mat_roofing), # w/R-10 rigid + GenericConstructionSet.new(0.0, 0.5, nil, mat_roofing), # Standard + GenericConstructionSet.new(0.0, 0.0, nil, mat_roofing), # Fallback + ] + match, constr_set, layer_r = Constructions.pick_generic_construction_set(assembly_r, constr_sets, inside_film, outside_film) + + cavity_r = 0 + cavity_ins_thick_in = 0 + framing_factor = 0 + framing_thick_in = 0 + + Constructions.apply_open_cavity_roof(model, surfaces, "#{roof.id} construction", + cavity_r, install_grade, cavity_ins_thick_in, + framing_factor, framing_thick_in, + constr_set.osb_thick_in, layer_r + constr_set.rigid_r, + constr_set.mat_ext_finish, has_radiant_barrier, + inside_film, outside_film, radiant_barrier_grade, + roof.solar_absorptance, roof.emittance) + end + Constructions.check_surface_assembly_rvalue(runner, surfaces, inside_film, outside_film, assembly_r, match) end - Constructions.check_surface_assembly_rvalue(runner, surfaces, inside_film, outside_film, assembly_r, match) end end @@ -513,26 +521,31 @@ def self.add_walls(runner, model, spaces) next if surfaces.empty? # Apply construction - # The code below constructs a reasonable wall construction based on the - # wall type while ensuring the correct assembly R-value. - inside_film = Material.AirFilmVertical - if wall.is_exterior - outside_film = Material.AirFilmOutside - mat_ext_finish = Material.ExteriorFinishMaterial(wall.siding) + inside_film = wall.additional_properties.inside_film + outside_film = wall.additional_properties.outside_film + + if wall.has_detailed_construction + # Layer-by-layer construction + + Constructions.apply_detailed_construction(model, surfaces, wall.detailed_construction, inside_film, + outside_film, wall.solar_absorptance, wall.emittance) else - outside_film = Material.AirFilmVertical - mat_ext_finish = nil - end - if @apply_ashrae140_assumptions - inside_film = Material.AirFilmVerticalASHRAE140 - outside_film = Material.AirFilmOutsideASHRAE140 - end - mat_int_finish = Material.InteriorFinishMaterial(wall.interior_finish_type, wall.interior_finish_thickness) + # Assembly R-value + # The code below constructs a reasonable wall construction based on the + # wall type while ensuring the correct assembly R-value. - Constructions.apply_wall_construction(runner, model, surfaces, wall.id, wall.wall_type, wall.insulation_assembly_r_value, - mat_int_finish, inside_film, outside_film, mat_ext_finish, wall.solar_absorptance, - wall.emittance) + if wall.is_exterior + mat_ext_finish = Material.ExteriorFinishMaterial(wall.siding) + else + mat_ext_finish = nil + end + mat_int_finish = Material.InteriorFinishMaterial(wall.interior_finish_type, wall.interior_finish_thickness) + + Constructions.apply_wall_construction(runner, model, surfaces, wall.id, wall.wall_type, wall.insulation_assembly_r_value, + mat_int_finish, inside_film, outside_film, mat_ext_finish, wall.solar_absorptance, + wall.emittance) + end end end @@ -578,33 +591,42 @@ def self.add_rim_joists(runner, model, spaces) # Apply construction - inside_film = Material.AirFilmVertical - if rim_joist.is_exterior - outside_film = Material.AirFilmOutside - mat_ext_finish = Material.ExteriorFinishMaterial(rim_joist.siding) + inside_film = rim_joist.additional_properties.inside_film + outside_film = rim_joist.additional_properties.outside_film + + if rim_joist.has_detailed_construction + # Layer-by-layer construction + + Constructions.apply_detailed_construction(model, surfaces, rim_joist.detailed_construction, inside_film, + outside_film, rim_joist.solar_absorptance, rim_joist.emittance) else - outside_film = Material.AirFilmVertical - mat_ext_finish = nil - end + # Assembly R-value + + if rim_joist.is_exterior + mat_ext_finish = Material.ExteriorFinishMaterial(rim_joist.siding) + else + mat_ext_finish = nil + end - assembly_r = rim_joist.insulation_assembly_r_value - - constr_sets = [ - WoodStudConstructionSet.new(Material.Stud2x(2.0), 0.17, 20.0, 2.0, nil, mat_ext_finish), # 2x4 + R20 - WoodStudConstructionSet.new(Material.Stud2x(2.0), 0.17, 10.0, 2.0, nil, mat_ext_finish), # 2x4 + R10 - WoodStudConstructionSet.new(Material.Stud2x(2.0), 0.17, 0.0, 2.0, nil, mat_ext_finish), # 2x4 - WoodStudConstructionSet.new(Material.Stud2x(2.0), 0.01, 0.0, 0.0, nil, mat_ext_finish), # Fallback - ] - match, constr_set, cavity_r = Constructions.pick_wood_stud_construction_set(assembly_r, constr_sets, inside_film, outside_film) - install_grade = 1 - - Constructions.apply_rim_joist(model, surfaces, "#{rim_joist.id} construction", - cavity_r, install_grade, constr_set.framing_factor, - constr_set.mat_int_finish, constr_set.osb_thick_in, - constr_set.rigid_r, constr_set.mat_ext_finish, - inside_film, outside_film, rim_joist.solar_absorptance, - rim_joist.emittance) - Constructions.check_surface_assembly_rvalue(runner, surfaces, inside_film, outside_film, assembly_r, match) + assembly_r = rim_joist.insulation_assembly_r_value + + constr_sets = [ + WoodStudConstructionSet.new(Material.Stud2x(2.0), 0.17, 20.0, 2.0, nil, mat_ext_finish), # 2x4 + R20 + WoodStudConstructionSet.new(Material.Stud2x(2.0), 0.17, 10.0, 2.0, nil, mat_ext_finish), # 2x4 + R10 + WoodStudConstructionSet.new(Material.Stud2x(2.0), 0.17, 0.0, 2.0, nil, mat_ext_finish), # 2x4 + WoodStudConstructionSet.new(Material.Stud2x(2.0), 0.01, 0.0, 0.0, nil, mat_ext_finish), # Fallback + ] + match, constr_set, cavity_r = Constructions.pick_wood_stud_construction_set(assembly_r, constr_sets, inside_film, outside_film) + install_grade = 1 + + Constructions.apply_rim_joist(model, surfaces, "#{rim_joist.id} construction", + cavity_r, install_grade, constr_set.framing_factor, + constr_set.mat_int_finish, constr_set.osb_thick_in, + constr_set.rigid_r, constr_set.mat_ext_finish, + inside_film, outside_film, rim_joist.solar_absorptance, + rim_joist.emittance) + Constructions.check_surface_assembly_rvalue(runner, surfaces, inside_film, outside_film, assembly_r, match) + end end end diff --git a/HPXMLtoOpenStudio/measure.xml b/HPXMLtoOpenStudio/measure.xml index c7da85e0c0..09e9e57754 100644 --- a/HPXMLtoOpenStudio/measure.xml +++ b/HPXMLtoOpenStudio/measure.xml @@ -3,8 +3,8 @@ 3.0 hpxm_lto_openstudio b1543b30-9465-45ff-ba04-1d1f85e763bc - 4816c10b-5a41-436e-a355-cfd8edd5b044 - 20230502T201104Z + 9ceb2d4d-cab8-437a-88c0-99e65f1bbff0 + 20230503T221427Z D8922A73 HPXMLtoOpenStudio HPXML to OpenStudio Translator @@ -259,12 +259,6 @@ test 03FE784E - - materials.rb - rb - resource - 24DCB986 - test_simcontrols.rb rb @@ -391,12 +385,6 @@ resource 9E02B1C4 - - constructions.rb - rb - resource - F6E89CBA - geometry.rb rb @@ -485,7 +473,25 @@ hpxml_schema/HPXML.xsd xsd resource - 75097E83 + CA905BB5 + + + materials.rb + rb + resource + 49C655B4 + + + constructions.rb + rb + resource + 0C36E521 + + + xmlvalidator.rb + rb + resource + CFCD83CD test_schedules.rb @@ -515,13 +521,7 @@ hvac_sizing.rb rb resource - 1AD76DC6 - - - xmlvalidator.rb - rb - resource - CFCD83CD + 218EA20F test_miscloads.rb @@ -541,29 +541,23 @@ resource EA5F3E1B + + hpxml_defaults.rb + rb + resource + 811F4F08 + hpxml.rb rb resource - 6E7B4D41 + 3104449F hpxml_schematron/EPvalidator.xml xml resource - 63A11C83 - - - test_validation.rb - rb - test - F0B3DCEF - - - test_defaults.rb - rb - test - F1FEF43F + 69B938B4 @@ -574,13 +568,19 @@ measure.rb rb script - 1E7FB5AF + BD97DFD4 - hpxml_defaults.rb + test_validation.rb rb - resource - A466A879 + test + 8D65BD7F + + + test_defaults.rb + rb + test + F1FEF43F diff --git a/HPXMLtoOpenStudio/resources/constructions.rb b/HPXMLtoOpenStudio/resources/constructions.rb index f7b2259c0a..033a424e28 100644 --- a/HPXMLtoOpenStudio/resources/constructions.rb +++ b/HPXMLtoOpenStudio/resources/constructions.rb @@ -3,6 +3,107 @@ class Constructions # Container class for walls, floors/ceilings, roofs, etc. + # Generic layer-by-layer construction from an HPXML DetailedConstruction element + def self.apply_detailed_construction(model, surfaces, detailed_construction, + inside_film, outside_film, solar_absorptance, emittance) + + return if surfaces.empty? + + # Define construction + constr = create_from_detailed_construction(detailed_construction, inside_film, outside_film) + + constr.set_exterior_material_properties(solar_absorptance, emittance) + constr.set_interior_material_properties() + + # Create and assign construction to surfaces + constr.create_and_assign_constructions(surfaces, model) + end + + def self.create_from_detailed_construction(detailed_construction, inside_film, outside_film) + # FIXME: Need to re-normalize area_fractions to sum to 1 + + # Define materials + mats = {} + detailed_construction.construction_layers.each_with_index do |layer, i| + layer.layer_materials.each_with_index do |material, j| + if material.material_type.nil? + material_name = "#{detailed_construction.id} - Layer #{i + 1}, Material #{j + 1}" + else + material_name = "#{detailed_construction.id} - #{material.material_type}" + end + if not material.conductivity.nil? + k_in = UnitConversions.convert(material.conductivity, 'ft', 'in') + elsif not layer.layer_thickness.nil? + k_in = layer.layer_thickness / material.r_value + end + if not k_in.nil? + mat = Material.new(name: material_name, thick_in: layer.layer_thickness, k_in: k_in, rho: material.density, cp: material.specific_heat) + else + mat = NoMassMaterial.new(name: material_name, rvalue: material.r_value) + end + mats[[i, j]] = mat + end + end + + # Determine unique paths + cumulative_area_fracs = [] + detailed_construction.construction_layers.each do |layer| + layer_area_fracs = [] + frac = 0.0 + layer.layer_materials.each do |material| + frac += material.area_fraction + layer_area_fracs << frac + end + cumulative_area_fracs << layer_area_fracs + end + cumulative_area_fracs = cumulative_area_fracs.flatten.uniq.sort + path_fracs = [] + cumulative_area_fracs.each_with_index do |area_frac, i| + if i == 0 + path_fracs << area_frac + else + path_fracs << area_frac - cumulative_area_fracs[0..i - 1].sum + end + end + + # Define construction + constr = Construction.new(detailed_construction.id, path_fracs) + constr.add_layer(outside_film) + detailed_construction.construction_layers.each_with_index do |layer, i| + if layer.layer_type.nil? + material_names = layer.layer_materials.map { |m| m.material_type } + if material_names.any? { |m| m.nil? } + layer_name = "#{detailed_construction.id} - Layer #{i + 1}" + else + layer_name = "#{detailed_construction.id} - #{material_names.join('|')}" + end + else + layer_name = "#{detailed_construction.id} - #{layer.layer_type}" + end + if layer.layer_materials.size == 1 + # Single material for entire layer + constr.add_layer(mats[[i, 0]], layer_name) + else + # Create array of materials that align with path_fracs + layer_mats = [] + path_fracs.each do |area_frac| + frac = 0.0 + mat = nil + layer.layer_materials.each_with_index do |material, j| + frac += material.area_fraction + mat = mats[[i, j]] + break if frac >= area_frac + end + layer_mats << mat + end + constr.add_layer(layer_mats, layer_name) + end + end + constr.add_layer(inside_film) + + return constr + end + def self.apply_wood_stud_wall(model, surfaces, constr_name, cavity_r, install_grade, cavity_depth_in, cavity_filled, framing_factor, mat_int_finish, osb_thick_in, @@ -2465,8 +2566,8 @@ def validate # Check for valid object types @layers_materials.each do |layer_materials| layer_materials.each do |mat| - if (not mat.is_a? Material) - fail 'Invalid construction: Materials must be instances of Material classes.' + if (not mat.is_a? Material) && (not mat.is_a? NoMassMaterial) + fail 'Invalid construction: Materials must be instances of Material or NoMassMaterial classes.' end end end @@ -2540,27 +2641,44 @@ def self.create_os_material(model, material) mat.setSolarHeatGainCoefficient(material.shgc) else # Material already exists? - model.getStandardOpaqueMaterials.each do |mat| - next if !mat.name.to_s.start_with?(material.name) - next if mat.roughness.downcase.to_s != 'rough' - next if (mat.thickness - UnitConversions.convert(material.thick_in, 'in', 'm')).abs > tolerance - next if (mat.conductivity - UnitConversions.convert(material.k, 'Btu/(hr*ft*R)', 'W/(m*K)')).abs > tolerance - next if (mat.density - UnitConversions.convert(material.rho, 'lbm/ft^3', 'kg/m^3')).abs > tolerance - next if (mat.specificHeat - UnitConversions.convert(material.cp, 'Btu/(lbm*R)', 'J/(kg*K)')).abs > tolerance - next if (mat.thermalAbsorptance - material.tAbs.to_f).abs > tolerance - next if (mat.solarAbsorptance - material.sAbs.to_f).abs > tolerance - - return mat + if material.is_a? Material + model.getStandardOpaqueMaterials.each do |mat| + next if !mat.name.to_s.start_with?(material.name) + next if mat.roughness.downcase.to_s != 'rough' + next if (mat.thickness - UnitConversions.convert(material.thick_in, 'in', 'm')).abs > tolerance + next if (mat.conductivity - UnitConversions.convert(material.k, 'Btu/(hr*ft*R)', 'W/(m*K)')).abs > tolerance + next if (mat.density - UnitConversions.convert(material.rho, 'lbm/ft^3', 'kg/m^3')).abs > tolerance + next if (mat.specificHeat - UnitConversions.convert(material.cp, 'Btu/(lbm*R)', 'J/(kg*K)')).abs > tolerance + next if (mat.thermalAbsorptance - material.tAbs.to_f).abs > tolerance + next if (mat.solarAbsorptance - material.sAbs.to_f).abs > tolerance + + return mat + end + elsif material.is_a? NoMassMaterial + model.getMasslessOpaqueMaterials.each do |mat| + next if !mat.name.to_s.start_with?(material.name) + next if mat.roughness.downcase.to_s != 'rough' + next if (mat.thermalResistance - UnitConversions.convert(material.rvalue, 'hr*ft^2*F/Btu', 'm^2*K/W')).abs > tolerance + next if (mat.thermalAbsorptance - material.tAbs.to_f).abs > tolerance + next if (mat.solarAbsorptance - material.sAbs.to_f).abs > tolerance + + return mat + end end # New material - mat = OpenStudio::Model::StandardOpaqueMaterial.new(model) + if material.is_a? Material + mat = OpenStudio::Model::StandardOpaqueMaterial.new(model) + mat.setThickness(UnitConversions.convert(material.thick_in, 'in', 'm')) + mat.setConductivity(UnitConversions.convert(material.k, 'Btu/(hr*ft*R)', 'W/(m*K)')) + mat.setDensity(UnitConversions.convert(material.rho, 'lbm/ft^3', 'kg/m^3')) + mat.setSpecificHeat(UnitConversions.convert(material.cp, 'Btu/(lbm*R)', 'J/(kg*K)')) + elsif material.is_a? NoMassMaterial + mat = OpenStudio::Model::MasslessOpaqueMaterial.new(model) + mat.setThermalResistance(UnitConversions.convert(material.rvalue, 'hr*ft^2*F/Btu', 'm^2*K/W')) + end mat.setName(name) mat.setRoughness('Rough') - mat.setThickness(UnitConversions.convert(material.thick_in, 'in', 'm')) - mat.setConductivity(UnitConversions.convert(material.k, 'Btu/(hr*ft*R)', 'W/(m*K)')) - mat.setDensity(UnitConversions.convert(material.rho, 'lbm/ft^3', 'kg/m^3')) - mat.setSpecificHeat(UnitConversions.convert(material.cp, 'Btu/(lbm*R)', 'J/(kg*K)')) if not material.tAbs.nil? mat.setThermalAbsorptance(material.tAbs) end diff --git a/HPXMLtoOpenStudio/resources/hpxml.rb b/HPXMLtoOpenStudio/resources/hpxml.rb index 42a88ce38b..7196cd276d 100644 --- a/HPXMLtoOpenStudio/resources/hpxml.rb +++ b/HPXMLtoOpenStudio/resources/hpxml.rb @@ -2192,6 +2192,10 @@ def from_oga(hpxml) end class Roof < BaseElement + def initialize(hpxml_object, *args) + @detailed_construction = DetailedConstruction.new(hpxml_object) + super(hpxml_object, *args) + end ATTRS = [:id, :interior_adjacent_to, :area, :azimuth, :orientation, :roof_type, :roof_color, :solar_absorptance, :emittance, :pitch, :radiant_barrier, :insulation_id, :insulation_assembly_r_value, :insulation_cavity_r_value, @@ -2199,6 +2203,7 @@ class Roof < BaseElement :interior_finish_type, :interior_finish_thickness, :framing_factor, :framing_size, :framing_spacing] attr_accessor(*ATTRS) + attr_reader(:detailed_construction) def skylights return @hpxml_object.skylights.select { |skylight| skylight.roof_idref == @id } @@ -2241,6 +2246,10 @@ def is_conditioned return HPXML::is_conditioned(self) end + def has_detailed_construction + return !@detailed_construction.construction_layers.empty? + end + def delete @hpxml_object.roofs.delete(self) skylights.reverse_each do |skylight| @@ -2254,6 +2263,7 @@ def delete def check_for_errors errors = [] begin; net_area; rescue StandardError => e; errors << e.message; end + errors += @detailed_construction.check_for_errors return errors end @@ -2286,25 +2296,28 @@ def to_oga(doc) XMLHelper.add_element(roof, 'Pitch', @pitch, :float) unless @pitch.nil? XMLHelper.add_element(roof, 'RadiantBarrier', @radiant_barrier, :boolean, @radiant_barrier_isdefaulted) unless @radiant_barrier.nil? XMLHelper.add_element(roof, 'RadiantBarrierGrade', @radiant_barrier_grade, :integer, @radiant_barrier_grade_isdefaulted) unless @radiant_barrier_grade.nil? - insulation = XMLHelper.add_element(roof, 'Insulation') - sys_id = XMLHelper.add_element(insulation, 'SystemIdentifier') - if not @insulation_id.nil? - XMLHelper.add_attribute(sys_id, 'id', @insulation_id) - else - XMLHelper.add_attribute(sys_id, 'id', @id + 'Insulation') - end - XMLHelper.add_element(insulation, 'InsulationGrade', @insulation_grade, :integer) unless @insulation_grade.nil? - XMLHelper.add_element(insulation, 'AssemblyEffectiveRValue', @insulation_assembly_r_value, :float) unless @insulation_assembly_r_value.nil? - if not @insulation_cavity_r_value.nil? - layer = XMLHelper.add_element(insulation, 'Layer') - XMLHelper.add_element(layer, 'InstallationType', 'cavity', :string) - XMLHelper.add_element(layer, 'NominalRValue', @insulation_cavity_r_value, :float) - end - if not @insulation_continuous_r_value.nil? - layer = XMLHelper.add_element(insulation, 'Layer') - XMLHelper.add_element(layer, 'InstallationType', 'continuous', :string) - XMLHelper.add_element(layer, 'NominalRValue', @insulation_continuous_r_value, :float) + if (not @insulation_assembly_r_value.nil?) || (not @insulation_cavity_r_value.nil?) || (not @insulation_continuous_r_value.nil?) + insulation = XMLHelper.add_element(roof, 'Insulation') + sys_id = XMLHelper.add_element(insulation, 'SystemIdentifier') + if not @insulation_id.nil? + XMLHelper.add_attribute(sys_id, 'id', @insulation_id) + else + XMLHelper.add_attribute(sys_id, 'id', @id + 'Insulation') + end + XMLHelper.add_element(insulation, 'InsulationGrade', @insulation_grade, :integer) unless @insulation_grade.nil? + XMLHelper.add_element(insulation, 'AssemblyEffectiveRValue', @insulation_assembly_r_value, :float, @insulation_assembly_r_value_isdefaulted) unless @insulation_assembly_r_value.nil? + if not @insulation_cavity_r_value.nil? + layer = XMLHelper.add_element(insulation, 'Layer') + XMLHelper.add_element(layer, 'InstallationType', 'cavity', :string) + XMLHelper.add_element(layer, 'NominalRValue', @insulation_cavity_r_value, :float) + end + if not @insulation_continuous_r_value.nil? + layer = XMLHelper.add_element(insulation, 'Layer') + XMLHelper.add_element(layer, 'InstallationType', 'continuous', :string) + XMLHelper.add_element(layer, 'NominalRValue', @insulation_continuous_r_value, :float) + end end + @detailed_construction.to_oga(roof) end def from_oga(roof) @@ -2338,6 +2351,7 @@ def from_oga(roof) @insulation_cavity_r_value = XMLHelper.get_value(insulation, "Layer[InstallationType='cavity']/NominalRValue", :float) @insulation_continuous_r_value = XMLHelper.get_value(insulation, "Layer[InstallationType='continuous']/NominalRValue", :float) end + @detailed_construction.from_oga(XMLHelper.get_element(roof, 'DetailedConstruction')) end end @@ -2356,10 +2370,15 @@ def from_oga(hpxml) end class RimJoist < BaseElement + def initialize(hpxml_object, *args) + @detailed_construction = DetailedConstruction.new(hpxml_object) + super(hpxml_object, *args) + end ATTRS = [:id, :exterior_adjacent_to, :interior_adjacent_to, :area, :orientation, :azimuth, :siding, :color, :solar_absorptance, :emittance, :insulation_id, :insulation_assembly_r_value, :insulation_cavity_r_value, :insulation_continuous_r_value, :framing_size] attr_accessor(*ATTRS) + attr_reader(:detailed_construction) def is_exterior if @exterior_adjacent_to == LocationOutside @@ -2389,6 +2408,10 @@ def is_conditioned return HPXML::is_conditioned(self) end + def has_detailed_construction + return !@detailed_construction.construction_layers.empty? + end + def delete @hpxml_object.rim_joists.delete(self) @hpxml_object.foundations.each do |foundation| @@ -2398,6 +2421,7 @@ def delete def check_for_errors errors = [] + errors += @detailed_construction.check_for_errors return errors end @@ -2417,28 +2441,31 @@ def to_oga(doc) XMLHelper.add_element(rim_joist, 'Color', @color, :string, @color_isdefaulted) unless @color.nil? XMLHelper.add_element(rim_joist, 'SolarAbsorptance', @solar_absorptance, :float, @solar_absorptance_isdefaulted) unless @solar_absorptance.nil? XMLHelper.add_element(rim_joist, 'Emittance', @emittance, :float, @emittance_isdefaulted) unless @emittance.nil? - insulation = XMLHelper.add_element(rim_joist, 'Insulation') - sys_id = XMLHelper.add_element(insulation, 'SystemIdentifier') - if not @insulation_id.nil? - XMLHelper.add_attribute(sys_id, 'id', @insulation_id) - else - XMLHelper.add_attribute(sys_id, 'id', @id + 'Insulation') - end - XMLHelper.add_element(insulation, 'AssemblyEffectiveRValue', @insulation_assembly_r_value, :float) unless @insulation_assembly_r_value.nil? - if not @insulation_cavity_r_value.nil? - layer = XMLHelper.add_element(insulation, 'Layer') - XMLHelper.add_element(layer, 'InstallationType', 'cavity', :string) - XMLHelper.add_element(layer, 'NominalRValue', @insulation_cavity_r_value, :float) - end - if not @insulation_continuous_r_value.nil? - layer = XMLHelper.add_element(insulation, 'Layer') - XMLHelper.add_element(layer, 'InstallationType', 'continuous', :string) - XMLHelper.add_element(layer, 'NominalRValue', @insulation_continuous_r_value, :float) + if (not @insulation_assembly_r_value.nil?) || (not @insulation_cavity_r_value.nil?) || (not @insulation_continuous_r_value.nil?) + insulation = XMLHelper.add_element(rim_joist, 'Insulation') + sys_id = XMLHelper.add_element(insulation, 'SystemIdentifier') + if not @insulation_id.nil? + XMLHelper.add_attribute(sys_id, 'id', @insulation_id) + else + XMLHelper.add_attribute(sys_id, 'id', @id + 'Insulation') + end + XMLHelper.add_element(insulation, 'AssemblyEffectiveRValue', @insulation_assembly_r_value, :float, @insulation_assembly_r_value_isdefaulted) unless @insulation_assembly_r_value.nil? + if not @insulation_cavity_r_value.nil? + layer = XMLHelper.add_element(insulation, 'Layer') + XMLHelper.add_element(layer, 'InstallationType', 'cavity', :string) + XMLHelper.add_element(layer, 'NominalRValue', @insulation_cavity_r_value, :float) + end + if not @insulation_continuous_r_value.nil? + layer = XMLHelper.add_element(insulation, 'Layer') + XMLHelper.add_element(layer, 'InstallationType', 'continuous', :string) + XMLHelper.add_element(layer, 'NominalRValue', @insulation_continuous_r_value, :float) + end end if not @framing_size.nil? floor_joists = XMLHelper.add_element(rim_joist, 'FloorJoists') XMLHelper.add_element(floor_joists, 'Size', @framing_size, :string) unless @framing_size.nil? end + @detailed_construction.to_oga(rim_joist) end def from_oga(rim_joist) @@ -2462,6 +2489,7 @@ def from_oga(rim_joist) @insulation_continuous_r_value = XMLHelper.get_value(insulation, "Layer[InstallationType='continuous']/NominalRValue", :float) end @framing_size = XMLHelper.get_value(rim_joist, 'FloorJoists/Size', :string) + @detailed_construction.from_oga(XMLHelper.get_element(rim_joist, 'DetailedConstruction')) end end @@ -2480,12 +2508,17 @@ def from_oga(hpxml) end class Wall < BaseElement + def initialize(hpxml_object, *args) + @detailed_construction = DetailedConstruction.new(hpxml_object) + super(hpxml_object, *args) + end ATTRS = [:id, :exterior_adjacent_to, :interior_adjacent_to, :wall_type, :optimum_value_engineering, :area, :orientation, :azimuth, :siding, :color, :solar_absorptance, :emittance, :insulation_id, :insulation_assembly_r_value, :insulation_cavity_r_value, :insulation_continuous_r_value, :interior_finish_type, :interior_finish_thickness, :attic_wall_type, :framing_factor, :framing_size, :framing_spacing, :insulation_grade] attr_accessor(*ATTRS) + attr_reader(:detailed_construction) def windows return @hpxml_object.windows.select { |window| window.wall_idref == @id } @@ -2536,6 +2569,10 @@ def is_conditioned return HPXML::is_conditioned(self) end + def has_detailed_construction + return !@detailed_construction.construction_layers.empty? + end + def delete @hpxml_object.walls.delete(self) windows.reverse_each do |window| @@ -2555,6 +2592,7 @@ def delete def check_for_errors errors = [] begin; net_area; rescue StandardError => e; errors << e.message; end + errors += @detailed_construction.check_for_errors return errors end @@ -2593,25 +2631,28 @@ def to_oga(doc) XMLHelper.add_element(interior_finish, 'Type', @interior_finish_type, :string, @interior_finish_type_isdefaulted) unless @interior_finish_type.nil? XMLHelper.add_element(interior_finish, 'Thickness', @interior_finish_thickness, :float, @interior_finish_thickness_isdefaulted) unless @interior_finish_thickness.nil? end - insulation = XMLHelper.add_element(wall, 'Insulation') - sys_id = XMLHelper.add_element(insulation, 'SystemIdentifier') - if not @insulation_id.nil? - XMLHelper.add_attribute(sys_id, 'id', @insulation_id) - else - XMLHelper.add_attribute(sys_id, 'id', @id + 'Insulation') - end - XMLHelper.add_element(insulation, 'InsulationGrade', @insulation_grade, :integer) unless @insulation_grade.nil? - XMLHelper.add_element(insulation, 'AssemblyEffectiveRValue', @insulation_assembly_r_value, :float) unless @insulation_assembly_r_value.nil? - if not @insulation_cavity_r_value.nil? - layer = XMLHelper.add_element(insulation, 'Layer') - XMLHelper.add_element(layer, 'InstallationType', 'cavity', :string) - XMLHelper.add_element(layer, 'NominalRValue', @insulation_cavity_r_value, :float) - end - if not @insulation_continuous_r_value.nil? - layer = XMLHelper.add_element(insulation, 'Layer') - XMLHelper.add_element(layer, 'InstallationType', 'continuous', :string) - XMLHelper.add_element(layer, 'NominalRValue', @insulation_continuous_r_value, :float) + if (not @insulation_assembly_r_value.nil?) || (not @insulation_cavity_r_value.nil?) || (not @insulation_continuous_r_value.nil?) + insulation = XMLHelper.add_element(wall, 'Insulation') + sys_id = XMLHelper.add_element(insulation, 'SystemIdentifier') + if not @insulation_id.nil? + XMLHelper.add_attribute(sys_id, 'id', @insulation_id) + else + XMLHelper.add_attribute(sys_id, 'id', @id + 'Insulation') + end + XMLHelper.add_element(insulation, 'InsulationGrade', @insulation_grade, :integer) unless @insulation_grade.nil? + XMLHelper.add_element(insulation, 'AssemblyEffectiveRValue', @insulation_assembly_r_value, :float, @insulation_assembly_r_value_isdefaulted) unless @insulation_assembly_r_value.nil? + if not @insulation_cavity_r_value.nil? + layer = XMLHelper.add_element(insulation, 'Layer') + XMLHelper.add_element(layer, 'InstallationType', 'cavity', :string) + XMLHelper.add_element(layer, 'NominalRValue', @insulation_cavity_r_value, :float) + end + if not @insulation_continuous_r_value.nil? + layer = XMLHelper.add_element(insulation, 'Layer') + XMLHelper.add_element(layer, 'InstallationType', 'continuous', :string) + XMLHelper.add_element(layer, 'NominalRValue', @insulation_continuous_r_value, :float) + end end + @detailed_construction.to_oga(wall) end def from_oga(wall) @@ -2648,6 +2689,7 @@ def from_oga(wall) @insulation_cavity_r_value = XMLHelper.get_value(insulation, "Layer[InstallationType='cavity']/NominalRValue", :float) @insulation_continuous_r_value = XMLHelper.get_value(insulation, "Layer[InstallationType='continuous']/NominalRValue", :float) end + @detailed_construction.from_oga(XMLHelper.get_element(wall, 'DetailedConstruction')) end end @@ -2766,27 +2808,29 @@ def to_oga(doc) XMLHelper.add_element(interior_finish, 'Type', @interior_finish_type, :string, @interior_finish_type_isdefaulted) unless @interior_finish_type.nil? XMLHelper.add_element(interior_finish, 'Thickness', @interior_finish_thickness, :float, @interior_finish_thickness_isdefaulted) unless @interior_finish_thickness.nil? end - insulation = XMLHelper.add_element(foundation_wall, 'Insulation') - sys_id = XMLHelper.add_element(insulation, 'SystemIdentifier') - if not @insulation_id.nil? - XMLHelper.add_attribute(sys_id, 'id', @insulation_id) - else - XMLHelper.add_attribute(sys_id, 'id', @id + 'Insulation') - end - XMLHelper.add_element(insulation, 'AssemblyEffectiveRValue', @insulation_assembly_r_value, :float) unless @insulation_assembly_r_value.nil? - if not @insulation_exterior_r_value.nil? - layer = XMLHelper.add_element(insulation, 'Layer') - XMLHelper.add_element(layer, 'InstallationType', 'continuous - exterior', :string) - XMLHelper.add_element(layer, 'NominalRValue', @insulation_exterior_r_value, :float) - XMLHelper.add_element(layer, 'DistanceToTopOfInsulation', @insulation_exterior_distance_to_top, :float, @insulation_exterior_distance_to_top_isdefaulted) unless @insulation_exterior_distance_to_top.nil? - XMLHelper.add_element(layer, 'DistanceToBottomOfInsulation', @insulation_exterior_distance_to_bottom, :float, @insulation_exterior_distance_to_bottom_isdefaulted) unless @insulation_exterior_distance_to_bottom.nil? - end - if not @insulation_interior_r_value.nil? - layer = XMLHelper.add_element(insulation, 'Layer') - XMLHelper.add_element(layer, 'InstallationType', 'continuous - interior', :string) - XMLHelper.add_element(layer, 'NominalRValue', @insulation_interior_r_value, :float) - XMLHelper.add_element(layer, 'DistanceToTopOfInsulation', @insulation_interior_distance_to_top, :float, @insulation_interior_distance_to_top_isdefaulted) unless @insulation_interior_distance_to_top.nil? - XMLHelper.add_element(layer, 'DistanceToBottomOfInsulation', @insulation_interior_distance_to_bottom, :float, @insulation_interior_distance_to_bottom_isdefaulted) unless @insulation_interior_distance_to_bottom.nil? + if (not @insulation_assembly_r_value.nil?) || (not @insulation_exterior_r_value.nil?) || (not @insulation_interior_r_value.nil?) + insulation = XMLHelper.add_element(foundation_wall, 'Insulation') + sys_id = XMLHelper.add_element(insulation, 'SystemIdentifier') + if not @insulation_id.nil? + XMLHelper.add_attribute(sys_id, 'id', @insulation_id) + else + XMLHelper.add_attribute(sys_id, 'id', @id + 'Insulation') + end + XMLHelper.add_element(insulation, 'AssemblyEffectiveRValue', @insulation_assembly_r_value, :float, @insulation_assembly_r_value_isdefaulted) unless @insulation_assembly_r_value.nil? + if not @insulation_exterior_r_value.nil? + layer = XMLHelper.add_element(insulation, 'Layer') + XMLHelper.add_element(layer, 'InstallationType', 'continuous - exterior', :string) + XMLHelper.add_element(layer, 'NominalRValue', @insulation_exterior_r_value, :float) + XMLHelper.add_element(layer, 'DistanceToTopOfInsulation', @insulation_exterior_distance_to_top, :float, @insulation_exterior_distance_to_top_isdefaulted) unless @insulation_exterior_distance_to_top.nil? + XMLHelper.add_element(layer, 'DistanceToBottomOfInsulation', @insulation_exterior_distance_to_bottom, :float, @insulation_exterior_distance_to_bottom_isdefaulted) unless @insulation_exterior_distance_to_bottom.nil? + end + if not @insulation_interior_r_value.nil? + layer = XMLHelper.add_element(insulation, 'Layer') + XMLHelper.add_element(layer, 'InstallationType', 'continuous - interior', :string) + XMLHelper.add_element(layer, 'NominalRValue', @insulation_interior_r_value, :float) + XMLHelper.add_element(layer, 'DistanceToTopOfInsulation', @insulation_interior_distance_to_top, :float, @insulation_interior_distance_to_top_isdefaulted) unless @insulation_interior_distance_to_top.nil? + XMLHelper.add_element(layer, 'DistanceToBottomOfInsulation', @insulation_interior_distance_to_bottom, :float, @insulation_interior_distance_to_bottom_isdefaulted) unless @insulation_interior_distance_to_bottom.nil? + end end end @@ -2839,11 +2883,16 @@ def from_oga(hpxml) end class Floor < BaseElement + def initialize(hpxml_object, *args) + @detailed_construction = DetailedConstruction.new(hpxml_object) + super(hpxml_object, *args) + end ATTRS = [:id, :exterior_adjacent_to, :interior_adjacent_to, :floor_type, :area, :insulation_id, :insulation_assembly_r_value, :insulation_cavity_r_value, :insulation_continuous_r_value, :floor_or_ceiling, :interior_finish_type, :interior_finish_thickness, :insulation_grade, :framing_factor, :framing_size, :framing_spacing] attr_accessor(*ATTRS) + attr_reader(:detailed_construction) def is_ceiling # From the perspective of the living space @@ -2893,6 +2942,10 @@ def is_conditioned return HPXML::is_conditioned(self) end + def has_detailed_construction + return !@detailed_construction.construction_layers.empty? + end + def delete @hpxml_object.floors.delete(self) @hpxml_object.attics.each do |attic| @@ -2908,6 +2961,7 @@ def delete def check_for_errors errors = [] + errors += @detailed_construction.check_for_errors return errors end @@ -2937,25 +2991,28 @@ def to_oga(doc) XMLHelper.add_element(interior_finish, 'Type', @interior_finish_type, :string, @interior_finish_type_isdefaulted) unless @interior_finish_type.nil? XMLHelper.add_element(interior_finish, 'Thickness', @interior_finish_thickness, :float, @interior_finish_thickness_isdefaulted) unless @interior_finish_thickness.nil? end - insulation = XMLHelper.add_element(floor, 'Insulation') - sys_id = XMLHelper.add_element(insulation, 'SystemIdentifier') - if not @insulation_id.nil? - XMLHelper.add_attribute(sys_id, 'id', @insulation_id) - else - XMLHelper.add_attribute(sys_id, 'id', @id + 'Insulation') - end - XMLHelper.add_element(insulation, 'InsulationGrade', @insulation_grade, :integer) unless @insulation_grade.nil? - XMLHelper.add_element(insulation, 'AssemblyEffectiveRValue', @insulation_assembly_r_value, :float) unless @insulation_assembly_r_value.nil? - if not @insulation_cavity_r_value.nil? - layer = XMLHelper.add_element(insulation, 'Layer') - XMLHelper.add_element(layer, 'InstallationType', 'cavity', :string) - XMLHelper.add_element(layer, 'NominalRValue', @insulation_cavity_r_value, :float) - end - if not @insulation_continuous_r_value.nil? - layer = XMLHelper.add_element(insulation, 'Layer') - XMLHelper.add_element(layer, 'InstallationType', 'continuous', :string) - XMLHelper.add_element(layer, 'NominalRValue', @insulation_continuous_r_value, :float) + if (not @insulation_assembly_r_value.nil?) || (not @insulation_cavity_r_value.nil?) || (not @insulation_continuous_r_value.nil?) + insulation = XMLHelper.add_element(floor, 'Insulation') + sys_id = XMLHelper.add_element(insulation, 'SystemIdentifier') + if not @insulation_id.nil? + XMLHelper.add_attribute(sys_id, 'id', @insulation_id) + else + XMLHelper.add_attribute(sys_id, 'id', @id + 'Insulation') + end + XMLHelper.add_element(insulation, 'InsulationGrade', @insulation_grade, :integer) unless @insulation_grade.nil? + XMLHelper.add_element(insulation, 'AssemblyEffectiveRValue', @insulation_assembly_r_value, :float, @insulation_assembly_r_value_isdefaulted) unless @insulation_assembly_r_value.nil? + if not @insulation_cavity_r_value.nil? + layer = XMLHelper.add_element(insulation, 'Layer') + XMLHelper.add_element(layer, 'InstallationType', 'cavity', :string) + XMLHelper.add_element(layer, 'NominalRValue', @insulation_cavity_r_value, :float) + end + if not @insulation_continuous_r_value.nil? + layer = XMLHelper.add_element(insulation, 'Layer') + XMLHelper.add_element(layer, 'InstallationType', 'continuous', :string) + XMLHelper.add_element(layer, 'NominalRValue', @insulation_continuous_r_value, :float) + end end + @detailed_construction.to_oga(floor) end def from_oga(floor) @@ -2983,6 +3040,7 @@ def from_oga(floor) @insulation_cavity_r_value = XMLHelper.get_value(insulation, "Layer[InstallationType='cavity']/NominalRValue", :float) @insulation_continuous_r_value = XMLHelper.get_value(insulation, "Layer[InstallationType='continuous']/NominalRValue", :float) end + @detailed_construction.from_oga(XMLHelper.get_element(floor, 'DetailedConstruction')) end end @@ -3493,6 +3551,119 @@ def from_oga(door) end end + class DetailedConstruction < BaseElement + def initialize(hpxml_object, *args) + @construction_layers = DetailedConstructionLayers.new(hpxml_object) + super(hpxml_object, *args) + end + ATTRS = [:id, :construction_layers] + attr_accessor(*ATTRS) + + def check_for_errors + errors = [] + return errors + end + + def to_oga(surface) + return if surface.nil? + return if @construction_layers.empty? + + detailed_construction = XMLHelper.add_element(surface, 'DetailedConstruction') + sys_id = XMLHelper.add_element(detailed_construction, 'SystemIdentifier') + XMLHelper.add_attribute(sys_id, 'id', @id) + + @construction_layers.to_oga(detailed_construction) + end + + def from_oga(detailed_construction) + return if detailed_construction.nil? + + @id = HPXML::get_id(detailed_construction) + @construction_layers.from_oga(detailed_construction) + end + end + + class DetailedConstructionLayers < BaseArrayElement + def add(**kwargs) + self << DetailedConstructionLayer.new(@hpxml_object, **kwargs) + end + + def from_oga(detailed_construction) + return if detailed_construction.nil? + + XMLHelper.get_elements(detailed_construction, 'ConstructionLayer').each do |construction_layer| + self << DetailedConstructionLayer.new(@hpxml_object, construction_layer) + end + end + end + + class DetailedConstructionLayer < BaseElement + def initialize(hpxml_object, *args) + @layer_materials = DetailedConstructionLayerMaterials.new(hpxml_object) + super(hpxml_object, *args) + end + ATTRS = [:layer_type, :layer_thickness, :layer_materials] + attr_accessor(*ATTRS) + + def to_oga(detailed_construction) + return if detailed_construction.nil? + + construction_layer = XMLHelper.add_element(detailed_construction, 'ConstructionLayer') + XMLHelper.add_element(construction_layer, 'LayerType', @layer_type, :string) unless @layer_type.nil? + XMLHelper.add_element(construction_layer, 'LayerThickness', @layer_thickness, :float) unless @layer_thickness.nil? + + @layer_materials.to_oga(construction_layer) + end + + def from_oga(construction_layer) + return if construction_layer.nil? + + @layer_type = XMLHelper.get_value(construction_layer, 'LayerType', :string) + @layer_thickness = XMLHelper.get_value(construction_layer, 'LayerThickness', :float) + @layer_materials.from_oga(construction_layer) + end + end + + class DetailedConstructionLayerMaterials < BaseArrayElement + def add(**kwargs) + self << DetailedConstructionLayerMaterial.new(@hpxml_object, **kwargs) + end + + def from_oga(construction_layer) + XMLHelper.get_elements(construction_layer, 'LayerMaterial').each do |layer_material| + self << DetailedConstructionLayerMaterial.new(@hpxml_object, layer_material) + end + end + end + + class DetailedConstructionLayerMaterial < BaseElement + ATTRS = [:area_fraction, :material_type, :conductivity, :density, :specific_heat, :r_value] + attr_accessor(*ATTRS) + + def to_oga(construction_layer) + return if construction_layer.nil? + + layer_material = XMLHelper.add_element(construction_layer, 'LayerMaterial') + XMLHelper.add_element(layer_material, 'AreaFraction', @area_fraction, :float) unless @area_fraction.nil? + XMLHelper.add_element(layer_material, 'MaterialType', @material_type, :string) unless @material_type.nil? + XMLHelper.add_element(layer_material, 'Conductivity', @conductivity, :float) unless @conductivity.nil? + XMLHelper.add_element(layer_material, 'Density', @density, :float) unless @density.nil? + XMLHelper.add_element(layer_material, 'SpecificHeat', @specific_heat, :float) unless @specific_heat.nil? + XMLHelper.add_element(layer_material, 'RValue', @r_value, :float) unless @r_value.nil? + end + + def from_oga(layer_material) + return if layer_material.nil? + + @area_fraction = XMLHelper.get_value(layer_material, 'AreaFraction', :float) + @material_type = XMLHelper.get_value(layer_material, 'MaterialType', :string) + @conductivity = XMLHelper.get_value(layer_material, 'Conductivity', :float) + @density = XMLHelper.get_value(layer_material, 'Density', :float) + @specific_heat = XMLHelper.get_value(layer_material, 'SpecificHeat', :float) + @r_value = XMLHelper.get_value(layer_material, 'RValue', :float) + end + end + class PartitionWallMass < BaseElement ATTRS = [:area_fraction, :interior_finish_type, :interior_finish_thickness] attr_accessor(*ATTRS) diff --git a/HPXMLtoOpenStudio/resources/hpxml_defaults.rb b/HPXMLtoOpenStudio/resources/hpxml_defaults.rb index 1849f9a8fc..3803c26126 100644 --- a/HPXMLtoOpenStudio/resources/hpxml_defaults.rb +++ b/HPXMLtoOpenStudio/resources/hpxml_defaults.rb @@ -13,6 +13,8 @@ def self.apply(runner, hpxml, eri_version, weather, epw_file: nil, schedules_fil ncfl_ag = hpxml.building_construction.number_of_conditioned_floors_above_grade has_uncond_bsmnt = hpxml.has_location(HPXML::LocationBasementUnconditioned) infil_measurement = Airflow.get_infiltration_measurement_of_interest(hpxml.air_infiltration_measurements) + apply_ashrae140_assumptions = hpxml.header.apply_ashrae140_assumptions # Hidden feature + apply_ashrae140_assumptions = false if apply_ashrae140_assumptions.nil? # Check for presence of fuels once has_fuel = {} @@ -33,9 +35,9 @@ def self.apply(runner, hpxml, eri_version, weather, epw_file: nil, schedules_fil apply_infiltration(hpxml, infil_measurement) apply_attics(hpxml) apply_foundations(hpxml) - apply_roofs(hpxml) - apply_rim_joists(hpxml) - apply_walls(hpxml) + apply_roofs(runner, hpxml, apply_ashrae140_assumptions) + apply_rim_joists(runner, hpxml) + apply_walls(runner, hpxml, apply_ashrae140_assumptions) apply_foundation_walls(hpxml) apply_floors(hpxml) apply_slabs(hpxml) @@ -646,7 +648,7 @@ def self.apply_foundations(hpxml) end end - def self.apply_roofs(hpxml) + def self.apply_roofs(runner, hpxml, apply_ashrae140_assumptions) hpxml.roofs.each do |roof| if roof.azimuth.nil? roof.azimuth = get_azimuth_from_orientation(roof.orientation) @@ -656,51 +658,75 @@ def self.apply_roofs(hpxml) roof.orientation = get_orientation_from_azimuth(roof.azimuth) roof.orientation_isdefaulted = true end - if roof.roof_type.nil? - roof.roof_type = HPXML::RoofTypeAsphaltShingles - roof.roof_type_isdefaulted = true - end - if roof.emittance.nil? - roof.emittance = 0.90 - roof.emittance_isdefaulted = true - end - if roof.radiant_barrier.nil? - roof.radiant_barrier = false - roof.radiant_barrier_isdefaulted = true - end - if roof.radiant_barrier && roof.radiant_barrier_grade.nil? - roof.radiant_barrier_grade = 1 - roof.radiant_barrier_grade_isdefaulted = true - end - if roof.roof_color.nil? && roof.solar_absorptance.nil? - roof.roof_color = HPXML::ColorMedium - roof.roof_color_isdefaulted = true - end - if roof.roof_color.nil? - roof.roof_color = Constructions.get_default_roof_color(roof.roof_type, roof.solar_absorptance) - roof.roof_color_isdefaulted = true - elsif roof.solar_absorptance.nil? - roof.solar_absorptance = Constructions.get_default_roof_solar_absorptance(roof.roof_type, roof.roof_color) - roof.solar_absorptance_isdefaulted = true - end - if roof.interior_finish_type.nil? - if HPXML::conditioned_finished_locations.include? roof.interior_adjacent_to - roof.interior_finish_type = HPXML::InteriorFinishGypsumBoard - else - roof.interior_finish_type = HPXML::InteriorFinishNone - end - roof.interior_finish_type_isdefaulted = true - end - next unless roof.interior_finish_thickness.nil? - if roof.interior_finish_type != HPXML::InteriorFinishNone - roof.interior_finish_thickness = 0.5 - roof.interior_finish_thickness_isdefaulted = true + roof_pitch_in_deg = UnitConversions.convert(Math.atan(roof.pitch / 12.0), 'rad', 'deg') + roof.additional_properties.inside_film = Material.AirFilmRoof(roof_pitch_in_deg) + roof.additional_properties.outside_film = Material.AirFilmOutside + if apply_ashrae140_assumptions + roof.additional_properties.inside_film = Material.AirFilmRoofASHRAE140 + roof.additional_properties.outside_film = Material.AirFilmOutsideASHRAE140 + end + + if roof.has_detailed_construction + # Detailed construction inputs + detailed_construction_assembly_r_value = calculate_assembly_r_value_from_detailed_construction(roof) + if roof.insulation_assembly_r_value.nil? + roof.insulation_assembly_r_value = detailed_construction_assembly_r_value + roof.insulation_assembly_r_value_isdefaulted = true + elsif (detailed_construction_assembly_r_value - roof.insulation_assembly_r_value).abs > 1 + # Detailed construction is more than R-1 different from user-specified assembly R-value + if not runner.nil? + runner.registerWarning("For roof '#{roof.id}', assembly R-value calculated from detailed construction (#{detailed_construction_assembly_r_value}) differs from AssemblyEffectiveRValue (#{roof.insulation_assembly_r_value}). The latter will be overridden.") + end + roof.insulation_assembly_r_value = detailed_construction_assembly_r_value + roof.insulation_assembly_r_value_isdefaulted = true + end + else + # Simple inputs + if roof.roof_type.nil? + roof.roof_type = HPXML::RoofTypeAsphaltShingles + roof.roof_type_isdefaulted = true + end + if roof.emittance.nil? + roof.emittance = 0.90 + roof.emittance_isdefaulted = true + end + if roof.radiant_barrier.nil? + roof.radiant_barrier = false + roof.radiant_barrier_isdefaulted = true + end + if roof.radiant_barrier && roof.radiant_barrier_grade.nil? + roof.radiant_barrier_grade = 1 + roof.radiant_barrier_grade_isdefaulted = true + end + if roof.roof_color.nil? && roof.solar_absorptance.nil? + roof.roof_color = HPXML::ColorMedium + roof.roof_color_isdefaulted = true + end + if roof.roof_color.nil? + roof.roof_color = Constructions.get_default_roof_color(roof.roof_type, roof.solar_absorptance) + roof.roof_color_isdefaulted = true + elsif roof.solar_absorptance.nil? + roof.solar_absorptance = Constructions.get_default_roof_solar_absorptance(roof.roof_type, roof.roof_color) + roof.solar_absorptance_isdefaulted = true + end + if roof.interior_finish_type.nil? + if HPXML::conditioned_finished_locations.include? roof.interior_adjacent_to + roof.interior_finish_type = HPXML::InteriorFinishGypsumBoard + else + roof.interior_finish_type = HPXML::InteriorFinishNone + end + roof.interior_finish_type_isdefaulted = true + end + if roof.interior_finish_thickness.nil? && roof.interior_finish_type != HPXML::InteriorFinishNone + roof.interior_finish_thickness = 0.5 + roof.interior_finish_thickness_isdefaulted = true + end end end end - def self.apply_rim_joists(hpxml) + def self.apply_rim_joists(runner, hpxml) hpxml.rim_joists.each do |rim_joist| if rim_joist.azimuth.nil? rim_joist.azimuth = get_azimuth_from_orientation(rim_joist.orientation) @@ -711,31 +737,55 @@ def self.apply_rim_joists(hpxml) rim_joist.orientation_isdefaulted = true end - next unless rim_joist.is_exterior - - if rim_joist.emittance.nil? - rim_joist.emittance = 0.90 - rim_joist.emittance_isdefaulted = true - end - if rim_joist.siding.nil? - rim_joist.siding = HPXML::SidingTypeWood - rim_joist.siding_isdefaulted = true - end - if rim_joist.color.nil? && rim_joist.solar_absorptance.nil? - rim_joist.color = HPXML::ColorMedium - rim_joist.color_isdefaulted = true - end - if rim_joist.color.nil? - rim_joist.color = Constructions.get_default_wall_color(rim_joist.solar_absorptance) - rim_joist.color_isdefaulted = true - elsif rim_joist.solar_absorptance.nil? - rim_joist.solar_absorptance = Constructions.get_default_wall_solar_absorptance(rim_joist.color) - rim_joist.solar_absorptance_isdefaulted = true + rim_joist.additional_properties.inside_film = Material.AirFilmVertical + if rim_joist.is_exterior + rim_joist.additional_properties.outside_film = Material.AirFilmOutside + else + rim_joist.additional_properties.outside_film = Material.AirFilmVertical + end + + if rim_joist.has_detailed_construction + # Detailed construction inputs + detailed_construction_assembly_r_value = calculate_assembly_r_value_from_detailed_construction(rim_joist) + if rim_joist.insulation_assembly_r_value.nil? + rim_joist.insulation_assembly_r_value = detailed_construction_assembly_r_value + rim_joist.insulation_assembly_r_value_isdefaulted = true + elsif (detailed_construction_assembly_r_value - rim_joist.insulation_assembly_r_value).abs > 1 + # Detailed construction is more than R-1 different from user-specified assembly R-value + if not runner.nil? + runner.registerWarning("For rim joist '#{rim_joist.id}', assembly R-value calculated from detailed construction (#{detailed_construction_assembly_r_value}) differs from AssemblyEffectiveRValue (#{rim_joist.insulation_assembly_r_value}). The latter will be overridden.") + end + rim_joist.insulation_assembly_r_value = detailed_construction_assembly_r_value + rim_joist.insulation_assembly_r_value_isdefaulted = true + end + else + # Simple inputs + if rim_joist.is_exterior + if rim_joist.emittance.nil? + rim_joist.emittance = 0.90 + rim_joist.emittance_isdefaulted = true + end + if rim_joist.siding.nil? + rim_joist.siding = HPXML::SidingTypeWood + rim_joist.siding_isdefaulted = true + end + if rim_joist.color.nil? && rim_joist.solar_absorptance.nil? + rim_joist.color = HPXML::ColorMedium + rim_joist.color_isdefaulted = true + end + if rim_joist.color.nil? + rim_joist.color = Constructions.get_default_wall_color(rim_joist.solar_absorptance) + rim_joist.color_isdefaulted = true + elsif rim_joist.solar_absorptance.nil? + rim_joist.solar_absorptance = Constructions.get_default_wall_solar_absorptance(rim_joist.color) + rim_joist.solar_absorptance_isdefaulted = true + end + end end end end - def self.apply_walls(hpxml) + def self.apply_walls(runner, hpxml, apply_ashrae140_assumptions) hpxml.walls.each do |wall| if wall.azimuth.nil? wall.azimuth = get_azimuth_from_orientation(wall.orientation) @@ -746,40 +796,66 @@ def self.apply_walls(hpxml) wall.orientation_isdefaulted = true end + wall.additional_properties.inside_film = Material.AirFilmVertical if wall.is_exterior - if wall.emittance.nil? - wall.emittance = 0.90 - wall.emittance_isdefaulted = true - end - if wall.siding.nil? - wall.siding = HPXML::SidingTypeWood - wall.siding_isdefaulted = true + wall.additional_properties.outside_film = Material.AirFilmOutside + else + wall.additional_properties.outside_film = Material.AirFilmVertical + end + if apply_ashrae140_assumptions + wall.additional_properties.inside_film = Material.AirFilmVerticalASHRAE140 + wall.additional_properties.outside_film = Material.AirFilmOutsideASHRAE140 + end + + if wall.has_detailed_construction + # Detailed construction inputs + detailed_construction_assembly_r_value = calculate_assembly_r_value_from_detailed_construction(wall) + if wall.insulation_assembly_r_value.nil? + wall.insulation_assembly_r_value = detailed_construction_assembly_r_value + wall.insulation_assembly_r_value_isdefaulted = true + elsif (detailed_construction_assembly_r_value - wall.insulation_assembly_r_value).abs > 1 + # Detailed construction is more than R-1 different from user-specified assembly R-value + if not runner.nil? + runner.registerWarning("For wall '#{wall.id}', assembly R-value calculated from detailed construction (#{detailed_construction_assembly_r_value}) differs from AssemblyEffectiveRValue (#{wall.insulation_assembly_r_value}). The latter will be overridden.") + end + wall.insulation_assembly_r_value = detailed_construction_assembly_r_value + wall.insulation_assembly_r_value_isdefaulted = true end - if wall.color.nil? && wall.solar_absorptance.nil? - wall.color = HPXML::ColorMedium - wall.color_isdefaulted = true + else + # Simple inputs + if wall.is_exterior + if wall.emittance.nil? + wall.emittance = 0.90 + wall.emittance_isdefaulted = true + end + if wall.siding.nil? + wall.siding = HPXML::SidingTypeWood + wall.siding_isdefaulted = true + end + if wall.color.nil? && wall.solar_absorptance.nil? + wall.color = HPXML::ColorMedium + wall.color_isdefaulted = true + end + if wall.color.nil? + wall.color = Constructions.get_default_wall_color(wall.solar_absorptance) + wall.color_isdefaulted = true + elsif wall.solar_absorptance.nil? + wall.solar_absorptance = Constructions.get_default_wall_solar_absorptance(wall.color) + wall.solar_absorptance_isdefaulted = true + end end - if wall.color.nil? - wall.color = Constructions.get_default_wall_color(wall.solar_absorptance) - wall.color_isdefaulted = true - elsif wall.solar_absorptance.nil? - wall.solar_absorptance = Constructions.get_default_wall_solar_absorptance(wall.color) - wall.solar_absorptance_isdefaulted = true + if wall.interior_finish_type.nil? + if HPXML::conditioned_finished_locations.include? wall.interior_adjacent_to + wall.interior_finish_type = HPXML::InteriorFinishGypsumBoard + else + wall.interior_finish_type = HPXML::InteriorFinishNone + end + wall.interior_finish_type_isdefaulted = true end - end - if wall.interior_finish_type.nil? - if HPXML::conditioned_finished_locations.include? wall.interior_adjacent_to - wall.interior_finish_type = HPXML::InteriorFinishGypsumBoard - else - wall.interior_finish_type = HPXML::InteriorFinishNone + if wall.interior_finish_thickness.nil? && wall.interior_finish_type != HPXML::InteriorFinishNone + wall.interior_finish_thickness = 0.5 + wall.interior_finish_thickness_isdefaulted = true end - wall.interior_finish_type_isdefaulted = true - end - next unless wall.interior_finish_thickness.nil? - - if wall.interior_finish_type != HPXML::InteriorFinishNone - wall.interior_finish_thickness = 0.5 - wall.interior_finish_thickness_isdefaulted = true end end end @@ -2903,4 +2979,11 @@ def self.get_nbeds_adjusted_for_operational_calculation(hpxml) fail "Unexpected residential facility type: #{unit_type}." end end + + def self.calculate_assembly_r_value_from_detailed_construction(surface) + constr = Constructions.create_from_detailed_construction(surface.detailed_construction, + surface.additional_properties.inside_film, + surface.additional_properties.outside_film) + return constr.assembly_rvalue.round(1) + end end diff --git a/HPXMLtoOpenStudio/resources/hpxml_schema/HPXML.xsd b/HPXMLtoOpenStudio/resources/hpxml_schema/HPXML.xsd index 43f97d950c..1bd1ca7d51 100644 --- a/HPXMLtoOpenStudio/resources/hpxml_schema/HPXML.xsd +++ b/HPXMLtoOpenStudio/resources/hpxml_schema/HPXML.xsd @@ -200,6 +200,65 @@ + + + Can be used to describe properties for each individual material in the construction + + + + + + Layers should be ordered from exterior to interior. Do not include air films. + + + + + + + [in] + + + + + + + + Fraction of the layer's area defined by this material. Values across all materials in a layer should sum to 1. + + + + + E.g., "wood", "concrete", "drywall", etc. + + + + + [Btu/hr-ft-F] + + + + + [lb/ft3] + + + + + [Btu/lb-F] + + + + + + + + + + + + + + + @@ -989,6 +1048,7 @@ + @@ -1029,6 +1089,7 @@ + @@ -1082,6 +1143,7 @@ + @@ -1149,6 +1211,7 @@ + @@ -1188,6 +1251,7 @@ + Indicate whether the floor section is a mobile home belly or wing section. Omit if not applicable. For a mobile home, if the foundation type is a belly and wing, ExteriorAdjacentTo should be set to 'outside'. @@ -1247,6 +1311,7 @@ + @@ -4136,6 +4201,11 @@ + + + Describes ducts buried in, e.g., attic loose-fill insulation. Partially buried ducts have insulation that does not cover the top of the ducts. Fully buried ducts have insulation that just covers the top of the ducts. Deeply buried ducts have insulation that continues above the top of the ducts. See https://basc.pnnl.gov/resource-guides/ducts-buried-attic-insulation for more information. + + @@ -6403,6 +6473,18 @@ + + + + + + + + + + + + @@ -8357,6 +8439,21 @@ + + + + + + + + + + + + + + + @@ -9707,6 +9804,19 @@ + + + A fraction that has to be between 0 (exclusive) and 1 (inclusive) + + + + + + + + + + @@ -11375,4 +11485,23 @@ + + + + + + + + + + + + + + + + + + + diff --git a/HPXMLtoOpenStudio/resources/hpxml_schematron/EPvalidator.xml b/HPXMLtoOpenStudio/resources/hpxml_schematron/EPvalidator.xml index 1c7590abd9..0aef29839b 100644 --- a/HPXMLtoOpenStudio/resources/hpxml_schematron/EPvalidator.xml +++ b/HPXMLtoOpenStudio/resources/hpxml_schematron/EPvalidator.xml @@ -413,7 +413,7 @@ Expected 0 or 1 element(s) for xpath: InteriorFinish/Thickness Expected 1 element(s) for xpath: Pitch Expected 0 or 1 element(s) for xpath: RadiantBarrier - Expected 1 element(s) for xpath: Insulation/AssemblyEffectiveRValue + Expected 1 or more element(s) for xpath: Insulation/AssemblyEffectiveRValue | DetailedConstruction @@ -444,7 +444,7 @@ Expected Siding to be 'wood siding' or 'vinyl siding' or 'stucco' or 'fiber cement siding' or 'brick veneer' or 'aluminum siding' or 'masonite siding' or 'composite shingle siding' or 'asbestos siding' or 'synthetic stucco' or 'none' Expected 0 or more element(s) for xpath: Color | SolarAbsorptance Expected 0 or 1 element(s) for xpath: Emittance - Expected 1 element(s) for xpath: Insulation/AssemblyEffectiveRValue + Expected 1 or more element(s) for xpath: Insulation/AssemblyEffectiveRValue | DetailedConstruction @@ -465,7 +465,7 @@ Expected 0 or 1 element(s) for xpath: InteriorFinish/Type Expected InteriorFinish/Type to be 'gypsum board' or 'gypsum composite board' or 'plaster' or 'wood' or 'none' Expected 0 or 1 element(s) for xpath: InteriorFinish/Thickness - Expected 1 element(s) for xpath: Insulation/AssemblyEffectiveRValue + Expected 1 or more element(s) for xpath: Insulation/AssemblyEffectiveRValue | DetailedConstruction @@ -489,6 +489,7 @@ Expected 1 element(s) for xpath: DepthBelowGrade Expected DepthBelowGrade to be less than or equal to Height + Expected 1 element(s) for xpath: Insulation/Layer[InstallationType[text()="continuous - interior"]] | Insulation/AssemblyEffectiveRValue Expected 1 element(s) for xpath: Insulation/Layer[InstallationType[text()="continuous - exterior"]] | Insulation/AssemblyEffectiveRValue @@ -527,7 +528,7 @@ Expected 0 or 1 element(s) for xpath: InteriorFinish/Type Expected InteriorFinish/Type to be 'gypsum board' or 'gypsum composite board' or 'plaster' or 'wood' or 'none' Expected 0 or 1 element(s) for xpath: InteriorFinish/Thickness - Expected 1 element(s) for xpath: Insulation/AssemblyEffectiveRValue + Expected 1 or more element(s) for xpath: Insulation/AssemblyEffectiveRValue | DetailedConstruction @@ -678,6 +679,38 @@ + + [DetailedConstruction] + + Expected 1 or more element(s) for xpath: ConstructionLayer + + + + + [DetailedConstructionLayer] + + Expected 1 or more element(s) for xpath: LayerMaterial + Expected sum(LayerMaterial/AreaFraction) to be 1 + + + + + [DetailedConstructionLayerMaterial] + + Expected 1 element(s) for xpath: AreaFraction + Expected 1 or more element(s) for xpath: Conductivity | RValue + + + + + [DetailedConstructionLayerMaterial=Detailed] + + Expected 1 element(s) for xpath: ../LayerThickness + Expected 1 element(s) for xpath: Density + Expected 1 element(s) for xpath: SpecificHeat + + + [PartitionWallMass] diff --git a/HPXMLtoOpenStudio/resources/hvac_sizing.rb b/HPXMLtoOpenStudio/resources/hvac_sizing.rb index 1752fbafe1..f87fd529e8 100644 --- a/HPXMLtoOpenStudio/resources/hvac_sizing.rb +++ b/HPXMLtoOpenStudio/resources/hvac_sizing.rb @@ -745,14 +745,19 @@ def self.process_load_walls(bldg_design_loads) wall_area = wall.net_area end + solar_absorptance = wall.solar_absorptance + if solar_absorptance.nil? + solar_absorptance = 0.9 # FIXME: Temporary + end + azimuths.each do |azimuth| if wall.is_exterior # Adjust base Cooling Load Temperature Difference (CLTD) # Assume absorptivity for light walls < 0.5, medium walls <= 0.75, dark walls > 0.75 (based on MJ8 Table 4B Notes) - if wall.solar_absorptance <= 0.5 + if solar_absorptance <= 0.5 colorMultiplier = 0.65 # MJ8 Table 4B Notes, pg 348 - elsif wall.solar_absorptance <= 0.75 + elsif solar_absorptance <= 0.75 colorMultiplier = 0.83 # MJ8 Appendix 12, pg 519 else colorMultiplier = 1.0 @@ -2483,10 +2488,19 @@ def self.get_wall_group(wall) end wall_ufactor = 1.0 / wall.insulation_assembly_r_value + if not wall.siding.nil? + wall_siding_is_brick = (wall.siding == HPXML::SidingTypeBrick) + elsif wall.has_detailed_construction + # Assume brick if at least 50% of the outermost construction layer is very high density + exterior_layer = wall.detailed_construction.construction_layers[0] + wall_siding_is_brick = exterior_layer.layer_materials.any? { |m| m.area_fraction >= 0.5 && m.density >= 100.0 } + else + wall_siding_is_brick = false + end # The following correlations were estimated by analyzing MJ8 construction tables. if wall_type == HPXML::WallTypeWoodStud - if wall.siding == HPXML::SidingTypeBrick + if wall_siding_is_brick if wall_ufactor <= 0.070 wall_group = 11 # K elsif wall_ufactor <= 0.083 @@ -2527,7 +2541,7 @@ def self.get_wall_group(wall) end elsif wall_type == HPXML::WallTypeSteelStud - if wall.siding == HPXML::SidingTypeBrick + if wall_siding_is_brick if wall_ufactor <= 0.090 wall_group = 11 # K elsif wall_ufactor <= 0.105 @@ -2569,20 +2583,20 @@ def self.get_wall_group(wall) elsif wall_type == HPXML::WallTypeDoubleWoodStud wall_group = 10 # J (assumed since MJ8 does not include double stud constructions) - if wall.siding == HPXML::SidingTypeBrick + if wall_siding_is_brick wall_group = 11 # K end elsif wall_type == HPXML::WallTypeSIP # Manual J refers to SIPs as Structural Foam Panel (SFP) if wall_ufactor >= (0.072 + 0.050) / 2 - if wall.siding == HPXML::SidingTypeBrick + if wall_siding_is_brick wall_group = 10 # J else wall_group = 7 # G end elsif wall_ufactor >= 0.050 - if wall.siding == HPXML::SidingTypeBrick + if wall_siding_is_brick wall_group = 11 # K else wall_group = 9 # I @@ -3187,10 +3201,10 @@ def initialize class Numeric def deg2rad - self * Math::PI / 180 + self * UnitConversions.convert(1.0, 'deg', 'rad') end def rad2deg - self * 180 / Math::PI + self * UnitConversions.convert(1.0, 'rad', 'deg') end end diff --git a/HPXMLtoOpenStudio/resources/materials.rb b/HPXMLtoOpenStudio/resources/materials.rb index 012f0d774e..8aba8363ed 100644 --- a/HPXMLtoOpenStudio/resources/materials.rb +++ b/HPXMLtoOpenStudio/resources/materials.rb @@ -424,6 +424,21 @@ def self.StrawBale end end +class NoMassMaterial + # name - NoMassMaterial name + # rvalue - R-value (h-ft^2-F/Btu) + # tAbs - thermal absorptance (emittance); 0.9 is EnergyPlus default + # sAbs - solar absorptance; 0.7 is EnergyPlus default + def initialize(name: nil, rvalue:, tAbs: 0.9, sAbs: 0.7) + @name = name + @rvalue = rvalue + @tAbs = tAbs + @sAbs = sAbs + end + + attr_accessor :name, :rvalue, :tAbs, :sAbs +end + class GlazingMaterial def initialize(name:, ufactor:, shgc:) @name = name diff --git a/HPXMLtoOpenStudio/tests/test_validation.rb b/HPXMLtoOpenStudio/tests/test_validation.rb index 0b13ea53e7..5ca84dcbc1 100644 --- a/HPXMLtoOpenStudio/tests/test_validation.rb +++ b/HPXMLtoOpenStudio/tests/test_validation.rb @@ -1218,6 +1218,9 @@ def test_ruby_error_messages def test_ruby_warning_messages # Test case => Error message all_expected_warnings = { 'cfis-undersized-supplemental-fan' => ["CFIS supplemental fan 'VentilationFan2' is undersized (90.0 cfm) compared to the target hourly ventilation rate (110.0 cfm)."], + 'detailed-construction-assembly-rvalue' => ["For rim joist 'RimJoist1', assembly R-value calculated from detailed construction (22.9) differs from AssemblyEffectiveRValue (10.0). The latter will be overridden.", + "For wall 'Wall1', assembly R-value calculated from detailed construction (22.9) differs from AssemblyEffectiveRValue (10.0). The latter will be overridden.", + "For roof 'Roof1', assembly R-value calculated from detailed construction (2.3) differs from AssemblyEffectiveRValue (10.0). The latter will be overridden."], 'hvac-setpoint-adjustments' => ['HVAC setpoints have been automatically adjusted to prevent periods where the heating setpoint is greater than the cooling setpoint.'], 'hvac-setpoint-adjustments-daily-setbacks' => ['HVAC setpoints have been automatically adjusted to prevent periods where the heating setpoint is greater than the cooling setpoint.'], 'hvac-setpoint-adjustments-daily-schedules' => ['HVAC setpoints have been automatically adjusted to prevent periods where the heating setpoint is greater than the cooling setpoint.'], @@ -1299,6 +1302,11 @@ def test_ruby_warning_messages hpxml = HPXML.new(hpxml_path: File.join(@sample_files_path, 'base-mechvent-cfis-supplemental-fan-exhaust.xml')) suppl_fan = hpxml.ventilation_fans.find { |f| f.is_cfis_supplemental_fan? } suppl_fan.tested_flow_rate = 90.0 + elsif ['detailed-construction-assembly-rvalue'].include? warning_case + hpxml = HPXML.new(hpxml_path: File.join(@sample_files_path, 'base-enclosure-detailed-constructions.xml')) + hpxml.walls[0].insulation_assembly_r_value = 10 + hpxml.rim_joists[0].insulation_assembly_r_value = 10 + hpxml.roofs[0].insulation_assembly_r_value = 10 elsif ['hvac-setpoint-adjustments'].include? warning_case hpxml = HPXML.new(hpxml_path: File.join(@sample_files_path, 'base.xml')) hpxml.hvac_controls[0].heating_setpoint_temp = 76.0 diff --git a/tasks.rb b/tasks.rb index ebce3d5a2d..88eb92e784 100644 --- a/tasks.rb +++ b/tasks.rb @@ -1164,6 +1164,68 @@ def apply_hpxml_modification(hpxml_file, hpxml) azimuth: 180, r_value: 4.4) end + if ['base-enclosure-detailed-constructions.xml'].include? hpxml_file + # Wall 1 (exterior): R-36 Closed Cell Spray Foam, 2x6, 24 in o.c. + # Wall 2 (attic gable): Uninsulated, 2x6, 24 in o.c., open cavity + # RimJoist 1: Same as Wall 1 + (hpxml.walls + hpxml.rim_joists).each_with_index do |surface, i| + surface.insulation_assembly_r_value = nil + surface.siding = nil + surface.solar_absorptance = nil + surface.emittance = nil + surface.interior_finish_type = nil if surface.is_a? HPXML::Wall + surface.detailed_construction.id = surface.id.gsub('Wall', 'WallConstruction').gsub('RimJoist', 'RimJoistConstruction') + surface.detailed_construction.construction_layers.add(layer_thickness: 0.375) + surface.detailed_construction.construction_layers[-1].layer_materials.add(area_fraction: 1.0, + material_type: 'vinyl siding', + conductivity: 0.052, + density: 11.1, + specific_heat: 0.25) + surface.detailed_construction.construction_layers.add(layer_thickness: 0.5) + surface.detailed_construction.construction_layers[-1].layer_materials.add(area_fraction: 1.0, + material_type: 'osb', + conductivity: 0.067, + density: 32.0, + specific_heat: 0.29) + if (i == 0) || (i == 2) + surface.detailed_construction.construction_layers.add(layer_thickness: 5.5) + surface.detailed_construction.construction_layers[-1].layer_materials.add(area_fraction: 0.22, + material_type: 'wood stud', + conductivity: 0.067, + density: 32.0, + specific_heat: 0.29) + surface.detailed_construction.construction_layers[-1].layer_materials.add(area_fraction: 0.78, + material_type: 'spray foam', + r_value: 36.0, + density: 2.85, + specific_heat: 0.25) + end + surface.detailed_construction.construction_layers.add(layer_thickness: 0.5) + surface.detailed_construction.construction_layers[-1].layer_materials.add(area_fraction: 1.0, + material_type: 'gypsum board', + conductivity: 0.093, + density: 50.0, + specific_heat: 0.2) + end + # Roof: Uninsulated, asphalt shingles + surface = hpxml.roofs[0] + surface.insulation_assembly_r_value = nil + surface.roof_type = nil + surface.solar_absorptance = nil + surface.emittance = nil + surface.radiant_barrier = nil + surface.detailed_construction.id = surface.id.gsub('Roof', 'RoofConstruction') + surface.detailed_construction.construction_layers.add() + surface.detailed_construction.construction_layers[-1].layer_materials.add(area_fraction: 1.0, + material_type: 'asphalt shingles', + r_value: 0.44) + surface.detailed_construction.construction_layers.add(layer_thickness: 0.75) + surface.detailed_construction.construction_layers[-1].layer_materials.add(area_fraction: 1.0, + material_type: 'osb', + conductivity: 0.067, + density: 32.0, + specific_heat: 0.29) + end # ---------- # # HPXML HVAC # diff --git a/workflow/hpxml_inputs.json b/workflow/hpxml_inputs.json index 31e0a8c90a..5f9fcd6f55 100644 --- a/workflow/hpxml_inputs.json +++ b/workflow/hpxml_inputs.json @@ -1336,6 +1336,9 @@ "sample_files/base-enclosure-ceilingtypes.xml": { "parent_hpxml": "sample_files/base.xml" }, + "sample_files/base-enclosure-detailed-constructions.xml": { + "parent_hpxml": "sample_files/base.xml" + }, "sample_files/base-enclosure-floortypes.xml": { "parent_hpxml": "sample_files/base-foundation-ambient.xml" }, diff --git a/workflow/sample_files/base-enclosure-detailed-constructions.xml b/workflow/sample_files/base-enclosure-detailed-constructions.xml new file mode 100644 index 0000000000..0854d46766 --- /dev/null +++ b/workflow/sample_files/base-enclosure-detailed-constructions.xml @@ -0,0 +1,674 @@ + + + + HPXML + tasks.rb + 2000-01-01T00:00:00-07:00 + create + + + + + 60 + + + + Bills + + + + + + + + +
+ CO +
+
+ + proposed workscope + + + + + suburban + stand-alone + no units above or below + 180 + + electricity + natural gas + + + + single-family detached + 2.0 + 1.0 + 8.0 + 3 + 2 + 2700.0 + 21600.0 + + + + + 2006 + 5B + + + + USA_CO_Denver.Intl.AP.725650_TMY3 + + USA_CO_Denver.Intl.AP.725650_TMY3.epw + + + + + + + + 50.0 + + ACH + 3.0 + + 21600.0 + + + + + + + + false + + + false + + + + + + + + + + + true + + + + + + + + + + + attic - unvented + 1509.3 + 6.0 + + + + + 1.0 + asphalt shingles + 0.44 + + + + 0.75 + + 1.0 + osb + 0.067 + 32.0 + 0.29 + + + + + + + + + outside + basement - conditioned + 115.6 + + + + 0.375 + + 1.0 + vinyl siding + 0.052 + 11.1 + 0.25 + + + + 0.5 + + 1.0 + osb + 0.067 + 32.0 + 0.29 + + + + 5.5 + + 0.22 + wood stud + 0.067 + 32.0 + 0.29 + + + 0.78 + spray foam + 2.85 + 0.25 + 36.0 + + + + 0.5 + + 1.0 + gypsum board + 0.093 + 50.0 + 0.2 + + + + + + + + + outside + living space + + + + 1200.0 + + + + 0.375 + + 1.0 + vinyl siding + 0.052 + 11.1 + 0.25 + + + + 0.5 + + 1.0 + osb + 0.067 + 32.0 + 0.29 + + + + 5.5 + + 0.22 + wood stud + 0.067 + 32.0 + 0.29 + + + 0.78 + spray foam + 2.85 + 0.25 + 36.0 + + + + 0.5 + + 1.0 + gypsum board + 0.093 + 50.0 + 0.2 + + + + + + + outside + attic - unvented + gable + + + + 225.0 + + + + 0.375 + + 1.0 + vinyl siding + 0.052 + 11.1 + 0.25 + + + + 0.5 + + 1.0 + osb + 0.067 + 32.0 + 0.29 + + + + 0.5 + + 1.0 + gypsum board + 0.093 + 50.0 + 0.2 + + + + + + + + + ground + basement - conditioned + 8.0 + 1200.0 + 8.0 + 7.0 + + gypsum board + + + + + continuous - exterior + 8.9 + 0.0 + 8.0 + + + continuous - interior + 0.0 + + + + + + + + attic - unvented + living space + ceiling + + + + 1350.0 + + gypsum board + + + + 39.3 + + + + + + + basement - conditioned + 1350.0 + 4.0 + 150.0 + + + + 0.0 + 0.0 + + + + + + 0.0 + 0.0 + + + + 0.0 + 0.0 + + + + + + + 108.0 + 0 + 0.33 + 0.45 + + + 0.7 + 0.85 + + 0.67 + + + + + 72.0 + 90 + 0.33 + 0.45 + + + 0.7 + 0.85 + + 0.67 + + + + + 108.0 + 180 + 0.33 + 0.45 + + + 0.7 + 0.85 + + 0.67 + + + + + 72.0 + 270 + 0.33 + 0.45 + + + 0.7 + 0.85 + + 0.67 + + + + + + + + 40.0 + 180 + 4.4 + + + + + + + + + + + + + + + + + natural gas + 36000.0 + + AFUE + 0.92 + + 1.0 + + + + + central air conditioner + electricity + 24000.0 + single stage + 1.0 + + SEER + 13.0 + + 0.73 + + + + + 68.0 + 78.0 + + + + + + regular velocity + + supply + + CFM25 + 75.0 + to outside + + + + return + + CFM25 + 25.0 + to outside + + + + + supply + 4.0 + attic - unvented + 150.0 + + + + return + 0.0 + attic - unvented + 50.0 + + + + + + + + + electricity + storage water heater + living space + 40.0 + 1.0 + 18767.0 + 0.95 + 125.0 + + + + + + 50.0 + + + + 0.0 + + + + + shower head + true + + + + faucet + false + + + + + + + living space + 1.21 + 380.0 + 0.12 + 1.09 + 27.0 + 6.0 + 3.2 + + + + living space + electricity + 3.73 + true + 150.0 + + + + living space + 307.0 + 12 + 0.12 + 1.09 + 22.32 + 4.0 + + + + living space + 650.0 + true + + + + living space + electricity + false + + + + false + + + + + + interior + 0.4 + + + + + + + interior + 0.1 + + + + + + + interior + 0.25 + + + + + + + exterior + 0.4 + + + + + + + exterior + 0.1 + + + + + + + exterior + 0.25 + + + + + + + + + TV other + + kWh/year + 620.0 + + + + + other + + kWh/year + 2457.0 + + + 0.855 + 0.045 + + + + +
+
\ No newline at end of file diff --git a/workflow/tests/base_results/results.csv b/workflow/tests/base_results/results.csv index fecb226025..77d706966a 100644 --- a/workflow/tests/base_results/results.csv +++ b/workflow/tests/base_results/results.csv @@ -124,6 +124,7 @@ base-enclosure-beds-2.xml,57.09,57.09,33.258,33.258,23.832,0.0,0.0,0.0,0.0,0.0,0 base-enclosure-beds-4.xml,60.372,60.372,38.533,38.533,21.839,0.0,0.0,0.0,0.0,0.0,0.0,0.36,0.0,0.0,4.432,0.862,10.889,0.0,0.0,4.51,0.0,0.334,0.0,0.0,0.0,0.0,2.22,0.0,0.0,0.376,0.421,1.744,1.661,0.0,2.35,8.374,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,21.839,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,20.452,0.0,14.56,11.089,0.614,0.0,0.0,0.0,0.0,2192.9,3501.7,22.758,18.225,0.0,3.555,3.64,0.512,7.518,0.629,10.513,-12.551,0.0,0.0,0.0,8.286,-0.06,4.805,0.0,0.729,0.0,4.742,-9.713,-2.498,0.0,-0.061,-0.469,-0.053,2.662,-0.028,-1.969,11.732,0.0,0.0,0.0,-6.384,-0.056,-1.182,-3.147,-0.167,0.0,3.185,8.666,2.012,1562.4,1177.9,13955.4,2960.3,36000.0,24000.0,0.0,6.8,91.76,31792.0,8583.0,7508.0,0.0,575.0,6409.0,0.0,0.0,1949.0,2171.0,4597.0,19030.0,5341.0,7037.0,0.0,207.0,265.0,0.0,0.0,0.0,2010.0,619.0,3550.0,160.0,0.0,-840.0,1000.0 base-enclosure-beds-5.xml,61.997,61.997,41.137,41.137,20.859,0.0,0.0,0.0,0.0,0.0,0.0,0.344,0.0,0.0,4.596,0.902,12.591,0.0,0.0,4.51,0.0,0.334,0.0,0.0,0.0,0.0,2.22,0.0,0.0,0.434,0.477,1.976,1.794,0.0,2.585,8.374,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,20.859,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,19.534,0.0,15.156,12.918,0.613,0.0,0.0,0.0,0.0,2561.6,3797.8,22.461,18.551,0.0,3.566,3.644,0.513,7.535,0.63,10.529,-12.537,0.0,0.0,0.0,8.301,-0.063,4.808,0.0,0.731,0.0,4.545,-10.52,-2.496,0.0,-0.08,-0.484,-0.055,2.615,-0.032,-2.014,11.746,0.0,0.0,0.0,-6.454,-0.059,-1.197,-3.228,-0.169,0.0,3.274,9.46,2.013,1770.0,1358.2,16511.3,3258.4,36000.0,24000.0,0.0,6.8,91.76,31792.0,8583.0,7508.0,0.0,575.0,6409.0,0.0,0.0,1949.0,2171.0,4597.0,19257.0,5338.0,7037.0,0.0,207.0,265.0,0.0,0.0,0.0,2010.0,619.0,3780.0,360.0,0.0,-840.0,1200.0 base-enclosure-ceilingtypes.xml,74.877,74.877,36.44,36.44,38.437,0.0,0.0,0.0,0.0,0.0,0.0,0.634,0.0,0.0,4.485,0.877,9.168,0.0,0.0,4.51,0.0,0.334,0.0,0.0,0.0,0.0,2.22,0.0,0.0,0.319,0.365,1.513,1.528,0.0,2.114,8.374,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,38.437,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,35.991,0.0,14.849,9.233,0.618,0.0,0.0,0.0,0.0,2163.1,3369.0,29.367,18.642,0.0,17.257,3.59,0.505,7.246,0.62,10.388,-12.681,0.0,0.0,0.0,7.733,-0.076,4.851,0.0,0.734,0.0,7.068,-9.079,-2.545,0.0,0.155,-0.318,-0.031,2.91,0.01,-1.499,11.603,0.0,0.0,0.0,-6.072,-0.066,-1.008,-2.883,-0.139,0.0,2.725,7.703,1.964,1354.8,997.6,11399.5,2615.8,36000.0,24000.0,0.0,6.8,91.76,44054.0,8851.0,7508.0,0.0,575.0,6409.0,0.0,0.0,1949.0,14165.0,4597.0,30006.0,5442.0,7037.0,0.0,207.0,265.0,0.0,0.0,0.0,13116.0,619.0,3320.0,0.0,0.0,0.0,0.0 +base-enclosure-detailed-constructions.xml,58.893,58.893,35.974,35.974,22.92,0.0,0.0,0.0,0.0,0.0,0.0,0.378,0.0,0.0,4.322,0.834,9.164,0.0,0.0,4.51,0.0,0.334,0.0,0.0,0.0,0.0,2.22,0.0,0.0,0.319,0.365,1.513,1.528,0.0,2.114,8.374,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,22.92,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,21.464,0.0,14.157,9.233,0.614,0.0,0.0,0.0,0.0,2132.0,3374.2,23.308,18.57,0.0,3.533,3.646,0.513,7.507,0.629,10.515,-12.551,0.0,0.0,0.0,8.288,-0.066,4.807,0.0,0.728,0.0,4.975,-8.907,-2.499,0.0,-0.02,-0.421,-0.045,2.709,-0.024,-1.915,11.732,0.0,0.0,0.0,-6.31,-0.061,-1.164,-3.055,-0.165,0.0,3.184,7.871,2.01,1354.8,997.6,11399.5,2615.8,36000.0,24000.0,0.0,6.8,91.76,31803.0,8583.0,7508.0,0.0,575.0,6420.0,0.0,0.0,1949.0,2171.0,4597.0,18911.0,5330.0,7037.0,0.0,207.0,388.0,0.0,0.0,0.0,2010.0,619.0,3320.0,0.0,0.0,0.0,0.0 base-enclosure-floortypes.xml,64.413,64.413,29.406,29.406,35.008,0.0,0.0,0.0,0.0,0.0,0.0,0.578,0.0,0.0,3.665,0.666,9.366,0.0,0.0,2.647,0.0,0.238,0.0,0.0,0.0,0.0,2.22,0.0,0.0,0.319,0.365,1.513,1.528,0.0,2.114,4.187,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,35.008,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,32.788,0.0,11.166,9.342,0.619,0.0,0.0,0.0,0.0,1757.2,3480.6,27.934,20.789,0.0,3.519,3.647,0.0,0.0,0.654,9.893,-12.804,0.0,0.0,26.42,0.0,-0.18,2.047,0.0,0.786,0.0,7.347,-7.418,-1.565,0.0,0.393,-0.069,0.0,0.0,0.096,0.25,11.037,0.0,0.0,-7.529,0.0,-0.176,-0.275,-1.935,-0.093,0.0,2.755,5.786,1.082,1354.8,997.6,11399.5,2808.9,36000.0,24000.0,0.0,6.8,91.76,37636.0,8721.0,7508.0,0.0,575.0,2198.0,0.0,14165.0,0.0,2171.0,2299.0,19985.0,5355.0,7037.0,0.0,207.0,232.0,0.0,1515.0,0.0,2010.0,310.0,3320.0,380.0,0.0,-420.0,800.0 base-enclosure-garage.xml,59.052,59.052,34.589,34.589,24.463,0.0,0.0,0.0,0.0,0.0,0.0,0.404,0.0,0.0,2.98,0.521,9.266,0.0,0.0,4.51,0.142,0.334,0.0,0.0,0.0,0.0,2.22,0.0,0.0,0.319,0.365,1.513,1.528,0.0,2.114,8.374,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,24.463,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,22.904,0.0,8.709,9.233,0.724,0.0,0.0,0.0,0.0,2122.9,2825.0,18.052,10.754,0.0,3.524,3.783,0.502,5.84,0.613,8.595,-6.603,0.0,0.0,0.0,6.558,-0.041,5.37,0.0,0.0,0.0,3.839,-6.765,-2.508,0.0,0.112,-0.273,-0.035,2.444,0.002,-1.643,8.266,0.0,0.0,0.0,-5.628,-0.038,-1.216,-2.073,0.0,0.0,1.265,5.681,2.001,1354.8,997.6,11399.5,2615.8,36000.0,24000.0,0.0,6.8,91.76,29361.0,8026.0,5506.0,0.0,575.0,6537.0,0.0,0.0,1949.0,2171.0,4597.0,15521.0,3263.0,5579.0,0.0,207.0,523.0,0.0,0.0,0.0,2010.0,619.0,3320.0,0.0,0.0,0.0,0.0 base-enclosure-infil-ach-house-pressure.xml,58.728,58.728,35.913,35.913,22.815,0.0,0.0,0.0,0.0,0.0,0.0,0.376,0.0,0.0,4.273,0.823,9.164,0.0,0.0,4.51,0.0,0.334,0.0,0.0,0.0,0.0,2.22,0.0,0.0,0.319,0.365,1.513,1.528,0.0,2.114,8.374,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,22.815,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,21.366,0.0,13.979,9.233,0.614,0.0,0.0,0.0,0.0,2115.3,3297.1,23.045,17.895,0.0,3.542,3.634,0.511,7.499,0.628,10.506,-12.551,0.0,0.0,0.0,8.272,-0.063,4.792,0.0,0.728,0.0,4.937,-8.907,-2.499,0.0,-0.043,-0.455,-0.051,2.706,-0.024,-1.917,11.732,0.0,0.0,0.0,-6.315,-0.059,-1.163,-3.064,-0.165,0.0,3.097,7.871,2.01,1354.8,997.6,11399.5,2615.8,36000.0,24000.0,0.0,6.8,91.76,31789.0,8583.0,7508.0,0.0,575.0,6409.0,0.0,0.0,1949.0,2171.0,4595.0,18786.0,5328.0,7037.0,0.0,207.0,265.0,0.0,0.0,0.0,2010.0,619.0,3320.0,0.0,0.0,0.0,0.0 diff --git a/workflow/tests/base_results/results_bills.csv b/workflow/tests/base_results/results_bills.csv index 2ea79df1b0..be2906f700 100644 --- a/workflow/tests/base_results/results_bills.csv +++ b/workflow/tests/base_results/results_bills.csv @@ -124,6 +124,7 @@ base-enclosure-beds-2.xml,1566.38,144.0,1107.5,0.0,1251.5,144.0,170.88,314.88,0. base-enclosure-beds-4.xml,1727.78,144.0,1283.19,0.0,1427.19,144.0,156.59,300.59,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 base-enclosure-beds-5.xml,1807.47,144.0,1369.91,0.0,1513.91,144.0,149.56,293.56,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 base-enclosure-ceilingtypes.xml,1777.08,144.0,1213.49,0.0,1357.49,144.0,275.59,419.59,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +base-enclosure-detailed-constructions.xml,1650.28,144.0,1197.95,0.0,1341.95,144.0,164.33,308.33,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 base-enclosure-floortypes.xml,1518.24,144.0,979.24,0.0,1123.24,144.0,251.0,395.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 base-enclosure-garage.xml,1615.24,144.0,1151.84,0.0,1295.84,144.0,175.4,319.4,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 base-enclosure-infil-ach-house-pressure.xml,1647.51,144.0,1195.93,0.0,1339.93,144.0,163.58,307.58,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 diff --git a/workflow/tests/hpxml_translator_test.rb b/workflow/tests/hpxml_translator_test.rb index 5dfed85e9c..64618bbf50 100644 --- a/workflow/tests/hpxml_translator_test.rb +++ b/workflow/tests/hpxml_translator_test.rb @@ -694,10 +694,12 @@ def _verify_outputs(rundir, hpxml_path, results, hpxml) assert_in_epsilon(hpxml_value, sql_value, 0.1) # Solar absorptance - hpxml_value = roof.solar_absorptance - query = "SELECT AVG(Value) FROM TabularDataWithStrings WHERE ReportName='EnvelopeSummary' AND ReportForString='Entire Facility' AND TableName='Opaque Exterior' AND (RowName='#{roof_id}' OR RowName LIKE '#{roof_id}:%') AND ColumnName='Reflectance'" - sql_value = 1.0 - sqlFile.execAndReturnFirstDouble(query).get - assert_in_epsilon(hpxml_value, sql_value, 0.01) + if not roof.solar_absorptance.nil? + hpxml_value = roof.solar_absorptance + query = "SELECT AVG(Value) FROM TabularDataWithStrings WHERE ReportName='EnvelopeSummary' AND ReportForString='Entire Facility' AND TableName='Opaque Exterior' AND (RowName='#{roof_id}' OR RowName LIKE '#{roof_id}:%') AND ColumnName='Reflectance'" + sql_value = 1.0 - sqlFile.execAndReturnFirstDouble(query).get + assert_in_epsilon(hpxml_value, sql_value, 0.01) + end # Tilt hpxml_value = UnitConversions.convert(Math.atan(roof.pitch / 12.0), 'rad', 'deg')