lib/openstudio-standards/geometry/create_bar.rb in openstudio-standards-0.6.0.rc2 vs lib/openstudio-standards/geometry/create_bar.rb in openstudio-standards-0.6.3
- old
+ new
@@ -18,25 +18,25 @@
# calculate aspect ratios not represented on Table 4.2
primary_footprint = 73958.0
primary_p = 619.0 # wrote measure using calculate_perimeter method in os_lib_geometry
primary_ns_ew_ratio = 2.829268293 # estimated from ratio of ns/ew total wall area
primary_width = Math.sqrt(primary_footprint / primary_ns_ew_ratio)
- primary_p_min = 2 * (primary_width + primary_width / primary_footprint)
+ primary_p_min = 2 * (primary_width + (primary_width / primary_footprint))
primary_p_mult = primary_p / primary_p_min
secondary_footprint = 210887.0 / 2.0 # floor area divided by area instead of true footprint 128112.0)
secondary_p = 708.0 # wrote measure using calculate_perimeter method in os_lib_geometry
secondary_ns_ew_ratio = 2.069230769 # estimated from ratio of ns/ew total wall area
secondary_width = Math.sqrt(secondary_footprint / secondary_ns_ew_ratio)
- secondary_p_min = 2 * (secondary_width + secondary_width / secondary_footprint)
+ secondary_p_min = 2 * (secondary_width + (secondary_width / secondary_footprint))
secondary_p_mult = secondary_p / secondary_p_min
outpatient_footprint = 40946.0 / 3.0 # floor area divided by area instead of true footprint 17872.0)
outpatient_p = 537.0 # wrote measure using calculate_perimeter method in os_lib_geometry
outpatient_ns_ew_ratio = 1.56448737 # estimated from ratio of ns/ew total wall area
outpatient_width = Math.sqrt(outpatient_footprint / outpatient_ns_ew_ratio)
- outpatient_p_min = 2 * (outpatient_width + outpatient_footprint / outpatient_width)
+ outpatient_p_min = 2 * (outpatient_width + (outpatient_footprint / outpatient_width))
outpatient_p_mult = outpatient_p / outpatient_p_min
# primary_aspet_ratio = OpenstudioStandards::Geometry.aspect_ratio(73958.0, 2060.0)
# secondary_aspet_ratio = OpenstudioStandards::Geometry.aspect_ratio(128112.0, 2447.0)
# outpatient_aspet_ratio = OpenstudioStandards::Geometry.aspect_ratio(14782.0, 588.0)
@@ -188,11 +188,11 @@
building_overhang_area_s = 0.0
building_overhang_area_e = 0.0
building_overhang_area_w = 0.0
# loop through stories based on mine z height of surfaces.
- sorted_stories = sort_building_stories_and_get_min_multiplier(model).sort_by { |k, v| v }
+ sorted_stories = OpenstudioStandards::Geometry.model_sort_building_stories_and_get_min_multiplier(model).sort_by { |k, v| v }
sorted_stories.each do |story, story_min_z|
story_min_multiplier = nil
story_footprint = nil
story_multiplied_floor_area = OpenstudioStandards::Geometry.spaces_get_floor_area(story.spaces)
# goal of footprint calc is to count multiplier for hotel room on facade,but not to count what is intended as a story multiplier
@@ -363,14 +363,14 @@
envelope_data_hash[:building_perimeter] = envelope_data_hash[:stories][story][:story_perimeter]
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', " * #{story.name} is the first above grade story and will be used for the building perimeter.")
end
end
- envelope_data_hash[:building_overhang_proj_factor_n] = building_overhang_area_n / ext_surfaces_hash['northWindow']
- envelope_data_hash[:building_overhang_proj_factor_s] = building_overhang_area_s / ext_surfaces_hash['southWindow']
- envelope_data_hash[:building_overhang_proj_factor_e] = building_overhang_area_e / ext_surfaces_hash['eastWindow']
- envelope_data_hash[:building_overhang_proj_factor_w] = building_overhang_area_w / ext_surfaces_hash['westWindow']
+ envelope_data_hash[:building_overhang_proj_factor_n] = building_overhang_area_n / ext_surfaces_hash['north_window']
+ envelope_data_hash[:building_overhang_proj_factor_s] = building_overhang_area_s / ext_surfaces_hash['south_window']
+ envelope_data_hash[:building_overhang_proj_factor_e] = building_overhang_area_e / ext_surfaces_hash['east_window']
+ envelope_data_hash[:building_overhang_proj_factor_w] = building_overhang_area_w / ext_surfaces_hash['west_window']
# warn for spaces that are not on a story (in future could infer stories for these)
model.getSpaces.sort.each do |space|
if !space.buildingStory.is_initialized
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', "#{space.name} is not on a building story, may have unexpected results.")
@@ -388,11 +388,11 @@
bar = {}
bounding_length = envelope_data_hash[:building_max_xyz][0] - envelope_data_hash[:building_min_xyz][0]
bounding_width = envelope_data_hash[:building_max_xyz][1] - envelope_data_hash[:building_min_xyz][1]
bounding_area = bounding_length * bounding_width
- footprint_area = envelope_data_hash[:building_floor_area] / envelope_data_hash[:effective_num_stories].to_f
+ footprint_area = envelope_data_hash[:building_floor_area] / (envelope_data_hash[:effective_num_stories_above_grade] + envelope_data_hash[:effective_num_stories_below_grade].to_f)
area_multiplier = footprint_area / bounding_area
edge_multiplier = Math.sqrt(area_multiplier)
bar[:length] = bounding_length * edge_multiplier
bar[:width] = bounding_width * edge_multiplier
@@ -406,11 +406,11 @@
def self.bar_reduced_width(envelope_data_hash)
bar = {}
bounding_length = envelope_data_hash[:building_max_xyz][0] - envelope_data_hash[:building_min_xyz][0]
bounding_width = envelope_data_hash[:building_max_xyz][1] - envelope_data_hash[:building_min_xyz][1]
- footprint_area = envelope_data_hash[:building_floor_area] / envelope_data_hash[:effective_num_stories].to_f
+ footprint_area = envelope_data_hash[:building_floor_area] / (envelope_data_hash[:effective_num_stories_above_grade] + envelope_data_hash[:effective_num_stories_below_grade].to_f)
if bounding_length >= bounding_width
bar[:length] = bounding_length
bar[:width] = footprint_area / bounding_length
else
@@ -428,19 +428,19 @@
def self.bar_stretched(envelope_data_hash)
bar = {}
bounding_length = envelope_data_hash[:building_max_xyz][0] - envelope_data_hash[:building_min_xyz][0]
bounding_width = envelope_data_hash[:building_max_xyz][1] - envelope_data_hash[:building_min_xyz][1]
- a = envelope_data_hash[:building_floor_area] / envelope_data_hash[:effective_num_stories].to_f
+ a = envelope_data_hash[:building_floor_area] / (envelope_data_hash[:effective_num_stories_above_grade] + envelope_data_hash[:effective_num_stories_below_grade].to_f)
p = envelope_data_hash[:building_perimeter]
if bounding_length >= bounding_width
- bar[:length] = 0.25 * (p + Math.sqrt(p**2 - 16 * a))
- bar[:width] = 0.25 * (p - Math.sqrt(p**2 - 16 * a))
+ bar[:length] = 0.25 * (p + Math.sqrt((p**2) - (16 * a)))
+ bar[:width] = 0.25 * (p - Math.sqrt((p**2) - (16 * a)))
else
- bar[:length] = 0.25 * (p - Math.sqrt(p**2 - 16 * a))
- bar[:width] = 0.25 * (p + Math.sqrt(p**2 - 16 * a))
+ bar[:length] = 0.25 * (p - Math.sqrt((p**2) - (16 * a)))
+ bar[:width] = 0.25 * (p + Math.sqrt((p**2) - (16 * a)))
end
return bar
end
@@ -507,11 +507,11 @@
if bar_hash[:num_stories_below_grade] > 0
# add in below grade levels (may want to add below grade multipliers at some point if we start running deep basements)
eff_below.times do |i|
- story_hash["B#{i + 1}"] = { space_origin_z: footprint_origin_point.z - typical_story_height * (i + 1), space_height: typical_story_height, multiplier: 1 }
+ story_hash["B#{i + 1}"] = { space_origin_z: footprint_origin_point.z - (typical_story_height * (i + 1)), space_height: typical_story_height, multiplier: 1 }
end
end
# add in above grade levels
if eff_above > 2
@@ -525,19 +525,19 @@
if footprint_counter == 0
string = 'mid'
else
string = "mid#{footprint_counter + 1}"
end
- story_hash[string] = { space_origin_z: footprint_origin_point.z + typical_story_height * effective_stories_counter + typical_story_height * (hash[:multiplier] - 1) / 2.0, space_height: typical_story_height, multiplier: hash[:multiplier] }
+ story_hash[string] = { space_origin_z: footprint_origin_point.z + (typical_story_height * effective_stories_counter) + (typical_story_height * (hash[:multiplier] - 1) / 2.0), space_height: typical_story_height, multiplier: hash[:multiplier] }
footprint_counter += 1
effective_stories_counter += hash[:multiplier]
end
- story_hash['top'] = { space_origin_z: footprint_origin_point.z + typical_story_height * (eff_above.ceil - 1), space_height: typical_story_height, multiplier: 1 }
+ story_hash['top'] = { space_origin_z: footprint_origin_point.z + (typical_story_height * (eff_above.ceil - 1)), space_height: typical_story_height, multiplier: 1 }
elsif eff_above > 1
story_hash['ground'] = { space_origin_z: footprint_origin_point.z, space_height: typical_story_height, multiplier: 1 }
- story_hash['top'] = { space_origin_z: footprint_origin_point.z + typical_story_height * (eff_above.ceil - 1), space_height: typical_story_height, multiplier: 1 }
+ story_hash['top'] = { space_origin_z: footprint_origin_point.z + (typical_story_height * (eff_above.ceil - 1)), space_height: typical_story_height, multiplier: 1 }
else # one story only
story_hash['ground'] = { space_origin_z: footprint_origin_point.z, space_height: typical_story_height, multiplier: 1 }
end
# create footprints
@@ -669,28 +669,11 @@
end
end
end
end
- if !(bar_hash[:make_mid_story_surfaces_adiabatic])
- # intersect and surface match two pair by pair
- spaces_b = model.getSpaces.sort
- # looping through vector of each space
- model.getSpaces.sort.each do |space_a|
- spaces_b.delete(space_a)
- spaces_b.each do |space_b|
- # OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "Intersecting and matching surfaces between #{space_a.name} and #{space.name}")
- spaces_temp = OpenStudio::Model::SpaceVector.new
- spaces_temp << space_a
- spaces_temp << space_b
- # intersect and sort
- OpenStudio::Model.intersectSurfaces(spaces_temp)
- OpenStudio::Model.matchSurfaces(spaces_temp)
- end
- end
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', 'Intersecting and matching surfaces in model, this will create additional geometry.')
- else
+ if (bar_hash[:make_mid_story_surfaces_adiabatic])
# elsif bar_hash[:double_loaded_corridor] # only intersect spaces in each story, not between wtory
model.getBuilding.buildingStories.sort.each do |story|
# intersect and surface match two pair by pair
spaces_b = story.spaces.sort
# looping through vector of each space
@@ -706,22 +689,30 @@
OpenStudio::Model.matchSurfaces(spaces_temp)
end
end
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "Intersecting and matching surfaces in story #{story.name}, this will create additional geometry.")
end
+ else
+ # intersect and surface match two pair by pair
+ spaces_b = model.getSpaces.sort
+ # looping through vector of each space
+ model.getSpaces.sort.each do |space_a|
+ spaces_b.delete(space_a)
+ spaces_b.each do |space_b|
+ # OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "Intersecting and matching surfaces between #{space_a.name} and #{space.name}")
+ spaces_temp = OpenStudio::Model::SpaceVector.new
+ spaces_temp << space_a
+ spaces_temp << space_b
+ # intersect and sort
+ OpenStudio::Model.intersectSurfaces(spaces_temp)
+ OpenStudio::Model.matchSurfaces(spaces_temp)
+ end
+ end
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', 'Intersecting and matching surfaces in model, this will create additional geometry.')
end
else
- if !(bar_hash[:make_mid_story_surfaces_adiabatic])
- # intersect surfaces
- # (when bottom floor has many space types and one above doesn't will end up with heavily subdivided floor. Maybe use adiabatic and don't intersect floor/ceilings)
- intersect_surfaces = true
- if intersect_surfaces
- OpenStudio::Model.intersectSurfaces(spaces)
- OpenStudio::Model.matchSurfaces(spaces)
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', 'Intersecting and matching surfaces in model, this will create additional geometry.')
- end
- else
+ if (bar_hash[:make_mid_story_surfaces_adiabatic])
# elsif bar_hash[:double_loaded_corridor] # only intersect spaces in each story, not between wtory
model.getBuilding.buildingStories.sort.each do |story|
story_spaces = OpenStudio::Model::SpaceVector.new
story.spaces.sort.each do |space|
story_spaces << space
@@ -730,12 +721,20 @@
# intersect and sort
OpenStudio::Model.intersectSurfaces(story_spaces)
OpenStudio::Model.matchSurfaces(story_spaces)
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "Intersecting and matching surfaces in story #{story.name}, this will create additional geometry.")
end
+ else
+ # intersect surfaces
+ # (when bottom floor has many space types and one above doesn't will end up with heavily subdivided floor. Maybe use adiabatic and don't intersect floor/ceilings)
+ intersect_surfaces = true
+ if intersect_surfaces
+ OpenStudio::Model.intersectSurfaces(spaces)
+ OpenStudio::Model.matchSurfaces(spaces)
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', 'Intersecting and matching surfaces in model, this will create additional geometry.')
+ end
end
-
end
# set boundary conditions if not already set when geometry was created
# @todo update this to use space original z value vs. story name
if bar_hash[:num_stories_below_grade] > 0
@@ -862,16 +861,14 @@
party_wall_facades = stories_flat[i][:story_party_walls]
story.spaces.each do |space|
next if !new_spaces.include?(space)
- space.surfaces. each do |surface|
+ space.surfaces.each do |surface|
# set floor to adiabatic if requited
- if adiabatic_floor && surface.surfaceType == 'Floor'
+ if (adiabatic_floor && surface.surfaceType == 'Floor') || (adiabatic_ceiling && surface.surfaceType == 'RoofCeiling')
surface.setOutsideBoundaryCondition('Adiabatic')
- elsif adiabatic_ceiling && surface.surfaceType == 'RoofCeiling'
- surface.setOutsideBoundaryCondition('Adiabatic')
end
# skip of not exterior wall
next if surface.surfaceType != 'Wall'
next if surface.outsideBoundaryCondition != 'Outdoors'
@@ -891,20 +888,17 @@
if surface.space.is_initialized && surface.space.get.spaceType.is_initialized
space_type = surface.space.get.spaceType.get
# see if space type has wwr value
bar_hash[:space_types].each do |k, v|
- if v.key?(:space_type) && space_type == v[:space_type]
-
+ if v.key?(:space_type) && space_type == v[:space_type] && v.key?(:wwr)
# if matching space type specifies a wwr then override the orientation specific recommendations for this surface.
- if v.key?(:wwr)
- wwr_n = v[:wwr]
- wwr_e = v[:wwr]
- wwr_s = v[:wwr]
- wwr_w = v[:wwr]
- space_type_wwr_overrides[space_type] = v[:wwr]
- end
+ wwr_n = v[:wwr]
+ wwr_e = v[:wwr]
+ wwr_s = v[:wwr]
+ wwr_w = v[:wwr]
+ space_type_wwr_overrides[space_type] = v[:wwr]
end
end
end
# add fenestration (wwr for now, maybe overhang and overhead doors later)
@@ -931,11 +925,11 @@
surface.setOutsideBoundaryCondition('Adiabatic')
else
surface.setWindowToWallRatio(wwr_w)
end
else
- OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create', 'Unexpected value of facade: ' + absolute_azimuth + '.')
+ OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create', "Unexpected value of facade: #{absolute_azimuth}.")
return false
end
end
end
end
@@ -1040,19 +1034,19 @@
top_story_length = length * edge_multiplier
top_story_width = width * edge_multiplier
top_story_length_facade_area = top_story_length * floor_height
top_story_width_facade_area = top_story_width * floor_height
- total_exterior_wall_area = 2 * (length + width) * (args[:num_stories_above_grade].ceil - 1.0) * floor_height + 2 * (top_story_length + top_story_width) * floor_height
+ total_exterior_wall_area = (2 * (length + width) * (args[:num_stories_above_grade].ceil - 1.0) * floor_height) + (2 * (top_story_length + top_story_width) * floor_height)
target_party_wall_area = total_exterior_wall_area * args[:party_wall_fraction]
width_counter = 0
width_area = 0.0
facade_area = typical_width_facade_area
- until (width_area + facade_area >= target_party_wall_area) || (width_counter == args[:num_stories_above_grade].ceil * 2)
+ until (width_area + facade_area >= target_party_wall_area) || (width_counter == (args[:num_stories_above_grade].ceil * 2))
# update facade area for top story
- if width_counter == args[:num_stories_above_grade].ceil - 1 || width_counter == args[:num_stories_above_grade].ceil * 2 - 1
+ if width_counter == args[:num_stories_above_grade].ceil - 1 || ((width_counter == (args[:num_stories_above_grade].ceil * 2)) - 1)
facade_area = top_story_width_facade_area
else
facade_area = typical_width_facade_area
end
@@ -1065,11 +1059,11 @@
length_counter = 0
length_area = 0.0
facade_area = typical_length_facade_area
until (length_area + facade_area >= target_party_wall_area) || (length_counter == args[:num_stories_above_grade].ceil * 2)
# update facade area for top story
- if length_counter == args[:num_stories_above_grade].ceil - 1 || length_counter == args[:num_stories_above_grade].ceil * 2 - 1
+ if length_counter == args[:num_stories_above_grade].ceil - 1 || ((length_counter == args[:num_stories_above_grade].ceil * 2) - 1)
facade_area = top_story_length_facade_area
else
facade_area = typical_length_facade_area
end
@@ -1173,11 +1167,11 @@
if ground_floor_area > target_footprint + 0.001 || roof_area > target_footprint + 0.001
# OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create', "Ground exposed floor or Roof area is larger than footprint, likely inter-floor surface matching and intersection error.")
# return false
# not providing adiabatic work around when top story is partial story.
- if args[:num_stories_above_grade].to_f != args[:num_stories_above_grade].ceil
+ if args[:num_stories_above_grade].to_i != args[:num_stories_above_grade].ceil
OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create', 'Ground exposed floor or Roof area is larger than footprint, likely inter-floor surface matching and intersection error.')
return false
else
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', 'Ground exposed floor or Roof area is larger than footprint, likely inter-floor surface matching and intersection error, altering impacted surfaces boundary condition to be adiabatic.')
match_error = true
@@ -1294,16 +1288,16 @@
# if aspect ratio, story height or wwr have argument value of 0 then use smart building type defaults
# store list of defaulted items
defaulted_args = []
- if args[:ns_to_ew_ratio] == 0.0
+ if args[:ns_to_ew_ratio].abs < 0.01
args[:ns_to_ew_ratio] = building_form_defaults[:aspect_ratio]
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "0.0 value for aspect ratio will be replaced with smart default for #{primary_building_type} of #{building_form_defaults[:aspect_ratio]}.")
end
- if args[:perim_mult] == 0.0
+ if args[:perim_mult].abs < 0.01
# if this is not defined then use default of 1.0
if !building_form_defaults.key?(:perim_mult)
args[:perim_mult] = 1.0
else
args[:perim_mult] = building_form_defaults[:perim_mult]
@@ -1312,17 +1306,17 @@
elsif args[:perim_mult] < 1.0
OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create', 'Other than the smart default value of 0, the minimum perimeter multiplier should be equal to 1.0 or greater.')
return false
end
- if args[:floor_height] == 0.0
+ if args[:floor_height].abs < 0.01
args[:floor_height] = building_form_defaults[:typical_story]
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "0.0 value for floor height will be replaced with smart default for #{primary_building_type} of #{building_form_defaults[:typical_story]}.")
defaulted_args << 'floor_height'
end
# because of this can't set wwr to 0.0. If that is desired then we can change this to check for 1.0 instead of 0.0
- if args[:wwr] == 0.0
+ if args[:wwr].abs < 0.01
args[:wwr] = building_form_defaults[:wwr]
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "0.0 value for window to wall ratio will be replaced with smart default for #{primary_building_type} of #{building_form_defaults[:wwr]}.")
end
# report initial condition of model
@@ -1448,11 +1442,11 @@
space_type = hash[:space_type]
ratio_of_bldg_total = hash[:ratio] * building_type_hash[:ratio_adjustment_multiplier] * building_type_hash[:frac_bldg_area]
final_floor_area = ratio_of_bldg_total * total_bldg_floor_area_si # I think I can just pass ratio but passing in area is cleaner
# only add custom height space if 0 is used for floor_height
- if defaulted_args.include?(:floor_height) && hash.key?(:story_height) && args[:custom_height_bar]
+ if defaulted_args.include?('floor_height') && hash.key?(:story_height) && args[:custom_height_bar]
multi_height_space_types_hash[space_type] = { floor_area: final_floor_area, space_type: space_type, story_height: hash[:story_height] }
if hash.key?(:orig_ratio) then multi_height_space_types_hash[space_type][:orig_ratio] = hash[:orig_ratio] end
custom_story_heights << hash[:story_height]
if args[:wwr] == 0 && hash.key?(:wwr)
multi_height_space_types_hash[space_type][:wwr] = hash[:wwr]
@@ -1539,11 +1533,11 @@
elsif args[:ns_to_ew_ratio] < 1.0 && args[:ns_to_ew_ratio] > length / width
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', "Can't meet target aspect ratio of #{args[:ns_to_ew_ratio]}, Increasing it to #{length / width} ")
args[:ns_to_ew_ratio] = length / width
else
# check if each bar would be longer then 15 feet, then set as dual bar and override perimeter multiplier
- length_alt1 = ((args[:ns_to_ew_ratio] * footprint_si) / width + 2 * args[:ns_to_ew_ratio] * width - 2 * width) / (1 + args[:ns_to_ew_ratio])
+ length_alt1 = (((args[:ns_to_ew_ratio] * footprint_si) / width) + ((2 * (args[:ns_to_ew_ratio] * width)) - (2 * width))) / (1 + args[:ns_to_ew_ratio])
length_alt2 = length - length_alt1
if [length_alt1, length_alt2].min >= min_allow_size
dual_bar = true
else
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', 'Second bar would be below minimum length, will model as single bar')
@@ -1562,11 +1556,11 @@
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', "You selected a perimeter multiplier greater than 1.0 but didn't select a bar division method that supports this. The value for this argument will be ignored by the measure")
end
# calculations for dual bar, which later will be setup to run create_bar twice
if dual_bar
- min_perim = 2 * width + 2 * length
+ min_perim = (2 * width) + (2 * length)
target_area = footprint_si
target_perim = min_perim * args[:perim_mult]
tol_testing = 0.00001
dual_bar_calc_approach = nil # stretched, adiabatic_ends_bar_b, dual_bar
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "Minimum rectangle is #{OpenStudio.toNeatString(OpenStudio.convert(length, 'm', 'ft').get, 0, true)} ft x #{OpenStudio.toNeatString(OpenStudio.convert(width, 'm', 'ft').get, 0, true)} ft with an area of #{OpenStudio.toNeatString(OpenStudio.convert(length * width, 'm^2', 'ft^2').get, 0, true)} ft^2. Perimeter is #{OpenStudio.toNeatString(OpenStudio.convert(min_perim, 'm', 'ft').get, 0, true)} ft.")
@@ -1576,22 +1570,22 @@
# A use dual bar non adiabatic
# B use dual bar adiabatic
# C use stretched bar (requires model to miss ns/ew ratio)
# custom quadratic equation to solve two bars with common width 2l^2 - p*l + 4a = 0
- if target_perim**2 - 32 * footprint_si > 0
+ if ((target_perim**2) - (32 * footprint_si)) > 0
if specified_bar_width_si > 0
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', 'Ignoring perimeter multiplier argument and using use specified bar width.')
dual_double_end_width = specified_bar_width_si
dual_double_end_length = footprint_si / dual_double_end_width
else
- dual_double_end_length = 0.25 * (target_perim + Math.sqrt(target_perim**2 - 32 * footprint_si))
+ dual_double_end_length = 0.25 * (target_perim + Math.sqrt((target_perim**2) - (32 * footprint_si)))
dual_double_end_width = footprint_si / dual_double_end_length
end
# now that stretched bar is made, determine where to split it and rotate
- bar_a_length = (args[:ns_to_ew_ratio] * (dual_double_end_length + dual_double_end_width) - dual_double_end_width) / (1 + args[:ns_to_ew_ratio])
+ bar_a_length = ((args[:ns_to_ew_ratio] * (dual_double_end_length + dual_double_end_width)) - dual_double_end_width) / (1 + args[:ns_to_ew_ratio])
bar_b_length = dual_double_end_length - bar_a_length
area_a = bar_a_length * dual_double_end_width
area_b = bar_b_length * dual_double_end_width
else
# this will throw it to adiabatic ends test
@@ -1601,18 +1595,18 @@
if bar_a_length >= min_allow_size && bar_b_length >= min_allow_size
dual_bar_calc_approach = 'dual_bar'
else
# adiabatic bar input calcs
- if target_perim**2 - 16 * footprint_si > 0
- adiabatic_dual_double_end_length = 0.25 * (target_perim + Math.sqrt(target_perim**2 - 16 * footprint_si))
+ if ((target_perim**2) - (16 * footprint_si)) > 0
+ adiabatic_dual_double_end_length = 0.25 * (target_perim + Math.sqrt((target_perim**2) - (16 * footprint_si)))
adiabatic_dual_double_end_width = footprint_si / adiabatic_dual_double_end_length
# test for unexpected
unexpected = false
- if (target_area - adiabatic_dual_double_end_length * adiabatic_dual_double_end_width).abs > tol_testing then unexpected = true end
+ if (target_area - (adiabatic_dual_double_end_length * adiabatic_dual_double_end_width)).abs > tol_testing then unexpected = true end
if specified_bar_width_si == 0
- if (target_perim - (adiabatic_dual_double_end_length * 2 + adiabatic_dual_double_end_width * 2)).abs > tol_testing then unexpected = true end
+ if (target_perim - ((adiabatic_dual_double_end_length * 2) + (adiabatic_dual_double_end_width * 2))).abs > tol_testing then unexpected = true end
end
if unexpected
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', 'Unexpected values for dual rectangle adiabatic ends bar b.')
end
# now that stretched bar is made, determine where to split it and rotate
@@ -1632,33 +1626,33 @@
end
end
# apply prescribed approach for stretched or dual bar
if dual_bar_calc_approach == 'dual_bar'
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "Stretched #{OpenStudio.toNeatString(OpenStudio.convert(dual_double_end_length, 'm', 'ft').get, 0, true)} ft x #{OpenStudio.toNeatString(OpenStudio.convert(dual_double_end_width, 'm', 'ft').get, 0, true)} ft rectangle has an area of #{OpenStudio.toNeatString(OpenStudio.convert(dual_double_end_length * dual_double_end_width, 'm^2', 'ft^2').get, 0, true)} ft^2. When split in two the perimeter will be #{OpenStudio.toNeatString(OpenStudio.convert(dual_double_end_length * 2 + dual_double_end_width * 4, 'm', 'ft').get, 0, true)} ft")
- if (target_area - dual_double_end_length * dual_double_end_width).abs > tol_testing || (target_perim - (dual_double_end_length * 2 + dual_double_end_width * 4)).abs > tol_testing
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "Stretched #{OpenStudio.toNeatString(OpenStudio.convert(dual_double_end_length, 'm', 'ft').get, 0, true)} ft x #{OpenStudio.toNeatString(OpenStudio.convert(dual_double_end_width, 'm', 'ft').get, 0, true)} ft rectangle has an area of #{OpenStudio.toNeatString(OpenStudio.convert(dual_double_end_length * dual_double_end_width, 'm^2', 'ft^2').get, 0, true)} ft^2. When split in two the perimeter will be #{OpenStudio.toNeatString(OpenStudio.convert((dual_double_end_length * 2) + (dual_double_end_width * 4), 'm', 'ft').get, 0, true)} ft")
+ if (target_area - (dual_double_end_length * dual_double_end_width)).abs > tol_testing || (target_perim - ((dual_double_end_length * 2) + (dual_double_end_width * 4))).abs > tol_testing
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', 'Unexpected values for dual rectangle.')
end
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "For stretched split bar, to match target ns/ew aspect ratio #{OpenStudio.toNeatString(OpenStudio.convert(bar_a_length, 'm', 'ft').get, 0, true)} ft of bar should be horizontal, with #{OpenStudio.toNeatString(OpenStudio.convert(bar_b_length, 'm', 'ft').get, 0, true)} ft turned 90 degrees. Combined area is #{OpenStudio.toNeatString(OpenStudio.convert(area_a + area_b, 'm^2', 'ft^2').get, 0, true)} ft^2. Combined perimeter is #{OpenStudio.toNeatString(OpenStudio.convert(bar_a_length * 2 + bar_b_length * 2 + dual_double_end_width * 4, 'm', 'ft').get, 0, true)} ft")
- if (target_area - (area_a + area_b)).abs > tol_testing || (target_perim - (bar_a_length * 2 + bar_b_length * 2 + dual_double_end_width * 4)).abs > tol_testing
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "For stretched split bar, to match target ns/ew aspect ratio #{OpenStudio.toNeatString(OpenStudio.convert(bar_a_length, 'm', 'ft').get, 0, true)} ft of bar should be horizontal, with #{OpenStudio.toNeatString(OpenStudio.convert(bar_b_length, 'm', 'ft').get, 0, true)} ft turned 90 degrees. Combined area is #{OpenStudio.toNeatString(OpenStudio.convert(area_a + area_b, 'm^2', 'ft^2').get, 0, true)} ft^2. Combined perimeter is #{OpenStudio.toNeatString(OpenStudio.convert((bar_a_length * 2) + (bar_b_length * 2) + (dual_double_end_width * 4), 'm', 'ft').get, 0, true)} ft")
+ if (target_area - (area_a + area_b)).abs > tol_testing || (target_perim - ((bar_a_length * 2) + (bar_b_length * 2) + (dual_double_end_width * 4))).abs > tol_testing
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', 'Unexpected values for rotated dual rectangle')
end
elsif dual_bar_calc_approach == 'adiabatic_ends_bar_b'
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "Can't hit target perimeter with two rectangles, need to make two ends adiabatic")
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "For dual bar with adiabatic ends on bar b, to reach target aspect ratio #{OpenStudio.toNeatString(OpenStudio.convert(adiabatic_bar_a_length, 'm', 'ft').get, 0, true)} ft of bar should be north/south, with #{OpenStudio.toNeatString(OpenStudio.convert(adiabatic_bar_b_length, 'm', 'ft').get, 0, true)} ft turned 90 degrees. Combined area is #{OpenStudio.toNeatString(OpenStudio.convert(adiabatic_area_a + adiabatic_area_b, 'm^2', 'ft^2').get, 0, true)} ft^2}. Combined perimeter is #{OpenStudio.toNeatString(OpenStudio.convert(adiabatic_bar_a_length * 2 + adiabatic_bar_b_length * 2 + adiabatic_dual_double_end_width * 2, 'm', 'ft').get, 0, true)} ft")
- if (target_area - (adiabatic_area_a + adiabatic_area_b)).abs > tol_testing || (target_perim - (adiabatic_bar_a_length * 2 + adiabatic_bar_b_length * 2 + adiabatic_dual_double_end_width * 2)).abs > tol_testing
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "For dual bar with adiabatic ends on bar b, to reach target aspect ratio #{OpenStudio.toNeatString(OpenStudio.convert(adiabatic_bar_a_length, 'm', 'ft').get, 0, true)} ft of bar should be north/south, with #{OpenStudio.toNeatString(OpenStudio.convert(adiabatic_bar_b_length, 'm', 'ft').get, 0, true)} ft turned 90 degrees. Combined area is #{OpenStudio.toNeatString(OpenStudio.convert(adiabatic_area_a + adiabatic_area_b, 'm^2', 'ft^2').get, 0, true)} ft^2}. Combined perimeter is #{OpenStudio.toNeatString(OpenStudio.convert((adiabatic_bar_a_length * 2) + (adiabatic_bar_b_length * 2) + (adiabatic_dual_double_end_width * 2), 'm', 'ft').get, 0, true)} ft")
+ if (target_area - (adiabatic_area_a + adiabatic_area_b)).abs > tol_testing || (target_perim - ((adiabatic_bar_a_length * 2) + (adiabatic_bar_b_length * 2) + (adiabatic_dual_double_end_width * 2))).abs > tol_testing
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', 'Unexpected values for rotated dual rectangle adiabatic ends bar b')
end
else
# stretched bar
dual_bar = false
- stretched_length = 0.25 * (target_perim + Math.sqrt(target_perim**2 - 16 * footprint_si))
+ stretched_length = 0.25 * (target_perim + Math.sqrt((target_perim**2) - (16 * footprint_si)))
stretched_width = footprint_si / stretched_length
- if (target_area - stretched_length * stretched_width).abs > tol_testing || (target_perim - (stretched_length + stretched_width) * 2) > tol_testing
+ if (target_area - (stretched_length * stretched_width)).abs > tol_testing || (target_perim - ((stretched_length + stretched_width) * 2)) > tol_testing
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', 'Unexpected values for single stretched')
end
width = stretched_width
length = stretched_length
@@ -1706,20 +1700,20 @@
pri_sec_min_area = 0.0001 # m^2
space_types_hash.each do |k, v|
space_type_left = v[:floor_area]
# do not go to next space type until this one is evaulate, which may span stories
- until space_type_left == 0.0 || story_counter >= num_stories
+ until (space_type_left.abs < 0.01) || (story_counter >= num_stories)
# use secondary footprint if any left
if secondary_footprint_counter > 0.0
hash_area = [space_type_left, secondary_footprint_counter].min
# confirm that the part of space type use or what is left is greater than min allowed value
projected_space_type_left = space_type_left - hash_area
test_a = hash_area >= pri_sec_min_area
- test_b = projected_space_type_left >= pri_sec_min_area || projected_space_type_left == 0.0 ? true : false
+ test_b = (projected_space_type_left >= pri_sec_min_area) || (projected_space_type_left.abs < 0.01)
test_c = k == space_types_hash.keys.last # if last space type accept sliver, no other space to infil
if (test_a && test_b) || test_c
if space_types_hash_secondary.key?(k)
# add to what was added for previous story
space_types_hash_secondary[k][:floor_area] += hash_area
@@ -1810,13 +1804,13 @@
if args[:party_wall_fraction] > 0 || args[:party_wall_stories_north] > 0 || args[:party_wall_stories_south] > 0 || args[:party_wall_stories_east] > 0 || args[:party_wall_stories_west] > 0
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', 'The combination of low perimeter multiplier and use of non zero party wall inputs may result in discrepency between target and actual adiabatic walls. This is due to the need to create adiabatic walls on secondary bar to maintian target building perimeter.')
else
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', 'Adiabatic ends added to secondary bar because target perimeter multiplier could not be met with two full rectangular footprints.')
end
- bars['secondary'][:center_of_footprint] = OpenStudio::Point3d.new(adiabatic_bar_a_length * 0.5 + adiabatic_dual_double_end_width * 0.5 + offset_val, adiabatic_bar_b_length * 0.5 + adiabatic_dual_double_end_width * 0.5 + offset_val, 0.0)
+ bars['secondary'][:center_of_footprint] = OpenStudio::Point3d.new((adiabatic_bar_a_length * 0.5) + (adiabatic_dual_double_end_width * 0.5) + offset_val, (adiabatic_bar_b_length * 0.5) + (adiabatic_dual_double_end_width * 0.5) + offset_val, 0.0)
else
- bars['secondary'][:center_of_footprint] = OpenStudio::Point3d.new(bar_a_length * 0.5 + dual_double_end_width * 0.5 + offset_val, bar_b_length * 0.5 + dual_double_end_width * 0.5 + offset_val, 0.0)
+ bars['secondary'][:center_of_footprint] = OpenStudio::Point3d.new((bar_a_length * 0.5) + (dual_double_end_width * 0.5) + offset_val, (bar_b_length * 0.5) + (dual_double_end_width * 0.5) + offset_val, 0.0)
end
bars['secondary'][:args] = args2
# setup bar_hash and run create_bar
v = bars['secondary']
@@ -1838,23 +1832,23 @@
if args[:party_wall_stories_east] + args[:party_wall_stories_west] + args[:party_wall_stories_south] + args[:party_wall_stories_north] > 0.0
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', 'Ignorning party wall inputs for custom height bar')
end
# disable party walls
- args3['party_wall_stories_east'] = 0
- args3['party_wall_stories_west'] = 0
- args3['party_wall_stories_south'] = 0
- args3['party_wall_stories_north'] = 0
+ args3[:party_wall_stories_east] = 0
+ args3[:party_wall_stories_west] = 0
+ args3[:party_wall_stories_south] = 0
+ args3[:party_wall_stories_north] = 0
# setup stories
- args3['num_stories_below_grade'] = 0
- args3['num_stories_above_grade'] = 1
+ args3[:num_stories_below_grade] = 0
+ args3[:num_stories_above_grade] = 1
# can make use of this when breaking out multi-height spaces
bars['custom_height'][:floor_height] = floor_height
bars['custom_height'][:num_stories] = num_stories
- bars['custom_height'][:center_of_footprint] = OpenStudio::Point3d.new(bars['primary'][:length] * -0.5 - length_cust_height * 0.5 - offset_val, 0.0, 0.0)
+ bars['custom_height'][:center_of_footprint] = OpenStudio::Point3d.new((bars['primary'][:length] * -0.5) - (length_cust_height * 0.5) - offset_val, 0.0, 0.0)
bars['custom_height'][:floor_height] = OpenStudio.convert(custom_story_heights.max, 'ft', 'm').get
bars['custom_height'][:num_stories] = 1
bars['custom_height'][:space_types_hash] = multi_height_space_types_hash
bars['custom_height'][:args] = args3
@@ -1928,11 +1922,11 @@
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', 'Target facade area by orientation not validated when party walls are applied')
elsif args[:num_stories_above_grade] != args[:num_stories_above_grade].ceil
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', 'Target facade area by orientation not validated when partial top story is used')
elsif dual_bar_calc_approach == 'stretched'
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', 'Target facade area by orientation not validated when single stretched bar has to be used to meet target minimum perimeter multiplier')
- elsif defaulted_args.include?(:floor_height) && args[:custom_height_bar] && !multi_height_space_types_hash.empty?
+ elsif defaulted_args.include?('floor_height') && args[:custom_height_bar] && !multi_height_space_types_hash.empty?
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', 'Target facade area by orientation not validated when a dedicated bar is added for space types with custom heights')
elsif args[:bar_width] > 0
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', 'Target facade area by orientation not validated when a dedicated custom bar width is defined')
else
@@ -1959,11 +1953,11 @@
end
# test for excessive exterior roof area (indication of problem with intersection and or surface matching)
ext_roof_area = model.getBuilding.exteriorSurfaceArea - model.getBuilding.exteriorWallArea
expected_roof_area = args[:total_bldg_floor_area] / (args[:num_stories_above_grade] + args[:num_stories_below_grade]).to_f
- if ext_roof_area > expected_roof_area && single_floor_area_si == 0.0 # only test if using whole-building area input
+ if ext_roof_area > expected_roof_area && (single_floor_area_si.abs < 0.01) # only test if using whole-building area input
OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create', 'Roof area larger than expected, may indicate problem with inter-floor surface intersection or matching.')
return false
end
# set building rotation
@@ -1998,10 +1992,12 @@
# @option args [String] :bldg_type_c_fract_bldg_area (0.0) building type c area fraction of total floor area
# @option args [String] :bldg_type_d_fract_bldg_area (0.0) building type d area fraction of total floor area
# @option args [String] :template ('90.1-2013') target standard
# @return [Boolean] returns true if successful, false if not
def self.create_bar_from_building_type_ratios(model, args)
+ # convert arguments to symbols
+ args = args.transform_keys(&:to_sym)
bldg_type_a = args.fetch(:bldg_type_a, 'SmallOffice')
bldg_type_b = args.fetch(:bldg_type_b, nil)
bldg_type_c = args.fetch(:bldg_type_c, nil)
bldg_type_d = args.fetch(:bldg_type_d, nil)
bldg_subtype_a = args.fetch(:bldg_subtype_a, 'NA')
@@ -2011,9 +2007,64 @@
bldg_type_a_fract_bldg_area = args.fetch(:bldg_type_a_fract_bldg_area, 1.0)
bldg_type_b_fract_bldg_area = args.fetch(:bldg_type_b_fract_bldg_area, 0.0)
bldg_type_c_fract_bldg_area = args.fetch(:bldg_type_c_fract_bldg_area, 0.0)
bldg_type_d_fract_bldg_area = args.fetch(:bldg_type_d_fract_bldg_area, 0.0)
template = args.fetch(:template, '90.1-2013')
+
+ # If DOE building type is supplied with a DEER template, map to the nearest DEER building type
+ if template.include?('DEER')
+ unless bldg_type_a.nil?
+ bldg_type_a_deer = OpenstudioStandards::CreateTypical.doe_to_deer_building_type(bldg_type_a)
+ if bldg_type_a_deer.nil?
+ OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create', "Could not find a DEER equivalent to #{bldg_type_a} to align with template #{template}.")
+ return false
+ elsif bldg_type_a == bldg_type_a_deer
+ # Already using a DEER building type with a DEER template, no need to change
+ else
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "Mapped input of DOE building type #{bldg_type_a} to DEER building type #{bldg_type_a_deer} to match template #{template}")
+ bldg_type_a = bldg_type_a_deer
+ end
+ end
+
+ unless bldg_type_b.nil?
+ bldg_type_b_deer = OpenstudioStandards::CreateTypical.doe_to_deer_building_type(bldg_type_b)
+ if bldg_type_b_deer.nil?
+ OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create', "Could not find a DEER equivalent to #{bldg_type_b} to align with template #{template}.")
+ return false
+ elsif bldg_type_b == bldg_type_b_deer
+ # Already using a DEER building type with a DEER template, no need to change
+ else
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "Mapped input of DOE building type #{bldg_type_b} to DEER building type #{bldg_type_b_deer} to match template #{template}")
+ bldg_type_b = bldg_type_b_deer
+ end
+ end
+
+ unless bldg_type_c.nil?
+ bldg_type_c_deer = OpenstudioStandards::CreateTypical.doe_to_deer_building_type(bldg_type_c)
+ if bldg_type_c_deer.nil?
+ OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create', "Could not find a DEER equivalent to #{bldg_type_c} to align with template #{template}.")
+ return false
+ elsif bldg_type_c == bldg_type_c_deer
+ # Already using a DEER building type with a DEER template, no need to change
+ else
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "Mapped input of DOE building type #{bldg_type_c} to DEER building type #{bldg_type_c_deer} to match template #{template}")
+ bldg_type_c = bldg_type_c_deer
+ end
+ end
+
+ unless bldg_type_d.nil?
+ bldg_type_d_deer = OpenstudioStandards::CreateTypical.doe_to_deer_building_type(bldg_type_d)
+ if bldg_type_d_deer.nil?
+ OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create', "Could not find a DEER equivalent to #{bldg_type_d} to align with template #{template}.")
+ return false
+ elsif bldg_type_d == bldg_type_d_deer
+ # Already using a DEER building type with a DEER template, no need to change
+ else
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "Mapped input of DOE building type #{bldg_type_d} to DEER building type #{bldg_type_d_deer} to match template #{template}")
+ bldg_type_d = bldg_type_d_deer
+ end
+ end
+ end
# check that sum of fractions for b,c, and d is less than 1.0 (so something is left for primary building type)
bldg_type_a_fract_bldg_area = 1.0 - bldg_type_b_fract_bldg_area - bldg_type_c_fract_bldg_area - bldg_type_d_fract_bldg_area
if bldg_type_a_fract_bldg_area <= 0.0
OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create', 'Primary Building Type fraction of floor area must be greater than 0. Please lower one or more of the fractions for Building Type B-D.')