lib/openstudio-standards/btap/envelope.rb in openstudio-standards-0.2.11 vs lib/openstudio-standards/btap/envelope.rb in openstudio-standards-0.2.12.rc1
- old
+ new
@@ -384,11 +384,11 @@
# @param model [OpenStudio::Model::Model]
# @param name [String] = "air test", of the material.
# @param gas_type [String] = "Air"
# @param thickness [Float] = 0.003
# @return [OpenStudio::Model::Gas::validGasTypes] gas
- def self.create_gas(model, name = "air test", gas_type = "Air", thickness=0.003)
+ def self.create_gas(model, name = "air test", gas_type = "Air", thickness = 0.003)
raise "gas_type #{gas_type} is not part of the allow values: #{OpenStudio::Model::Gas::validGasTypes()}" unless OpenStudio::Model::Gas::validGasTypes().include?(gas_type)
gas = OpenStudio::Model::Gas.new(model)
gas.setGasType(gas_type)
gas.setThickness(thickness)
gas.setName(name)
@@ -406,11 +406,11 @@
# @param backSideSlatBeamSolarReflectance [Float] = 0.1
# @param frontSideSlatDiffuseSolarReflectance [Float] = 0.1
# @param backSideSlatDiffuseSolarReflectance [Float] = 0.1
# @param slatBeamVisibleTransmittance [Float] = 0.1
# @return [OpenStudio::Model::Blind] blind
- def self.create_blind(model, name = "blind test", slatWidth=0.1, slatSeparation=0.1, frontSideSlatBeamSolarReflectance=0.1, backSideSlatBeamSolarReflectance=0.1, frontSideSlatDiffuseSolarReflectance=0.1, backSideSlatDiffuseSolarReflectance=0.1, slatBeamVisibleTransmittance=0.1)
+ def self.create_blind(model, name = "blind test", slatWidth = 0.1, slatSeparation = 0.1, frontSideSlatBeamSolarReflectance = 0.1, backSideSlatBeamSolarReflectance = 0.1, frontSideSlatDiffuseSolarReflectance = 0.1, backSideSlatDiffuseSolarReflectance = 0.1, slatBeamVisibleTransmittance = 0.1)
blind = OpenStudio::Model::Blind.new(model, slatWidth, slatSeparation, frontSideSlatBeamSolarReflectance, backSideSlatBeamSolarReflectance, frontSideSlatDiffuseSolarReflectance, backSideSlatDiffuseSolarReflectance, slatBeamVisibleTransmittance)
blind.setName(name)
return blind
end
@@ -422,11 +422,11 @@
# @param diffuseSolarReflectance [Float] = 0.1
# @param diffuseVisibleReflectance [Float] = 0.1
# @param screenMaterialSpacing [Float] = 0.1
# @param screenMaterialDiameter [Float] = 0.1
# @return [OpenStudio::Model::Screen] screen
- def self.create_screen(model, name = "screen test", diffuseSolarReflectance=0.1, diffuseVisibleReflectance=0.1, screenMaterialSpacing=0.1, screenMaterialDiameter=0.1)
+ def self.create_screen(model, name = "screen test", diffuseSolarReflectance = 0.1, diffuseVisibleReflectance = 0.1, screenMaterialSpacing = 0.1, screenMaterialDiameter = 0.1)
screen = OpenStudio::Model::Screen.new(model, diffuseSolarReflectance, diffuseVisibleReflectance, screenMaterialSpacing, screenMaterialDiameter)
screen.setName(name)
return screen
end
@@ -442,11 +442,11 @@
# @param thermalHemisphericalEmissivity [Float] = 0.1
# @param thermalTransmittance [Float] = 0.1
# @param thickness [Float] = 0.1
# @param conductivity [Float] = 0.1
# @return [OpenStudio::Model::Shade.new] shade
- def self.create_shade(model, name ="shade test", solarTransmittance=0.1, solarReflectance=0.1, visibleTransmittance=0.1, visibleReflectance=0.1, thermalHemisphericalEmissivity=0.1, thermalTransmittance=0.1, thickness=0.1, conductivity=0.1)
+ def self.create_shade(model, name = "shade test", solarTransmittance = 0.1, solarReflectance = 0.1, visibleTransmittance = 0.1, visibleReflectance = 0.1, thermalHemisphericalEmissivity = 0.1, thermalTransmittance = 0.1, thickness = 0.1, conductivity = 0.1)
shade = OpenStudio::Model::Shade.new(model, solarTransmittance, solarReflectance, visibleTransmittance, visibleReflectance, thermalHemisphericalEmissivity, thermalTransmittance, thickness, conductivity)
shade.setName(name)
return shade
end
@@ -624,25 +624,185 @@
#Determine how low the resistance can be set. Subtract exisiting insulation
#Values from the total resistance to see how low we can go.
minimum_resistance = (1 / new_construction.thermalConductance.to_f) - (1.0 / new_construction.insulation.get.thermalConductance.to_f)
#Check if the requested resistance is smaller than the minimum
- # resistance. If so, use the minimum resistance instead.
+ # resistance. If so, revise the construction layers.
if minimum_resistance > (1 / conductance)
- #tell user why we are defaulting and set the conductance of the
- # construction.
- raise ("could not set conductance of construction #{new_construction.name.to_s} to because existing layers make this impossible. Change the construction to allow for this conductance to be set." + (conductance).to_s + "setting to closest value possible value:" + (1.0 / minimum_resistance).to_s)
- # new_construction.setConductance((1.0/minimum_resistance))
+ # Changing the insulation layer will not be enough so either start removing layers or modify them to get
+ # to the required conductance.
+ new_construction = adjust_opaque_construction(construction: new_construction, req_conductance: conductance.to_f)
else
unless new_construction.setConductance(conductance)
raise("could not set conductance of construction #{new_construction.name.to_s}")
end
end
end
return new_construction
end
+ # This removes construction layers if the required conductance for a construction is higher than the maximum
+ # conductance that construction can have. Otherwise it modifies existing layers to set their thickness or
+ # resistance values (depending on what is in the layer) to achieve the required conductance.
+ # @author Chris Kirney <chris.kirney@canada.ca>
+ # @param construction <String> the construction we are modifying
+ # @param req_conductance [Fixnum] the conductance we are trying to reach
+ # @return [<String]OpenStudio::Model::getConstructionByName] the final construction after modification
+ def self.adjust_opaque_construction(construction:, req_conductance:)
+ layer_comp = []
+ # Extract the thickness, conductivity, resistance of each layer of the construction. If the material is
+ # "No Mass", or "Air gap" set the conductivity (how well the material conducts heat) and conductance (how well
+ # the material with a given thickness conducts heat) to the inverse of the resistance. This is because
+ # No Mass and Air gap materials do not have a thickness value. Also, include which layer index the material
+ # has and the material object itself.
+ construction.layers.each_with_index do |layer, layer_index|
+ mat_type = layer.iddObjectType.valueName.to_s
+ case mat_type
+ when "OS_Material"
+ mat_layer = layer.to_StandardOpaqueMaterial.get
+ layer_comp << {
+ thickness_m: mat_layer.thickness.to_f,
+ conductivity_SI: mat_layer.conductivity.to_f,
+ conductance_SI: (mat_layer.conductivity.to_f/mat_layer.thickness.to_f),
+ resistance_SI: (mat_layer.thickness.to_f/mat_layer.conductivity.to_f),
+ construction_index: layer_index,
+ layer_object: mat_layer
+ }
+ when "OS_Material_NoMass"
+ mat_layer = layer.to_MasslessOpaqueMaterial.get
+ layer_comp << {
+ thickness_m: 0,
+ conductivity_SI: 1.0/mat_layer.thermalResistance.to_f,
+ conductance_SI: 1.0/mat_layer.thermalResistance.to_f,
+ resistance_SI: mat_layer.thermalResistance.to_f,
+ construction_index: layer_index,
+ layer_object: mat_layer
+ }
+ when "OS_Material_AirGap"
+ mat_layer = layer.to_AirGap.get
+ layer_comp << {
+ thickness_m: 0,
+ conductivity_SI: 1.0/mat_layer.thermalResistance.to_f,
+ conductance_SI: 1.0/mat_layer.thermalResistance.to_f,
+ resistance_SI: mat_layer.thermalResistance.to_f,
+ construction_index: layer_index,
+ layer_object: mat_layer
+ }
+ end
+ end
+ # Sort the above layers by the conductivity of the layers. The lowest conductivity layers first followed by
+ # layers with progressively higher conductivities.
+ sorted_layers = layer_comp.sort{ |a, b| b[:conductivity_SI] <=> a[:conductivity_SI]}
+ index = 0
+ total_conductance = construction.thermalConductance.to_f
+ # The following loop steps through the array of layers, sorted form highest conductivity to lowest. It
+ # deletes a layer in the construction if the conductance for the layer is not enough to reach the total
+ # conductance for the construction that we are trying to reach. If modifies the thickness or resistance
+ # (depending on the layer material type) of a layer if doing so will reach the overall construction
+ # conductance target. The total conductance of the construction is rounded because the conductance never seems
+ # to be set precisely enough.
+
+ while total_conductance.round(4) < req_conductance
+ # There are too indicies that are tracked:
+ # index: The index of the element in the sorted array of layers that we are currently considering
+ # const_index: The index of the layer we are currently considering in the construction
+ # Note that both the construction array and sorted array contain the same elements. However these elements
+ # may be in a different order. Thus, the index and const_index may be different. They will both indicate
+ # the same layer. However, they may differ because the sorted array is sorted by conductivity while the
+ # construction array is ordered with the first layer outside (a given space) and the final layer inside (a
+ # given space).
+ const_index = sorted_layers[index][:construction_index]
+ # Check if modifying the resistance of the currently layer will be enough to reach our total construction
+ # conductance goal. If it will, modify the layer. If it will not, delete the layer.
+ if sorted_layers[index][:resistance_SI] > ((1.0/total_conductance) - (1.0/req_conductance))
+ # If the current layer is a NoMass or AirGap material its thickness is zero so we set the resistance.
+ if sorted_layers[index][:thickness_m] == 0
+ # Determine the resistance we want to set the layer to.
+ res_mod = sorted_layers[index][:resistance_SI] - ((1.0/total_conductance) - (1.0/req_conductance))
+ # Find out if the layer is an AirGap or NoMass and set the resistance for the layer with the right
+ # command systax.
+ mat_type = construction.layers[const_index].iddObjectType.valueName.to_s
+ case mat_type
+ when "OS_Material_NoMass"
+ construction.layers[const_index].to_MasslessOpaqueMaterial.get.setThermalResistance(res_mod)
+ when "OS_Material_AirGap"
+ construction.layers[const_index].to_AirGap.get.setThermalResistance(res_mod)
+ end
+ else
+ # The the current layer is a regular opaque material it has a thickness so we set that to reach the
+ # desired resistance for that layer.
+ # Determine the thickness we want to set the layer.
+ thick_mod = (sorted_layers[index][:resistance_SI] - ((1.0/total_conductance) - (1.0/req_conductance)))*(sorted_layers[index][:conductivity_SI])
+ # Set the thickness of the layer.
+ construction.layers[const_index].to_StandardOpaqueMaterial.get.setThickness(thick_mod)
+ end
+ # Step the index of the sorted array forward by 1. We should be able to leave the loop now because the
+ # construction should have the conductance we want now. But you never know.
+ index += 1
+ else
+ # There the layer could not be adjusted to reach the desired conductance for the construction so get rid
+ # of the layer.
+ # If this is the only layer then we cannot get rid of it so throw an error. This should never happen but
+ # you never know.
+ if sorted_layers.size == 1
+ raise ("Could not set conductance of construction #{construction.name.to_s} to #{req_conductance} because existing layers make this impossible. Could not automatically change the constructions. Change the construction to allow for this conductance to be set.")
+ return construction
+ end
+ # Delete the layer from the construction.
+ construction.eraseLayer(const_index)
+ # Delete the layer from the sorted set of layers (so that both the construction array and sorted array
+ # continue to contain the same layers).
+ sorted_layers.delete_at(index)
+ # Go through the sorted array and change the construction indicies so that they continue to point to the
+ # correct layers of the construction array. Note that index is not increased. This is because the element
+ # we were looking at just got removed so its index will be the same as that of what would have been the
+ # next element.
+ sorted_layers.each do |sorted_layer|
+ if sorted_layer[:construction_index] > (const_index - 1)
+ sorted_layer[:construction_index] -= 1
+ end
+ end
+ end
+ # Get the revised conductance for the construction now that it has been modified (by either removing or
+ # modifying layers).
+ total_conductance = construction.thermalConductance.to_f
+ # Check if we have anything left to modify. If yes, then keep going. If not, then if we have done enough
+ # we can stop and return the revised construction, otherwise throw an error.
+ if construction.layers.size < index + 1
+ if total_conductance.round(4) >= req_conductance
+ return construction
+ else
+ raise ("Could not set conductance of construction #{construction.name.to_s} from the current conductance of #{total_conductance} to #{req_conductance} because existing layers make this impossible. Change the construction to allow for this conductance to be set.")
+ return construction
+ end
+ end
+ end
+ # We have achieved our goal, return the revised construction.
+ return construction
+ end
+
+ # This checks if the construction layer can be modified to set thermal resistance of the whole construction to
+ # be less than the required resistance
+ # @author Chris Kirney <chris.kirney@canada.ca>
+ # @param mat_resistance <Fixnum>
+ # @param total_conductance <Fixnum>
+ # @param req_conductance <Fixnum>
+ # @return [<Fixnum>] layer resistance needed to meet construction material resistance, -999 if this is not enough
+ def self.should_modify_layer(mat_resistance:, total_conductance:, req_conductance:)
+ # Determine if the amount of resistance you can modify in this layer is greater than the amount of resistance
+ # you have to change.
+ if mat_resistance > ((1.0/total_conductance) - (1.0/req_conductance))
+ # If yes, determine what the resistance for this layer should be to meet the required resistance of the
+ # entire assembly. Then return the new resistance value.
+ target_res = mat_resistance - ((1.0/total_conductance) - (1.0/req_conductance))
+ return target_res
+ else
+ # If no, then return an unambiguous no.
+ return -999
+ end
+ end
+
#This model gets tsol
#@author Phylroy A. Lopez <plopez@nrcan.gc.ca>
#@param model [OpenStudio::Model::Model]
#@param construction <String>
#@return [Float] tsol
@@ -715,11 +875,11 @@
#@return [String] new_construction
def self.deep_copy(model, construction)
construction = BTAP::Common::validate_array(model, construction, "Construction").first
new_construction = construction.clone.to_Construction.get
#interating through layers."
- (0..new_construction.layers.length-1).each do |layernumber|
+ (0..new_construction.layers.length - 1).each do |layernumber|
#cloning material"
cloned_layer = new_construction.getLayer(layernumber).clone.to_Material.get
#"setting material to new construction."
new_construction.setLayer(layernumber, cloned_layer)
end
@@ -758,11 +918,11 @@
#@param solarTransmittanceatNormalIncidence [Float] = nil
#@param visibleTransmittance [Float] = nil
#@param at_temperature_c [Float] = 0.0
#@return [String] create_construction
def self.customize_fenestration_construction(
- model,
+ model,
construction,
conductance = nil,
solarTransmittanceatNormalIncidence = nil,
visibleTransmittance = nil,
at_temperature_c = 0.0)
@@ -888,14 +1048,14 @@
construction = BTAP::Resources::Envelope::Constructions::create_construction(model, "test construction", [opaque, air_gap, insulation, massless, opaque], insulation)
walls_cons = floor_cons = roof_cons = construction
construction_set = BTAP::Resources::Envelope::ConstructionSets::create_default_surface_constructions(model, "test construction set", walls_cons, floor_cons, roof_cons)
#Check that the construction was created
assert(!(construction_set.to_DefaultSurfaceConstructions.empty?))
- new_set = BTAP::Resources::Envelope::ConstructionSets::customize_default_surface_constructions_rsi(model, "changed_rsi", construction_set, 1.0/2.45, 1.0/2.55, 1.0/2.65)
- assert_in_delta(1.0/2.45, BTAP::Resources::Envelope::Constructions::get_conductance(new_set.wallConstruction.get).to_f, 0.00001)
- assert_in_delta(1.0/2.55, BTAP::Resources::Envelope::Constructions::get_conductance(new_set.floorConstruction.get).to_f, 0.00001)
- assert_in_delta(1.0/2.65, BTAP::Resources::Envelope::Constructions::get_conductance(new_set.roofCeilingConstruction.get).to_f, 0.00001)
+ new_set = BTAP::Resources::Envelope::ConstructionSets::customize_default_surface_constructions_rsi(model, "changed_rsi", construction_set, 1.0 / 2.45, 1.0 / 2.55, 1.0 / 2.65)
+ assert_in_delta(1.0 / 2.45, BTAP::Resources::Envelope::Constructions::get_conductance(new_set.wallConstruction.get).to_f, 0.00001)
+ assert_in_delta(1.0 / 2.55, BTAP::Resources::Envelope::Constructions::get_conductance(new_set.floorConstruction.get).to_f, 0.00001)
+ assert_in_delta(1.0 / 2.65, BTAP::Resources::Envelope::Constructions::get_conductance(new_set.roofCeilingConstruction.get).to_f, 0.00001)
end
#This method creates default subsurface constructions
#@author phylroy.lopez@nrcan.gc.ca
@@ -1030,21 +1190,38 @@
#@param tubular_daylight_dome_solar_trans [Float] = nil
#@param tubular_daylight_dome_vis_trans [Float] = nil,
#@param tubular_daylight_diffuser_rsi [Float] = nil
#@param tubular_daylight_diffuser_solar_trans [Float] = nil
#@param tubular_daylight_diffuser_vis_trans [Float] = nil
- def self.customize_default_surface_construction_set_rsi!(model, name, default_surface_construction_set,
- ext_wall_rsi = nil, ext_floor_rsi = nil, ext_roof_rsi = nil,
- ground_wall_rsi = nil, ground_floor_rsi = nil, ground_roof_rsi = nil,
- fixed_window_rsi = nil, fixed_wind_solar_trans = nil, fixed_wind_vis_trans = nil,
- operable_window_rsi = nil, operable_wind_solar_trans = nil, operable_wind_vis_trans = nil,
+ def self.customize_default_surface_construction_set_rsi!(model,
+ name,
+ default_surface_construction_set,
+ ext_wall_rsi = nil,
+ ext_floor_rsi = nil,
+ ext_roof_rsi = nil,
+ ground_wall_rsi = nil,
+ ground_floor_rsi = nil,
+ ground_roof_rsi = nil,
+ #subsurfaces
+ fixed_window_rsi = nil,
+ fixed_wind_solar_trans = nil,
+ fixed_wind_vis_trans = nil,
+ operable_window_rsi = nil,
+ operable_wind_solar_trans = nil,
+ operable_wind_vis_trans = nil,
door_construction_rsi = nil,
glass_door_rsi = nil, glass_door_solar_trans = nil, glass_door_vis_trans = nil,
overhead_door_rsi = nil,
- skylight_rsi = nil, skylight_solar_trans = nil, skylight_vis_trans = nil,
- tubular_daylight_dome_rsi = nil, tubular_daylight_dome_solar_trans = nil, tubular_daylight_dome_vis_trans = nil,
- tubular_daylight_diffuser_rsi = nil, tubular_daylight_diffuser_solar_trans = nil, tubular_daylight_diffuser_vis_trans = nil
+ skylight_rsi = nil,
+ skylight_solar_trans = nil,
+ skylight_vis_trans = nil,
+ tubular_daylight_dome_rsi = nil,
+ tubular_daylight_dome_solar_trans = nil,
+ tubular_daylight_dome_vis_trans = nil,
+ tubular_daylight_diffuser_rsi = nil,
+ tubular_daylight_diffuser_solar_trans = nil,
+ tubular_daylight_diffuser_vis_trans = nil
)
#Change name if required.
default_surface_construction_set.setName(name) unless name.nil?
ext_surface_set = default_surface_construction_set.defaultExteriorSurfaceConstructions.get
new_ext_surface_set = self.customize_default_surface_constructions_rsi(model, name, ext_surface_set, ext_wall_rsi, ext_floor_rsi, ext_roof_rsi)
@@ -1343,10 +1520,10 @@
fixedWindowConstruction = BTAP::Common::validate_array(model, fixedWindowConstruction, "Construction").first
operableWindowConstruction = BTAP::Common::validate_array(model, operableWindowConstruction, "Construction").first
setDoorConstruction = BTAP::Common::validate_array(model, setDoorConstruction, "Construction").first
setGlassDoorConstruction = BTAP::Common::validate_array(model, setGlassDoorConstruction, "Construction").first
overheadDoorConstruction = BTAP::Common::validate_array(model, overheadDoorConstruction, "Construction").first
- skylightConstruction= BTAP::Common::validate_array(model, skylightConstruction, "Construction").first
+ skylightConstruction = BTAP::Common::validate_array(model, skylightConstruction, "Construction").first
tubularDaylightDomeConstruction = BTAP::Common::validate_array(model, tubularDaylightDomeConstruction, "Construction").first
tubularDaylightDiffuserConstruction = BTAP::Common::validate_array(model, tubularDaylightDiffuserConstruction, "Construction").first
set = OpenStudio::Model::DefaultSubSurfaceConstructions.new(model)
set.setFixedWindowConstruction(fixedWindowConstruction) unless fixedWindowConstruction.nil?
\ No newline at end of file