# ******************************************************************************* # OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. # See also https://openstudio.net/license # ******************************************************************************* module OsLib_QAQC # include any general notes about QAQC method here # common methods def map_surface_props(surface, check_elems, defaulted_const_type) # see of standards info to get standard construction type if set construction = surface.construction.get const_standards = construction.standardsInformation if const_standards.standardsConstructionType.is_initialized const_type = const_standards.standardsConstructionType.get if surface.surfaceType == "Wall" ext_surf_type = "ExteriorWall" elsif surface.surfaceType == "RoofCeiling" ext_surf_type = "ExteriorRoof" elsif surface.surfaceType == "Floor" ext_surf_type = "ExteriorFloor" else ext_surf_type = nil # should not hit this end else if surface.surfaceType == "Wall" ext_surf_type = 'ExteriorWall' const_type = 'SteelFramed' elsif surface.surfaceType == "RoofCeiling" ext_surf_type = 'ExteriorRoof' const_type = 'IEAD' elsif surface.surfaceType == "Floor" ext_surf_type = 'ExteriorFloor' const_type = 'Mass' end if !defaulted_const_type.include?(construction) check_elems << OpenStudio::Attribute.new('flag', "#{construction.name} is not associated with a standards construction type, checking based on #{const_type} for #{ext_surf_type}.") defaulted_const_type << construction end end return {ext_surf_type: ext_surf_type, const_type: const_type, construction: construction} end def map_sub_surfaces_props(sub_surface, check_elems, defaulted_const_type) construction = sub_surface.construction.get const_standards = construction.standardsInformation if const_standards.standardsConstructionType.is_initialized sub_const_type = const_standards.standardsConstructionType.get if sub_surface.subSurfaceType == "Door" || sub_surface.subSurfaceType == "OverheadDoor" ext_sub_surf_type = "ExteriorDoor" elsif sub_surface.subSurfaceType == "FixedWindow" || sub_surface.subSurfaceType == "OperableWindow" ext_sub_surf_type = "ExteriorWindow" elsif sub_surface.subSurfaceType == "Skylight" ext_sub_surf_type = "Skylight" else # todo - add message about constructions not being checked ext_sub_surf_type = sub_surface.surfaceType # address and test GlassDoor end else if sub_surface.subSurfaceType == "Door" ext_sub_surf_type = 'ExteriorDoor' sub_const_type = 'Swinging' elsif sub_surface.subSurfaceType == "OverheadDoor" ext_sub_surf_type = 'ExteriorDoor' sub_const_type = 'NonSwinging' elsif sub_surface.subSurfaceType == "FixedWindow" || sub_surface.subSurfaceType == "OperableWindow" ext_sub_surf_type = 'ExteriorWindow' sub_const_type = 'Metal framing (all other)' elsif sub_surface.subSurfaceType == "Skylight" ext_sub_surf_type = 'Skylight' sub_const_type = 'Glass with Curb' else check_elems << OpenStudio::Attribute.new('flag', "#{construction.name} is not associated with a standards construction type, this measure does not have default target mapping for #{sub_surface.surfaceType} sub-surface types.") end if !defaulted_const_type.include?(construction) check_elems << OpenStudio::Attribute.new('flag', "#{construction.name} is not associated with a standards construction type, checking based on #{sub_const_type} for #{ext_sub_surf_type}.") defaulted_const_type << construction end end return {ext_sub_surf_type: ext_sub_surf_type, sub_const_type: sub_const_type, construction: construction} end # checks the number of unmet hours in the model # todo - do I need unique tolerance ranges for conductance, reflectance, and shgc def check_envelope_conductance(category, target_standard, min_pass, max_pass, name_only = false) # summary of the check check_elems = OpenStudio::AttributeVector.new check_elems << OpenStudio::Attribute.new('name', 'Envelope R-Value') check_elems << OpenStudio::Attribute.new('category', category) if target_standard == 'ICC IECC 2015' dislay_standard = target_standard check_elems << OpenStudio::Attribute.new('description', "Check envelope against Table R402.1.2 and R402.1.4 in #{dislay_standard} Residential Provisions.") elsif target_standard.include?('90.1-2013') display_standard = "ASHRAE #{target_standard}" check_elems << OpenStudio::Attribute.new('description', "Check envelope against #{display_standard} Table 5.5.2, Table G2.1.5 b,c,d,e, Section 5.5.3.1.1a. Roof reflectance of 55%, wall relfectance of 30%.") else # TODO: - could add more elsifs if want to dsiplay tables and sections for additional 90.1 standards if target_standard.include?('90.1') display_standard = "ASHRAE #{target_standard}" else display_standard = target_standard end check_elems << OpenStudio::Attribute.new('description', "Check envelope against #{display_standard}. Roof reflectance of 55%, wall relfectance of 30%.") end check_elems << OpenStudio::Attribute.new('min_pass', min_pass * 100) check_elems << OpenStudio::Attribute.new('max_pass', max_pass * 100) # stop here if only name is requested this is used to populate display name for arguments if name_only == true results = [] check_elems.each do |elem| next if ['Double','Integer'].include? (elem.valueType.valueDescription) results << elem.valueAsString end return results end begin # setup standard std = Standard.build(target_standard) # gather building type for summary if Gem::Version.new(OpenstudioStandards::VERSION) > Gem::Version.new('0.2.16') bt_cz = std.model_get_building_properties(@model) else bt_cz = std.model_get_building_climate_zone_and_building_type(@model) end building_type = bt_cz['building_type'] climate_zone = bt_cz['climate_zone'] prototype_prefix = "#{target_standard} #{building_type} #{climate_zone}" # if building type, or climate zone are empty don't run this section if [building_type, climate_zone].include?("") check_elems << OpenStudio::Attribute.new('flag', "Can't calculate target Envelope performance. Make sure model has climate zone and building type defined.") check_elem = OpenStudio::Attribute.new('check', check_elems) return check_elem end # make array of construction details for surfaces surface_details = [] missing_surface_constructions = [] sub_surface_details = [] missing_sub_surface_constructions = [] construction_type_array = [] space_type_const_properties = {} defaulted_const_type = [] data_not_returned_for = [] # loop through all space types used in the model @model.getSpaceTypes.each do |space_type| next if space_type.floorArea <= 0 # loop through spaces space_type.spaces.each do |space| space.surfaces.each do |surface| next if surface.outsideBoundaryCondition != 'Outdoors' if surface.construction.is_initialized surf_props = self.map_surface_props(surface,check_elems,defaulted_const_type) ext_surf_type = surf_props[:ext_surf_type] const_type = surf_props[:const_type] construction = surf_props[:construction] # todo - need to get and add the building_category for this space/space type and add to surface_details. If can't identify then issue warning and assume it is nonresidential data = std.space_type_get_construction_properties(space_type, ext_surf_type, const_type) if !data.nil? const_bldg_cat = data['building_category'] surface_details << { boundary_condition: surface.outsideBoundaryCondition, surface_type: ext_surf_type, construction: construction, construction_type: const_type, const_bldg_cat: const_bldg_cat } if !construction_type_array.include? [ext_surf_type,const_type,const_bldg_cat] construction_type_array << [ext_surf_type,const_type,const_bldg_cat] end else if !data_not_returned_for.include?([space_type,ext_surf_type,const_type]) check_elems << OpenStudio::Attribute.new('flag', "Data not returned for #{space_type.name} on #{const_type} for #{ext_surf_type}.") data_not_returned_for << [space_type,ext_surf_type,const_type] end end else missing_constructions << surface.name.get end # make array of construction details for sub_surfaces surface.subSurfaces.each do |sub_surface| if sub_surface.construction.is_initialized sub_surf_props = self.map_sub_surfaces_props(sub_surface,check_elems,defaulted_const_type) ext_sub_surf_type = sub_surf_props[:ext_sub_surf_type] sub_const_type = sub_surf_props[:sub_const_type] construction = sub_surf_props[:construction] data = std.space_type_get_construction_properties(space_type, ext_sub_surf_type, sub_const_type) if !data.nil? const_bldg_cat = data['building_category'] sub_surface_details << {boundary_condition: sub_surface.outsideBoundaryCondition, surface_type: ext_sub_surf_type, construction: sub_surface.construction.get, construction_type: sub_const_type, const_bldg_cat: const_bldg_cat} if !construction_type_array.include? [ext_sub_surf_type,sub_const_type,const_bldg_cat] construction_type_array << [ext_sub_surf_type,sub_const_type,const_bldg_cat] end else if !data_not_returned_for.include?([space_type,ext_sub_surf_type,sub_const_type]) check_elems << OpenStudio::Attribute.new('flag', "Data not returned for #{space_type.name} on #{sub_const_type} for #{ext_sub_surf_type}.") data_not_returned_for << [space_type,ext_sub_surf_type,sub_const_type] end end else missing_constructions << sub_surface.name.get end end end end if !missing_surface_constructions.empty? check_elems << OpenStudio::Attribute.new('flag', "#{missing_constructions.size} surfaces are missing constructions in #{space_type.name}.") end if !missing_sub_surface_constructions.empty? check_elems << OpenStudio::Attribute.new('flag', "#{missing_constructions.size} sub surfaces are missing constructions in #{space_type.name}.") end construction_type_array.each do |const_attributes| # gather data for exterior wall intended_surface_type = const_attributes[0] standards_construction_type = const_attributes[1] if !space_type_const_properties.key?(intended_surface_type) space_type_const_properties[intended_surface_type] = {} end if !space_type_const_properties[intended_surface_type].key?(standards_construction_type) space_type_const_properties[intended_surface_type][standards_construction_type] = {} end data = std.space_type_get_construction_properties(space_type, intended_surface_type, standards_construction_type) if data.nil? check_elems << OpenStudio::Attribute.new('flag', "Didn't find target construction values for #{target_standard} #{standards_construction_type} #{intended_surface_type} for #{space_type.name}.") elsif ['ExteriorWall','ExteriorDoor'].include? intended_surface_type const_bldg_cat = data['building_category'] if !space_type_const_properties[intended_surface_type][standards_construction_type].key?(const_bldg_cat) space_type_const_properties[intended_surface_type][standards_construction_type][const_bldg_cat] = {} end space_type_const_properties[intended_surface_type][standards_construction_type][const_bldg_cat]['u_value'] = data['assembly_maximum_u_value'] space_type_const_properties[intended_surface_type][standards_construction_type][const_bldg_cat]['reflectance'] = 0.30 # hard coded value elsif intended_surface_type.include? 'ExteriorFloor' const_bldg_cat = data['building_category'] if !space_type_const_properties[intended_surface_type][standards_construction_type].key?(const_bldg_cat) space_type_const_properties[intended_surface_type][standards_construction_type][const_bldg_cat] = {} end space_type_const_properties[intended_surface_type][standards_construction_type][const_bldg_cat]['u_value'] = data['assembly_maximum_u_value'] elsif intended_surface_type.include? 'ExteriorRoof' const_bldg_cat = data['building_category'] if !space_type_const_properties[intended_surface_type][standards_construction_type].key?(const_bldg_cat) space_type_const_properties[intended_surface_type][standards_construction_type][const_bldg_cat] = {} end space_type_const_properties[intended_surface_type][standards_construction_type][const_bldg_cat]['u_value'] = data['assembly_maximum_u_value'] space_type_const_properties[intended_surface_type][standards_construction_type][const_bldg_cat]['reflectance'] = 0.55 # hard coded value else # glazing const_bldg_cat = data['building_category'] if !space_type_const_properties[intended_surface_type][standards_construction_type].key?(const_bldg_cat) space_type_const_properties[intended_surface_type][standards_construction_type][const_bldg_cat] = {} end space_type_const_properties[intended_surface_type][standards_construction_type][const_bldg_cat]['u_value'] = data['assembly_maximum_u_value'] space_type_const_properties[intended_surface_type][standards_construction_type][const_bldg_cat]['shgc'] = data['assembly_maximum_solar_heat_gain_coefficient'] end end end # loop through unique construction arary combinations surface_details.uniq.each do |surface_detail| if surface_detail[:construction].thermalConductance.is_initialized # don't use intened surface type of construction, look map based on surface type and boundary condition boundary_condition = surface_detail[:boundary_condition] intended_surface_type = surface_detail[:surface_type] construction_type = surface_detail[:construction_type] next if boundary_condition.to_s != 'Outdoors' film_coefficients_r_value = std.film_coefficients_r_value(intended_surface_type, includes_int_film = true, includes_ext_film = true) thermal_conductance = surface_detail[:construction].thermalConductance.get r_value_with_film = 1 / thermal_conductance + film_coefficients_r_value source_units = 'm^2*K/W' target_units = 'ft^2*h*R/Btu' r_value_ip = OpenStudio.convert(r_value_with_film, source_units, target_units).get solar_reflectance = surface_detail[:construction].to_LayeredConstruction.get.layers[0].to_OpaqueMaterial.get.solarReflectance .get # TODO: - check optional first does what happens with ext. air wall const_bldg_cat = surface_detail[:const_bldg_cat] # lookup target_r_value_ip target_r_value_ip = 1.0 / space_type_const_properties[intended_surface_type][construction_type][const_bldg_cat]['u_value'].to_f # stop if didn't find values (0 or infinity) next if construction_type == 0.0 next if construction_type == Float::INFINITY # check r avlues if r_value_ip < target_r_value_ip * (1.0 - min_pass) check_elems << OpenStudio::Attribute.new('flag', "R value of #{r_value_ip.round(2)} (#{target_units}) for #{surface_detail[:construction].name} in #{const_bldg_cat} space type is more than #{min_pass * 100} % below the value of #{target_r_value_ip.round(2)} (#{target_units}) for #{prototype_prefix} #{surface_detail[:construction_type]}.") elsif r_value_ip > target_r_value_ip * (1.0 + max_pass) check_elems << OpenStudio::Attribute.new('flag', "R value of #{r_value_ip.round(2)} (#{target_units}) for #{surface_detail[:construction].name} in #{const_bldg_cat} space type is more than #{max_pass * 100} % above the value of #{target_r_value_ip.round(2)} (#{target_units}) for #{prototype_prefix} #{surface_detail[:construction_type]}.") end # lookup target_reflectance target_reflectance = space_type_const_properties[intended_surface_type][construction_type][const_bldg_cat]['reflectance'].to_f # check solar reflectance next if intended_surface_type == 'ExteriorFloor' # do not check reflectance exterior floors (overhang) if (solar_reflectance < target_reflectance * (1.0 - min_pass)) && (target_standard != 'ICC IECC 2015') check_elems << OpenStudio::Attribute.new('flag', "Solar Reflectance of #{(solar_reflectance * 100).round} % for #{surface_detail[:construction].name} in #{const_bldg_cat} space type is more than #{min_pass * 100} % below the value of #{(target_reflectance * 100).round} %.") elsif (solar_reflectance > target_reflectance * (1.0 + max_pass)) && (target_standard != 'ICC IECC 2015') check_elems << OpenStudio::Attribute.new('flag', "Solar Reflectance of #{(solar_reflectance * 100).round} % for #{surface_detail[:construction].name} in #{const_bldg_cat} space type is more than #{max_pass * 100} % above the value of #{(target_reflectance * 100).round} %.") end else check_elems << OpenStudio::Attribute.new('flag', "Can't calculate R value for #{surface_detail[:construction].name}.") end end # loop through unique construction arary combinations sub_surface_details.uniq.each do |sub_surface_detail| if sub_surface_detail[:surface_type] == 'ExteriorWindow' || sub_surface_detail[:surface_type] == 'Skylight' # check for non opaque sub surfaces source_units = 'W/m^2*K' target_units = 'Btu/ft^2*h*R' u_factor_si = std.construction_calculated_u_factor(sub_surface_detail[:construction].to_LayeredConstruction.get.to_Construction.get) u_factor_ip = OpenStudio.convert(u_factor_si, source_units, target_units).get shgc = std.construction_calculated_solar_heat_gain_coefficient(sub_surface_detail[:construction].to_LayeredConstruction.get.to_Construction.get) intended_surface_type = sub_surface_detail[:surface_type] construction_type = sub_surface_detail[:construction_type] const_bldg_cat = sub_surface_detail[:const_bldg_cat] boundary_condition = sub_surface_detail[:boundary_condition] next if boundary_condition.to_s != 'Outdoors' # lookup target_u_value_ip target_u_value_ip = space_type_const_properties[intended_surface_type][construction_type][const_bldg_cat]['u_value'].to_f # stop if didn't find values (0 or infinity) next if target_u_value_ip == 0.0 next if target_u_value_ip == Float::INFINITY # check u avlues if u_factor_ip < target_u_value_ip * (1.0 - min_pass) check_elems << OpenStudio::Attribute.new('flag', "U value of #{u_factor_ip.round(2)} (#{target_units}) for #{sub_surface_detail[:construction].name} in #{const_bldg_cat} space type is more than #{min_pass * 100} % below the value of #{target_u_value_ip.round(2)} (#{target_units}) for #{prototype_prefix} #{sub_surface_detail[:construction_type]}.") elsif u_factor_ip > target_u_value_ip * (1.0 + max_pass) check_elems << OpenStudio::Attribute.new('flag', "U value of #{u_factor_ip.round(2)} (#{target_units}) for #{sub_surface_detail[:construction].name} in #{const_bldg_cat} space type is more than #{max_pass * 100} % above the value of #{target_u_value_ip.round(2)} (#{target_units}) for #{prototype_prefix} #{sub_surface_detail[:construction_type]}.") end # lookup target_shgc target_shgc = space_type_const_properties[intended_surface_type][construction_type][const_bldg_cat]['shgc'].to_f # check shgc if shgc < target_shgc * (1.0 - min_pass) check_elems << OpenStudio::Attribute.new('flag', "SHGC of #{shgc.round(2)} % for #{sub_surface_detail[:construction].name} in #{const_bldg_cat} space type is more than #{min_pass * 100} % below the value of #{target_shgc.round(2)} %.") elsif shgc > target_shgc * (1.0 + max_pass) check_elems << OpenStudio::Attribute.new('flag', "SHGC of #{shgc.round(2)} % for #{sub_surface_detail[:construction].name} in #{const_bldg_cat} space type is more than #{max_pass * 100} % above the value of #{target_shgc.round(2)} %.") end else # check for opaque sub surfaces if sub_surface_detail[:construction].thermalConductance.is_initialized # don't use intened surface type of construction, look map based on surface type and boundary condition boundary_condition = sub_surface_detail[:boundary_condition] intended_surface_type = sub_surface_detail[:surface_type] construction_type = sub_surface_detail[:construction_type] next if boundary_condition.to_s != 'Outdoors' film_coefficients_r_value = std.film_coefficients_r_value(intended_surface_type, includes_int_film = true, includes_ext_film = true) thermal_conductance = sub_surface_detail[:construction].thermalConductance.get r_value_with_film = 1 / thermal_conductance + film_coefficients_r_value source_units = 'm^2*K/W' target_units = 'ft^2*h*R/Btu' r_value_ip = OpenStudio.convert(r_value_with_film, source_units, target_units).get solar_reflectance = sub_surface_detail[:construction].to_LayeredConstruction.get.layers[0].to_OpaqueMaterial.get.solarReflectance .get # TODO: - check optional first does what happens with ext. air wall const_bldg_cat = sub_surface_detail[:const_bldg_cat] # lookup target_r_value_ip target_r_value_ip = 1.0 / space_type_const_properties[intended_surface_type][construction_type][const_bldg_cat]['u_value'].to_f # stop if didn't find values (0 or infinity) next if target_r_value_ip == 0.0 next if target_r_value_ip == Float::INFINITY # check r avlues if r_value_ip < target_r_value_ip * (1.0 - min_pass) check_elems << OpenStudio::Attribute.new('flag', "R value of #{r_value_ip.round(2)} (#{target_units}) for #{sub_surface_detail[:construction].name} in #{const_bldg_cat} space type is more than #{min_pass * 100} % below the value of #{target_r_value_ip.round(2)} % (#{target_units}) for #{prototype_prefix} #{sub_surface_detail[:construction_type]} #{sub_surface_detail[:surface_type]}.") elsif r_value_ip > target_r_value_ip * (1.0 + max_pass) check_elems << OpenStudio::Attribute.new('flag', "R value of #{r_value_ip.round(2)} (#{target_units}) for #{sub_surface_detail[:construction].name} in #{const_bldg_cat} space type is more than #{max_pass * 100} % above the value of #{target_r_value_ip.round(2)} % (#{target_units}) for #{prototype_prefix} #{sub_surface_detail[:construction_type]} #{sub_surface_detail[:surface_type]}.") end # lookup target_reflectance target_reflectance = space_type_const_properties[intended_surface_type][construction_type][const_bldg_cat]['reflectance'].to_f # check solar reflectance if (solar_reflectance < target_reflectance* (1.0 - min_pass)) && (target_standard != 'ICC IECC 2015') check_elems << OpenStudio::Attribute.new('flag', "Solar Reflectance of #{(solar_reflectance * 100).round} % for #{sub_surface_detail[:construction].name} in #{const_bldg_cat} space type is more than #{min_pass * 100} % below the value of #{(target_reflectance * 100).round} % for #{prototype_prefix} #{sub_surface_detail[:construction_type]} #{sub_surface_detail[:surface_type]}.") elsif (solar_reflectance > target_reflectance * (1.0 + max_pass)) && (target_standard != 'ICC IECC 2015') check_elems << OpenStudio::Attribute.new('flag', "Solar Reflectance of #{(solar_reflectance * 100).round} % for #{sub_surface_detail[:construction].name} in #{const_bldg_cat} space type is more than #{max_pass * 100} % above the value of #{(target_reflectance * 100).round} % for #{prototype_prefix} #{sub_surface_detail[:construction_type]} #{sub_surface_detail[:surface_type]}.") end else check_elems << OpenStudio::Attribute.new('flag', "Can't calculate R value for #{sub_surface_detail[:construction].name}.") end end end # check spaces without space types against Nonresidential for this climate zone @model.getSpaces.each do |space| if !space.spaceType.is_initialized # make array of construction details for surfaces surface_details = [] missing_surface_constructions = [] sub_surface_details = [] missing_sub_surface_constructions = [] const_bldg_cat = 'Nonresidential' check_elems << OpenStudio::Attribute.new('flag', "Treating surfaces and sub-surfaces in space #{space.name} as Nonresidential since no space type is assigned.") space.surfaces.each do |surface| next if surface.outsideBoundaryCondition != 'Outdoors' if surface.construction.is_initialized surf_props = self.map_surface_props(surface,check_elems,defaulted_const_type) ext_surf_type = surf_props[:ext_surf_type] const_type = surf_props[:const_type] construction = surf_props[:construction] surface_details << { boundary_condition: surface.outsideBoundaryCondition, surface_type: ext_surf_type, construction: surface.construction.get,construction_type: const_type, const_bldg_cat: const_bldg_cat} else missing_constructions << surface.name.get end # make array of construction details for sub_surfaces surface.subSurfaces.each do |sub_surface| if sub_surface.construction.is_initialized sub_surf_props = self.map_sub_surfaces_props(sub_surface,check_elems,defaulted_const_type) ext_sub_surf_type = sub_surf_props[:ext_sub_surf_type] sub_const_type = sub_surf_props[:sub_const_type] construction = sub_surf_props[:construction] sub_surface_details << {boundary_condition: sub_surface.outsideBoundaryCondition, surface_type: ext_sub_surf_type, construction: sub_surface.construction.get, construction_type: sub_const_type, const_bldg_cat: const_bldg_cat} else missing_constructions << sub_surface.name.get end end end if !missing_surface_constructions.empty? check_elems << OpenStudio::Attribute.new('flag', "#{missing_constructions.size} surfaces are missing constructions in #{space_type.name}. Spaces and can't be checked.") end if !missing_sub_surface_constructions.empty? check_elems << OpenStudio::Attribute.new('flag', "#{missing_constructions.size} sub surfaces are missing constructions in #{space_type.name}. Spaces and can't be checked.") end surface_details.uniq.each do |surface_detail| if surface_detail[:construction].thermalConductance.is_initialized # don't use intened surface type of construction, look map based on surface type and boundary condition boundary_condition = surface_detail[:boundary_condition] intended_surface_type = surface_detail[:surface_type] construction_type = surface_detail[:construction_type] film_coefficients_r_value = std.film_coefficients_r_value(intended_surface_type, includes_int_film = true, includes_ext_film = true) thermal_conductance = surface_detail[:construction].thermalConductance.get r_value_with_film = 1 / thermal_conductance + film_coefficients_r_value source_units = 'm^2*K/W' target_units = 'ft^2*h*R/Btu' r_value_ip = OpenStudio.convert(r_value_with_film, source_units, target_units).get solar_reflectance = surface_detail[:construction].to_LayeredConstruction.get.layers[0].to_OpaqueMaterial.get.solarReflectance .get # TODO: - check optional first does what happens with ext. air wall # calculate target_r_value_ip target_reflectance = nil data = std.model_get_construction_properties(@model, intended_surface_type, construction_type, const_bldg_cat) if data.nil? check_elems << OpenStudio::Attribute.new('flag', "Didn't find construction for #{construction_type} #{intended_surface_type} for #{space.name}.") next elsif ['ExteriorWall','ExteriorDoor'].include? intended_surface_type assembly_maximum_u_value = data['assembly_maximum_u_value'] target_reflectance = 0.30 elsif intended_surface_type.include? 'ExteriorFloor' assembly_maximum_u_value = data['assembly_maximum_u_value'] elsif intended_surface_type.include? 'ExteriorRoof' assembly_maximum_u_value = data['assembly_maximum_u_value'] target_reflectance = 0.55 else assembly_maximum_u_value = data['assembly_maximum_u_value'] assembly_maximum_solar_heat_gain_coefficient = data['assembly_maximum_solar_heat_gain_coefficient'] end assembly_maximum_r_value_ip = 1 / assembly_maximum_u_value # stop if didn't find values (0 or infinity) next if assembly_maximum_r_value_ip == 0.0 next if assembly_maximum_r_value_ip == Float::INFINITY # check r avlues if r_value_ip < assembly_maximum_r_value_ip * (1.0 - min_pass) check_elems << OpenStudio::Attribute.new('flag', "R value of #{r_value_ip.round(2)} (#{target_units}) for #{surface_detail[:construction].name} in #{space.name} is more than #{min_pass * 100} % below the value of #{assembly_maximum_r_value_ip.round(2)} (#{target_units}) for #{prototype_prefix} #{surface_detail[:construction_type]} #{surface_detail[:const_bldg_cat]}.") elsif r_value_ip > assembly_maximum_r_value_ip * (1.0 + max_pass) check_elems << OpenStudio::Attribute.new('flag', "R value of #{r_value_ip.round(2)} (#{target_units}) for #{sub_surface_detail[:construction].name} in #{space_type.name} is more than #{max_pass * 100} % above the value of #{target_r_value_ip.round(2)} (#{target_units}) for #{prototype_prefix} #{surface_detail[:construction_type]} #{surface_detail[:const_bldg_cat]}") elsif r_value_ip > assembly_maximum_r_value_ip * (1.0 + max_pass) check_elems << OpenStudio::Attribute.new('flag', "R value of #{r_value_ip.round(2)} (#{target_units}) for #{surface_detail[:construction].name} in #{space.name} is more than #{max_pass * 100} % above the value of #{assembly_maximum_r_value_ip.round(2)} (#{target_units}) for #{prototype_prefix} #{surface_detail[:construction_type]} #{surface_detail[:building_type_category]}.") end # check solar reflectance next if intended_surface_type == 'ExteriorFloor' # do not check reflectance exterior floors (overhang) if (solar_reflectance < target_reflectance * (1.0 - min_pass)) && (target_standard != 'ICC IECC 2015') check_elems << OpenStudio::Attribute.new('flag', "Solar Reflectance of #{(solar_reflectance * 100).round} % for #{surface_detail[:construction].name} in #{space.name} is more than #{min_pass * 100} % below the value of #{(target_reflectance * 100).round} %.") elsif (solar_reflectance > target_reflectance * (1.0 + max_pass)) && (target_standard != 'ICC IECC 2015') check_elems << OpenStudio::Attribute.new('flag', "Solar Reflectance of #{(solar_reflectance * 100).round} % for #{surface_detail[:construction].name} in #{space.name} is more than #{max_pass * 100} % above the value of #{(target_reflectance * 100).round} %.") end else check_elems << OpenStudio::Attribute.new('flag', "Can't calculate R value for #{surface_detail[:construction].name}.") end end sub_surface_details.uniq.each do |sub_surface_detail| # TODO: update this so it works for doors and windows check_elems << OpenStudio::Attribute.new('flag', "Not setup to check sub-surfaces of spaces without space types. Can't check properties for #{sub_surface_detail[:construction].name}.") end end end rescue StandardError => e # brief description of ruby error check_elems << OpenStudio::Attribute.new('flag', "Error prevented QAQC check from running (#{e}).") # backtrace of ruby error for diagnostic use if @error_backtrace then check_elems << OpenStudio::Attribute.new('flag', e.backtrace.join("\n").to_s) end end # add check_elms to new attribute check_elem = OpenStudio::Attribute.new('check', check_elems) return check_elem # note: registerWarning and registerValue will be added for checks downstream using os_lib_reporting_qaqc.rb end end