lib/openstudio-standards/constructions/modify.rb in openstudio-standards-0.5.0 vs lib/openstudio-standards/constructions/modify.rb in openstudio-standards-0.6.0.rc1
- old
+ new
@@ -1,19 +1,20 @@
-# Methods to modify construction properties
module OpenstudioStandards
+ # The Constructions module provides methods create, modify, and get information about model Constructions
module Constructions
# @!group Modify
+ # Methods to modify Constructions
# add new material layer to a construction
#
# @param construction [OpenStudio::Model::Construction] OpenStudio Construction object
# @param layer_index [Integer] the layer index, default is 0
# @param name [String] name of the new material layer
# @param roughness [String] surface roughness of the new material layer.
# Options are 'VeryRough', 'Rough', 'MediumRough', 'MediumSmooth', 'Smooth', and 'VerySmooth'
# @param thickness [Double] thickness of the new material layer in meters
- # @param conductivity [Double] conductivity of new material layer in W/m*K
+ # @param conductivity [Double] thermal conductivity of new material layer in W/m*K
# @param density [Double] density of the new material layer in kg/m^3
# @param specific_heat [Double] specific heat of the new material layer in J/kg*K
# @param thermal_absorptance [Double] target thermal absorptance
# @param solar_absorptance [Double] target solar absorptance
# @param visible_absorptance [Double] target visible absorptance
@@ -52,10 +53,221 @@
construction.insertLayer(layer_index, new_material)
return new_material
end
+ # Find and set the insulation layer for a layered construction
+ #
+ # @param construction [OpenStudio::Model::Construction] OpenStudio Construction object
+ # @return [OpenStudio::Model::OpaqueMaterial] OpenStudio OpaqueMaterial representing the insulation layer
+ def self.construction_find_and_set_insulation_layer(construction)
+ # skip and return the insulation layer if already set
+ return construction.insulation.get if construction.insulation.is_initialized
+
+ # loop through construction layers to find insulation layer
+ min_conductance = 100.0
+ insulation_material = nil
+ construction.layers.each do |layer|
+ # skip layers that aren't an OpaqueMaterial
+ next unless layer.to_OpaqueMaterial.is_initialized
+
+ material = layer.to_OpaqueMaterial.get
+ material_conductance = OpenstudioStandards::Constructions::Materials.material_get_conductance(material)
+ if material_conductance < min_conductance
+ min_conductance = material_conductance
+ insulation_material = material
+ end
+ end
+ construction.setInsulation(insulation_material) unless insulation_material.nil?
+
+ if construction.isOpaque && !construction.insulation.is_initialized
+ OpenStudio.logFree(OpenStudio::Error, 'OpenstudioStandards::Constructions', "Unable to determine the insulation layer for construction #{construction.name.get}.")
+ return nil
+ end
+
+ return construction.insulation.get
+ end
+
+ # Sets the heat transfer coefficient (U-value) of a construction to a specified value by modifying the thickness of the insulation layer.
+ #
+ # @param construction [OpenStudio::Model::Construction] OpenStudio Construction object
+ # @param target_u_value_ip [Double] Target heat transfer coefficient (U-Value) (Btu/ft^2*hr*R)
+ # @param insulation_layer_name [String] The insulation layer in this construction. If none provided, the method will attempt to determine the insulation layer.
+ # @param intended_surface_type [String] Intended surface type, used for determining film coefficients.
+ # Valid choices: 'AtticFloor', 'AtticWall', 'AtticRoof', 'DemisingFloor', 'InteriorFloor', 'InteriorCeiling',
+ # 'DemisingWall', 'InteriorWall', 'InteriorPartition', 'InteriorWindow', 'InteriorDoor', 'DemisingRoof',
+ # 'ExteriorRoof', 'Skylight', 'TubularDaylightDome', 'TubularDaylightDiffuser', 'ExteriorFloor',
+ # 'ExteriorWall', 'ExteriorWindow', 'ExteriorDoor', 'GlassDoor', 'OverheadDoor', 'GroundContactFloor',
+ # 'GroundContactWall', 'GroundContactRoof'
+ # @param target_includes_interior_film_coefficients [Boolean] If true, subtracts off standard interior film coefficients
+ # from the target heat transfer coefficient before modifying insulation thickness.
+ # @param target_includes_exterior_film_coefficients [Boolean] If true, subtracts off standard exterior film coefficients
+ # from the target heat transfer coefficient before modifying insulation thickness.
+ # @return [Boolean] returns true if successful, false if not
+ def self.construction_set_u_value(construction, target_u_value_ip,
+ insulation_layer_name: nil,
+ intended_surface_type: 'ExteriorWall',
+ target_includes_interior_film_coefficients: true,
+ target_includes_exterior_film_coefficients: true)
+ # Skip layer-by-layer fenestration constructions
+ if construction.isFenestration
+ OpenStudio.logFree(OpenStudio::Warn, 'OpenstudioStandards::Constructions', "Can only set the u-value of opaque constructions or simple glazing. #{construction.name} is not opaque or simple glazing.")
+ return false
+ end
+
+ # Make sure an insulation layer was specified
+ if insulation_layer_name.nil? && target_u_value_ip == 0.0
+ # Do nothing if the construction already doesn't have an insulation layer
+ elsif insulation_layer_name.nil?
+ insulation_layer_name = OpenstudioStandards::Constructions.construction_find_and_set_insulation_layer(construction).name.get
+ end
+
+ # Remove the insulation layer if the specified U-value is zero.
+ if target_u_value_ip == 0.0
+ layer_index = 0
+ construction.layers.each do |layer|
+ break if layer.name.get == insulation_layer_name
+
+ layer_index += 1
+ end
+ construction.eraseLayer(layer_index)
+ return true
+ end
+
+ min_r_value_si = OpenstudioStandards::Constructions.film_coefficients_r_value(intended_surface_type, target_includes_interior_film_coefficients, target_includes_exterior_film_coefficients)
+ max_u_value_si = 1.0 / min_r_value_si
+ max_u_value_ip = OpenStudio.convert(max_u_value_si, 'W/m^2*K', 'Btu/ft^2*hr*R').get
+ if target_u_value_ip >= max_u_value_ip
+ target_u_value_ip = 1.0 / OpenStudio.convert(min_r_value_si + 0.001, 'm^2*K/W', 'ft^2*hr*R/Btu').get
+ OpenStudio.logFree(OpenStudio::Warn, 'OpenstudioStandards::Constructions', "Requested U-value of #{target_u_value_ip} for #{construction.name} is greater than the sum of the inside and outside resistance, and the max U-value (6.636 SI) is used instead.")
+ end
+
+ # Convert the target U-value to SI
+ target_r_value_ip = 1.0 / target_u_value_ip.to_f
+ target_u_value_si = OpenStudio.convert(target_u_value_ip, 'Btu/ft^2*hr*R', 'W/m^2*K').get
+ target_r_value_si = 1.0 / target_u_value_si
+
+ OpenStudio.logFree(OpenStudio::Debug, 'OpenstudioStandards::Constructions', "Setting U-Value for #{construction.name} to #{target_u_value_si.round(3)} W/m^2*K or #{target_u_value_ip.round(3)} 'Btu/ft^2*hr*R', which is an R-value of #{target_r_value_si.round(3)} m^2*K/W or #{target_r_value_ip.round(3)} 'ft^2*hr*R/Btu'.")
+
+ # Determine the R-value of the non-insulation layers
+ other_layer_r_value_si = 0.0
+ construction.layers.each do |layer|
+ next if layer.to_OpaqueMaterial.empty?
+ next if layer.name.get == insulation_layer_name
+
+ other_layer_r_value_si += layer.to_OpaqueMaterial.get.thermalResistance
+ end
+
+ # Determine the R-value of the air films, if requested
+ other_layer_r_value_si += OpenstudioStandards::Constructions.film_coefficients_r_value(intended_surface_type, target_includes_interior_film_coefficients, target_includes_exterior_film_coefficients)
+
+ # Determine the difference between the desired R-value
+ # and the R-value of the non-insulation layers and air films.
+ # This is the desired R-value of the insulation.
+ ins_r_value_si = target_r_value_si - other_layer_r_value_si
+
+ # Set the R-value of the insulation layer
+ construction.layers.each_with_index do |layer, l|
+ next unless layer.name.get == insulation_layer_name
+
+ # Remove insulation layer if requested R-value is lower than sum of non-insulation materials
+ if ins_r_value_si <= 0.0
+ OpenStudio.logFree(OpenStudio::Warn, 'OpenstudioStandards::Construction', "Requested U-value of #{target_u_value_ip} for #{construction.name} is too low given the other materials in the construction; insulation layer will be removed.")
+ construction.eraseLayer(l)
+ # Set the target R-value to the sum of other layers to make name match properties
+ target_r_value_ip = OpenStudio.convert(other_layer_r_value_si, 'm^2*K/W', 'ft^2*hr*R/Btu').get
+ break # Don't modify the insulation layer since it has been removed
+ end
+
+ # Modify the insulation layer
+ ins_r_value_ip = OpenStudio.convert(ins_r_value_si, 'm^2*K/W', 'ft^2*h*R/Btu').get
+ if layer.to_StandardOpaqueMaterial.is_initialized
+ layer = layer.to_StandardOpaqueMaterial.get
+ layer.setThickness(ins_r_value_si * layer.conductivity)
+ layer.setName("#{layer.name} R-#{ins_r_value_ip.round(2)}")
+ break # Stop looking for the insulation layer once found
+ elsif layer.to_MasslessOpaqueMaterial.is_initialized
+ layer = layer.to_MasslessOpaqueMaterial.get
+ layer.setThermalResistance(ins_r_value_si)
+ layer.setName("#{layer.name} R-#{ins_r_value_ip.round(2)}")
+ break # Stop looking for the insulation layer once found
+ elsif layer.to_AirGap.is_initialized
+ layer = layer.to_AirGap.get
+ target_thickness = ins_r_value_si * layer.thermalConductivity
+ layer.setThickness(target_thickness)
+ layer.setName("#{layer.name} R-#{ins_r_value_ip.round(2)}")
+ break # Stop looking for the insulation layer once found
+ end
+ end
+
+ # Modify the construction name
+ construction.setName("#{construction.name} R-#{target_r_value_ip.round(2)}")
+
+ return true
+ end
+
+ # Sets the U-value of a simple glazing construction to a specified value by modifying the SimpleGlazing material.
+ #
+ # @param construction [OpenStudio::Model::Construction] OpenStudio Construction object that contains SimpleGlazing
+ # @param target_u_value_ip [Double] Target heat transfer coefficient (U-Value) (Btu/ft^2*hr*R)
+ # @param target_includes_interior_film_coefficients [Boolean] If true, subtracts off standard interior film coefficients
+ # from the target heat transfer coefficient before modifying insulation thickness.
+ # @param target_includes_exterior_film_coefficients [Boolean] If true, subtracts off standard exterior film coefficients
+ # from the target heat transfer coefficient before modifying insulation thickness.
+ # @return [Boolean] returns true if successful, false if not
+ def self.construction_set_glazing_u_value(construction, target_u_value_ip,
+ target_includes_interior_film_coefficients: true,
+ target_includes_exterior_film_coefficients: true)
+ # Skip layer-by-layer fenestration constructions
+ unless OpenstudioStandards::Constructions.construction_simple_glazing?(construction)
+ OpenStudio.logFree(OpenStudio::Warn, 'OpenstudioStandards::Construction', "The construction_set_glazing_u_value method can only set the u-value of simple glazing. #{construction.name} does not containg simple glazing.")
+ return false
+ end
+
+ # Convert the target U-value to SI
+ target_r_value_ip = 1.0 / target_u_value_ip.to_f
+ target_u_value_si = OpenStudio.convert(target_u_value_ip, 'Btu/ft^2*hr*R', 'W/m^2*K').get
+ target_r_value_si = 1.0 / target_u_value_si
+
+ # Determine the R-value of the air films, if requested
+ # In EnergyPlus, the U-factor input of the WindowMaterial:SimpleGlazingSystem
+ # object includes the film coefficients (see IDD description, and I/O reference
+ # guide) so the target_includes_interior_film_coefficients and target_includes_exterior_film_coefficients
+ # variable values are changed to their opposite so if the target value includes a film
+ # the target value is unchanged
+ film_coeff_r_value_si = 0.0
+ film_coeff_r_value_si += OpenstudioStandards::Constructions.film_coefficients_r_value('ExteriorWindow', !target_includes_interior_film_coefficients, !target_includes_exterior_film_coefficients)
+ film_coeff_u_value_si = 1.0 / film_coeff_r_value_si
+ film_coeff_u_value_ip = OpenStudio.convert(film_coeff_u_value_si, 'W/m^2*K', 'Btu/ft^2*hr*R').get
+ film_coeff_r_value_ip = 1.0 / film_coeff_u_value_ip
+
+ # Determine the difference between the desired R-value
+ # and the R-value of the and air films.
+ # This is the desired R-value of the insulation.
+ ins_r_value_si = target_r_value_si - film_coeff_r_value_si
+ if ins_r_value_si <= 0.0
+ ins_r_value_si = 0.001
+ OpenStudio.logFree(OpenStudio::Warn, 'OpenstudioStandards::Construction', "Requested U-value of #{target_u_value_ip} Btu/ft^2*hr*R for #{construction.name} is too high given the film coefficients of U-#{film_coeff_u_value_ip.round(2)} Btu/ft^2*hr*R.")
+ end
+ ins_u_value_si = 1.0 / ins_r_value_si
+
+ if ins_u_value_si > 7.0
+ OpenStudio.logFree(OpenStudio::Warn, 'OpenstudioStandards::Construction', "Requested U-value of #{target_u_value_ip} for #{construction.name} is too high given the film coefficients of U-#{film_coeff_u_value_ip.round(2)}; setting U-value to EnergyPlus limit of 7.0 W/m^2*K (1.23 Btu/ft^2*hr*R).")
+ ins_u_value_si = 7.0
+ end
+ ins_u_value_ip = OpenStudio.convert(ins_u_value_si, 'W/m^2*K', 'Btu/ft^2*hr*R').get
+
+ # Set the U-value of the insulation layer
+ glazing = construction.layers.first.to_SimpleGlazing.get
+ starting_u_value_si = glazing.uFactor.round(2)
+ staring_u_value_ip = OpenStudio.convert(starting_u_value_si, 'W/m^2*K', 'Btu/ft^2*hr*R').get
+ OpenStudio.logFree(OpenStudio::Debug, 'OpenstudioStandards::Construction', "Construction #{construction.name} contains SimpleGlazing '#{glazing.name}' with starting u_factor #{starting_u_value_si.round(2)} 'W/m^2*K' = #{staring_u_value_ip.round(2)} 'Btu/ft^2*hr*R'. Changing to u_factor #{ins_u_value_si.round(2)} 'W/m^2*K' = #{ins_u_value_ip.round(2)} 'Btu/ft^2*hr*R'.")
+ glazing.setUFactor(ins_u_value_si)
+
+ return true
+ end
+
# set construction surface properties
#
# @param construction [OpenStudio::Model::Construction] OpenStudio Construction object
# @param roughness [String] surface roughness
# @param thermal_absorptance [Double] target thermal absorptance
@@ -73,8 +285,127 @@
roughness: roughness,
thermal_absorptance: thermal_absorptance,
solar_absorptance: solar_absorptance,
visible_absorptance: visible_absorptance)
return new_material
+ end
+
+ # Set the F-Factor of a slab to a specified value.
+ # Assumes an unheated, fully insulated slab, and modifies
+ # the insulation layer according to the values from 90.1-2004
+ # Table A6.3 Assembly F-Factors for Slab-on-Grade Floors.
+ #
+ # @param construction [OpenStudio::Model::Construction] OpenStudio Construction object
+ # @param target_f_factor_ip [Double] Target F-Factor (Btu/ft*h*R)
+ # @param insulation_layer_name [String] The name of the insulation layer in this construction
+ # @return [Boolean] returns true if successful, false if not
+ def self.construction_set_slab_f_factor(construction, target_f_factor_ip, insulation_layer_name: nil)
+ # Regression from table A6.3 unheated, fully insulated slab
+ r_value_ip = 1.0248 * target_f_factor_ip**-2.186
+ u_value_ip = 1.0 / r_value_ip
+
+ # Set the insulation U-value
+ OpenstudioStandards::Constructions.construction_set_u_value(construction, u_value_ip,
+ insulation_layer_name: insulation_layer_name,
+ intended_surface_type: 'GroundContactFloor',
+ target_includes_interior_film_coefficients: true,
+ target_includes_exterior_film_coefficients: true)
+
+ # Modify the construction name
+ construction.setName("#{construction.name} F-#{target_f_factor_ip.round(3)}")
+
+ return true
+ end
+
+ # Set the surface specific F-factor parameters of a construction.
+ # This method only assumes one floor per space when calculating perimeter and area
+ #
+ # @param construction [OpenStudio::Model::FFactorGroundFloorConstruction] OpenStudio F-factor Construction object
+ # @param target_f_factor_ip [Float] Target F-Factor (Btu/ft*h*R)
+ # @param surface [OpenStudio::Model::Surface] OpenStudio Surface object
+ # @return [Boolean] returns true if successful, false if not
+ def self.construction_set_surface_slab_f_factor(construction, target_f_factor_ip, surface)
+ # Get space associated with surface
+ space = surface.space.get
+
+ # Find this space's exposed floor area and perimeter.
+ perimeter = OpenstudioStandards::Geometry.space_get_f_floor_perimeter(space)
+ area = OpenstudioStandards::Geometry.space_get_f_floor_area(space)
+
+ if area.zero?
+ OpenStudio.logFree(OpenStudio::Error, 'OpenstudioStandards::Construction', "Area for #{surface.name} was calculated to be 0 m2, slab f-factor cannot be set.")
+ return false
+ end
+
+ # Change construction name
+ construction.setName("#{construction.name}_#{surface.name}_#{target_f_factor_ip}")
+
+ # Set properties
+ f_factor_si = target_f_factor_ip * OpenStudio.convert(1.0, 'Btu/ft*h*R', 'W/m*K').get
+ construction.setFFactor(f_factor_si)
+ construction.setArea(area)
+ construction.setPerimeterExposed(perimeter)
+
+ # Set surface outside boundary condition
+ surface.setOutsideBoundaryCondition('GroundFCfactorMethod')
+
+ return true
+ end
+
+ # Set the C-Factor of an underground wall to a specified value.
+ # Assumes continuous exterior insulation and modifies
+ # the insulation layer according to the values from 90.1-2004
+ # Table A4.2 Assembly C-Factors for Below-Grade walls.
+ #
+ # @param construction [OpenStudio::Model::Construction] OpenStudio Construction object
+ # @param target_c_factor_ip [Double] Target C-Factor (Btu/ft^2*h*R)
+ # @param insulation_layer_name [String] The name of the insulation layer in this construction
+ # @return [Boolean] returns true if successful, false if not
+ def self.construction_set_underground_wall_c_factor(construction, target_c_factor_ip, insulation_layer_name: nil)
+ # Regression from table A4.2 continuous exterior insulation
+ r_value_ip = 0.775 * target_c_factor_ip**-1.067
+ u_value_ip = 1.0 / r_value_ip
+
+ # Set the insulation U-value
+ OpenstudioStandards::Constructions.construction_set_u_value(construction, u_value_ip,
+ insulation_layer_name: insulation_layer_name,
+ intended_surface_type: 'GroundContactWall',
+ target_includes_interior_film_coefficients: true,
+ target_includes_exterior_film_coefficients: true)
+
+ # Modify the construction name
+ construction.setName("#{construction.name} C-#{target_c_factor_ip.round(3)}")
+
+ return true
+ end
+
+ # Set the surface specific C-factor parameters of a construction
+ #
+ # @param construction [OpenStudio::Model::CFactorUndergroundWallConstruction] OpenStudio C-factor Construction object
+ # @param target_c_factor_ip [Float] Target C-Factor (Btu/ft^2*h*R)
+ # @param surface [OpenStudio::Model::Surface] OpenStudio surface object
+ # @return [Boolean] returns true if successful, false if not
+ def self.construction_set_surface_underground_wall_c_factor(construction, target_c_factor_ip, surface)
+ # Get space associated with surface
+ space = surface.space.get
+
+ # Get height of the first below grade wall in this space.
+ below_grade_wall_height = OpenstudioStandards::Geometry.space_get_below_grade_wall_height(space)
+
+ if below_grade_wall_height.zero?
+ OpenStudio.logFree(OpenStudio::Error, 'OpenstudioStandards::Construction', "Below grade wall height for #{surface.name} was calculated to be 0 m2, below grade wall c-factor cannot be set.")
+ return false
+ end
+
+ # Change construction name
+ construction.setName("#{construction.name}_#{surface.name}_#{target_c_factor_ip}")
+
+ # Set properties
+ c_factor_si = target_c_factor_ip * OpenStudio.convert(1.0, 'Btu/ft^2*h*R', 'W/m^2*K').get
+ construction.setCFactor(c_factor_si)
+ construction.setHeight(below_grade_wall_height)
+
+ # Set surface outside boundary condition
+ surface.setOutsideBoundaryCondition('GroundFCfactorMethod')
end
end
end