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