lib/openstudio-standards/standards/necb/NECB2020/building_envelope.rb in openstudio-standards-0.2.15 vs lib/openstudio-standards/standards/necb/NECB2020/building_envelope.rb in openstudio-standards-0.2.16.rc1
- old
+ new
@@ -1,317 +1,8 @@
+
class NECB2020
- # Reduces the WWR to the values specified by the NECB
- # NECB 3.2.1.4
- def apply_standard_window_to_wall_ratio(model:, fdwr_set: -1.0)
- # NECB FDWR limit
- hdd = self.get_necb_hdd18(model)
-
- # Get the maximum NECB fdwr
- # fdwr_set settings:
- # 0-1: Remove all windows and add windows to match this fdwr
- # -1: Remove all windows and add windows to match max fdwr from NECB
- # -2: Do not apply any fdwr changes, leave windows alone (also works for fdwr > 1)
- # -3: Use old method which reduces existing window size (if necessary) to meet maximum NECB fdwr limit
- # <-3.1: Remove all the windows
- # > 1: Do nothing
-
- if fdwr_set.to_f > 1.0
- return
- elsif fdwr_set.to_f >= 0.0 and fdwr_set <= 1.0
- apply_max_fdwr_nrcan(model: model, fdwr_lim: fdwr_set.to_f)
- return
- elsif fdwr_set.to_f >= -1.1 and fdwr_set <= -0.9
- fdwr_lim = (max_fwdr(hdd)).round(3)
- apply_max_fdwr_nrcan(model: model, fdwr_lim: fdwr_lim.to_f)
- return
- elsif fdwr_set.to_f >= -2.1 and fdwr_set <= -1.9
- return
- elsif fdwr_set.to_f >= -3.1 and fdwr_set <= -2.9
- fdwr_lim = (max_fwdr(hdd) * 100.0).round(1)
- return apply_limit_fdwr(model: model, fdwr_lim: fdwr_lim.to_f)
- elsif fdwr_set < -3.1
- apply_max_fdwr_nrcan(model: model, fdwr_lim: fdwr_set.to_f)
- return
- end
- end
-
- def apply_limit_fdwr(model:, fdwr_lim:)
- # Loop through all spaces in the model, and
- # per the PNNL PRM Reference Manual, find the areas
- # of each space conditioning category (res, nonres, semi-heated)
- # separately. Include space multipliers.
- nr_wall_m2 = 0.001 # Avoids divide by zero errors later
- nr_wind_m2 = 0
- res_wall_m2 = 0.001
- res_wind_m2 = 0
- sh_wall_m2 = 0.001
- sh_wind_m2 = 0
- total_wall_m2 = 0.001
- total_subsurface_m2 = 0.0
- # Store the space conditioning category for later use
- space_cats = {}
- model.getSpaces.sort.each do |space|
- # Loop through all surfaces in this space
- wall_area_m2 = 0
- wind_area_m2 = 0
- space.surfaces.sort.each do |surface|
- # Skip non-outdoor surfaces
- next unless surface.outsideBoundaryCondition == 'Outdoors'
- # Skip non-walls
- next unless surface.surfaceType.casecmp('wall').zero?
- # This wall's gross area (including window area)
- wall_area_m2 += surface.grossArea * space.multiplier
- # Subsurfaces in this surface
- surface.subSurfaces.sort.each do |ss|
- wind_area_m2 += ss.netArea * space.multiplier
- end
- end
-
- # Determine the space category
- # zTODO This should really use the heating/cooling loads
- # from the proposed building. However, in an attempt
- # to avoid another sizing run just for this purpose,
- # conditioned status is based on heating/cooling
- # setpoints. If heated-only, will be assumed Semiheated.
- # The full-bore method is on the next line in case needed.
- # cat = thermal_zone_conditioning_category(space, template, climate_zone)
- cooled = space_cooled?(space)
- heated = space_heated?(space)
- cat = 'Unconditioned'
- # Unconditioned
- if !heated && !cooled
- cat = 'Unconditioned'
- # Heated-Only
- elsif heated && !cooled
- cat = 'Semiheated'
- # Heated and Cooled
- else
- res = thermal_zone_residential?(space.thermalZone.get)
- cat = if res
- 'ResConditioned'
- else
- 'NonResConditioned'
- end
- end
- space_cats[space] = cat
- # NECB2011 keep track of totals for NECB regardless of conditioned or not.
- total_wall_m2 += wall_area_m2
- total_subsurface_m2 += wind_area_m2 # this contains doors as well.
-
- # Add to the correct category
- case cat
- when 'Unconditioned'
- next # Skip unconditioned spaces
- when 'NonResConditioned'
- nr_wall_m2 += wall_area_m2
- nr_wind_m2 += wind_area_m2
- when 'ResConditioned'
- res_wall_m2 += wall_area_m2
- res_wind_m2 += wind_area_m2
- when 'Semiheated'
- sh_wall_m2 += wall_area_m2
- sh_wind_m2 += wind_area_m2
- end
- end
-
- # Calculate the WWR of each category
- wwr_nr = ((nr_wind_m2 / nr_wall_m2) * 100.0).round(1)
- wwr_res = ((res_wind_m2 / res_wall_m2) * 100).round(1)
- wwr_sh = ((sh_wind_m2 / sh_wall_m2) * 100).round(1)
- fdwr = ((total_subsurface_m2 / total_wall_m2) * 100).round(1) # used by NECB2011
-
- # Convert to IP and report
- nr_wind_ft2 = OpenStudio.convert(nr_wind_m2, 'm^2', 'ft^2').get
- nr_wall_ft2 = OpenStudio.convert(nr_wall_m2, 'm^2', 'ft^2').get
-
- res_wind_ft2 = OpenStudio.convert(res_wind_m2, 'm^2', 'ft^2').get
- res_wall_ft2 = OpenStudio.convert(res_wall_m2, 'm^2', 'ft^2').get
-
- sh_wind_ft2 = OpenStudio.convert(sh_wind_m2, 'm^2', 'ft^2').get
- sh_wall_ft2 = OpenStudio.convert(sh_wall_m2, 'm^2', 'ft^2').get
-
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "WWR NonRes = #{wwr_nr.round}%; window = #{nr_wind_ft2.round} ft2, wall = #{nr_wall_ft2.round} ft2.")
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "WWR Res = #{wwr_res.round}%; window = #{res_wind_ft2.round} ft2, wall = #{res_wall_ft2.round} ft2.")
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "WWR Semiheated = #{wwr_sh.round}%; window = #{sh_wind_ft2.round} ft2, wall = #{sh_wall_ft2.round} ft2.")
-
- # WWR limit
- wwr_lim = 40.0
-
- # Check against WWR limit
- red_nr = wwr_nr > wwr_lim
- red_res = wwr_res > wwr_lim
- red_sh = wwr_sh > wwr_lim
-
- # puts "Current FDWR is #{fdwr}, must be less than #{fdwr_lim}."
- # puts "Current subsurf area is #{total_subsurface_m2} and gross surface area is #{total_wall_m2}"
- # Stop here unless windows / doors need reducing
- return true unless fdwr > fdwr_lim
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "Reducing the size of all windows (by raising sill height) to reduce window area down to the limit of #{wwr_lim.round}%.")
- # Determine the factors by which to reduce the window / door area
- mult = fdwr_lim / fdwr
- # Reduce the window area if any of the categories necessary
- model.getSpaces.sort.each do |space|
- # Loop through all surfaces in this space
- space.surfaces.sort.each do |surface|
- # Skip non-outdoor surfaces
- next unless surface.outsideBoundaryCondition == 'Outdoors'
- # Skip non-walls
- next unless surface.surfaceType == 'Wall'
- # Subsurfaces in this surface
- surface.subSurfaces.sort.each do |ss|
- # Reduce the size of the window
- red = 1.0 - mult
- sub_surface_reduce_area_by_percent_by_raising_sill(ss, red)
- end
- end
- end
- return true
- end
-
- # Reduces the SRR to the values specified by the PRM. SRR reduction
- # will be done by shrinking vertices toward the centroid.
- #
- def apply_standard_skylight_to_roof_ratio(model:, srr_set: -1.0)
-
- # If srr_set is between 1.0 and 1.2 set it to the maximum allowed by the NECB. If srr_set is between 0.0 and 1.0
- # apply whatever was passed. If srr_set >= 1.2 then set the existing srr of the building to be the necb maximum
- # only if the the srr exceeds this maximum (otherwise leave it to be whatever was modeled).
-
- # srr_set settings:
- # 0-1: Remove all skylights and add skylights to match this srr
- # -1: Remove all skylights and add skylights to match max srr from NECB
- # -2: Do not apply any srr changes, leave skylights alone (also works for srr > 1)
- # -3: Use old method which reduces existing skylight size (if necessary) to meet maximum NECB skylight limit
- # <-3.1: Remove all the skylights
- # > 1: Do nothing
-
- if srr_set.to_f > 1.0
- return
- elsif srr_set.to_f >= 0.0 && srr_set <= 1.0
- apply_max_srr_nrcan(model: model, srr_lim: srr_set.to_f)
- return
- elsif srr_set.to_f >= -1.1 && srr_set <= -0.9
- # Get the maximum NECB srr
- srr_lim = self.get_standards_constant('skylight_to_roof_ratio_max_value')
- apply_max_srr_nrcan(model: model, srr_lim: srr_lim.to_f)
- return
- elsif srr_set.to_f >= -2.1 && srr_set <= -1.9
- return
- elsif srr_set.to_f >= -3.1 && srr_set <= -2.9
- # Continue with the rest of this method, use old method which reduces existing skylight size (if necessary) to
- # meet maximum srr limit
- elsif srr_set < -3.1
- apply_max_srr_nrcan(model: model, srr_lim: srr_set.to_f)
- return
- else
- return
- end
-
- # SRR limit
- srr_lim = self.get_standards_constant('skylight_to_roof_ratio_max_value') * 100.0
-
- # Loop through all spaces in the model, and
- # per the PNNL PRM Reference Manual, find the areas
- # of each space conditioning category (res, nonres, semi-heated)
- # separately. Include space multipliers.
- nr_wall_m2 = 0.001 # Avoids divide by zero errors later
- nr_sky_m2 = 0
- res_wall_m2 = 0.001
- res_sky_m2 = 0
- sh_wall_m2 = 0.001
- sh_sky_m2 = 0
- total_roof_m2 = 0.001
- total_subsurface_m2 = 0
- model.getSpaces.sort.each do |space|
- # Loop through all surfaces in this space
- wall_area_m2 = 0
- sky_area_m2 = 0
- space.surfaces.sort.each do |surface|
- # Skip non-outdoor surfaces
- next unless surface.outsideBoundaryCondition == 'Outdoors'
- # Skip non-walls
- next unless surface.surfaceType == 'RoofCeiling'
- # This wall's gross area (including skylight area)
- wall_area_m2 += surface.grossArea * space.multiplier
- # Subsurfaces in this surface
- surface.subSurfaces.sort.each do |ss|
- sky_area_m2 += ss.netArea * space.multiplier
- end
- end
-
- # Determine the space category
- cat = 'NonRes'
- if space_residential?(space)
- cat = 'Res'
- end
- # if space.is_semiheated
- # cat = 'Semiheated'
- # end
-
- # Add to the correct category
- case cat
- when 'NonRes'
- nr_wall_m2 += wall_area_m2
- nr_sky_m2 += sky_area_m2
- when 'Res'
- res_wall_m2 += wall_area_m2
- res_sky_m2 += sky_area_m2
- when 'Semiheated'
- sh_wall_m2 += wall_area_m2
- sh_sky_m2 += sky_area_m2
- end
- total_roof_m2 += wall_area_m2
- total_subsurface_m2 += sky_area_m2
- end
-
- # Calculate the SRR of each category
- srr_nr = ((nr_sky_m2 / nr_wall_m2) * 100).round(1)
- srr_res = ((res_sky_m2 / res_wall_m2) * 100).round(1)
- srr_sh = ((sh_sky_m2 / sh_wall_m2) * 100).round(1)
- srr = ((total_subsurface_m2 / total_roof_m2) * 100.0).round(1)
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "The skylight to roof ratios (SRRs) are: NonRes: #{srr_nr.round}%, Res: #{srr_res.round}%.")
-
- # Check against SRR limit
- red_nr = srr_nr > srr_lim
- red_res = srr_res > srr_lim
- red_sh = srr_sh > srr_lim
-
- # Stop here unless windows need reducing
- return true unless srr > srr_lim
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "Reducing the size of all windows (by raising sill height) to reduce window area down to the limit of #{srr_lim.round}%.")
- # Determine the factors by which to reduce the window / door area
- mult = srr_lim / srr
-
- # Reduce the subsurface areas
- model.getSpaces.sort.each do |space|
- # Loop through all surfaces in this space
- space.surfaces.sort.each do |surface|
- # Skip non-outdoor surfaces
- next unless surface.outsideBoundaryCondition == 'Outdoors'
- # Skip non-walls
- next unless surface.surfaceType == 'RoofCeiling'
- # Subsurfaces in this surface
- surface.subSurfaces.sort.each do |ss|
- # Reduce the size of the subsurface
- red = 1.0 - mult
- sub_surface_reduce_area_by_percent_by_shrinking_toward_centroid(ss, red)
- end
- end
- end
-
- return true
- end
-
- # @author phylroy.lopez@nrcan.gc.ca
- # @param hdd [Float]
- # @return [Double] a constant float
- def max_fwdr(hdd)
- # get formula from json database.
- return eval(get_standards_formula('fdwr_formula'))
- end
-
# Go through the default construction sets and hard-assigned
# constructions. Clone the existing constructions and set their
# intended surface type and standards construction type per
# the PRM. For some standards, this will involve making
# modifications. For others, it will not.
@@ -363,24 +54,25 @@
BTAP.runner_register('Error', 'Weather file is not defined. Please ensure the weather file is defined and exists.', runner)
return false
end
+ # hdd required to get correct conductance values from the json file.
+ hdd = get_necb_hdd18(model)
+
# Lambdas are preferred over methods in methods for small utility methods.
correct_cond = lambda do |conductivity, surface_type|
- # hdd required in scope for eval function.
- hdd = get_necb_hdd18(model)
- return conductivity.nil? || conductivity.to_f <= 0.0 || conductivity =="NECB_Default" ? eval(model_find_objects(@standards_data['surface_thermal_transmittance'], surface_type)[0]['formula']) : conductivity.to_f
+ return conductivity.nil? || conductivity.to_f <= 0.0 || conductivity == 'NECB_Default' ? eval(model_find_objects(@standards_data['surface_thermal_transmittance'], surface_type)[0]['formula']) : conductivity.to_f
end
# Converts trans and vis to nil if requesting default.. or casts the string to a float.
correct_vis_trans = lambda do |value|
- return value.nil? || value.to_f <= 0.0 || value =="NECB_Default" ? nil : value.to_f
+ return value.nil? || value.to_f <= 0.0 || value == 'NECB_Default' ? nil : value.to_f
end
BTAP::Resources::Envelope::ConstructionSets.customize_default_surface_construction_set!(model: model,
- name: "#{default_surface_construction_set.name.get} at hdd = #{get_necb_hdd18(model)}",
+ name: "#{default_surface_construction_set.name.get} at hdd = #{hdd}",
default_surface_construction_set: default_surface_construction_set,
# ext surfaces
ext_wall_cond: correct_cond.call(ext_wall_cond, {'boundary_condition' => 'Outdoors', 'surface' => 'Wall'}),
ext_floor_cond: correct_cond.call(ext_floor_cond, {'boundary_condition' => 'Outdoors', 'surface' => 'Floor'}),
ext_roof_cond: correct_cond.call(ext_roof_cond, {'boundary_condition' => 'Outdoors', 'surface' => 'RoofCeiling'}),
@@ -414,63 +106,18 @@
# tubular daylight diffuser
tubular_daylight_diffuser_cond: correct_cond.call(skylight_cond, {'boundary_condition' => 'Outdoors', 'surface' => 'Skylight'}),
tubular_daylight_diffuser_solar_trans: correct_vis_trans.call(tubular_daylight_diffuser_solar_trans),
tubular_daylight_diffuser_vis_trans: correct_vis_trans.call(tubular_daylight_diffuser_vis_trans)
)
-
-
end
# sets all surfaces to use default constructions sets except adiabatic, where it does a hard assignment of the interior wall construction type.
model.getPlanarSurfaces.sort.each(&:resetConstruction)
# if the default construction set is defined..try to assign the interior wall to the adiabatic surfaces
BTAP::Resources::Envelope.assign_interior_surface_construction_to_adiabatic_surfaces(model, nil)
BTAP.runner_register('Info', ' apply_standard_construction_properties was sucessful.', runner)
-
end
- # Set all external surface conductances to NECB values.
- # @author phylroy.lopez@nrcan.gc.ca
- # @param surface [String]
- # @param hdd [Float]
- # @param is_radiant [Boolian]
- # @param scaling_factor [Float]
- # @return [String] surface as RSI
- def set_necb_external_surface_conductance(surface, hdd, is_radiant = false, scaling_factor = 1.0)
- conductance_value = 0
-
- if surface.outsideBoundaryCondition.casecmp('outdoors').zero?
-
- case surface.surfaceType.downcase
- when 'wall'
- conductance_value = @standards_data['conductances']['Wall'].find { |i| i['hdd'] > hdd }['thermal_transmittance'] * scaling_factor
- when 'floor'
- conductance_value = @standards_data['conductances']['Floor'].find { |i| i['hdd'] > hdd }['thermal_transmittance'] * scaling_factor
- when 'roofceiling'
- conductance_value = @standards_data['conductances']['Roof'].find { |i| i['hdd'] > hdd }['thermal_transmittance'] * scaling_factor
- end
- if is_radiant
- conductance_value *= 0.80
- end
- return BTAP::Geometry::Surfaces.set_surfaces_construction_conductance([surface], conductance_value)
- end
-
- if surface.outsideBoundaryCondition.downcase =~ /ground/
- case surface.surfaceType.downcase
- when 'wall'
- conductance_value = @standards_data['conductances']['GroundWall'].find { |i| i['hdd'] > hdd }['thermal_transmittance'] * scaling_factor
- when 'floor'
- conductance_value = @standards_data['conductances']['GroundFloor'].find { |i| i['hdd'] > hdd }['thermal_transmittance'] * scaling_factor
- when 'roofceiling'
- conductance_value = @standards_data['conductances']['GroundRoof'].find { |i| i['hdd'] > hdd }['thermal_transmittance'] * scaling_factor
- end
- if is_radiant
- conductance_value *= 0.80
- end
- return BTAP::Geometry::Surfaces.set_surfaces_construction_conductance([surface], conductance_value)
- end
- end
-
# Set all external subsurfaces (doors, windows, skylights) to NECB values.
# @author phylroy.lopez@nrcan.gc.ca
# @param subsurface [String]
# @param hdd [Float]
def set_necb_external_subsurface_conductance(subsurface, hdd)
@@ -478,302 +125,14 @@
if subsurface.outsideBoundaryCondition.downcase.match('outdoors')
case subsurface.subSurfaceType.downcase
when /window/
conductance_value = @standards_data['conductances']['Window'].find { |i| i['hdd'] > hdd }['thermal_transmittance'] * scaling_factor
+ when /skylight/
+ conductance_value = @standards_data['conductances']['Skylight'].find { |i| i['hdd'] > hdd }['thermal_transmittance'] * scaling_factor
when /door/
conductance_value = @standards_data['conductances']['Door'].find { |i| i['hdd'] > hdd }['thermal_transmittance'] * scaling_factor
end
subsurface.setRSI(1 / conductance_value)
end
end
-
- # Adds code-minimum constructions based on the building type
- # as defined in the OpenStudio_Standards_construction_sets.json file.
- # Where there is a separate construction set specified for the
- # individual space type, this construction set will be created and applied
- # to this space type, overriding the whole-building construction set.
- #
- # @param building_type [String] the type of building
- # @param climate_zone [String] the name of the climate zone the building is in
- # @return [Bool] returns true if successful, false if not
- def model_add_constructions(model)
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', 'Started applying constructions')
-
- # Assign construction to adiabatic construction
- # Assign a material to all internal mass objects
- assign_contruction_to_adiabatic_surfaces(model)
- # The constructions lookup table uses a slightly different list of
- # building types.
- apply_building_default_constructionset(model)
- # Make a construction set for each space type, if one is specified
- # apply_default_constructionsets_to_spacetypes(climate_zone, model)
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', 'Finished applying constructions')
- return true
- end
-
- def apply_building_default_constructionset(model)
-
- bldg_def_const_set = model_add_construction_set_from_osm(model: model)
- model.getBuilding.setDefaultConstructionSet(bldg_def_const_set)
-
- end
-
- def apply_default_constructionsets_to_spacetypes(climate_zone, model)
- model.getSpaceTypes.sort.each do |space_type|
- # Get the standards building type
- stds_building_type = nil
- if space_type.standardsBuildingType.is_initialized
- stds_building_type = space_type.standardsBuildingType.get
- else
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', "Space type called '#{space_type.name}' has no standards building type.")
- end
-
- # Get the standards space type
- stds_spc_type = nil
- if space_type.standardsSpaceType.is_initialized
- stds_spc_type = space_type.standardsSpaceType.get
- else
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', "Space type called '#{space_type.name}' has no standards space type.")
- end
-
- # If the standards space type is Attic,
- # the building type should be blank.
- if stds_spc_type == 'Attic'
- stds_building_type = ''
- end
-
- # Attempt to make a construction set for this space type
- # and assign it if it can be created.
- spc_type_const_set = model_add_construction_set_from_osm(model: model)
- if spc_type_const_set.is_initialized
- space_type.setDefaultConstructionSet(spc_type_const_set.get)
- end
- end
- end
-
- def model_add_construction_set_from_osm(model:,
- construction_set_name: 'BTAP-Mass',
- osm_path: File.absolute_path(File.join(__FILE__, '..', '..', 'common/construction_defaults.osm')))
- # load resources model
- construction_library = BTAP::FileIO::load_osm(osm_path)
-
- if not construction_library.getDefaultConstructionSetByName(construction_set_name.to_s).is_initialized
- runner.registerError('Did not find the expected construction in library.')
- return false
- end
- selected_construction_set = construction_library.getDefaultConstructionSetByName(construction_set_name.to_s).get
- new_construction_set = selected_construction_set.clone(model).to_DefaultConstructionSet.get
- return new_construction_set
- end
-
- def assign_contruction_to_adiabatic_surfaces(model)
- cp02_carpet_pad = OpenStudio::Model::MasslessOpaqueMaterial.new(model)
- cp02_carpet_pad.setName('CP02 CARPET PAD')
- cp02_carpet_pad.setRoughness('VeryRough')
- cp02_carpet_pad.setThermalResistance(0.21648)
- cp02_carpet_pad.setThermalAbsorptance(0.9)
- cp02_carpet_pad.setSolarAbsorptance(0.7)
- cp02_carpet_pad.setVisibleAbsorptance(0.8)
-
- normalweight_concrete_floor = OpenStudio::Model::StandardOpaqueMaterial.new(model)
- normalweight_concrete_floor.setName('100mm Normalweight concrete floor')
- normalweight_concrete_floor.setRoughness('MediumSmooth')
- normalweight_concrete_floor.setThickness(0.1016)
- normalweight_concrete_floor.setConductivity(2.31)
- normalweight_concrete_floor.setDensity(2322)
- normalweight_concrete_floor.setSpecificHeat(832)
-
- nonres_floor_insulation = OpenStudio::Model::MasslessOpaqueMaterial.new(model)
- nonres_floor_insulation.setName('Nonres_Floor_Insulation')
- nonres_floor_insulation.setRoughness('MediumSmooth')
- nonres_floor_insulation.setThermalResistance(2.88291975297193)
- nonres_floor_insulation.setThermalAbsorptance(0.9)
- nonres_floor_insulation.setSolarAbsorptance(0.7)
- nonres_floor_insulation.setVisibleAbsorptance(0.7)
-
- floor_adiabatic_construction = OpenStudio::Model::Construction.new(model)
- floor_adiabatic_construction.setName('Floor Adiabatic construction')
- floor_layers = OpenStudio::Model::MaterialVector.new
- floor_layers << cp02_carpet_pad
- floor_layers << normalweight_concrete_floor
- floor_layers << nonres_floor_insulation
- floor_adiabatic_construction.setLayers(floor_layers)
-
- g01_13mm_gypsum_board = OpenStudio::Model::StandardOpaqueMaterial.new(model)
- g01_13mm_gypsum_board.setName('G01 13mm gypsum board')
- g01_13mm_gypsum_board.setRoughness('Smooth')
- g01_13mm_gypsum_board.setThickness(0.0127)
- g01_13mm_gypsum_board.setConductivity(0.1600)
- g01_13mm_gypsum_board.setDensity(800)
- g01_13mm_gypsum_board.setSpecificHeat(1090)
- g01_13mm_gypsum_board.setThermalAbsorptance(0.9)
- g01_13mm_gypsum_board.setSolarAbsorptance(0.7)
- g01_13mm_gypsum_board.setVisibleAbsorptance(0.5)
-
- wall_adiabatic_construction = OpenStudio::Model::Construction.new(model)
- wall_adiabatic_construction.setName('Wall Adiabatic construction')
- wall_layers = OpenStudio::Model::MaterialVector.new
- wall_layers << g01_13mm_gypsum_board
- wall_layers << g01_13mm_gypsum_board
- wall_adiabatic_construction.setLayers(wall_layers)
-
- m10_200mm_concrete_block_basement_wall = OpenStudio::Model::StandardOpaqueMaterial.new(model)
- m10_200mm_concrete_block_basement_wall.setName('M10 200mm concrete block basement wall')
- m10_200mm_concrete_block_basement_wall.setRoughness('MediumRough')
- m10_200mm_concrete_block_basement_wall.setThickness(0.2032)
- m10_200mm_concrete_block_basement_wall.setConductivity(1.326)
- m10_200mm_concrete_block_basement_wall.setDensity(1842)
- m10_200mm_concrete_block_basement_wall.setSpecificHeat(912)
-
- basement_wall_construction = OpenStudio::Model::Construction.new(model)
- basement_wall_construction.setName('Basement Wall construction')
- basement_wall_layers = OpenStudio::Model::MaterialVector.new
- basement_wall_layers << m10_200mm_concrete_block_basement_wall
- basement_wall_construction.setLayers(basement_wall_layers)
-
- basement_floor_construction = OpenStudio::Model::Construction.new(model)
- basement_floor_construction.setName('Basement Floor construction')
- basement_floor_layers = OpenStudio::Model::MaterialVector.new
- basement_floor_layers << m10_200mm_concrete_block_basement_wall
- basement_floor_layers << cp02_carpet_pad
- basement_floor_construction.setLayers(basement_floor_layers)
-
- model.getSurfaces.sort.each do |surface|
- if surface.outsideBoundaryCondition.to_s == 'Adiabatic'
- if surface.surfaceType.to_s == 'Wall'
- surface.setConstruction(wall_adiabatic_construction)
- else
- surface.setConstruction(floor_adiabatic_construction)
- end
- elsif surface.outsideBoundaryCondition.to_s == 'OtherSideCoefficients'
- # Ground
- if surface.surfaceType.to_s == 'Wall'
- surface.setOutsideBoundaryCondition('Ground')
- surface.setConstruction(basement_wall_construction)
- else
- surface.setOutsideBoundaryCondition('Ground')
- surface.setConstruction(basement_floor_construction)
- end
- end
- end
- end
-
- def scale_model_geometry(model, x_scale, y_scale, z_scale)
- # Identity matrix for setting space origins
- m = OpenStudio::Matrix.new(4, 4, 0)
-
- m[0, 0] = 1.0 / x_scale
- m[1, 1] = 1.0 / y_scale
- m[2, 2] = 1.0 / z_scale
- m[3, 3] = 1.0
- t = OpenStudio::Transformation.new(m)
- model.getPlanarSurfaceGroups().each do |planar_surface|
- planar_surface.changeTransformation(t)
- end
- return model
- end
-
- # This method applies the maximum fenestration and door to wall ratio to a building as per NECB 2011 8.4.4.3 and
- # 3.2.1.4 (or equivalent in other versions of the NECB). It first checks for al exterior walls adjacent to conditioned
- # spaces. It distinguishes between plenums and other conditioned spaces. It uses both to calculate the maximum window
- # area to be applied to the building but attempts to put these windows only on non-plenum conditioned spaces (if
- # possible).
- def apply_max_fdwr_nrcan(model:, fdwr_lim:)
- # First determine which vertical (between 89 and 91 degrees from horizontal) walls are adjacent to conditioned
- # spaces.
- exp_surf_info = find_exposed_conditioned_vertical_surfaces(model)
- # If there are none (or very few) then throw a warning.
- if exp_surf_info["total_exp_wall_area_m2"] < 0.1
- OpenStudio.logFree(OpenStudio::Warn, 'openstudio.model.Model', "This building has no exposed walls adjacent to heated spaces.")
- return false
- end
-
- construct_set = model.getBuilding.defaultConstructionSet.get
- fixed_window_construct_set = construct_set.defaultExteriorSubSurfaceConstructions.get.fixedWindowConstruction.get
-
- # IF FDWR is greater than 1 then something is wrong raise an error. If it is less than 0.001 assume all the windows
- # should go.
- if fdwr_lim > 1
- OpenStudio.logFree(OpenStudio::Error, 'openstudio.model.Model', "This building requires a larger window area than there is wall area.")
- return false
- elsif fdwr_lim < 0.001
- exp_surf_info["exp_nonplenum_walls"].sort.each do |exp_surf|
- remove_All_Subsurfaces(surface: exp_surf)
- end
- return true
- end
- # Get the required window area.
- win_area = fdwr_lim * exp_surf_info["total_exp_wall_area_m2"]
- # Try to put the windows on non-plenum walls if possible. So determine if you can fit the required window area
- # on the non-plenum wall area.
- if win_area <= exp_surf_info["exp_nonplenum_wall_area_m2"]
- # If you can fit the windows on the non-plenum wall area then recalculate the window ratio so that is is only for
- # the non-plenum walls.
- nonplenum_fdwr = win_area / exp_surf_info["exp_nonplenum_wall_area_m2"]
- exp_surf_info["exp_nonplenum_walls"].sort.each do |exp_surf|
- # Remove any subsurfaces, add the window, set the name to be whatever the surface name is plus the subsurface
- # type (which will be 'fixedwindow')
- remove_All_Subsurfaces(surface: exp_surf)
- set_Window_To_Wall_Ratio_set_name(surface: exp_surf, area_fraction: nonplenum_fdwr, construction: fixed_window_construct_set)
- end
- else
- # There was not enough non-plenum wall area so add the windows to both the plenum and non-plenum walls. This is
- # done separately because the 'find_exposed_conditioned_vertical_surfaces' method returns the plenum and
- # non-plenum walls separately.
- exp_surf_info["exp_nonplenum_walls"].sort.each do |exp_surf|
- # Remove any subsurfaces, add the window, set the name to be whatever the surface name is plus the subsurface
- # type (which will be 'fixedwindow')
- remove_All_Subsurfaces(surface: exp_surf)
- set_Window_To_Wall_Ratio_set_name(surface: exp_surf, area_fraction: fdwr_lim, construction: fixed_window_construct_set)
- end
- exp_surf_info["exp_plenum_walls"].sort.each do |exp_surf|
- # Remove any subsurfaces, add the window, set the name to be whatever the surface name is plus the subsurface
- # type (which will be 'fixedwindow')
- remove_All_Subsurfaces(surface: exp_surf)
- set_Window_To_Wall_Ratio_set_name(surface: exp_surf, area_fraction: fdwr_lim, construction: fixed_window_construct_set)
- end
- end
- return true
- end
-
- # This method is similar to the 'apply_max_fdwr' method above but applies the maximum skylight to roof area ratio to a
- # building as per NECB 2011 8.4.4.3 and 3.2.1.4 (or equivalent in other versions of the NECB). It first checks for all
- # exterior roofs adjacent to conditioned spaces. It distinguishes between plenums and other conditioned spaces. It
- # uses only the non-plenum roof area to calculate the maximum skylight area to be applied to the building.
- def apply_max_srr_nrcan(model:, srr_lim:)
- # First determine which roof surfaces are adjacent to heated spaces (both plenum and non-plenum).
- exp_surf_info = find_exposed_conditioned_roof_surfaces(model)
- # If the non-plenum roof area is very small raise a warning. It may be perfectly fine but it is probably a good
- # idea to warn the user.
- if exp_surf_info["exp_nonplenum_roof_area_m2"] < 0.1
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', "This building has no exposed ceilings adjacent to spaces that are not attics or plenums. No skylights will be added.")
- return false
- end
-
- # If the SRR is greater than one something is seriously wrong so raise an error. If it is less than 0.001 assume
- # all the skylights should go.
- if srr_lim > 1
- OpenStudio.logFree(OpenStudio::Error, 'openstudio.model.Model', "This building requires a larger skylight area than there is roof area.")
- return false
- elsif srr_lim < 0.001
- exp_surf_info["exp_nonplenum_roofs"].sort.each do |exp_surf|
- remove_All_Subsurfaces(surface: exp_surf)
- end
- return true
- end
-
- construct_set = model.getBuilding.defaultConstructionSet.get
- skylight_construct_set = construct_set.defaultExteriorSubSurfaceConstructions.get.skylightConstruction.get
-
- # Go through all of exposed roofs adjacent to heated, non-plenum spaces, remove any existing subsurfaces, and add
- # a skylight in the centroid of the surface, with the same shape of the surface, only scaled to be the area
- # determined by the SRR. The name of the skylight will be the surface name with the subsurface type attached
- # ('skylight' in this case). Note that this method will only work if the surface does not fold into itself (like an
- # L or a V).
- exp_surf_info["exp_nonplenum_roofs"].sort.each do |roof|
- # sub_surface_create_centered_subsurface_from_scaled_surface(roof, srr_lim, model)
- sub_surface_create_scaled_subsurfaces_from_surface(surface: roof, area_fraction: srr_lim, model: model, consturction: skylight_construct_set)
- end
- return true
- end
-end
\ No newline at end of file
+end