Module: OpenstudioStandards::Geometry
- Defined in:
- lib/openstudio-standards/geometry/group.rb,
lib/openstudio-standards/geometry/create.rb,
lib/openstudio-standards/geometry/modify.rb,
lib/openstudio-standards/geometry/create_bar.rb,
lib/openstudio-standards/geometry/information.rb,
lib/openstudio-standards/geometry/create_shape.rb
Overview
This Module provides methods to create, modify, and get information about model geometry
Group collapse
-
.model_group_thermal_zones_by_building_story(model, thermal_zones) ⇒ Array<Array<OpenStudio::Model::ThermalZone>>
Group an array of zones into multiple arrays, one for each story in the building.
-
.model_group_thermal_zones_by_building_type(model, min_area_m2: 1858.0608) ⇒ Array<Hash>
Split all zones in the model into groups that are big enough to justify their own HVAC system type.
-
.model_group_thermal_zones_by_occupancy_type(model, min_area_m2: 1858.0608) ⇒ Array<Hash>
Split all zones in the model into groups that are big enough to justify their own HVAC system type.
Create collapse
-
.create_core_and_perimeter_polygons(length, width, footprint_origin_point = OpenStudio::Point3d.new(0.0, 0.0, 0.0), perimeter_zone_depth = OpenStudio.convert(15.0, 'ft', 'm').get) ⇒ Hash
create core and perimeter polygons from length width and origin.
-
.create_sliced_bar_multi_polygons(space_types, length, width, footprint_origin_point, story_hash) ⇒ Hash
sliced bar multi creates and array of multiple sliced bar simple hashes.
-
.create_sliced_bar_simple_polygons(space_types, length, width, footprint_origin_point = OpenStudio::Point3d.new(0.0, 0.0, 0.0), perimeter_zone_depth = OpenStudio.convert(15.0, 'ft', 'm').get) ⇒ Hash
sliced bar simple creates a single sliced bar for space types passed in look at length and width to adjust slicing direction.
-
.create_space_from_polygon(model, space_origin, point_3d_vector, options = {}) ⇒ OpenStudio::Model::Space
add def to create a space from input, optionally take a name, space type, story and thermal zone.
-
.create_spaces_from_polygons(model, footprints, typical_story_height, effective_num_stories, footprint_origin_point = OpenStudio::Point3d.new(0.0, 0.0, 0.0), story_hash = {}) ⇒ Array<OpenStudio::Model::Space>
take diagram made by create_core_and_perimeter_polygons and make multi-story building.
-
.space_create_point_at_center_of_floor(space, z_offset_m) ⇒ OpenStudio::Point3d
method to create a point object at the center of a floor.
-
.sub_surface_create_point_at_specific_height(sub_surface, reference_floor, distance_from_window_m, height_above_subsurface_bottom_m) ⇒ OpenStudio::Point3d
method to create a point object from a sub surface.
Modify:SubSurface collapse
-
.sub_surface_reduce_area_by_percent_by_raising_sill(sub_surface, percent_reduction) ⇒ Boolean
Reduce the area of the subsurface by raising the sill height.
-
.sub_surface_reduce_area_by_percent_by_shrinking_toward_centroid(sub_surface, percent_reduction) ⇒ Boolean
Reduce the area of the subsurface by shrinking it toward the centroid.
Modify:Model collapse
-
.model_assign_spaces_to_building_stories(model) ⇒ Boolean
Assign each space in the model to a building story based on common z (height) values.
-
.model_set_building_north_axis(model, north_axis) ⇒ Boolean
Set the model’s north axis (degrees from true North).
CreateBar collapse
-
.bar_hash_setup_run(model, args, length, width, floor_height, center_of_footprint, space_types_hash, num_stories) ⇒ Boolean
give info messages bar hash for create_bar method.
-
.bar_reduced_bounding_box(envelope_data_hash) ⇒ Hash
get length and width of rectangle matching bounding box aspect ratio will maintaining proper floor area.
-
.bar_reduced_width(envelope_data_hash) ⇒ Hash
get length and width of rectangle matching longer of two edges, and reducing the other way until floor area matches.
-
.bar_stretched(envelope_data_hash) ⇒ Hash
get length and width of rectangle by stretching it until both floor area and exterior wall area or perimeter match.
-
.building_form_defaults(building_type) ⇒ Hash
Building Form Defaults from Table 4.2 in Achieving the 30% Goal: Energy and Cost Savings Analysis of ASHRAE Standard 90.1-2010 aspect ratio for NA replaced with floor area to perimeter ratio from prototype model currently no reason to split apart doe and deer inputs here.
-
.create_bar(model, bar_hash) ⇒ Array<OpenStudio::Model::Space>
create_bar creates spaces based on a set of geometric characteristics.
-
.create_bar_from_args_and_building_type_hash(model, args, building_type_hash) ⇒ Boolean
create bar from arguments and building type hash.
-
.create_bar_from_building_type_ratios(model, args) ⇒ Boolean
create bar from building type ratios arguments are passed through to lower level methods.
-
.create_bar_from_space_type_ratios(model, args) ⇒ Boolean
create bar from space type ratios arguments are passed through to lower level methods.
-
.model_envelope_data(model) ⇒ Hash
gather envelope data for envelope simplification.
-
.model_sort_building_stories_and_get_min_multiplier(model) ⇒ Hash
sort building stories.
Information:Calculations collapse
-
.aspect_ratio(area, perimeter) ⇒ Double
calculate aspect ratio from area and perimeter.
-
.wall_and_floor_intersection_length(wall, floor) ⇒ Double
This function returns the length of intersection between a wall and floor sharing space.
Information:Surface collapse
-
.surface_get_absolute_azimuth(surface) ⇒ Double
Calculate a surface’s absolute azimuth.
-
.surface_get_cardinal_direction(surface) ⇒ String
Determine a surface absolute cardinal direction.
-
.surface_get_door_to_wall_ratio(surface) ⇒ Double
Calculate the door to wall ratio of a surface.
-
.surface_get_edges(surface) ⇒ Array<Array(OpenStudio::Point3D, OpenStudio::Point3D)>
Returns an array of OpenStudio::Point3D pairs of an OpenStudio::Model::Surface’s edges.
-
.surface_get_window_to_wall_ratio(surface) ⇒ Double
Calculate the window to wall ratio of a surface.
Information:Surfaces collapse
-
.surfaces_contain_point?(surfaces, point) ⇒ Boolean
Check if a point is contained on any surface in an array of surfaces.
-
.surfaces_get_z_values(surfaces) ⇒ Array<Double>
return an array of z values for surfaces passed in.
Information:SubSurface collapse
-
.sub_surface_vertical_rectangle?(sub_surface) ⇒ Boolean
Determine if the sub surface is a vertical rectangle, meaning a rectangle where the bottom is parallel to the ground.
Information:Space collapse
-
.space_get_adjacent_space_with_most_shared_wall_area(space, same_floor: true) ⇒ OpenStudio::Model::Space
Find the space that has the most wall area touching this space.
-
.space_get_adjacent_spaces_with_shared_wall_areas(space, same_floor: true) ⇒ Hash
Get a sorted array of tuples containing a list of spaces and connected area in descending order.
-
.space_get_below_grade_wall_height(space) ⇒ Double
Finds heights of the first below grade walls and returns them as a numeric.
-
.space_get_envelope_area(space, multiplier: true) ⇒ Double
Calculate the space envelope area.
-
.space_get_exterior_wall_and_subsurface_and_roof_area(space, multiplier: false) ⇒ Double
Calculate the area of the exterior walls, including the area of the windows and doors on these walls, and the area of roofs.
-
.space_get_exterior_wall_and_subsurface_area(space, multiplier: false) ⇒ Double
Calculate the area of the exterior walls, including the area of the windows and doors on these walls.
-
.space_get_f_floor_area(space) ⇒ Double
This function returns the space’s ground area.
-
.space_get_f_floor_perimeter(space) ⇒ Double
This function returns the space’s ground perimeter length.
Information:Spaces collapse
-
.spaces_get_exterior_area(spaces, multiplier: true) ⇒ Double
Get the total exterior area of selected spaces.
-
.spaces_get_exterior_wall_area(spaces, multiplier: true) ⇒ Double
Get the total exterior wall area of selected spaces.
-
.spaces_get_floor_area(spaces, multiplier: true) ⇒ Double
Get the total floor area of selected spaces.
Information:ThermalZone collapse
-
.thermal_zone_get_adjacent_zones_with_shared_walls(thermal_zone, same_floor: true) ⇒ Array<OpenStudio::Model::ThermalZone>
Return an array of zones that share a wall with the zone.
Information:ThermalZones collapse
-
.thermal_zones_get_number_of_stories_spanned(thermal_zones) ⇒ Integer
Determine the number of stories spanned by the supplied thermal zones.
Information:Story collapse
-
.building_story_get_exterior_wall_perimeter(story, multiplier_adjustment: nil, exterior_boundary_conditions: ['Outdoors', 'Ground'], bounding_box: nil) ⇒ Object
Calculate the story exterior wall perimeter.
-
.building_story_get_floor_multiplier(building_story) ⇒ Integer
Checks all spaces on this story that are part of the total floor area to see if they have the same multiplier.
-
.building_story_get_minimum_height(building_story) ⇒ Double
Gets the minimum height of the building story.
-
.building_story_get_thermal_zones(building_story) ⇒ Array<OpenStudio::Model::ThermalZone>
Get an array of OpenStudio ThermalZone objects for an OpenStudio BuildingStory.
Information:Model collapse
-
.model_get_building_stories_above_ground(model) ⇒ Array<OpenStudio::Model::BuildingStory>
Returns an array of the above ground building stories in the model.
-
.model_get_building_stories_below_ground(model) ⇒ Array<OpenStudio::Model::BuildingStory>
Returns an array of the below ground building stories in the model.
-
.model_get_building_story_for_nominal_height(model, minimum_height, tolerance: 0.3) ⇒ OpenStudio::Model::BuildingStory
Returns the building story associated with a given minimum height.
-
.model_get_exterior_window_and_wall_area_by_orientation(model, spaces: []) ⇒ Hash
Returns the wall area and window area by orientation.
-
.model_get_exterior_window_to_wall_ratio(model, spaces: [], cardinal_direction: nil) ⇒ Double
Returns the window to wall ratio.
-
.model_get_perimeter(model) ⇒ Double
Calculates the exterior perimeter length, checking checks for edges shared by a ground exposed floor and exterior exposed wall.
Create:Shape collapse
-
.create_shape_aspect_ratio(model, aspect_ratio = 0.5, floor_area = 1000.0, rotation = 0.0, num_floors = 3, floor_to_floor_height = 3.8, plenum_height = 1.0, perimeter_zone_depth = 4.57) ⇒ OpenStudio::Model::Model
Create a Rectangle shape in a model based on a given aspect ratio.
-
.create_shape_courtyard(model, length = 50.0, width = 30.0, courtyard_length = 15.0, courtyard_width = 5.0, num_floors = 3, floor_to_floor_height = 3.8, plenum_height = 1.0, perimeter_zone_depth = 4.57) ⇒ OpenStudio::Model::Model
Create a Courtyard shape in a model.
-
.create_shape_h(model, length = 40.0, left_width = 40.0, center_width = 10.0, right_width = 40.0, left_end_length = 15.0, right_end_length = 15.0, left_upper_end_offset = 15.0, right_upper_end_offset = 15.0, num_floors = 3, floor_to_floor_height = 3.8, plenum_height = 1, perimeter_zone_depth = 4.57) ⇒ OpenStudio::Model::Model
Create an H shape in a model.
-
.create_shape_l(model, length = 40.0, width = 40.0, lower_end_width = 20.0, upper_end_length = 20.0, num_floors = 3, floor_to_floor_height = 3.8, plenum_height = 1.0, perimeter_zone_depth = 4.57) ⇒ OpenStudio::Model::Model
Create an L shape in a model.
-
.create_shape_rectangle(model, length = 100.0, width = 100.0, above_ground_storys = 3, under_ground_storys = 1, floor_to_floor_height = 3.8, plenum_height = 1.0, perimeter_zone_depth = 4.57, initial_height = 0.0) ⇒ OpenStudio::Model::Model
Create a Rectangle shape in a model.
-
.create_shape_t(model, length = 40.0, width = 40.0, upper_end_width = 20.0, lower_end_length = 20.0, left_end_offset = 10.0, num_floors = 3, floor_to_floor_height = 3.8, plenum_height = 1.0, perimeter_zone_depth = 4.57) ⇒ OpenStudio::Model::Model
Create a T shape in a model.
-
.create_shape_u(model, length = 40.0, left_width = 40.0, right_width = 40.0, left_end_length = 15.0, right_end_length = 15.0, left_end_offset = 25.0, num_floors = 3.0, floor_to_floor_height = 3.8, plenum_height = 1.0, perimeter_zone_depth = 4.57) ⇒ OpenStudio::Model::Model
Create a U shape in a model.
Class Method Details
.aspect_ratio(area, perimeter) ⇒ Double
calculate aspect ratio from area and perimeter
13 14 15 16 17 18 19 |
# File 'lib/openstudio-standards/geometry/information.rb', line 13 def self.aspect_ratio(area, perimeter) length = 0.25 * (perimeter + Math.sqrt((perimeter**2) - (16 * area))) width = 0.25 * (perimeter - Math.sqrt((perimeter**2) - (16 * area))) aspect_ratio = length / width return aspect_ratio end |
.bar_hash_setup_run(model, args, length, width, floor_height, center_of_footprint, space_types_hash, num_stories) ⇒ Boolean
give info messages bar hash for create_bar method
969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 |
# File 'lib/openstudio-standards/geometry/create_bar.rb', line 969 def self.(model, args, length, width, floor_height, center_of_footprint, space_types_hash, num_stories) # create envelope # populate bar_hash and create envelope with data from envelope_data_hash and user arguments = {} [:length] = length [:width] = width [:num_stories_below_grade] = args[:num_stories_below_grade] [:num_stories_above_grade] = args[:num_stories_above_grade] [:floor_height] = floor_height [:center_of_footprint] = center_of_footprint [:bar_division_method] = args[:bar_division_method] [:story_multiplier_method] = args[:story_multiplier_method] [:make_mid_story_surfaces_adiabatic] = args[:make_mid_story_surfaces_adiabatic] [:space_types] = space_types_hash [:building_wwr_n] = args[:wwr] [:building_wwr_s] = args[:wwr] [:building_wwr_e] = args[:wwr] [:building_wwr_w] = args[:wwr] # round up non integer stoires to next integer num_stories_round_up = num_stories.ceil OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "Making bar with length of #{OpenStudio.toNeatString(OpenStudio.convert(length, 'm', 'ft').get, 0, true)} ft and width of #{OpenStudio.toNeatString(OpenStudio.convert(width, 'm', 'ft').get, 0, true)} ft") # party_walls_array to be used by orientation specific or fractional party wall values party_walls_array = [] # this is an array of arrays, where each entry is effective building story with array of directions if args[:party_wall_stories_north] + args[:party_wall_stories_south] + args[:party_wall_stories_east] + args[:party_wall_stories_west] > 0 # loop through effective number of stories add orientation specific party walls per user arguments num_stories_round_up.times do |i| test_value = i + 1 - [:num_stories_below_grade] array = [] if args[:party_wall_stories_north] >= test_value array << 'north' end if args[:party_wall_stories_south] >= test_value array << 'south' end if args[:party_wall_stories_east] >= test_value array << 'east' end if args[:party_wall_stories_west] >= test_value array << 'west' end # populate party_wall_array for this story party_walls_array << array end end # calculate party walls if using party_wall_fraction method if args[:party_wall_fraction] > 0 && !party_walls_array.empty? OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', 'Both orientation and fractional party wall values arguments were populated, will ignore fractional party wall input') elsif args[:party_wall_fraction] > 0 # orientation of long and short side of building will vary based on building rotation # full story ext wall area typical_length_facade_area = length * floor_height typical_width_facade_area = width * floor_height # top story ext wall area, may be partial story partial_story_multiplier = (1.0 - args[:num_stories_above_grade].ceil + args[:num_stories_above_grade]) area_multiplier = partial_story_multiplier edge_multiplier = Math.sqrt(area_multiplier) 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) 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)) # 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) facade_area = top_story_width_facade_area else facade_area = typical_width_facade_area end width_counter += 1 width_area += facade_area end width_area_remainder = target_party_wall_area - width_area 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) facade_area = top_story_length_facade_area else facade_area = typical_length_facade_area end length_counter += 1 length_area += facade_area end length_area_remainder = target_party_wall_area - length_area # get rotation and best fit to adjust orientation for fraction party wall rotation = args[:building_rotation] % 360.0 # should result in value between 0 and 360 card_dir_array = [0.0, 90.0, 180.0, 270.0, 360.0] # reverse array to properly handle 45, 135, 225, and 315 best_fit = card_dir_array.reverse.min_by { |x| (x.to_f - rotation).abs } if ![90.0, 270.0].include? best_fit width_card_dir = ['east', 'west'] length_card_dir = ['north', 'south'] else # if rotation is closest to 90 or 270 then reverse which orientation is used for length and width width_card_dir = ['north', 'south'] length_card_dir = ['east', 'west'] end # if dont' find enough on short sides if width_area_remainder <= typical_length_facade_area num_stories_round_up.times do |i| if i + 1 <= args[:num_stories_below_grade] party_walls_array << [] next end if i + 1 - args[:num_stories_below_grade] <= width_counter if i + 1 - args[:num_stories_below_grade] <= width_counter - args[:num_stories_above_grade] party_walls_array << width_card_dir else party_walls_array << [width_card_dir.first] end else party_walls_array << [] end end else # use long sides instead num_stories_round_up.times do |i| if i + 1 <= args[:num_stories_below_grade] party_walls_array << [] next end if i + 1 - args[:num_stories_below_grade] <= length_counter if i + 1 - args[:num_stories_below_grade] <= length_counter - args[:num_stories_above_grade] party_walls_array << length_card_dir else party_walls_array << [length_card_dir.first] end else party_walls_array << [] end end end # @todo currently won't go past making two opposing sets of walls party walls. Info and registerValue are after create_bar in measure.rb end # populate bar hash with story information [:stories] = {} num_stories_round_up.times do |i| if party_walls_array.empty? party_walls = [] else party_walls = party_walls_array[i] end # add below_partial_story if num_stories.ceil > num_stories && i == num_stories_round_up - 2 below_partial_story = true else below_partial_story = false end # bottom_story_ground_exposed_floor and top_story_exterior_exposed_roof already setup as bool [:stories]["key #{i}"] = { story_party_walls: party_walls, story_min_multiplier: 1, story_included_in_building_area: true, below_partial_story: below_partial_story, bottom_story_ground_exposed_floor: args[:bottom_story_ground_exposed_floor], top_story_exterior_exposed_roof: args[:top_story_exterior_exposed_roof] } end # create bar new_spaces = (model, ) # check expect roof and wall area target_footprint = [:length] * [:width] ground_floor_area = 0.0 roof_area = 0.0 new_spaces.each do |space| space.surfaces.each do |surface| if surface.surfaceType == 'Floor' && surface.outsideBoundaryCondition == 'Ground' ground_floor_area += surface.netArea elsif surface.surfaceType == 'RoofCeiling' && surface.outsideBoundaryCondition == 'Outdoors' roof_area += surface.netArea end end end # @todo extend to address when top and or bottom story are not exposed via argument 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_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 end else match_error = false end # @todo should be able to remove this fix after OpenStudio intersection issue is fixed. At that time turn the above message into an error with return false after it return true unless match_error # identify z value of top and bottom story bottom_story = nil top_story = nil new_spaces.each do |space| story = space.buildingStory.get nom_z = story.nominalZCoordinate.get if bottom_story.nil? bottom_story = nom_z elsif bottom_story > nom_z bottom_story = nom_z end if top_story.nil? top_story = nom_z elsif top_story < nom_z top_story = nom_z end end # change boundary condition and intersection as needed. new_spaces.each do |space| if space.buildingStory.get.nominalZCoordinate.get > bottom_story # change floors space.surfaces.each do |surface| next if !(surface.surfaceType == 'Floor' && surface.outsideBoundaryCondition == 'Ground') surface.setOutsideBoundaryCondition('Adiabatic') end end if space.buildingStory.get.nominalZCoordinate.get < top_story # change ceilings space.surfaces.each do |surface| next if !(surface.surfaceType == 'RoofCeiling' && surface.outsideBoundaryCondition == 'Outdoors') surface.setOutsideBoundaryCondition('Adiabatic') end end end end |
.bar_reduced_bounding_box(envelope_data_hash) ⇒ Hash
get length and width of rectangle matching bounding box aspect ratio will maintaining proper floor area
387 388 389 390 391 392 393 394 395 396 397 398 399 400 |
# File 'lib/openstudio-standards/geometry/create_bar.rb', line 387 def self.(envelope_data_hash) = {} 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_above_grade] + envelope_data_hash[:effective_num_stories_below_grade].to_f) area_multiplier = footprint_area / bounding_area edge_multiplier = Math.sqrt(area_multiplier) [:length] = bounding_length * edge_multiplier [:width] = bounding_width * edge_multiplier return end |
.bar_reduced_width(envelope_data_hash) ⇒ Hash
get length and width of rectangle matching longer of two edges, and reducing the other way until floor area matches
406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 |
# File 'lib/openstudio-standards/geometry/create_bar.rb', line 406 def self.(envelope_data_hash) = {} 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_above_grade] + envelope_data_hash[:effective_num_stories_below_grade].to_f) if bounding_length >= bounding_width [:length] = bounding_length [:width] = footprint_area / bounding_length else [:width] = bounding_width [:length] = footprint_area / bounding_width end return end |
.bar_stretched(envelope_data_hash) ⇒ Hash
get length and width of rectangle by stretching it until both floor area and exterior wall area or perimeter match
428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 |
# File 'lib/openstudio-standards/geometry/create_bar.rb', line 428 def self.(envelope_data_hash) = {} 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_above_grade] + envelope_data_hash[:effective_num_stories_below_grade].to_f) p = envelope_data_hash[:building_perimeter] if bounding_length >= bounding_width [:length] = 0.25 * (p + Math.sqrt((p**2) - (16 * a))) [:width] = 0.25 * (p - Math.sqrt((p**2) - (16 * a))) else [:length] = 0.25 * (p - Math.sqrt((p**2) - (16 * a))) [:width] = 0.25 * (p + Math.sqrt((p**2) - (16 * a))) end return end |
.building_form_defaults(building_type) ⇒ Hash
Building Form Defaults from Table 4.2 in Achieving the 30% Goal: Energy and Cost Savings Analysis of ASHRAE Standard 90.1-2010 aspect ratio for NA replaced with floor area to perimeter ratio from prototype model currently no reason to split apart doe and deer inputs here
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
# File 'lib/openstudio-standards/geometry/create_bar.rb', line 13 def self.building_form_defaults(building_type) hash = {} # DOE Prototypes # 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_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_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_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) supermarket_a = 45001.0 supermarket_p = 866.0 supermarket_wwr = 1880.0 / (supermarket_p * 20.0) supermarket_aspect_ratio = OpenstudioStandards::Geometry.aspect_ratio(supermarket_a, supermarket_p) hash['SmallOffice'] = { aspect_ratio: 1.5, wwr: 0.15, typical_story: 10.0, perim_mult: 1.0 } hash['MediumOffice'] = { aspect_ratio: 1.5, wwr: 0.33, typical_story: 13.0, perim_mult: 1.0 } hash['LargeOffice'] = { aspect_ratio: 1.5, wwr: 0.15, typical_story: 13.0, perim_mult: 1.0 } hash['RetailStandalone'] = { aspect_ratio: 1.28, wwr: 0.07, typical_story: 20.0, perim_mult: 1.0 } hash['RetailStripmall'] = { aspect_ratio: 4.0, wwr: 0.11, typical_story: 17.0, perim_mult: 1.0 } hash['PrimarySchool'] = { aspect_ratio: primary_ns_ew_ratio.round(1), wwr: 0.35, typical_story: 13.0, perim_mult: primary_p_mult.round(3) } hash['SecondarySchool'] = { aspect_ratio: secondary_ns_ew_ratio.round(1), wwr: 0.33, typical_story: 13.0, perim_mult: secondary_p_mult.round(3) } hash['Outpatient'] = { aspect_ratio: outpatient_ns_ew_ratio.round(1), wwr: 0.20, typical_story: 10.0, perim_mult: outpatient_p_mult.round(3) } hash['Hospital'] = { aspect_ratio: 1.33, wwr: 0.16, typical_story: 14.0, perim_mult: 1.0 } hash['SmallHotel'] = { aspect_ratio: 3.0, wwr: 0.11, typical_story: 9.0, first_story: 11.0, perim_mult: 1.0 } hash['LargeHotel'] = { aspect_ratio: 5.1, wwr: 0.27, typical_story: 10.0, first_story: 13.0, perim_mult: 1.0 } # code in get_space_types_from_building_type is used to override building wwr with space type specific wwr hash['Warehouse'] = { aspect_ratio: 2.2, wwr: 0.0, typical_story: 28.0, perim_mult: 1.0 } hash['FullServiceRestaurant'] = { aspect_ratio: 1.0, wwr: 0.18, typical_story: 10.0, perim_mult: 1.0 } hash['QuickServiceRestaurant'] = { aspect_ratio: 1.0, wwr: 0.18, typical_story: 10.0, perim_mult: 1.0 } hash['MidriseApartment'] = { aspect_ratio: 2.75, wwr: 0.15, typical_story: 10.0, perim_mult: 1.0 } hash['HighriseApartment'] = { aspect_ratio: 2.75, wwr: 0.15, typical_story: 10.0, perim_mult: 1.0 } # SuperMarket inputs come from prototype model hash['SuperMarket'] = { aspect_ratio: supermarket_aspect_ratio.round(1), wwr: supermarket_wwr.round(2), typical_story: 20.0, perim_mult: 1.0 } # Add Laboratory and Data Centers hash['Laboratory'] = { aspect_ratio: 1.33, wwr: 0.12, typical_story: 10.0, perim_mult: 1.0 } hash['LargeDataCenterLowITE'] = { aspect_ratio: 1.67, wwr: 0.0, typical_story: 14.0, perim_mult: 1.0 } hash['LargeDataCenterHighITE'] = { aspect_ratio: 1.67, wwr: 0.0, typical_story: 14.0, perim_mult: 1.0 } hash['SmallDataCenterLowITE'] = { aspect_ratio: 1.5, wwr: 0.0, typical_story: 14.0, perim_mult: 1.0 } hash['SmallDataCenterHighITE'] = { aspect_ratio: 1.5, wwr: 0.0, typical_story: 14.0, perim_mult: 1.0 } # Add Courthouse and Education hash['Courthouse'] = { aspect_ratio: 2.06, wwr: 0.18, typical_story: 16.0, perim_mult: 1.0 } hash['College'] = { aspect_ratio: 2.5, wwr: 0.037, typical_story: 13.0, perim_mult: 1.0 } # DEER Prototypes hash['Asm'] = { aspect_ratio: 1.0, wwr: 0.19, typical_story: 15.0 } hash['ECC'] = { aspect_ratio: 4.0, wwr: 0.25, typical_story: 13.0 } hash['EPr'] = { aspect_ratio: 2.0, wwr: 0.16, typical_story: 12.0 } hash['ERC'] = { aspect_ratio: 1.7, wwr: 0.03, typical_story: 12.0 } hash['ESe'] = { aspect_ratio: 1.0, wwr: 0.15, typical_story: 13.0 } hash['EUn'] = { aspect_ratio: 2.5, wwr: 0.3, typical_story: 14.0 } hash['Gro'] = { aspect_ratio: 1.0, wwr: 0.07, typical_story: 25.0 } hash['Hsp'] = { aspect_ratio: 1.5, wwr: 0.11, typical_story: 13.0 } hash['Htl'] = { aspect_ratio: 3.0, wwr: 0.23, typical_story: 9.5, first_story: 12.0 } hash['MBT'] = { aspect_ratio: 10.7, wwr: 0.12, typical_story: 15.0 } hash['MFm'] = { aspect_ratio: 1.4, wwr: 0.24, typical_story: 9.5 } hash['MLI'] = { aspect_ratio: 1.0, wwr: 0.01, typical_story: 35.0 } hash['Mtl'] = { aspect_ratio: 5.1, wwr: 0.41, typical_story: 9.0 } hash['Nrs'] = { aspect_ratio: 10.3, wwr: 0.2, typical_story: 13.0 } hash['OfL'] = { aspect_ratio: 1.5, wwr: 0.33, typical_story: 12.0 } hash['OfS'] = { aspect_ratio: 1.5, wwr: 0.33, typical_story: 12.0 } hash['RFF'] = { aspect_ratio: 1.0, wwr: 0.25, typical_story: 13.0 } hash['RSD'] = { aspect_ratio: 1.0, wwr: 0.13, typical_story: 13.0 } hash['Rt3'] = { aspect_ratio: 1.0, wwr: 0.02, typical_story: 20.8 } hash['RtL'] = { aspect_ratio: 1.0, wwr: 0.03, typical_story: 20.5 } hash['RtS'] = { aspect_ratio: 1.0, wwr: 0.13, typical_story: 12.0 } hash['SCn'] = { aspect_ratio: 1.0, wwr: 0.01, typical_story: 48.0 } hash['SUn'] = { aspect_ratio: 1.0, wwr: 0.01, typical_story: 48.0 } hash['WRf'] = { aspect_ratio: 1.6, wwr: 0.0, typical_story: 32.0 } return hash[building_type] end |
.building_story_get_exterior_wall_perimeter(story, multiplier_adjustment: nil, exterior_boundary_conditions: ['Outdoors', 'Ground'], bounding_box: nil) ⇒ Object
this doesn’t catch walls that are split that sit above floor surfaces that are not (e.g. main corridoor in secondary school model)
also odd with multi-height spaces
Calculate the story exterior wall perimeter. Selected story should have above grade walls. If not perimeter may return zero.
695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 |
# File 'lib/openstudio-standards/geometry/information.rb', line 695 def self.building_story_get_exterior_wall_perimeter(story, multiplier_adjustment: nil, exterior_boundary_conditions: ['Outdoors', 'Ground'], bounding_box: nil) perimeter = 0 party_walls = [] story.spaces.each do |space| # counter to use later edge_hash = {} edge_counter = 0 space.surfaces.each do |surface| # get vertices vertex_hash = {} vertex_counter = 0 surface.vertices.each do |vertex| vertex_counter += 1 vertex_hash[vertex_counter] = [vertex.x, vertex.y, vertex.z] end # make edges counter = 0 vertex_hash.each do |k, v| edge_counter += 1 counter += 1 if vertex_hash.size == counter # different code for wrap around vertex edge_hash[edge_counter] = [v, vertex_hash[1], surface, surface.outsideBoundaryCondition, surface.surfaceType] else edge_hash[edge_counter] = [v, vertex_hash[counter + 1], surface, surface.outsideBoundaryCondition, surface.surfaceType] end end end # check edges for matches (need opposite vertices and proper boundary conditions) edge_hash.each do |k1, v1| # apply to any floor boundary condition. This supports used in floors above basements next if v1[4] != 'Floor' edge_hash.each do |k2, v2| test_boundary_cond = false next if !exterior_boundary_conditions.include?(v2[3]) # method arg takes multiple conditions next if v2[4] != 'Wall' # see if edges have same geometry # found cases where the two lines below removed edges and resulted in lower than actual perimeter. Added new code with tolerance. # next if not v1[0] == v2[1] # next if not same geometry reversed # next if not v1[1] == v2[0] # these are three item array's add in tolerance for each array entry tolerance = 0.0001 test_a = true test_b = true 3.times.each do |i| if (v1[0][i] - v2[1][i]).abs > tolerance test_a = false end if (v1[1][i] - v2[0][i]).abs > tolerance test_b = false end end next if test_a != true next if test_b != true # edge_bounding_box = OpenStudio::BoundingBox.new # edge_bounding_box.addPoints(space.transformation() * v2[2].vertices) # if not edge_bounding_box.intersects(bounding_box) doesn't seem to work reliably, writing custom code to check point_one = OpenStudio::Point3d.new(v2[0][0], v2[0][1], v2[0][2]) point_one = (space.transformation * point_one) point_two = OpenStudio::Point3d.new(v2[1][0], v2[1][1], v2[1][2]) point_two = (space.transformation * point_two) if !bounding_box.nil? && (v2[3] == 'Adiabatic') on_bounding_box = false if ((bounding_box.minX.to_f - point_one.x).abs < tolerance) && ((bounding_box.minX.to_f - point_two.x).abs < tolerance) on_bounding_box = true elsif ((bounding_box.maxX.to_f - point_one.x).abs < tolerance) && ((bounding_box.maxX.to_f - point_two.x).abs < tolerance) on_bounding_box = true elsif ((bounding_box.minY.to_f - point_one.y).abs < tolerance) && ((bounding_box.minY.to_f - point_two.y).abs < tolerance) on_bounding_box = true elsif ((bounding_box.maxY.to_f - point_one.y).abs < tolerance) && ((bounding_box.maxY.to_f - point_two.y).abs < tolerance) on_bounding_box = true end # if not edge_bounding_box.intersects(bounding_box) doesn't seem to work reliably, writing custom code to check # todo - this is basic check for adiabatic party walls and won't catch all situations. Can be made more robust in the future if on_bounding_box == true length = OpenStudio::Vector3d.new(point_one - point_two).length party_walls << v2[2] length_ip_display = OpenStudio.convert(length, 'm', 'ft').get.round(2) OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Information', " * #{v2[2].name} has an adiabatic boundary condition and sits in plane with the building bounding box. Adding #{length_ip_display} (ft) to perimeter length of #{story.name} for this surface, assuming it is a party wall.") elsif space.multiplier == 1 length = OpenStudio::Vector3d.new(point_one - point_two).length party_walls << v2[2] length_ip_display = OpenStudio.convert(length, 'm', 'ft').get.round(2) OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Information', " * #{v2[2].name} has an adiabatic boundary condition and is in a zone with a multiplier of 1. Adding #{length_ip_display} (ft) to perimeter length of #{story.name} for this surface, assuming it is a party wall.") else length = 0 end else length = OpenStudio::Vector3d.new(point_one - point_two).length end if multiplier_adjustment.nil? perimeter += length else # adjust for multiplier non_story_multiplier = space.multiplier / multiplier_adjustment.to_f perimeter += length * non_story_multiplier end end end end return { perimeter: perimeter, party_walls: party_walls } end |
.building_story_get_floor_multiplier(building_story) ⇒ Integer
Checks all spaces on this story that are part of the total floor area to see if they have the same multiplier. If they do, assume that the multipliers are being used as a floor multiplier.
820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 |
# File 'lib/openstudio-standards/geometry/information.rb', line 820 def self.building_story_get_floor_multiplier(building_story) floor_multiplier = 1 # Determine the multipliers for all spaces multipliers = [] building_story.spaces.each do |space| # Ignore spaces that aren't part of the total floor area next unless space.partofTotalFloorArea multipliers << space.multiplier end # If there are no spaces on this story, assume # a multiplier of 1 if multipliers.empty? return floor_multiplier end # Calculate the average multiplier and # then convert to integer. avg_multiplier = (multipliers.inject { |a, e| a + e }.to_f / multipliers.size).to_i # If the multiplier is greater than 1, report this if avg_multiplier > 1 floor_multiplier = avg_multiplier OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Information', "Story #{building_story.name} has a multiplier of #{floor_multiplier}.") end return floor_multiplier end |
.building_story_get_minimum_height(building_story) ⇒ Double
Gets the minimum height of the building story. This is considered to be the minimum z value of any vertex of any surface of any space on the story, with the exception of plenum spaces.
856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 |
# File 'lib/openstudio-standards/geometry/information.rb', line 856 def self.building_story_get_minimum_height(building_story) z_heights = [] building_story.spaces.each do |space| # Skip plenum spaces next if OpenstudioStandards::Space.space_plenum?(space) # Get the z value of the space, which # vertices in space surfaces are relative to. z_origin = space.zOrigin # loop through space surfaces to find min z value space.surfaces.each do |surface| surface.vertices.each do |vertex| z_heights << (vertex.z + z_origin) end end end # Error if no z heights were found z = 999.9 if z_heights.empty? OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Information', "For #{building_story.name} could not find the minimum_z_value, which means the story has no spaces assigned or the spaces have no surfaces.") else z = z_heights.min end return z end |
.building_story_get_thermal_zones(building_story) ⇒ Array<OpenStudio::Model::ThermalZone>
Get an array of OpenStudio ThermalZone objects for an OpenStudio BuildingStory
889 890 891 892 893 894 895 896 897 |
# File 'lib/openstudio-standards/geometry/information.rb', line 889 def self.building_story_get_thermal_zones(building_story) zones = [] building_story.spaces.sort.each do |space| zones << space.thermalZone.get if space.thermalZone.is_initialized end zones = zones.uniq return zones end |
.create_bar(model, bar_hash) ⇒ Array<OpenStudio::Model::Space>
create_bar creates spaces based on a set of geometric characteristics
452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 |
# File 'lib/openstudio-standards/geometry/create_bar.rb', line 452 def self.(model, ) # make custom story hash when number of stories below grade > 0 # @todo update this so have option basements are not below 0? (useful for simplifying existing model and maintaining z position relative to site shading) story_hash = {} eff_below = [:num_stories_below_grade] eff_above = [:num_stories_above_grade] footprint_origin_point = [:center_of_footprint] typical_story_height = [:floor_height] # warn about site shading if !model.getSite.shadingSurfaceGroups.empty? OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', 'The model has one or more site shading surfaces. New geometry may not be positioned where expected, it will be centered over the center of the original geometry.') end # flatten story_hash out to individual stories included in building area stories_flat = [] stories_flat_counter = 0 [:stories].each_with_index do |(k, v), i| # k is invalid in some cases, old story object that has been removed, should be from low to high including basement # skip if source story insn't included in building area if v[:story_included_in_building_area].nil? || (v[:story_included_in_building_area] == true) # add to counter stories_flat_counter += v[:story_min_multiplier] flat_hash = {} flat_hash[:story_party_walls] = v[:story_party_walls] flat_hash[:below_partial_story] = v[:below_partial_story] flat_hash[:bottom_story_ground_exposed_floor] = v[:bottom_story_ground_exposed_floor] flat_hash[:top_story_exterior_exposed_roof] = v[:top_story_exterior_exposed_roof] if i < eff_below flat_hash[:story_type] = 'b' flat_hash[:multiplier] = 1 elsif i == eff_below flat_hash[:story_type] = 'ground' flat_hash[:multiplier] = 1 elsif stories_flat_counter == eff_below + eff_above.ceil flat_hash[:story_type] = 'top' flat_hash[:multiplier] = 1 else flat_hash[:story_type] = 'mid' flat_hash[:multiplier] = v[:story_min_multiplier] end compare_hash = {} if !stories_flat.empty? stories_flat.last.each { |s, m| compare_hash[s] = flat_hash[s] if flat_hash[s] != m } end if ([:story_multiplier_method] != 'None' && stories_flat.last == flat_hash) || ([:story_multiplier_method] != 'None' && compare_hash.size == 1 && compare_hash.include?(:multiplier)) stories_flat.last[:multiplier] += v[:story_min_multiplier] else stories_flat << flat_hash end end end if [: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 } end end # add in above grade levels if eff_above > 2 story_hash['ground'] = { space_origin_z: footprint_origin_point.z, space_height: typical_story_height, multiplier: 1 } footprint_counter = 0 effective_stories_counter = 1 stories_flat.each do |hash| next if hash[:story_type] != 'mid' 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] } 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 } 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 } else # one story only story_hash['ground'] = { space_origin_z: footprint_origin_point.z, space_height: typical_story_height, multiplier: 1 } end # create footprints if [:bar_division_method] == 'Multiple Space Types - Simple Sliced' footprints = [] story_hash.size.times do |i| # adjust size of bar of top story is not a full story if i + 1 == story_hash.size area_multiplier = (1.0 - [:num_stories_above_grade].ceil + [:num_stories_above_grade]) edge_multiplier = Math.sqrt(area_multiplier) length = [:length] * edge_multiplier width = [:width] * edge_multiplier else length = [:length] width = [:width] end footprints << OpenstudioStandards::Geometry.([:space_types], length, width, [:center_of_footprint]) end elsif [:bar_division_method] == 'Multiple Space Types - Individual Stories Sliced' # update story_hash for partial_story_above story_hash.each_with_index do |(k, v), i| # adjust size of bar of top story is not a full story if i + 1 == story_hash.size story_hash[k][:partial_story_multiplier] = (1.0 - [:num_stories_above_grade].ceil + [:num_stories_above_grade]) end end footprints = OpenstudioStandards::Geometry.([:space_types], [:length], [:width], [:center_of_footprint], story_hash) else footprints = [] story_hash.size.times do |i| # adjust size of bar of top story is not a full story if i + 1 == story_hash.size area_multiplier = (1.0 - [:num_stories_above_grade].ceil + [:num_stories_above_grade]) edge_multiplier = Math.sqrt(area_multiplier) length = [:length] * edge_multiplier width = [:width] * edge_multiplier else length = [:length] width = [:width] end # perimeter defaults to 15 ft footprints << OpenstudioStandards::Geometry.create_core_and_perimeter_polygons(length, width, [:center_of_footprint]) end # set primary space type to building default space type space_types = [:space_types].sort_by { |k, v| v[:floor_area] } if space_types.last.first.class.to_s == 'OpenStudio::Model::SpaceType' model.getBuilding.setSpaceType(space_types.last.first) end end # make spaces from polygons new_spaces = OpenstudioStandards::Geometry.create_spaces_from_polygons(model, footprints, [:floor_height], [:num_stories], [:center_of_footprint], story_hash) # put all of the spaces in the model into a vector for intersection and surface matching spaces = OpenStudio::Model::SpaceVector.new model.getSpaces.sort.each do |space| spaces << space end # flag for intersection and matching type diagnostic_intersect = true # only intersect if make_mid_story_surfaces_adiabatic false if diagnostic_intersect model.getPlanarSurfaces.sort.each do |surface| array = [] vertices = surface.vertices fixed = false vertices.each do |vertex| next if fixed if array.include?(vertex) # create a new set of vertices new_vertices = OpenStudio::Point3dVector.new array_b = [] surface.vertices.each do |vertex_b| next if array_b.include?(vertex_b) new_vertices << vertex_b array_b << vertex_b end surface.setVertices(new_vertices) num_removed = vertices.size - surface.vertices.size OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', "#{surface.name} has duplicate vertices. Started with #{vertices.size} vertices, removed #{num_removed}.") fixed = true else array << vertex end end end # remove collinear points in a surface model.getPlanarSurfaces.sort.each do |surface| new_vertices = OpenStudio.removeCollinear(surface.vertices) starting_count = surface.vertices.size final_count = new_vertices.size if final_count < starting_count OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', "Removing #{starting_count - final_count} collinear vertices from #{surface.name}.") surface.setVertices(new_vertices) end end # remove duplicate surfaces in a space (should be done after remove duplicate and collinear points) model.getSpaces.sort.each do |space| # secondary array to compare against surfaces_b = space.surfaces.sort space.surfaces.sort.each do |surface_a| # delete from secondary array surfaces_b.delete(surface_a) surfaces_b.each do |surface_b| next if surface_a == surface_b # dont' test against same surface if surface_a.equalVertices(surface_b) OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', "#{surface_a.name} and #{surface_b.name} in #{space.name} have duplicate geometry, removing #{surface_b.name}.") surface_b.remove elsif surface_a.reverseEqualVertices(surface_b) # @todo add logic to determine which face naormal is reversed and which is correct OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', "#{surface_a.name} and #{surface_b.name} in #{space.name} have reversed geometry, removing #{surface_b.name}.") surface_b.remove end end end end if ([: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 story.spaces.sort.each do |space_a| spaces_b.delete(space_a) spaces_b.each do |space_b| 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 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 ([: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 end # 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 [:num_stories_below_grade] > 0 model.getBuildingStorys.sort.each do |story| next if !story.name.to_s.include?('Story B') story.spaces.sort.each do |space| next if !new_spaces.include?(space) space.surfaces.sort.each do |surface| next if surface.surfaceType != 'Wall' next if surface.outsideBoundaryCondition != 'Outdoors' surface.setOutsideBoundaryCondition('Ground') end end end end # set wall boundary condtions to adiabatic if using make_mid_story_surfaces_adiabatic prior to windows being made if [:make_mid_story_surfaces_adiabatic] OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', 'Finding non-exterior walls and setting boundary condition to adiabatic') # need to organize by story incase top story is partial story # should also be only for a single bar story_bounding = {} missed_match_count = 0 # gather new spaces by story new_spaces.each do |space| story = space.buildingStory.get if story_bounding.key?(story) story_bounding[story][:spaces] << space else story_bounding[story] = { spaces: [space] } end end # get bounding box for each story story_bounding.each do |story, v| # get bounding_box bounding_box = OpenStudio::BoundingBox.new v[:spaces].each do |space| space.surfaces.each do |space_surface| bounding_box.addPoints(space.transformation * space_surface.vertices) end end min_x = bounding_box.minX.get min_y = bounding_box.minY.get max_x = bounding_box.maxX.get max_y = bounding_box.maxY.get ext_wall_toll = 0.01 # check surfaces again against min/max and change to adiabatic if not fully on one min or max x or y # todo - may need to look at aidiabiatc constructions in downstream measure. Some may be exterior party wall others may be interior walls v[:spaces].each do |space| space.surfaces.each do |space_surface| next if space_surface.surfaceType != 'Wall' next if space_surface.outsideBoundaryCondition == 'Surface' # if if found a match leave it alone, don't change to adiabiatc surface_bounding_box = OpenStudio::BoundingBox.new surface_bounding_box.addPoints(space.transformation * space_surface.vertices) surface_on_outside = false # check xmin if (surface_bounding_box.minX.get - min_x).abs < ext_wall_toll && (surface_bounding_box.maxX.get - min_x).abs < ext_wall_toll then surface_on_outside = true end # check xmax if (surface_bounding_box.minX.get - max_x).abs < ext_wall_toll && (surface_bounding_box.maxX.get - max_x).abs < ext_wall_toll then surface_on_outside = true end # check ymin if (surface_bounding_box.minY.get - min_y).abs < ext_wall_toll && (surface_bounding_box.maxY.get - min_y).abs < ext_wall_toll then surface_on_outside = true end # check ymax if (surface_bounding_box.minY.get - max_y).abs < ext_wall_toll && (surface_bounding_box.maxY.get - max_y).abs < ext_wall_toll then surface_on_outside = true end # change if not exterior if !surface_on_outside space_surface.setOutsideBoundaryCondition('Adiabatic') missed_match_count += 1 end end end end if missed_match_count > 0 OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "#{missed_match_count} surfaces that were exterior appear to be interior walls and had boundary condition chagned to adiabiatic.") end end # sort stories (by name for now but need better way) sorted_stories = {} new_spaces.each do |space| next if !space.buildingStory.is_initialized story = space.buildingStory.get if !sorted_stories.key?(name.to_s) sorted_stories[story.name.to_s] = story end end # flag space types that have wwr overrides space_type_wwr_overrides = {} # loop through building stories, spaces, and surfaces sorted_stories.sort.each_with_index do |(key, story), i| # flag for adiabatic floor if building doesn't have ground exposed floor if stories_flat[i][:bottom_story_ground_exposed_floor] == false adiabatic_floor = true end # flag for adiabatic roof if building doesn't have exterior exposed roof if stories_flat[i][:top_story_exterior_exposed_roof] == false adiabatic_ceiling = true end # make all mid story floor and ceilings adiabatic if requested if [:make_mid_story_surfaces_adiabatic] if i > 0 adiabatic_floor = true end if i < sorted_stories.size - 1 adiabatic_ceiling = true end end # flag orientations for this story to recieve party walls 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| # set floor to adiabatic if requited if (adiabatic_floor && surface.surfaceType == 'Floor') || (adiabatic_ceiling && surface.surfaceType == 'RoofCeiling') surface.setOutsideBoundaryCondition('Adiabatic') end # skip of not exterior wall next if surface.surfaceType != 'Wall' next if surface.outsideBoundaryCondition != 'Outdoors' # get the absolute azimuth for the surface so we can categorize it absolute_azimuth = OpenStudio.convert(surface.azimuth, 'rad', 'deg').get + surface.space.get.directionofRelativeNorth + model.getBuilding.northAxis absolute_azimuth = absolute_azimuth % 360.0 # should result in value between 0 and 360 absolute_azimuth = absolute_azimuth.round(5) # this was creating issues at 45 deg angles with opposing facades # target wwr values that may be changed for specific space types wwr_n = [:building_wwr_n] wwr_e = [:building_wwr_e] wwr_s = [:building_wwr_s] wwr_w = [:building_wwr_w] # look for space type specific wwr values 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 [:space_types].each do |k, v| 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. 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) if (absolute_azimuth >= 315.0) || (absolute_azimuth < 45.0) if party_wall_facades.include?('north') surface.setOutsideBoundaryCondition('Adiabatic') else surface.setWindowToWallRatio(wwr_n) end elsif (absolute_azimuth >= 45.0) && (absolute_azimuth < 135.0) if party_wall_facades.include?('east') surface.setOutsideBoundaryCondition('Adiabatic') else surface.setWindowToWallRatio(wwr_e) end elsif (absolute_azimuth >= 135.0) && (absolute_azimuth < 225.0) if party_wall_facades.include?('south') surface.setOutsideBoundaryCondition('Adiabatic') else surface.setWindowToWallRatio(wwr_s) end elsif (absolute_azimuth >= 225.0) && (absolute_azimuth < 315.0) if party_wall_facades.include?('west') surface.setOutsideBoundaryCondition('Adiabatic') else surface.setWindowToWallRatio(wwr_w) end else OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create', "Unexpected value of facade: #{absolute_azimuth}.") return false end end end end # report space types with custom wwr values space_type_wwr_overrides.each do |space_type, wwr| OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "For #{space_type.name} the default building wwr was replaced with a space type specfic value of #{wwr}") end new_floor_area_si = 0.0 new_spaces.each do |space| new_floor_area_si += space.floorArea * space.multiplier end new_floor_area_ip = OpenStudio.convert(new_floor_area_si, 'm^2', 'ft^2').get final_floor_area_ip = OpenStudio.convert(model.getBuilding.floorArea, 'm^2', 'ft^2').get if new_floor_area_ip == final_floor_area_ip OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "Created bar envelope with floor area of #{OpenStudio.toNeatString(new_floor_area_ip, 0, true)} ft^2.") else OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "Created bar envelope with floor area of #{OpenStudio.toNeatString(new_floor_area_ip, 0, true)} ft^2. Total building area is #{OpenStudio.toNeatString(final_floor_area_ip, 0, true)} ft^2.") end return new_spaces end |
.create_bar_from_args_and_building_type_hash(model, args, building_type_hash) ⇒ Boolean
create bar from arguments and building type hash
1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 |
# File 'lib/openstudio-standards/geometry/create_bar.rb', line 1257 def self.(model, args, building_type_hash) # set argument defaults if not present args[:single_floor_area] = args.fetch(:single_floor_area, 0.0) args[:total_bldg_floor_area] = args.fetch(:total_bldg_floor_area, 10000.0) args[:floor_height] = args.fetch(:floor_height, 0.0) args[:custom_height_bar] = args.fetch(:custom_height_bar, true) args[:num_stories_above_grade] = args.fetch(:num_stories_above_grade, 1) args[:num_stories_below_grade] = args.fetch(:num_stories_below_grade, 0) args[:building_rotation] = args.fetch(:building_rotation, 0.0) args[:ns_to_ew_ratio] = args.fetch(:ns_to_ew_ratio, 0.0) args[:perim_mult] = args.fetch(:perim_mult, 0.0) args[:bar_width] = args.fetch(:bar_width, 0.0) args[:bar_sep_dist_mult] = args.fetch(:bar_sep_dist_mult, 10.0) args[:wwr] = args.fetch(:wwr, 0.0) args[:party_wall_fraction] = args.fetch(:party_wall_fraction, 0.0) args[:party_wall_stories_north] = args.fetch(:party_wall_stories_north, 0) args[:party_wall_stories_south] = args.fetch(:party_wall_stories_south, 0) args[:party_wall_stories_east] = args.fetch(:party_wall_stories_east, 0) args[:party_wall_stories_west] = args.fetch(:party_wall_stories_west, 0) args[:bottom_story_ground_exposed_floor] = args.fetch(:bottom_story_ground_exposed_floor, true) args[:top_story_exterior_exposed_roof] = args.fetch(:top_story_exterior_exposed_roof, true) args[:story_multiplier_method] = args.fetch(:story_multiplier_method, 'Basements Ground Mid Top') args[:make_mid_story_surfaces_adiabatic] = args.fetch(:make_mid_story_surfaces_adiabatic, true) args[:bar_division_method] = args.fetch(:bar_division_method, 'Multiple Space Types - Individual Stories Sliced') args[:double_loaded_corridor] = args.fetch(:double_loaded_corridor, 'Primary Space Type') args[:space_type_sort_logic] = args.fetch(:space_type_sort_logic, 'Building Type > Size') args[:template] = args.fetch(:template, '90.1-2013') # get defaults for the primary building type primary_building_type = args[:primary_building_type] building_form_defaults = OpenstudioStandards::Geometry.building_form_defaults(primary_building_type) # 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].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].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] end OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "0.0 value for minimum perimeter multiplier will be replaced with smart default for #{primary_building_type} of #{building_form_defaults[:perim_mult]}.") 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].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].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 OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "The building started with #{model.getSpaces.size} spaces.") # determine of ns_ew needs to be mirrored mirror_ns_ew = false rotation = model.getBuilding.northAxis if rotation > 45.0 && rotation < 135.0 mirror_ns_ew = true elsif rotation > 45.0 && rotation < 135.0 mirror_ns_ew = true end # remove non-resource objects not removed by removing the building # remove_non_resource_objects(model) # creating space types for requested building types building_type_hash.each do |building_type, building_type_hash| OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "Creating Space Types for #{building_type}.") # mapping building_type name is needed for a few methods temp_standard = Standard.build(args[:template]) building_type = temp_standard.model_get_lookup_name(building_type) # create space_type_map from array sum_of_ratios = 0.0 building_type_hash[:space_types] = building_type_hash[:space_types].sort_by { |k, v| v[:ratio] }.to_h building_type_hash[:space_types].each do |space_type_name, hash| next if hash[:space_type_gen] == false # space types like undeveloped and basement are skipped. # create space type space_type = OpenStudio::Model::SpaceType.new(model) space_type.setStandardsBuildingType(building_type) space_type.setStandardsSpaceType(space_type_name) space_type.setName("#{building_type} #{space_type_name}") # set color test = temp_standard.space_type_apply_rendering_color(space_type) if !test # @todo once fixed in standards un-comment this # OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', "Could not find color for #{space_type.name}") end # extend hash to hold new space type object hash[:space_type] = space_type # add to sum_of_ratios counter for adjustment multiplier sum_of_ratios += hash[:ratio] end # store multiplier needed to adjust sum of ratios to equal 1.0 building_type_hash[:ratio_adjustment_multiplier] = 1.0 / sum_of_ratios end # calculate length and with of bar total_bldg_floor_area_si = OpenStudio.convert(args[:total_bldg_floor_area], 'ft^2', 'm^2').get single_floor_area_si = OpenStudio.convert(args[:single_floor_area], 'ft^2', 'm^2').get # store number of stories num_stories = args[:num_stories_below_grade] + args[:num_stories_above_grade] # handle user-assigned single floor plate size condition if args[:single_floor_area] > 0.0 footprint_si = single_floor_area_si total_bldg_floor_area_si = footprint_si * num_stories.to_f OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', 'User-defined single floor area was used for calculation of total building floor area') # add warning if custom_height_bar is true and applicable building type is selected if args[:custom_height_bar] OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', 'Cannot use custom height bar with single floor area method, will not create custom height bar.') args[:custom_height_bar] = false end else footprint_si = nil end # populate space_types_hash space_types_hash = {} multi_height_space_types_hash = {} custom_story_heights = [] if args[:space_type_sort_logic] == 'Building Type > Size' building_type_hash = building_type_hash.sort_by { |k, v| v[:frac_bldg_area] } end building_type_hash.each do |building_type, building_type_hash| if args[:double_loaded_corridor] == 'Primary Space Type' # see if building type has circulation space type, if so then merge that along with default space type into hash key in place of space type default_st = nil circ_st = nil building_type_hash[:space_types].each do |space_type_name, hash| if hash[:default] then default_st = space_type_name end if hash[:circ] then circ_st = space_type_name end end # update building hash if !default_st.nil? && !circ_st.nil? OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "Combining #{default_st} and #{circ_st} into a group representing a double loaded corridor") # add new item building_type_hash[:space_types]['Double Loaded Corridor'] = {} double_loaded_st = building_type_hash[:space_types]['Double Loaded Corridor'] double_loaded_st[:ratio] = building_type_hash[:space_types][default_st][:ratio] + building_type_hash[:space_types][circ_st][:ratio] double_loaded_st[:double_loaded_corridor] = true double_loaded_st[:space_type] = model.getBuilding double_loaded_st[:children] = {} building_type_hash[:space_types][default_st][:orig_ratio] = building_type_hash[:ratio_adjustment_multiplier] * building_type_hash[:frac_bldg_area] * building_type_hash[:space_types][default_st][:ratio] building_type_hash[:space_types][circ_st][:orig_ratio] = building_type_hash[:ratio_adjustment_multiplier] * building_type_hash[:frac_bldg_area] * building_type_hash[:space_types][circ_st][:ratio] building_type_hash[:space_types][default_st][:name] = default_st building_type_hash[:space_types][circ_st][:name] = circ_st double_loaded_st[:children][:default] = building_type_hash[:space_types][default_st] double_loaded_st[:children][:circ] = building_type_hash[:space_types][circ_st] double_loaded_st[:orig_ratio] = 0.0 # zero out ratios from old item (don't delete because I still want the space types made) building_type_hash[:space_types][default_st][:ratio] = 0.0 building_type_hash[:space_types][circ_st][:ratio] = 0.0 end end building_type_hash[:space_types].each do |space_type_name, hash| next if hash[:space_type_gen] == false 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] 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] end else # only add wwr if 0 used for wwr arg and if space type has wwr as key space_types_hash[space_type] = { floor_area: final_floor_area, space_type: space_type } if hash.key?(:orig_ratio) then space_types_hash[space_type][:orig_ratio] = hash[:orig_ratio] end if args[:wwr] == 0 && hash.key?(:wwr) space_types_hash[space_type][:wwr] = hash[:wwr] end if hash[:double_loaded_corridor] space_types_hash[space_type][:children] = hash[:children] end end end end # resort if not sorted by building type if args[:space_type_sort_logic] == 'Size' # added code to convert to hash. I use sort_by 3 other times, but those seem to be working fine as is now. space_types_hash = Hash[space_types_hash.sort_by { |k, v| v[:floor_area] }] end # calculate targets for testing target_areas = {} # used for checks target_areas_cust_height = 0.0 space_types_hash.each do |k, v| if v.key?(:orig_ratio) target_areas[k] = v[:orig_ratio] * total_bldg_floor_area_si else target_areas[k] = v[:floor_area] end end multi_height_space_types_hash.each do |k, v| if v.key?(:orig_ratio) target_areas[k] = v[:orig_ratio] * total_bldg_floor_area_si target_areas_cust_height += v[:orig_ratio] * total_bldg_floor_area_si else target_areas[k] = v[:floor_area] target_areas_cust_height += v[:floor_area] end end # gather inputs if footprint_si.nil? footprint_si = (total_bldg_floor_area_si - target_areas_cust_height) / num_stories.to_f end floor_height = OpenStudio.convert(args[:floor_height], 'ft', 'm').get min_allow_size = OpenStudio.convert(15.0, 'ft', 'm').get = OpenStudio.convert(args[:bar_width], 'ft', 'm').get # set custom width if > 0 OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', 'Ignoring perimeter multiplier argument when non zero width argument is used') if footprint_si / >= min_allow_size width = length = footprint_si / width else length = min_allow_size width = footprint_si / length OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', 'User specified width results in a length that is too short, adjusting width to be narrower than specified.') end width_cust_height = else width = Math.sqrt(footprint_si / args[:ns_to_ew_ratio]) length = footprint_si / width width_cust_height = Math.sqrt(target_areas_cust_height / args[:ns_to_ew_ratio]) end length_cust_height = target_areas_cust_height / width_cust_height if args[:perim_mult] > 1.0 && target_areas_cust_height > 0.0 # @todo update tests that hit this warning OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', 'Ignoring perimeter multiplier for bar that represents custom height spaces.') end # check if dual bar is needed = false if > 0.0 && args[:bar_division_method] == 'Multiple Space Types - Individual Stories Sliced' if length / width != args[:ns_to_ew_ratio] if 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]}, Lowering it to #{length / width} ") args[:ns_to_ew_ratio] = length / width 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_alt2 = length - length_alt1 if [length_alt1, length_alt2].min >= min_allow_size = true else OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', 'Second bar would be below minimum length, will model as single bar') # swap length and width if single bar and aspect ratio less than 1 if args[:ns_to_ew_ratio] < 1.0 width = length length = end end end end elsif args[:perim_mult] > 1.0 && args[:bar_division_method] == 'Multiple Space Types - Individual Stories Sliced' OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', 'You selected a perimeter multiplier greater than 1.0 for a supported bar division method. This will result in two detached rectangular buildings if secondary bar meets minimum size requirements.') = true elsif args[:perim_mult] > 1.0 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 min_perim = (2 * width) + (2 * length) target_area = footprint_si target_perim = min_perim * args[:perim_mult] tol_testing = 0.00001 = 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.") OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "Target dual bar perimeter is #{OpenStudio.toNeatString(OpenStudio.convert(target_perim, 'm', 'ft').get, 0, true)} ft.") # determine which of the three paths to hit target perimeter multiplier are possible # 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 > 0 OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', 'Ignoring perimeter multiplier argument and using use specified bar width.') dual_double_end_width = 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_width = footprint_si / dual_double_end_length end # now that stretched bar is made, determine where to split it and rotate = ((args[:ns_to_ew_ratio] * (dual_double_end_length + dual_double_end_width)) - dual_double_end_width) / (1 + args[:ns_to_ew_ratio]) = dual_double_end_length - area_a = * dual_double_end_width area_b = * dual_double_end_width else # this will throw it to adiabatic ends test = 0 = 0 end if >= min_allow_size && >= min_allow_size = '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))) 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 == 0 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 = (args[:ns_to_ew_ratio] * (adiabatic_dual_double_end_length + adiabatic_dual_double_end_width)) / (1 + args[:ns_to_ew_ratio]) = adiabatic_dual_double_end_length - adiabatic_area_a = * adiabatic_dual_double_end_width adiabatic_area_b = * adiabatic_dual_double_end_width else # this will throw it stretched single bar = 0 = 0 end if >= min_allow_size && >= min_allow_size = 'adiabatic_ends_bar_b' else = 'stretched' end end # apply prescribed approach for stretched or dual bar if == '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::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(, 'm', 'ft').get, 0, true)} ft of bar should be horizontal, with #{OpenStudio.toNeatString(OpenStudio.convert(, '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(( * 2) + ( * 2) + (dual_double_end_width * 4), 'm', 'ft').get, 0, true)} ft") if (target_area - (area_a + area_b)).abs > tol_testing || (target_perim - (( * 2) + ( * 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 == '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(, 'm', 'ft').get, 0, true)} ft of bar should be north/south, with #{OpenStudio.toNeatString(OpenStudio.convert(, '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(( * 2) + ( * 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 - (( * 2) + ( * 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 = false 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 OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', 'Unexpected values for single stretched') end width = stretched_width length = stretched_length OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "Creating a dual bar to match the target minimum perimeter multiplier at the given aspect ratio would result in a bar with edge shorter than #{OpenStudio.toNeatString(OpenStudio.convert(min_allow_size, 'm', 'ft').get, 0, true)} ft. Will create a single stretched bar instead that hits the target perimeter with a slightly different ns/ew aspect ratio.") end end = {} ['primary'] = {} if if mirror_ns_ew && == 'dual_bar' ['primary'][:length] = dual_double_end_width ['primary'][:width] = elsif == 'dual_bar' ['primary'][:length] = ['primary'][:width] = dual_double_end_width elsif mirror_ns_ew ['primary'][:length] = adiabatic_dual_double_end_width ['primary'][:width] = else ['primary'][:length] = ['primary'][:width] = adiabatic_dual_double_end_width end else if mirror_ns_ew ['primary'][:length] = width ['primary'][:width] = length else ['primary'][:length] = length ['primary'][:width] = width end end ['primary'][:floor_height] = floor_height # can make use of this when breaking out multi-height spaces ['primary'][:num_stories] = num_stories ['primary'][:center_of_footprint] = OpenStudio::Point3d.new(0.0, 0.0, 0.0) space_types_hash_secondary = {} if # loop through each story and move portion for other bar to its own hash primary_footprint = ['primary'][:length] * ['primary'][:width] secondary_footprint = target_area - primary_footprint footprint_counter = primary_footprint secondary_footprint_counter = secondary_footprint story_counter = 0 pri_sec_tol = 0.0001 # m^2 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.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.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 else # add new space type to hash if v.key?(:children) space_types_hash_secondary[k] = { floor_area: hash_area, space_type: v[:space_type], children: v[:children] } else space_types_hash_secondary[k] = { floor_area: hash_area, space_type: v[:space_type] } end end space_types_hash[k][:floor_area] -= hash_area secondary_footprint_counter -= hash_area space_type_left -= hash_area else OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "Shifting space types between bars to avoid sliver of #{k.name}.") end end # remove space if entirely used up by secondary bar if space_types_hash[k][:floor_area] <= pri_sec_tol space_types_hash.delete(k) space_type_left = 0.0 else # then look at primary bar hash_area_pri = [space_type_left, footprint_counter].min footprint_counter -= hash_area_pri space_type_left -= hash_area_pri end # reset counter when full if footprint_counter <= pri_sec_tol && secondary_footprint_counter <= pri_sec_tol # check if this is partial top floor story_counter += 1 if num_stories < story_counter + 1 footprint_counter = primary_footprint * (num_stories - story_counter) secondary_footprint_counter = secondary_footprint * (num_stories - story_counter) else footprint_counter = primary_footprint secondary_footprint_counter = secondary_footprint end end end end end # setup bar_hash and run create_bar ['primary'][:space_types_hash] = space_types_hash ['primary'][:args] = args v = ['primary'] OpenstudioStandards::Geometry.(model, v[:args], v[:length], v[:width], v[:floor_height], v[:center_of_footprint], v[:space_types_hash], v[:num_stories]) # store offset value for multiple bars if args.key?(:bar_sep_dist_mult) && args[:bar_sep_dist_mult] > 0.0 offset_val = num_stories.ceil * floor_height * args[:bar_sep_dist_mult] elsif args.key?(:bar_sep_dist_mult) OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', 'Positive value is required for bar_sep_dist_mult, ignoring input and using value of 0.1') offset_val = num_stories.ceil * floor_height * 0.1 else offset_val = num_stories.ceil * floor_height * 10.0 end if args2 = args.clone ['secondary'] = {} if mirror_ns_ew && == 'dual_bar' ['secondary'][:length] = ['secondary'][:width] = dual_double_end_width elsif == 'dual_bar' ['secondary'][:length] = dual_double_end_width ['secondary'][:width] = elsif mirror_ns_ew ['secondary'][:length] = ['secondary'][:width] = adiabatic_dual_double_end_width args2[:party_wall_stories_east] = num_stories.ceil args2[:party_wall_stories_west] = num_stories.ceil else ['secondary'][:length] = adiabatic_dual_double_end_width ['secondary'][:width] = args2[:party_wall_stories_south] = num_stories.ceil args2[:party_wall_stories_north] = num_stories.ceil end ['secondary'][:floor_height] = floor_height # can make use of this when breaking out multi-height spaces ['secondary'][:num_stories] = num_stories ['secondary'][:space_types_hash] = space_types_hash_secondary if == 'adiabatic_ends_bar_b' # warn that combination of dual bar with low perimeter multiplier and use of party wall may result in discrepency between target and actual adiabatic walls 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 ['secondary'][:center_of_footprint] = OpenStudio::Point3d.new(( * 0.5) + (adiabatic_dual_double_end_width * 0.5) + offset_val, ( * 0.5) + (adiabatic_dual_double_end_width * 0.5) + offset_val, 0.0) else ['secondary'][:center_of_footprint] = OpenStudio::Point3d.new(( * 0.5) + (dual_double_end_width * 0.5) + offset_val, ( * 0.5) + (dual_double_end_width * 0.5) + offset_val, 0.0) end ['secondary'][:args] = args2 # setup bar_hash and run create_bar v = ['secondary'] OpenstudioStandards::Geometry.(model, v[:args], v[:length], v[:width], v[:floor_height], v[:center_of_footprint], v[:space_types_hash], v[:num_stories]) end # future development (up against primary bar run intersection and surface matching after add all bars, avoid interior windows) # I could loop through each space type and give them unique height but for now will just take largest height and make bar of that height, which is fine for prototypes if !multi_height_space_types_hash.empty? args3 = args.clone ['custom_height'] = {} if mirror_ns_ew ['custom_height'][:length] = width_cust_height ['custom_height'][:width] = length_cust_height else ['custom_height'][:length] = length_cust_height ['custom_height'][:width] = width_cust_height end 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 # setup stories args3[:num_stories_below_grade] = 0 args3[:num_stories_above_grade] = 1 # can make use of this when breaking out multi-height spaces ['custom_height'][:floor_height] = floor_height ['custom_height'][:num_stories] = num_stories ['custom_height'][:center_of_footprint] = OpenStudio::Point3d.new((['primary'][:length] * -0.5) - (length_cust_height * 0.5) - offset_val, 0.0, 0.0) ['custom_height'][:floor_height] = OpenStudio.convert(custom_story_heights.max, 'ft', 'm').get ['custom_height'][:num_stories] = 1 ['custom_height'][:space_types_hash] = multi_height_space_types_hash ['custom_height'][:args] = args3 v = ['custom_height'] OpenstudioStandards::Geometry.(model, v[:args], v[:length], v[:width], v[:floor_height], v[:center_of_footprint], v[:space_types_hash], v[:num_stories]) end # diagnostic log sum_actual = 0.0 sum_target = 0.0 throw_error = false # check expected floor areas against actual model.getSpaceTypes.sort.each do |space_type| next if !target_areas.key? space_type # space type in model not part of building type(s), maybe issue warning # convert to IP actual_ip = OpenStudio.convert(space_type.floorArea, 'm^2', 'ft^2').get target_ip = OpenStudio.convert(target_areas[space_type], 'm^2', 'ft^2').get sum_actual += actual_ip sum_target += target_ip if (space_type.floorArea - target_areas[space_type]).abs >= 1.0 if !args[:bar_division_method].include? 'Single Space Type' OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create', "#{space_type.name} doesn't have the expected floor area (actual #{OpenStudio.toNeatString(actual_ip, 0, true)} ft^2, target #{OpenStudio.toNeatString(target_ip, 0, true)} ft^2)") throw_error = true else # will see this if use Single Space type division method on multi-use building or single building type without whole building space type OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', "#{space_type.name} doesn't have the expected floor area (actual #{OpenStudio.toNeatString(actual_ip, 0, true)} ft^2, target #{OpenStudio.toNeatString(target_ip, 0, true)} ft^2)") end end end # report summary then throw error if throw_error OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create', "Sum of actual floor area is #{sum_actual} ft^2, sum of target floor area is #{sum_target}.") return false end # check party wall fraction by looping through surfaces if args[:party_wall_fraction] > 0 actual_ext_wall_area = model.getBuilding.exteriorWallArea actual_party_wall_area = 0.0 model.getSurfaces.sort.each do |surface| next if surface.outsideBoundaryCondition != 'Adiabatic' next if surface.surfaceType != 'Wall' actual_party_wall_area += surface.grossArea * surface.space.get.multiplier end actual_party_wall_fraction = actual_party_wall_area / (actual_party_wall_area + actual_ext_wall_area) OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "Target party wall fraction is #{args[:party_wall_fraction]}. Realized fraction is #{actual_party_wall_fraction.round(2)}") OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "party_wall_fraction_actual: #{actual_party_wall_fraction}") end # check ns/ew aspect ratio (harder to check when party walls are added) wall_and_window_by_orientation = OpenstudioStandards::Geometry.model_get_exterior_window_and_wall_area_by_orientation(model) wall_ns = (wall_and_window_by_orientation['north_wall'] + wall_and_window_by_orientation['south_wall']) wall_ew = wall_and_window_by_orientation['east_wall'] + wall_and_window_by_orientation['west_wall'] wall_ns_ip = OpenStudio.convert(wall_ns, 'm^2', 'ft^2').get wall_ew_ip = OpenStudio.convert(wall_ew, 'm^2', 'ft^2').get OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "wall_area_ip: #{wall_ns_ip + wall_ew_ip} ft^2") OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "ns_wall_area_ip: #{wall_ns_ip} ft^2") OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "ew_wall_area_ip: #{wall_ew_ip} ft^2") # for now using perimeter of ground floor and average story area (building area / num_stories) OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "floor_area_to_perim_ratio: #{model.getBuilding.floorArea / (OpenstudioStandards::Geometry.model_get_perimeter(model) * num_stories)}") OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "bar_width: #{OpenStudio.convert(['primary'][:width], 'm', 'ft').get} ft") 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::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 == '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? 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 # adjust length versus width based on building rotation if mirror_ns_ew wall_target_ns_ip = 2 * OpenStudio.convert(width, 'm', 'ft').get * args[:perim_mult] * args[:num_stories_above_grade] * args[:floor_height] wall_target_ew_ip = 2 * OpenStudio.convert(length, 'm', 'ft').get * args[:perim_mult] * args[:num_stories_above_grade] * args[:floor_height] else wall_target_ns_ip = 2 * OpenStudio.convert(length, 'm', 'ft').get * args[:perim_mult] * args[:num_stories_above_grade] * args[:floor_height] wall_target_ew_ip = 2 * OpenStudio.convert(width, 'm', 'ft').get * args[:perim_mult] * args[:num_stories_above_grade] * args[:floor_height] end flag_error = false if (wall_target_ns_ip - wall_ns_ip).abs > 0.1 OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create', "North/South walls don't have the expected area (actual #{OpenStudio.toNeatString(wall_ns_ip, 4, true)} ft^2, target #{OpenStudio.toNeatString(wall_target_ns_ip, 4, true)} ft^2)") flag_error = true end if (wall_target_ew_ip - wall_ew_ip).abs > 0.1 OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create', "East/West walls don't have the expected area (actual #{OpenStudio.toNeatString(wall_ew_ip, 4, true)} ft^2, target #{OpenStudio.toNeatString(wall_target_ew_ip, 4, true)} ft^2)") flag_error = true end if flag_error return false end 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.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 initial_rotation = model.getBuilding.northAxis if args[:building_rotation] != initial_rotation model.getBuilding.setNorthAxis(args[:building_rotation]) OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "Set Building Rotation to #{model.getBuilding.northAxis}. Rotation altered after geometry generation is completed, as a result party wall orientation and aspect ratio may not reflect input values.") end # report final condition of model OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "The building finished with #{model.getSpaces.size} spaces.") return true end |
.create_bar_from_building_type_ratios(model, args) ⇒ Boolean
create bar from building type ratios arguments are passed through to lower level methods. See create_bar_from_args_and_building_type_hash for additional argument options.
1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 |
# File 'lib/openstudio-standards/geometry/create_bar.rb', line 1996 def self.(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') bldg_subtype_b = args.fetch(:bldg_subtype_b, 'NA') bldg_subtype_c = args.fetch(:bldg_subtype_c, 'NA') bldg_subtype_d = args.fetch(:bldg_subtype_d, 'NA') 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.') return false end # report initial condition of model OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "The building started with #{model.getSpaces.size} spaces.") # determine of ns_ew needs to be mirrored mirror_ns_ew = false rotation = model.getBuilding.northAxis if rotation > 45.0 && rotation < 135.0 mirror_ns_ew = true elsif rotation > 45.0 && rotation < 135.0 mirror_ns_ew = true end # remove non-resource objects not removed by removing the building # remove_non_resource_objects(model) # rename building to infer template in downstream measure name_array = [template, bldg_type_a] if bldg_type_b_fract_bldg_area > 0 then name_array << bldg_type_b end if bldg_type_c_fract_bldg_area > 0 then name_array << bldg_type_c end if bldg_type_d_fract_bldg_area > 0 then name_array << bldg_type_d end model.getBuilding.setName(name_array.join('|').to_s) # hash to whole building type data building_type_hash = {} # gather data for bldg_type_a building_type_hash[bldg_type_a] = {} building_type_hash[bldg_type_a][:frac_bldg_area] = bldg_type_a_fract_bldg_area building_type_hash[bldg_type_a][:space_types] = OpenstudioStandards::CreateTypical.get_space_types_from_building_type(bldg_type_a, building_subtype: bldg_subtype_a, template: template, whole_building: true) # gather data for bldg_type_b if bldg_type_b_fract_bldg_area > 0 building_type_hash[bldg_type_b] = {} building_type_hash[bldg_type_b][:frac_bldg_area] = bldg_type_b_fract_bldg_area building_type_hash[bldg_type_b][:space_types] = OpenstudioStandards::CreateTypical.get_space_types_from_building_type(bldg_type_b, building_subtype: bldg_subtype_b, template: template, whole_building: true) end # gather data for bldg_type_c if bldg_type_c_fract_bldg_area > 0 building_type_hash[bldg_type_c] = {} building_type_hash[bldg_type_c][:frac_bldg_area] = bldg_type_c_fract_bldg_area building_type_hash[bldg_type_c][:space_types] = OpenstudioStandards::CreateTypical.get_space_types_from_building_type(bldg_type_c, building_subtype: bldg_subtype_c, template: template, whole_building: true) end # gather data for bldg_type_d if bldg_type_d_fract_bldg_area > 0 building_type_hash[bldg_type_d] = {} building_type_hash[bldg_type_d][:frac_bldg_area] = bldg_type_d_fract_bldg_area building_type_hash[bldg_type_d][:space_types] = OpenstudioStandards::CreateTypical.get_space_types_from_building_type(bldg_type_d, building_subtype: bldg_subtype_d, template: template, whole_building: true) end OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "Creating bar based on space ratios from #{bldg_type_a} for building form defaults.") # call create_bar_from_args_and_building_type_hash to generate bar args[:primary_building_type] = bldg_type_a OpenstudioStandards::Geometry.(model, args, building_type_hash) return true end |
.create_bar_from_space_type_ratios(model, args) ⇒ Boolean
create bar from space type ratios arguments are passed through to lower level methods. See create_bar_from_args_and_building_type_hash for additional argument options.
2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 |
# File 'lib/openstudio-standards/geometry/create_bar.rb', line 2143 def self.(model, args) if args[:space_type_hash_string].empty? OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', 'args hash passed to create_bar_from_space_type_ratios must include a non-empty :space_type_hash_string') return false end template = args.fetch(:template, '90.1-2013') # process arg into hash space_type_hash_name = {} args[:space_type_hash_string][0..-1].split(/, /) do |entry| entry_map = entry.split(/=>/) value_str = entry_map[1] space_type_hash_name[entry_map[0].strip[0..-1].to_s] = value_str.nil? ? '' : value_str.strip[0..-1].to_f end # create building type hash from space type ratios building_type_hash = {} building_type_fraction_of_building = 0.0 space_type_hash_name.each do |building_space_type, ratio| building_type = building_space_type.split('|')[0].strip space_type = building_space_type.split('|')[1].strip # harvest height and circ info from get_space_types_from_building_type(building_type, template, whole_building = true) building_type_lookup_info = OpenstudioStandards::CreateTypical.get_space_types_from_building_type(building_type, template: template) if building_type_lookup_info.empty? OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', "#{building_type} looks like an invalid building type for #{template}") end space_type_info_hash = {} if building_type_lookup_info.key?(space_type) if building_type_lookup_info[space_type].key?(:story_height) space_type_info_hash[:story_height] = building_type_lookup_info[space_type][:story_height] end if building_type_lookup_info[space_type].key?(:default) space_type_info_hash[:default] = building_type_lookup_info[space_type][:default] end if building_type_lookup_info[space_type].key?(:circ) space_type_info_hash[:circ] = building_type_lookup_info[space_type][:circ] end else OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', "#{space_type} looks like an invalid space type for #{building_type}") end # extend harvested data with custom ratios from space type ratio string argument. if building_type_hash.key?(building_type) building_type_hash[building_type][:frac_bldg_area] += ratio space_type_info_hash[:ratio] = ratio building_type_hash[building_type][:space_types][space_type] = space_type_info_hash else building_type_hash[building_type] = {} building_type_hash[building_type][:frac_bldg_area] = ratio space_type_info_hash[:ratio] = ratio space_types = {} space_types[space_type] = space_type_info_hash building_type_hash[building_type][:space_types] = space_types end building_type_fraction_of_building += ratio end # @todo confirm if this will get normalized up/down later of if I should fix or stop here instead of just a warning if building_type_fraction_of_building > 1.0 OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', "Sum of Space Type Ratio of #{building_type_fraction_of_building} is greater than the expected value of 1.0") elsif building_type_fraction_of_building < 1.0 OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', "Sum of Space Type Ratio of #{building_type_fraction_of_building} is less than the expected value of 1.0") end # identify primary building type for building form defaults # update to choose building with highest ratio primary_building_type = building_type_hash.keys.first OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "Creating bar based space type ratios provided. Using building type #{primary_building_type} from the first ratio as the primary building type. This determines the building form defaults.") # call create_bar_from_args_and_building_type_hash to generate bar args[:primary_building_type] = primary_building_type OpenstudioStandards::Geometry.(model, args, building_type_hash) return true end |
.create_core_and_perimeter_polygons(length, width, footprint_origin_point = OpenStudio::Point3d.new(0.0, 0.0, 0.0), perimeter_zone_depth = OpenStudio.convert(15.0, 'ft', 'm').get) ⇒ Hash
create core and perimeter polygons from length width and origin
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 |
# File 'lib/openstudio-standards/geometry/create.rb', line 80 def self.create_core_and_perimeter_polygons(length, width, footprint_origin_point = OpenStudio::Point3d.new(0.0, 0.0, 0.0), perimeter_zone_depth = OpenStudio.convert(15.0, 'ft', 'm').get) # key is name, value is a hash, one item of which is polygon. Another could be space type. hash_of_point_vectors = {} # determine if core and perimeter zoning can be used if !(length > perimeter_zone_depth * 2.5 && width > perimeter_zone_depth * 2.5) # if any size is to small then just model floor as single zone, issue warning perimeter_zone_depth = 0.0 OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', 'Due to the size of the building modeling each floor as a single zone.') end x_delta = footprint_origin_point.x - (length / 2.0) y_delta = footprint_origin_point.y - (width / 2.0) z = 0 nw_point = OpenStudio::Point3d.new(x_delta, y_delta + width, z) ne_point = OpenStudio::Point3d.new(x_delta + length, y_delta + width, z) se_point = OpenStudio::Point3d.new(x_delta + length, y_delta, z) sw_point = OpenStudio::Point3d.new(x_delta, y_delta, z) # Define polygons for a rectangular building if perimeter_zone_depth > 0 perimeter_nw_point = nw_point + OpenStudio::Vector3d.new(perimeter_zone_depth, -perimeter_zone_depth, 0) perimeter_ne_point = ne_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, -perimeter_zone_depth, 0) perimeter_se_point = se_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, perimeter_zone_depth, 0) perimeter_sw_point = sw_point + OpenStudio::Vector3d.new(perimeter_zone_depth, perimeter_zone_depth, 0) west_polygon = OpenStudio::Point3dVector.new west_polygon << sw_point west_polygon << nw_point west_polygon << perimeter_nw_point west_polygon << perimeter_sw_point hash_of_point_vectors['West Perimeter Space'] = {} hash_of_point_vectors['West Perimeter Space'][:space_type] = nil # other methods being used by makeSpacesFromPolygons may have space types associated with each polygon but this doesn't. hash_of_point_vectors['West Perimeter Space'][:polygon] = west_polygon north_polygon = OpenStudio::Point3dVector.new north_polygon << nw_point north_polygon << ne_point north_polygon << perimeter_ne_point north_polygon << perimeter_nw_point hash_of_point_vectors['North Perimeter Space'] = {} hash_of_point_vectors['North Perimeter Space'][:space_type] = nil hash_of_point_vectors['North Perimeter Space'][:polygon] = north_polygon east_polygon = OpenStudio::Point3dVector.new east_polygon << ne_point east_polygon << se_point east_polygon << perimeter_se_point east_polygon << perimeter_ne_point hash_of_point_vectors['East Perimeter Space'] = {} hash_of_point_vectors['East Perimeter Space'][:space_type] = nil hash_of_point_vectors['East Perimeter Space'][:polygon] = east_polygon south_polygon = OpenStudio::Point3dVector.new south_polygon << se_point south_polygon << sw_point south_polygon << perimeter_sw_point south_polygon << perimeter_se_point hash_of_point_vectors['South Perimeter Space'] = {} hash_of_point_vectors['South Perimeter Space'][:space_type] = nil hash_of_point_vectors['South Perimeter Space'][:polygon] = south_polygon core_polygon = OpenStudio::Point3dVector.new core_polygon << perimeter_sw_point core_polygon << perimeter_nw_point core_polygon << perimeter_ne_point core_polygon << perimeter_se_point hash_of_point_vectors['Core Space'] = {} hash_of_point_vectors['Core Space'][:space_type] = nil hash_of_point_vectors['Core Space'][:polygon] = core_polygon # Minimal zones else whole_story_polygon = OpenStudio::Point3dVector.new whole_story_polygon << sw_point whole_story_polygon << nw_point whole_story_polygon << ne_point whole_story_polygon << se_point hash_of_point_vectors['Whole Story Space'] = {} hash_of_point_vectors['Whole Story Space'][:space_type] = nil hash_of_point_vectors['Whole Story Space'][:polygon] = whole_story_polygon end return hash_of_point_vectors end |
.create_shape_aspect_ratio(model, aspect_ratio = 0.5, floor_area = 1000.0, rotation = 0.0, num_floors = 3, floor_to_floor_height = 3.8, plenum_height = 1.0, perimeter_zone_depth = 4.57) ⇒ OpenStudio::Model::Model
Create a Rectangle shape in a model based on a given aspect ratio
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 |
# File 'lib/openstudio-standards/geometry/create_shape.rb', line 199 def self.create_shape_aspect_ratio(model, aspect_ratio = 0.5, floor_area = 1000.0, rotation = 0.0, num_floors = 3, floor_to_floor_height = 3.8, plenum_height = 1.0, perimeter_zone_depth = 4.57) # determine length and width length = Math.sqrt((floor_area / (num_floors * 1.0)) / aspect_ratio) width = Math.sqrt((floor_area / (num_floors * 1.0)) * aspect_ratio) OpenstudioStandards::Geometry.create_shape_rectangle(model, length = length, width = width, above_ground_storys = num_floors, under_ground_storys = 0, floor_to_floor_height = floor_to_floor_height, plenum_height = plenum_height, perimeter_zone_depth = perimeter_zone_depth) BTAP::Geometry.rotate_model(model, rotation) return model end |
.create_shape_courtyard(model, length = 50.0, width = 30.0, courtyard_length = 15.0, courtyard_width = 5.0, num_floors = 3, floor_to_floor_height = 3.8, plenum_height = 1.0, perimeter_zone_depth = 4.57) ⇒ OpenStudio::Model::Model
Create a Courtyard shape in a model
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 |
# File 'lib/openstudio-standards/geometry/create_shape.rb', line 235 def self.create_shape_courtyard(model, length = 50.0, width = 30.0, courtyard_length = 15.0, courtyard_width = 5.0, num_floors = 3, floor_to_floor_height = 3.8, plenum_height = 1.0, perimeter_zone_depth = 4.57) if length <= 1e-4 OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', 'Length must be greater than 0.') return nil end if width <= 1e-4 OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', 'Width must be greater than 0.') return nil end if courtyard_length <= 1e-4 OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', 'Courtyard length must be greater than 0.') return nil end if courtyard_width <= 1e-4 OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', 'Courtyard width must be greater than 0.') return nil end if num_floors <= 1e-4 OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', 'Number of floors must be greater than 0.') return nil end if floor_to_floor_height <= 1e-4 OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', 'Floor to floor height must be greater than 0.') return nil end if plenum_height < 0 OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', 'Plenum height must be greater than 0.') return nil end shortest_side = [length, width].min if perimeter_zone_depth < 0 || 4 * perimeter_zone_depth >= (shortest_side - 1e-4) OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', "Perimeter zone depth must be greater than or equal to 0 and less than #{shortest_side / 4.0}m.") return nil end if courtyard_length >= (length - (4 * perimeter_zone_depth) - 1e-4) OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', "Courtyard length must be less than #{length - (4.0 * perimeter_zone_depth)}m.") return nil end if courtyard_width >= (width - (4 * perimeter_zone_depth) - 1e-4) OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', "Courtyard width must be less than #{width - (4.0 * perimeter_zone_depth)}m.") return nil end # Loop through the number of floors for floor in (0..num_floors - 1) z = floor_to_floor_height * floor # Create a new story within the building story = OpenStudio::Model::BuildingStory.new(model) story.setNominalFloortoFloorHeight(floor_to_floor_height) story.setName("Story #{floor + 1}") nw_point = OpenStudio::Point3d.new(0.0, width, z) ne_point = OpenStudio::Point3d.new(length, width, z) se_point = OpenStudio::Point3d.new(length, 0.0, z) sw_point = OpenStudio::Point3d.new(0.0, 0.0, z) courtyard_nw_point = OpenStudio::Point3d.new((length - courtyard_length) / 2.0, ((width - courtyard_width) / 2.0) + courtyard_width, z) courtyard_ne_point = OpenStudio::Point3d.new(((length - courtyard_length) / 2.0) + courtyard_length, ((width - courtyard_width) / 2.0) + courtyard_width, z) courtyard_se_point = OpenStudio::Point3d.new(((length - courtyard_length) / 2.0) + courtyard_length, (width - courtyard_width) / 2.0, z) courtyard_sw_point = OpenStudio::Point3d.new((length - courtyard_length) / 2.0, (width - courtyard_width) / 2.0, z) # Identity matrix for setting space origins m = OpenStudio::Matrix.new(4, 4, 0.0) m[0, 0] = 1.0 m[1, 1] = 1.0 m[2, 2] = 1.0 m[3, 3] = 1.0 # Define polygons for a building with a courtyard if perimeter_zone_depth > 0 outer_perimeter_nw_point = nw_point + OpenStudio::Vector3d.new(perimeter_zone_depth, -perimeter_zone_depth, 0.0) outer_perimeter_ne_point = ne_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, -perimeter_zone_depth, 0.0) outer_perimeter_se_point = se_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, perimeter_zone_depth, 0.0) outer_perimeter_sw_point = sw_point + OpenStudio::Vector3d.new(perimeter_zone_depth, perimeter_zone_depth, 0.0) inner_perimeter_nw_point = courtyard_nw_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, perimeter_zone_depth, 0.0) inner_perimeter_ne_point = courtyard_ne_point + OpenStudio::Vector3d.new(perimeter_zone_depth, perimeter_zone_depth, 0.0) inner_perimeter_se_point = courtyard_se_point + OpenStudio::Vector3d.new(perimeter_zone_depth, -perimeter_zone_depth, 0.0) inner_perimeter_sw_point = courtyard_sw_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, -perimeter_zone_depth, 0.0) west_outer_perimeter_polygon = OpenStudio::Point3dVector.new west_outer_perimeter_polygon << sw_point west_outer_perimeter_polygon << nw_point west_outer_perimeter_polygon << outer_perimeter_nw_point west_outer_perimeter_polygon << outer_perimeter_sw_point west_outer_perimeter_space = OpenStudio::Model::Space.fromFloorPrint(west_outer_perimeter_polygon, floor_to_floor_height, model) west_outer_perimeter_space = west_outer_perimeter_space.get m[0, 3] = sw_point.x m[1, 3] = sw_point.y m[2, 3] = sw_point.z west_outer_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m)) west_outer_perimeter_space.setBuildingStory(story) west_outer_perimeter_space.setName("Story #{floor + 1} West Outer Perimeter Space") north_outer_perimeter_polygon = OpenStudio::Point3dVector.new north_outer_perimeter_polygon << nw_point north_outer_perimeter_polygon << ne_point north_outer_perimeter_polygon << outer_perimeter_ne_point north_outer_perimeter_polygon << outer_perimeter_nw_point north_outer_perimeter_space = OpenStudio::Model::Space.fromFloorPrint(north_outer_perimeter_polygon, floor_to_floor_height, model) north_outer_perimeter_space = north_outer_perimeter_space.get m[0, 3] = outer_perimeter_nw_point.x m[1, 3] = outer_perimeter_nw_point.y m[2, 3] = outer_perimeter_nw_point.z north_outer_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m)) north_outer_perimeter_space.setBuildingStory(story) north_outer_perimeter_space.setName("Story #{floor + 1} North Outer Perimeter Space") east_outer_perimeter_polygon = OpenStudio::Point3dVector.new east_outer_perimeter_polygon << ne_point east_outer_perimeter_polygon << se_point east_outer_perimeter_polygon << outer_perimeter_se_point east_outer_perimeter_polygon << outer_perimeter_ne_point east_outer_perimeter_space = OpenStudio::Model::Space.fromFloorPrint(east_outer_perimeter_polygon, floor_to_floor_height, model) east_outer_perimeter_space = east_outer_perimeter_space.get m[0, 3] = outer_perimeter_se_point.x m[1, 3] = outer_perimeter_se_point.y m[2, 3] = outer_perimeter_se_point.z east_outer_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m)) east_outer_perimeter_space.setBuildingStory(story) east_outer_perimeter_space.setName("Story #{floor + 1} East Outer Perimeter Space") south_outer_perimeter_polygon = OpenStudio::Point3dVector.new south_outer_perimeter_polygon << se_point south_outer_perimeter_polygon << sw_point south_outer_perimeter_polygon << outer_perimeter_sw_point south_outer_perimeter_polygon << outer_perimeter_se_point south_outer_perimeter_space = OpenStudio::Model::Space.fromFloorPrint(south_outer_perimeter_polygon, floor_to_floor_height, model) south_outer_perimeter_space = south_outer_perimeter_space.get m[0, 3] = sw_point.x m[1, 3] = sw_point.y m[2, 3] = sw_point.z south_outer_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m)) south_outer_perimeter_space.setBuildingStory(story) south_outer_perimeter_space.setName("Story #{floor + 1} South Outer Perimeter Space") west_core_polygon = OpenStudio::Point3dVector.new west_core_polygon << outer_perimeter_sw_point west_core_polygon << outer_perimeter_nw_point west_core_polygon << inner_perimeter_nw_point west_core_polygon << inner_perimeter_sw_point west_core_space = OpenStudio::Model::Space.fromFloorPrint(west_core_polygon, floor_to_floor_height, model) west_core_space = west_core_space.get m[0, 3] = outer_perimeter_sw_point.x m[1, 3] = outer_perimeter_sw_point.y m[2, 3] = outer_perimeter_sw_point.z west_core_space.changeTransformation(OpenStudio::Transformation.new(m)) west_core_space.setBuildingStory(story) west_core_space.setName("Story #{floor + 1} West Core Space") north_core_polygon = OpenStudio::Point3dVector.new north_core_polygon << outer_perimeter_nw_point north_core_polygon << outer_perimeter_ne_point north_core_polygon << inner_perimeter_ne_point north_core_polygon << inner_perimeter_nw_point north_core_space = OpenStudio::Model::Space.fromFloorPrint(north_core_polygon, floor_to_floor_height, model) north_core_space = north_core_space.get m[0, 3] = inner_perimeter_nw_point.x m[1, 3] = inner_perimeter_nw_point.y m[2, 3] = inner_perimeter_nw_point.z north_core_space.changeTransformation(OpenStudio::Transformation.new(m)) north_core_space.setBuildingStory(story) north_core_space.setName("Story #{floor + 1} North Core Space") east_core_polygon = OpenStudio::Point3dVector.new east_core_polygon << outer_perimeter_ne_point east_core_polygon << outer_perimeter_se_point east_core_polygon << inner_perimeter_se_point east_core_polygon << inner_perimeter_ne_point east_core_space = OpenStudio::Model::Space.fromFloorPrint(east_core_polygon, floor_to_floor_height, model) east_core_space = east_core_space.get m[0, 3] = inner_perimeter_se_point.x m[1, 3] = inner_perimeter_se_point.y m[2, 3] = inner_perimeter_se_point.z east_core_space.changeTransformation(OpenStudio::Transformation.new(m)) east_core_space.setBuildingStory(story) east_core_space.setName("Story #{floor + 1} East Core Space") south_core_polygon = OpenStudio::Point3dVector.new south_core_polygon << outer_perimeter_se_point south_core_polygon << outer_perimeter_sw_point south_core_polygon << inner_perimeter_sw_point south_core_polygon << inner_perimeter_se_point south_core_space = OpenStudio::Model::Space.fromFloorPrint(south_core_polygon, floor_to_floor_height, model) south_core_space = south_core_space.get m[0, 3] = outer_perimeter_sw_point.x m[1, 3] = outer_perimeter_sw_point.y m[2, 3] = outer_perimeter_sw_point.z south_core_space.changeTransformation(OpenStudio::Transformation.new(m)) south_core_space.setBuildingStory(story) south_core_space.setName("Story #{floor + 1} South Core Space") west_inner_perimeter_polygon = OpenStudio::Point3dVector.new west_inner_perimeter_polygon << inner_perimeter_sw_point west_inner_perimeter_polygon << inner_perimeter_nw_point west_inner_perimeter_polygon << courtyard_nw_point west_inner_perimeter_polygon << courtyard_sw_point west_inner_perimeter_space = OpenStudio::Model::Space.fromFloorPrint(west_inner_perimeter_polygon, floor_to_floor_height, model) west_inner_perimeter_space = west_inner_perimeter_space.get m[0, 3] = inner_perimeter_sw_point.x m[1, 3] = inner_perimeter_sw_point.y m[2, 3] = inner_perimeter_sw_point.z west_inner_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m)) west_inner_perimeter_space.setBuildingStory(story) west_inner_perimeter_space.setName("Story #{floor + 1} West Inner Perimeter Space") north_inner_perimeter_polygon = OpenStudio::Point3dVector.new north_inner_perimeter_polygon << inner_perimeter_nw_point north_inner_perimeter_polygon << inner_perimeter_ne_point north_inner_perimeter_polygon << courtyard_ne_point north_inner_perimeter_polygon << courtyard_nw_point north_inner_perimeter_space = OpenStudio::Model::Space.fromFloorPrint(north_inner_perimeter_polygon, floor_to_floor_height, model) north_inner_perimeter_space = north_inner_perimeter_space.get m[0, 3] = courtyard_nw_point.x m[1, 3] = courtyard_nw_point.y m[2, 3] = courtyard_nw_point.z north_inner_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m)) north_inner_perimeter_space.setBuildingStory(story) north_inner_perimeter_space.setName("Story #{floor + 1} North Inner Perimeter Space") east_inner_perimeter_polygon = OpenStudio::Point3dVector.new east_inner_perimeter_polygon << inner_perimeter_ne_point east_inner_perimeter_polygon << inner_perimeter_se_point east_inner_perimeter_polygon << courtyard_se_point east_inner_perimeter_polygon << courtyard_ne_point east_inner_perimeter_space = OpenStudio::Model::Space.fromFloorPrint(east_inner_perimeter_polygon, floor_to_floor_height, model) east_inner_perimeter_space = east_inner_perimeter_space.get m[0, 3] = courtyard_se_point.x m[1, 3] = courtyard_se_point.y m[2, 3] = courtyard_se_point.z east_inner_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m)) east_inner_perimeter_space.setBuildingStory(story) east_inner_perimeter_space.setName("Story #{floor + 1} East Inner Perimeter Space") south_inner_perimeter_polygon = OpenStudio::Point3dVector.new south_inner_perimeter_polygon << inner_perimeter_se_point south_inner_perimeter_polygon << inner_perimeter_sw_point south_inner_perimeter_polygon << courtyard_sw_point south_inner_perimeter_polygon << courtyard_se_point south_inner_perimeter_space = OpenStudio::Model::Space.fromFloorPrint(south_inner_perimeter_polygon, floor_to_floor_height, model) south_inner_perimeter_space = south_inner_perimeter_space.get m[0, 3] = inner_perimeter_sw_point.x m[1, 3] = inner_perimeter_sw_point.y m[2, 3] = inner_perimeter_sw_point.z south_inner_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m)) south_inner_perimeter_space.setBuildingStory(story) south_inner_perimeter_space.setName("Story #{floor + 1} South Inner Perimeter Space") else # Minimal zones west_polygon = OpenStudio::Point3dVector.new west_polygon << sw_point west_polygon << nw_point west_polygon << courtyard_nw_point west_polygon << courtyard_sw_point west_space = OpenStudio::Model::Space.fromFloorPrint(west_polygon, floor_to_floor_height, model) west_space = west_space.get m[0, 3] = sw_point.x m[1, 3] = sw_point.y m[2, 3] = sw_point.z west_space.changeTransformation(OpenStudio::Transformation.new(m)) west_space.setBuildingStory(story) west_space.setName("Story #{floor + 1} West Space") north_polygon = OpenStudio::Point3dVector.new north_polygon << nw_point north_polygon << ne_point north_polygon << courtyard_ne_point north_polygon << courtyard_nw_point north_space = OpenStudio::Model::Space.fromFloorPrint(north_polygon, floor_to_floor_height, model) north_space = north_space.get m[0, 3] = courtyard_nw_point.x m[1, 3] = courtyard_nw_point.y m[2, 3] = courtyard_nw_point.z north_space.changeTransformation(OpenStudio::Transformation.new(m)) north_space.setBuildingStory(story) north_space.setName("Story #{floor + 1} North Space") east_polygon = OpenStudio::Point3dVector.new east_polygon << ne_point east_polygon << se_point east_polygon << courtyard_se_point east_polygon << courtyard_ne_point east_space = OpenStudio::Model::Space.fromFloorPrint(east_polygon, floor_to_floor_height, model) east_space = east_space.get m[0, 3] = courtyard_se_point.x m[1, 3] = courtyard_se_point.y m[2, 3] = courtyard_se_point.z east_space.changeTransformation(OpenStudio::Transformation.new(m)) east_space.setBuildingStory(story) east_space.setName("Story #{floor + 1} East Space") south_polygon = OpenStudio::Point3dVector.new south_polygon << se_point south_polygon << sw_point south_polygon << courtyard_sw_point south_polygon << courtyard_se_point south_space = OpenStudio::Model::Space.fromFloorPrint(south_polygon, floor_to_floor_height, model) south_space = south_space.get m[0, 3] = sw_point.x m[1, 3] = sw_point.y m[2, 3] = sw_point.z south_space.changeTransformation(OpenStudio::Transformation.new(m)) south_space.setBuildingStory(story) south_space.setName("Story #{floor + 1} South Space") end # Set vertical story position story.setNominalZCoordinate(z) end BTAP::Geometry.match_surfaces(model) return model end |
.create_shape_h(model, length = 40.0, left_width = 40.0, center_width = 10.0, right_width = 40.0, left_end_length = 15.0, right_end_length = 15.0, left_upper_end_offset = 15.0, right_upper_end_offset = 15.0, num_floors = 3, floor_to_floor_height = 3.8, plenum_height = 1, perimeter_zone_depth = 4.57) ⇒ OpenStudio::Model::Model
Create an H shape in a model
581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 |
# File 'lib/openstudio-standards/geometry/create_shape.rb', line 581 def self.create_shape_h(model, length = 40.0, left_width = 40.0, center_width = 10.0, right_width = 40.0, left_end_length = 15.0, right_end_length = 15.0, left_upper_end_offset = 15.0, right_upper_end_offset = 15.0, num_floors = 3, floor_to_floor_height = 3.8, plenum_height = 1, perimeter_zone_depth = 4.57) if length <= 1e-4 OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', 'Length must be greater than 0.') return nil end if left_width <= 1e-4 OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', 'Left width must be greater than 0.') return nil end if right_width <= 1e-4 OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', 'Right width must be greater than 0.') return nil end if center_width <= 1e-4 || center_width >= ([left_width, right_width].min - 1e-4) OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', "Center width must be greater than 0 and less than #{[left_width, right_width].min}m.") return nil end if left_end_length <= 1e-4 || left_end_length >= (length - 1e-4) OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', "Left end length must be greater than 0 and less than #{length}m.") return nil end if right_end_length <= 1e-4 || right_end_length >= (length - left_end_length - 1e-4) OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', "Right end length must be greater than 0 and less than #{length - left_end_length}m.") return nil end if left_upper_end_offset <= 1e-4 || left_upper_end_offset >= (left_width - center_width - 1e-4) OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', "Left upper end offset must be greater than 0 and less than #{left_width - center_width}m.") return nil end if right_upper_end_offset <= 1e-4 || right_upper_end_offset >= (right_width - center_width - 1e-4) OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', "Right upper end offset must be greater than 0 and less than #{right_width - center_width}m.") return nil end if num_floors <= 1e-4 OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', 'Number of floors must be greater than 0.') return nil end if floor_to_floor_height <= 1e-4 OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', 'Floor to floor height must be greater than 0.') return nil end if plenum_height < 0 OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', 'Plenum height must be greater than 0.') return nil end shortest_side = [length / 2, left_width, center_width, right_width, left_end_length, right_end_length].min if perimeter_zone_depth < 0 || 2 * perimeter_zone_depth >= (shortest_side - 1e-4) OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', "Perimeter zone depth must be greater than or equal to 0 and less than #{shortest_side / 2}m.") return nil end # Loop through the number of floors for floor in (0..num_floors - 1) z = floor_to_floor_height * floor # Create a new story within the building story = OpenStudio::Model::BuildingStory.new(model) story.setNominalFloortoFloorHeight(floor_to_floor_height) story.setName("Story #{floor + 1}") left_origin = (right_width - right_upper_end_offset) > (left_width - left_upper_end_offset) ? (right_width - right_upper_end_offset) - (left_width - left_upper_end_offset) : 0 left_nw_point = OpenStudio::Point3d.new(0, left_width + left_origin, z) left_ne_point = OpenStudio::Point3d.new(left_end_length, left_width + left_origin, z) left_se_point = OpenStudio::Point3d.new(left_end_length, left_origin, z) left_sw_point = OpenStudio::Point3d.new(0, left_origin, z) center_nw_point = OpenStudio::Point3d.new(left_end_length, left_ne_point.y - left_upper_end_offset, z) center_ne_point = OpenStudio::Point3d.new(length - right_end_length, center_nw_point.y, z) center_se_point = OpenStudio::Point3d.new(length - right_end_length, center_nw_point.y - center_width, z) center_sw_point = OpenStudio::Point3d.new(left_end_length, center_se_point.y, z) right_nw_point = OpenStudio::Point3d.new(length - right_end_length, center_ne_point.y + right_upper_end_offset, z) right_ne_point = OpenStudio::Point3d.new(length, right_nw_point.y, z) right_se_point = OpenStudio::Point3d.new(length, right_ne_point.y - right_width, z) right_sw_point = OpenStudio::Point3d.new(length - right_end_length, right_se_point.y, z) # Identity matrix for setting space origins m = OpenStudio::Matrix.new(4, 4, 0) m[0, 0] = 1 m[1, 1] = 1 m[2, 2] = 1 m[3, 3] = 1 # Define polygons for an H-shape building with perimeter core zoning if perimeter_zone_depth > 0 perimeter_left_nw_point = left_nw_point + OpenStudio::Vector3d.new(perimeter_zone_depth, -perimeter_zone_depth, 0) perimeter_left_ne_point = left_ne_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, -perimeter_zone_depth, 0) perimeter_left_se_point = left_se_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, perimeter_zone_depth, 0) perimeter_left_sw_point = left_sw_point + OpenStudio::Vector3d.new(perimeter_zone_depth, perimeter_zone_depth, 0) perimeter_center_nw_point = center_nw_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, -perimeter_zone_depth, 0) perimeter_center_ne_point = center_ne_point + OpenStudio::Vector3d.new(perimeter_zone_depth, -perimeter_zone_depth, 0) perimeter_center_se_point = center_se_point + OpenStudio::Vector3d.new(perimeter_zone_depth, perimeter_zone_depth, 0) perimeter_center_sw_point = center_sw_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, perimeter_zone_depth, 0) perimeter_right_nw_point = right_nw_point + OpenStudio::Vector3d.new(perimeter_zone_depth, -perimeter_zone_depth, 0) perimeter_right_ne_point = right_ne_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, -perimeter_zone_depth, 0) perimeter_right_se_point = right_se_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, perimeter_zone_depth, 0) perimeter_right_sw_point = right_sw_point + OpenStudio::Vector3d.new(perimeter_zone_depth, perimeter_zone_depth, 0) west_left_perimeter_polygon = OpenStudio::Point3dVector.new west_left_perimeter_polygon << left_sw_point west_left_perimeter_polygon << left_nw_point west_left_perimeter_polygon << perimeter_left_nw_point west_left_perimeter_polygon << perimeter_left_sw_point west_left_perimeter_space = OpenStudio::Model::Space.fromFloorPrint(west_left_perimeter_polygon, floor_to_floor_height, model) west_left_perimeter_space = west_left_perimeter_space.get m[0, 3] = left_sw_point.x m[1, 3] = left_sw_point.y m[2, 3] = left_sw_point.z west_left_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m)) west_left_perimeter_space.setBuildingStory(story) west_left_perimeter_space.setName("Story #{floor + 1} West Left Perimeter Space") north_left_perimeter_polygon = OpenStudio::Point3dVector.new north_left_perimeter_polygon << left_nw_point north_left_perimeter_polygon << left_ne_point north_left_perimeter_polygon << perimeter_left_ne_point north_left_perimeter_polygon << perimeter_left_nw_point north_left_perimeter_space = OpenStudio::Model::Space.fromFloorPrint(north_left_perimeter_polygon, floor_to_floor_height, model) north_left_perimeter_space = north_left_perimeter_space.get m[0, 3] = perimeter_left_nw_point.x m[1, 3] = perimeter_left_nw_point.y m[2, 3] = perimeter_left_nw_point.z north_left_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m)) north_left_perimeter_space.setBuildingStory(story) north_left_perimeter_space.setName("Story #{floor + 1} North Left Perimeter Space") east_upper_left_perimeter_polygon = OpenStudio::Point3dVector.new east_upper_left_perimeter_polygon << left_ne_point east_upper_left_perimeter_polygon << center_nw_point east_upper_left_perimeter_polygon << perimeter_center_nw_point east_upper_left_perimeter_polygon << perimeter_left_ne_point east_upper_left_perimeter_space = OpenStudio::Model::Space.fromFloorPrint(east_upper_left_perimeter_polygon, floor_to_floor_height, model) east_upper_left_perimeter_space = east_upper_left_perimeter_space.get m[0, 3] = perimeter_center_nw_point.x m[1, 3] = perimeter_center_nw_point.y m[2, 3] = perimeter_center_nw_point.z east_upper_left_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m)) east_upper_left_perimeter_space.setBuildingStory(story) east_upper_left_perimeter_space.setName("Story #{floor + 1} East Upper Left Perimeter Space") north_center_perimeter_polygon = OpenStudio::Point3dVector.new north_center_perimeter_polygon << center_nw_point north_center_perimeter_polygon << center_ne_point north_center_perimeter_polygon << perimeter_center_ne_point north_center_perimeter_polygon << perimeter_center_nw_point north_center_perimeter_space = OpenStudio::Model::Space.fromFloorPrint(north_center_perimeter_polygon, floor_to_floor_height, model) north_center_perimeter_space = north_center_perimeter_space.get m[0, 3] = perimeter_center_nw_point.x m[1, 3] = perimeter_center_nw_point.y m[2, 3] = perimeter_center_nw_point.z north_center_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m)) north_center_perimeter_space.setBuildingStory(story) north_center_perimeter_space.setName("Story #{floor + 1} North Center Perimeter Space") west_upper_right_perimeter_polygon = OpenStudio::Point3dVector.new west_upper_right_perimeter_polygon << center_ne_point west_upper_right_perimeter_polygon << right_nw_point west_upper_right_perimeter_polygon << perimeter_right_nw_point west_upper_right_perimeter_polygon << perimeter_center_ne_point west_upper_right_perimeter_space = OpenStudio::Model::Space.fromFloorPrint(west_upper_right_perimeter_polygon, floor_to_floor_height, model) west_upper_right_perimeter_space = west_upper_right_perimeter_space.get m[0, 3] = center_ne_point.x m[1, 3] = center_ne_point.y m[2, 3] = center_ne_point.z west_upper_right_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m)) west_upper_right_perimeter_space.setBuildingStory(story) west_upper_right_perimeter_space.setName("Story #{floor + 1} West Upper Right Perimeter Space") north_right_perimeter_polygon = OpenStudio::Point3dVector.new north_right_perimeter_polygon << right_nw_point north_right_perimeter_polygon << right_ne_point north_right_perimeter_polygon << perimeter_right_ne_point north_right_perimeter_polygon << perimeter_right_nw_point north_right_perimeter_space = OpenStudio::Model::Space.fromFloorPrint(north_right_perimeter_polygon, floor_to_floor_height, model) north_right_perimeter_space = north_right_perimeter_space.get m[0, 3] = perimeter_right_nw_point.x m[1, 3] = perimeter_right_nw_point.y m[2, 3] = perimeter_right_nw_point.z north_right_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m)) north_right_perimeter_space.setBuildingStory(story) north_right_perimeter_space.setName("Story #{floor + 1} North Right Perimeter Space") east_right_perimeter_polygon = OpenStudio::Point3dVector.new east_right_perimeter_polygon << right_ne_point east_right_perimeter_polygon << right_se_point east_right_perimeter_polygon << perimeter_right_se_point east_right_perimeter_polygon << perimeter_right_ne_point east_right_perimeter_space = OpenStudio::Model::Space.fromFloorPrint(east_right_perimeter_polygon, floor_to_floor_height, model) east_right_perimeter_space = east_right_perimeter_space.get m[0, 3] = perimeter_right_se_point.x m[1, 3] = perimeter_right_se_point.y m[2, 3] = perimeter_right_se_point.z east_right_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m)) east_right_perimeter_space.setBuildingStory(story) east_right_perimeter_space.setName("Story #{floor + 1} East Right Perimeter Space") south_right_perimeter_polygon = OpenStudio::Point3dVector.new south_right_perimeter_polygon << right_se_point south_right_perimeter_polygon << right_sw_point south_right_perimeter_polygon << perimeter_right_sw_point south_right_perimeter_polygon << perimeter_right_se_point south_right_perimeter_space = OpenStudio::Model::Space.fromFloorPrint(south_right_perimeter_polygon, floor_to_floor_height, model) south_right_perimeter_space = south_right_perimeter_space.get m[0, 3] = right_sw_point.x m[1, 3] = right_sw_point.y m[2, 3] = right_sw_point.z south_right_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m)) south_right_perimeter_space.setBuildingStory(story) south_right_perimeter_space.setName("Story #{floor + 1} South Right Perimeter Space") west_lower_right_perimeter_polygon = OpenStudio::Point3dVector.new west_lower_right_perimeter_polygon << right_sw_point west_lower_right_perimeter_polygon << center_se_point west_lower_right_perimeter_polygon << perimeter_center_se_point west_lower_right_perimeter_polygon << perimeter_right_sw_point west_lower_right_perimeter_space = OpenStudio::Model::Space.fromFloorPrint(west_lower_right_perimeter_polygon, floor_to_floor_height, model) west_lower_right_perimeter_space = west_lower_right_perimeter_space.get m[0, 3] = right_sw_point.x m[1, 3] = right_sw_point.y m[2, 3] = right_sw_point.z west_lower_right_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m)) west_lower_right_perimeter_space.setBuildingStory(story) west_lower_right_perimeter_space.setName("Story #{floor + 1} West Lower Right Perimeter Space") south_center_perimeter_polygon = OpenStudio::Point3dVector.new south_center_perimeter_polygon << center_se_point south_center_perimeter_polygon << center_sw_point south_center_perimeter_polygon << perimeter_center_sw_point south_center_perimeter_polygon << perimeter_center_se_point south_center_perimeter_space = OpenStudio::Model::Space.fromFloorPrint(south_center_perimeter_polygon, floor_to_floor_height, model) south_center_perimeter_space = south_center_perimeter_space.get m[0, 3] = center_sw_point.x m[1, 3] = center_sw_point.y m[2, 3] = center_sw_point.z south_center_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m)) south_center_perimeter_space.setBuildingStory(story) south_center_perimeter_space.setName("Story #{floor + 1} South Center Perimeter Space") east_lower_left_perimeter_polygon = OpenStudio::Point3dVector.new east_lower_left_perimeter_polygon << center_sw_point east_lower_left_perimeter_polygon << left_se_point east_lower_left_perimeter_polygon << perimeter_left_se_point east_lower_left_perimeter_polygon << perimeter_center_sw_point east_lower_left_perimeter_space = OpenStudio::Model::Space.fromFloorPrint(east_lower_left_perimeter_polygon, floor_to_floor_height, model) east_lower_left_perimeter_space = east_lower_left_perimeter_space.get m[0, 3] = perimeter_left_se_point.x m[1, 3] = perimeter_left_se_point.y m[2, 3] = perimeter_left_se_point.z east_lower_left_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m)) east_lower_left_perimeter_space.setBuildingStory(story) east_lower_left_perimeter_space.setName("Story #{floor + 1} East Lower Left Perimeter Space") south_left_perimeter_polygon = OpenStudio::Point3dVector.new south_left_perimeter_polygon << left_se_point south_left_perimeter_polygon << left_sw_point south_left_perimeter_polygon << perimeter_left_sw_point south_left_perimeter_polygon << perimeter_left_se_point south_left_perimeter_space = OpenStudio::Model::Space.fromFloorPrint(south_left_perimeter_polygon, floor_to_floor_height, model) south_left_perimeter_space = south_left_perimeter_space.get m[0, 3] = left_sw_point.x m[1, 3] = left_sw_point.y m[2, 3] = left_sw_point.z south_left_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m)) south_left_perimeter_space.setBuildingStory(story) south_left_perimeter_space.setName("Story #{floor + 1} South Left Perimeter Space") west_core_polygon = OpenStudio::Point3dVector.new west_core_polygon << perimeter_left_sw_point west_core_polygon << perimeter_left_nw_point west_core_polygon << perimeter_left_ne_point west_core_polygon << perimeter_center_nw_point west_core_polygon << perimeter_center_sw_point west_core_polygon << perimeter_left_se_point west_core_space = OpenStudio::Model::Space.fromFloorPrint(west_core_polygon, floor_to_floor_height, model) west_core_space = west_core_space.get m[0, 3] = perimeter_left_sw_point.x m[1, 3] = perimeter_left_sw_point.y m[2, 3] = perimeter_left_sw_point.z west_core_space.changeTransformation(OpenStudio::Transformation.new(m)) west_core_space.setBuildingStory(story) west_core_space.setName("Story #{floor + 1} West Core Space") center_core_polygon = OpenStudio::Point3dVector.new center_core_polygon << perimeter_center_sw_point center_core_polygon << perimeter_center_nw_point center_core_polygon << perimeter_center_ne_point center_core_polygon << perimeter_center_se_point center_core_space = OpenStudio::Model::Space.fromFloorPrint(center_core_polygon, floor_to_floor_height, model) center_core_space = center_core_space.get m[0, 3] = perimeter_center_sw_point.x m[1, 3] = perimeter_center_sw_point.y m[2, 3] = perimeter_center_sw_point.z center_core_space.changeTransformation(OpenStudio::Transformation.new(m)) center_core_space.setBuildingStory(story) center_core_space.setName("Story #{floor + 1} Center Core Space") east_core_polygon = OpenStudio::Point3dVector.new east_core_polygon << perimeter_right_sw_point east_core_polygon << perimeter_center_se_point east_core_polygon << perimeter_center_ne_point east_core_polygon << perimeter_right_nw_point east_core_polygon << perimeter_right_ne_point east_core_polygon << perimeter_right_se_point east_core_space = OpenStudio::Model::Space.fromFloorPrint(east_core_polygon, floor_to_floor_height, model) east_core_space = east_core_space.get m[0, 3] = perimeter_right_sw_point.x m[1, 3] = perimeter_right_sw_point.y m[2, 3] = perimeter_right_sw_point.z east_core_space.changeTransformation(OpenStudio::Transformation.new(m)) east_core_space.setBuildingStory(story) east_core_space.setName("Story #{floor + 1} East Core Space") else # Minimal zones west_polygon = OpenStudio::Point3dVector.new west_polygon << left_sw_point west_polygon << left_nw_point west_polygon << left_ne_point west_polygon << center_nw_point west_polygon << center_sw_point west_polygon << left_se_point west_space = OpenStudio::Model::Space.fromFloorPrint(west_polygon, floor_to_floor_height, model) west_space = west_space.get m[0, 3] = left_sw_point.x m[1, 3] = left_sw_point.y m[2, 3] = left_sw_point.z west_space.changeTransformation(OpenStudio::Transformation.new(m)) west_space.setBuildingStory(story) west_space.setName("Story #{floor + 1} West Space") center_polygon = OpenStudio::Point3dVector.new center_polygon << center_sw_point center_polygon << center_nw_point center_polygon << center_ne_point center_polygon << center_se_point center_space = OpenStudio::Model::Space.fromFloorPrint(center_polygon, floor_to_floor_height, model) center_space = center_space.get m[0, 3] = center_sw_point.x m[1, 3] = center_sw_point.y m[2, 3] = center_sw_point.z center_space.changeTransformation(OpenStudio::Transformation.new(m)) center_space.setBuildingStory(story) center_space.setName("Story #{floor + 1} Center Space") east_polygon = OpenStudio::Point3dVector.new east_polygon << right_sw_point east_polygon << center_se_point east_polygon << center_ne_point east_polygon << right_nw_point east_polygon << right_ne_point east_polygon << right_se_point east_space = OpenStudio::Model::Space.fromFloorPrint(east_polygon, floor_to_floor_height, model) east_space = east_space.get m[0, 3] = right_sw_point.x m[1, 3] = right_sw_point.y m[2, 3] = right_sw_point.z east_space.changeTransformation(OpenStudio::Transformation.new(m)) east_space.setBuildingStory(story) east_space.setName("Story #{floor + 1} East Space") end # Set vertical story position story.setNominalZCoordinate(z) end BTAP::Geometry.match_surfaces(model) return model end |
.create_shape_l(model, length = 40.0, width = 40.0, lower_end_width = 20.0, upper_end_length = 20.0, num_floors = 3, floor_to_floor_height = 3.8, plenum_height = 1.0, perimeter_zone_depth = 4.57) ⇒ OpenStudio::Model::Model
Create an L shape in a model
982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 |
# File 'lib/openstudio-standards/geometry/create_shape.rb', line 982 def self.create_shape_l(model, length = 40.0, width = 40.0, lower_end_width = 20.0, upper_end_length = 20.0, num_floors = 3, floor_to_floor_height = 3.8, plenum_height = 1.0, perimeter_zone_depth = 4.57) if length <= 1e-4 OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', 'Length must be greater than 0.') return nil end if width <= 1e-4 OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', 'Width must be greater than 0.') return nil end if lower_end_width <= 1e-4 || lower_end_width >= (width - 1e-4) OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', "Lower end width must be greater than 0 and less than #{width}m.") return nil end if upper_end_length <= 1e-4 || upper_end_length >= (length - 1e-4) OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', "Upper end length must be greater than 0 and less than #{length}m.") return nil end if num_floors <= 1e-4 OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', 'Number of floors must be greater than 0.') return nil end if floor_to_floor_height <= 1e-4 OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', 'Floor to floor height must be greater than 0.') return nil end if plenum_height < 0 OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', 'Plenum height must be greater than 0.') return nil end shortest_side = [lower_end_width, upper_end_length].min if perimeter_zone_depth < 0 || 2 * perimeter_zone_depth >= (shortest_side - 1e-4) OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', "Perimeter zone depth must be greater than or equal to 0 and less than #{shortest_side / 2}m.") return nil end # Loop through the number of floors for floor in (0..num_floors - 1) z = floor_to_floor_height * floor # Create a new story within the building story = OpenStudio::Model::BuildingStory.new(model) story.setNominalFloortoFloorHeight(floor_to_floor_height) story.setName("Story #{floor + 1}") nw_point = OpenStudio::Point3d.new(0, width, z) upper_ne_point = OpenStudio::Point3d.new(upper_end_length, width, z) upper_sw_point = OpenStudio::Point3d.new(upper_end_length, lower_end_width, z) lower_ne_point = OpenStudio::Point3d.new(length, lower_end_width, z) se_point = OpenStudio::Point3d.new(length, 0, z) sw_point = OpenStudio::Point3d.new(0, 0, z) # Identity matrix for setting space origins m = OpenStudio::Matrix.new(4, 4, 0) m[0, 0] = 1 m[1, 1] = 1 m[2, 2] = 1 m[3, 3] = 1 # Define polygons for a L-shape building with perimeter core zoning if perimeter_zone_depth > 0 perimeter_nw_point = nw_point + OpenStudio::Vector3d.new(perimeter_zone_depth, -perimeter_zone_depth, 0) perimeter_upper_ne_point = upper_ne_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, -perimeter_zone_depth, 0) perimeter_upper_sw_point = upper_sw_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, -perimeter_zone_depth, 0) perimeter_lower_ne_point = lower_ne_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, -perimeter_zone_depth, 0) perimeter_se_point = se_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, perimeter_zone_depth, 0) perimeter_lower_sw_point = sw_point + OpenStudio::Vector3d.new(perimeter_zone_depth, perimeter_zone_depth, 0) west_perimeter_polygon = OpenStudio::Point3dVector.new west_perimeter_polygon << sw_point west_perimeter_polygon << nw_point west_perimeter_polygon << perimeter_nw_point west_perimeter_polygon << perimeter_lower_sw_point west_perimeter_space = OpenStudio::Model::Space.fromFloorPrint(west_perimeter_polygon, floor_to_floor_height, model) west_perimeter_space = west_perimeter_space.get m[0, 3] = sw_point.x m[1, 3] = sw_point.y m[2, 3] = sw_point.z west_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m)) west_perimeter_space.setBuildingStory(story) west_perimeter_space.setName("Story #{floor + 1} West Perimeter Space") north_upper_perimeter_polygon = OpenStudio::Point3dVector.new north_upper_perimeter_polygon << nw_point north_upper_perimeter_polygon << upper_ne_point north_upper_perimeter_polygon << perimeter_upper_ne_point north_upper_perimeter_polygon << perimeter_nw_point north_upper_perimeter_space = OpenStudio::Model::Space.fromFloorPrint(north_upper_perimeter_polygon, floor_to_floor_height, model) north_upper_perimeter_space = north_upper_perimeter_space.get m[0, 3] = perimeter_nw_point.x m[1, 3] = perimeter_nw_point.y m[2, 3] = perimeter_nw_point.z north_upper_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m)) north_upper_perimeter_space.setBuildingStory(story) north_upper_perimeter_space.setName("Story #{floor + 1} North Upper Perimeter Space") east_upper_perimeter_polygon = OpenStudio::Point3dVector.new east_upper_perimeter_polygon << upper_ne_point east_upper_perimeter_polygon << upper_sw_point east_upper_perimeter_polygon << perimeter_upper_sw_point east_upper_perimeter_polygon << perimeter_upper_ne_point east_upper_perimeter_space = OpenStudio::Model::Space.fromFloorPrint(east_upper_perimeter_polygon, floor_to_floor_height, model) east_upper_perimeter_space = east_upper_perimeter_space.get m[0, 3] = perimeter_upper_sw_point.x m[1, 3] = perimeter_upper_sw_point.y m[2, 3] = perimeter_upper_sw_point.z east_upper_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m)) east_upper_perimeter_space.setBuildingStory(story) east_upper_perimeter_space.setName("Story #{floor + 1} East Upper Perimeter Space") north_lower_perimeter_polygon = OpenStudio::Point3dVector.new north_lower_perimeter_polygon << upper_sw_point north_lower_perimeter_polygon << lower_ne_point north_lower_perimeter_polygon << perimeter_lower_ne_point north_lower_perimeter_polygon << perimeter_upper_sw_point north_lower_perimeter_space = OpenStudio::Model::Space.fromFloorPrint(north_lower_perimeter_polygon, floor_to_floor_height, model) north_lower_perimeter_space = north_lower_perimeter_space.get m[0, 3] = perimeter_upper_sw_point.x m[1, 3] = perimeter_upper_sw_point.y m[2, 3] = perimeter_upper_sw_point.z north_lower_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m)) north_lower_perimeter_space.setBuildingStory(story) north_lower_perimeter_space.setName("Story #{floor + 1} North Lower Perimeter Space") east_lower_perimeter_polygon = OpenStudio::Point3dVector.new east_lower_perimeter_polygon << lower_ne_point east_lower_perimeter_polygon << se_point east_lower_perimeter_polygon << perimeter_se_point east_lower_perimeter_polygon << perimeter_lower_ne_point east_lower_perimeter_space = OpenStudio::Model::Space.fromFloorPrint(east_lower_perimeter_polygon, floor_to_floor_height, model) east_lower_perimeter_space = east_lower_perimeter_space.get m[0, 3] = perimeter_se_point.x m[1, 3] = perimeter_se_point.y m[2, 3] = perimeter_se_point.z east_lower_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m)) east_lower_perimeter_space.setBuildingStory(story) east_lower_perimeter_space.setName("Story #{floor + 1} East Lower Perimeter Space") south_perimeter_polygon = OpenStudio::Point3dVector.new south_perimeter_polygon << se_point south_perimeter_polygon << sw_point south_perimeter_polygon << perimeter_lower_sw_point south_perimeter_polygon << perimeter_se_point south_perimeter_space = OpenStudio::Model::Space.fromFloorPrint(south_perimeter_polygon, floor_to_floor_height, model) south_perimeter_space = south_perimeter_space.get m[0, 3] = sw_point.x m[1, 3] = sw_point.y m[2, 3] = sw_point.z south_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m)) south_perimeter_space.setBuildingStory(story) south_perimeter_space.setName("Story #{floor + 1} South Perimeter Space") west_core_polygon = OpenStudio::Point3dVector.new west_core_polygon << perimeter_lower_sw_point west_core_polygon << perimeter_nw_point west_core_polygon << perimeter_upper_ne_point west_core_polygon << perimeter_upper_sw_point west_core_space = OpenStudio::Model::Space.fromFloorPrint(west_core_polygon, floor_to_floor_height, model) west_core_space = west_core_space.get m[0, 3] = perimeter_lower_sw_point.x m[1, 3] = perimeter_lower_sw_point.y m[2, 3] = perimeter_lower_sw_point.z west_core_space.changeTransformation(OpenStudio::Transformation.new(m)) west_core_space.setBuildingStory(story) west_core_space.setName("Story #{floor + 1} West Core Space") east_core_polygon = OpenStudio::Point3dVector.new east_core_polygon << perimeter_upper_sw_point east_core_polygon << perimeter_lower_ne_point east_core_polygon << perimeter_se_point east_core_polygon << perimeter_lower_sw_point east_core_space = OpenStudio::Model::Space.fromFloorPrint(east_core_polygon, floor_to_floor_height, model) east_core_space = east_core_space.get m[0, 3] = perimeter_lower_sw_point.x m[1, 3] = perimeter_lower_sw_point.y m[2, 3] = perimeter_lower_sw_point.z east_core_space.changeTransformation(OpenStudio::Transformation.new(m)) east_core_space.setBuildingStory(story) east_core_space.setName("Story #{floor + 1} East Core Space") else # Minimal zones west_polygon = OpenStudio::Point3dVector.new west_polygon << sw_point west_polygon << nw_point west_polygon << upper_ne_point west_polygon << upper_sw_point west_space = OpenStudio::Model::Space.fromFloorPrint(west_polygon, floor_to_floor_height, model) west_space = west_space.get m[0, 3] = sw_point.x m[1, 3] = sw_point.y m[2, 3] = sw_point.z west_space.changeTransformation(OpenStudio::Transformation.new(m)) west_space.setBuildingStory(story) west_space.setName("Story #{floor + 1} West Space") east_polygon = OpenStudio::Point3dVector.new east_polygon << sw_point east_polygon << upper_sw_point east_polygon << lower_ne_point east_polygon << se_point east_space = OpenStudio::Model::Space.fromFloorPrint(east_polygon, floor_to_floor_height, model) east_space = east_space.get m[0, 3] = sw_point.x m[1, 3] = sw_point.y m[2, 3] = sw_point.z east_space.changeTransformation(OpenStudio::Transformation.new(m)) east_space.setBuildingStory(story) east_space.setName("Story #{floor + 1} East Space") end # Set vertical story position story.setNominalZCoordinate(z) end BTAP::Geometry.match_surfaces(model) return model end |
.create_shape_rectangle(model, length = 100.0, width = 100.0, above_ground_storys = 3, under_ground_storys = 1, floor_to_floor_height = 3.8, plenum_height = 1.0, perimeter_zone_depth = 4.57, initial_height = 0.0) ⇒ OpenStudio::Model::Model
Create a Rectangle shape in a model
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 |
# File 'lib/openstudio-standards/geometry/create_shape.rb', line 20 def self.create_shape_rectangle(model, length = 100.0, width = 100.0, above_ground_storys = 3, under_ground_storys = 1, floor_to_floor_height = 3.8, plenum_height = 1.0, perimeter_zone_depth = 4.57, initial_height = 0.0) if length <= 1e-4 OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', 'Length must be greater than 0.') return nil end if width <= 1e-4 OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', 'Width must be greater than 0.') return nil end if (above_ground_storys + under_ground_storys) <= 0 OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', 'Number of floors must be greater than 0.') return nil end if floor_to_floor_height <= 1e-4 OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', 'Floor to floor height must be greater than 0.') return nil end if plenum_height < 0 OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', 'Plenum height must be greater than 0.') return nil end shortest_side = [length, width].min if perimeter_zone_depth < 0 || 2 * perimeter_zone_depth >= (shortest_side - 1e-4) OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', "Perimeter zone depth must be greater than or equal to 0 and less than half of the smaller of length and width, #{(shortest_side / 2).round(2)}m") return nil end # Loop through the number of floors building_stories = [] for floor in ((under_ground_storys * -1)..above_ground_storys - 1) z = (floor_to_floor_height * floor) + initial_height # Create a new story within the building story = OpenStudio::Model::BuildingStory.new(model) story.setNominalFloortoFloorHeight(floor_to_floor_height) story.setName("Story #{floor + 1}") building_stories << story nw_point = OpenStudio::Point3d.new(0, width, z) ne_point = OpenStudio::Point3d.new(length, width, z) se_point = OpenStudio::Point3d.new(length, 0, z) sw_point = OpenStudio::Point3d.new(0, 0, z) # Identity matrix for setting space origins m = OpenStudio::Matrix.new(4, 4, 0) m[0, 0] = 1 m[1, 1] = 1 m[2, 2] = 1 m[3, 3] = 1 # Define polygons for a rectangular building if perimeter_zone_depth > 0 perimeter_nw_point = nw_point + OpenStudio::Vector3d.new(perimeter_zone_depth, -perimeter_zone_depth, 0) perimeter_ne_point = ne_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, -perimeter_zone_depth, 0) perimeter_se_point = se_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, perimeter_zone_depth, 0) perimeter_sw_point = sw_point + OpenStudio::Vector3d.new(perimeter_zone_depth, perimeter_zone_depth, 0) west_polygon = OpenStudio::Point3dVector.new west_polygon << sw_point west_polygon << nw_point west_polygon << perimeter_nw_point west_polygon << perimeter_sw_point west_space = OpenStudio::Model::Space.fromFloorPrint(west_polygon, floor_to_floor_height, model) west_space = west_space.get m[0, 3] = sw_point.x m[1, 3] = sw_point.y m[2, 3] = sw_point.z west_space.changeTransformation(OpenStudio::Transformation.new(m)) west_space.setBuildingStory(story) west_space.setName("Story #{floor + 1} West Perimeter Space") north_polygon = OpenStudio::Point3dVector.new north_polygon << nw_point north_polygon << ne_point north_polygon << perimeter_ne_point north_polygon << perimeter_nw_point north_space = OpenStudio::Model::Space.fromFloorPrint(north_polygon, floor_to_floor_height, model) north_space = north_space.get m[0, 3] = perimeter_nw_point.x m[1, 3] = perimeter_nw_point.y m[2, 3] = perimeter_nw_point.z north_space.changeTransformation(OpenStudio::Transformation.new(m)) north_space.setBuildingStory(story) north_space.setName("Story #{floor + 1} North Perimeter Space") east_polygon = OpenStudio::Point3dVector.new east_polygon << ne_point east_polygon << se_point east_polygon << perimeter_se_point east_polygon << perimeter_ne_point east_space = OpenStudio::Model::Space.fromFloorPrint(east_polygon, floor_to_floor_height, model) east_space = east_space.get m[0, 3] = perimeter_se_point.x m[1, 3] = perimeter_se_point.y m[2, 3] = perimeter_se_point.z east_space.changeTransformation(OpenStudio::Transformation.new(m)) east_space.setBuildingStory(story) east_space.setName("Story #{floor + 1} East Perimeter Space") south_polygon = OpenStudio::Point3dVector.new south_polygon << se_point south_polygon << sw_point south_polygon << perimeter_sw_point south_polygon << perimeter_se_point south_space = OpenStudio::Model::Space.fromFloorPrint(south_polygon, floor_to_floor_height, model) south_space = south_space.get m[0, 3] = sw_point.x m[1, 3] = sw_point.y m[2, 3] = sw_point.z south_space.changeTransformation(OpenStudio::Transformation.new(m)) south_space.setBuildingStory(story) south_space.setName("Story #{floor + 1} South Perimeter Space") core_polygon = OpenStudio::Point3dVector.new core_polygon << perimeter_sw_point core_polygon << perimeter_nw_point core_polygon << perimeter_ne_point core_polygon << perimeter_se_point core_space = OpenStudio::Model::Space.fromFloorPrint(core_polygon, floor_to_floor_height, model) core_space = core_space.get m[0, 3] = perimeter_sw_point.x m[1, 3] = perimeter_sw_point.y m[2, 3] = perimeter_sw_point.z core_space.changeTransformation(OpenStudio::Transformation.new(m)) core_space.setBuildingStory(story) core_space.setName("Story #{floor + 1} Core Space") else # Minimal zones core_polygon = OpenStudio::Point3dVector.new core_polygon << sw_point core_polygon << nw_point core_polygon << ne_point core_polygon << se_point core_space = OpenStudio::Model::Space.fromFloorPrint(core_polygon, floor_to_floor_height, model) core_space = core_space.get m[0, 3] = sw_point.x m[1, 3] = sw_point.y m[2, 3] = sw_point.z core_space.changeTransformation(OpenStudio::Transformation.new(m)) core_space.setBuildingStory(story) core_space.setName("Story #{floor + 1} Core Space") end # Set vertical story position story.setNominalZCoordinate(z) # Ensure that underground stories (when z<0 have Ground set as Boundary conditions). # Apply the Ground BC to all surfaces, the top ceiling will be corrected below when the surface matching algorithm is called. underground_surfaces = story.spaces.flat_map(&:surfaces) BTAP::Geometry::Surfaces.set_surfaces_boundary_condition(model, underground_surfaces, 'Ground') if z < 0 end BTAP::Geometry.match_surfaces(model) return model end |
.create_shape_t(model, length = 40.0, width = 40.0, upper_end_width = 20.0, lower_end_length = 20.0, left_end_offset = 10.0, num_floors = 3, floor_to_floor_height = 3.8, plenum_height = 1.0, perimeter_zone_depth = 4.57) ⇒ OpenStudio::Model::Model
Create a T shape in a model
1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 |
# File 'lib/openstudio-standards/geometry/create_shape.rb', line 1226 def self.create_shape_t(model, length = 40.0, width = 40.0, upper_end_width = 20.0, lower_end_length = 20.0, left_end_offset = 10.0, num_floors = 3, floor_to_floor_height = 3.8, plenum_height = 1.0, perimeter_zone_depth = 4.57) if length <= 1e-4 OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', 'Length must be greater than 0.') return nil end if width <= 1e-4 OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', 'Width must be greater than 0.') return nil end if upper_end_width <= 1e-4 || upper_end_width >= (width - 1e-4) OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', "Upper end width must be greater than 0 and less than #{width}m.") return nil end if lower_end_length <= 1e-4 || lower_end_length >= (length - 1e-4) OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', "Lower end length must be greater than 0 and less than #{length}m.") return nil end if left_end_offset <= 1e-4 || left_end_offset >= (length - lower_end_length - 1e-4) OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', "Left end offset must be greater than 0 and less than #{length - lower_end_length}m.") return nil end if num_floors <= 1e-4 OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', 'Number of floors must be greater than 0.') return nil end if floor_to_floor_height <= 1e-4 OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', 'Floor to floor height must be greater than 0.') return nil end if plenum_height < 0 OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', 'Plenum height must be greater than 0.') return nil end shortest_side = [length, width, upper_end_width, lower_end_length].min if perimeter_zone_depth < 0 || 2 * perimeter_zone_depth >= (shortest_side - 1e-4) OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', "Perimeter zone depth must be greater than or equal to 0 and less than #{shortest_side / 2}m.") return nil end # Loop through the number of floors for floor in (0..num_floors - 1) z = floor_to_floor_height * floor # Create a new story within the building story = OpenStudio::Model::BuildingStory.new(model) story.setNominalFloortoFloorHeight(floor_to_floor_height) story.setName("Story #{floor + 1}") lower_ne_point = OpenStudio::Point3d.new(left_end_offset, width - upper_end_width, z) upper_sw_point = OpenStudio::Point3d.new(0, width - upper_end_width, z) upper_nw_point = OpenStudio::Point3d.new(0, width, z) upper_ne_point = OpenStudio::Point3d.new(length, width, z) upper_se_point = OpenStudio::Point3d.new(length, width - upper_end_width, z) lower_nw_point = OpenStudio::Point3d.new(left_end_offset + lower_end_length, width - upper_end_width, z) lower_se_point = OpenStudio::Point3d.new(left_end_offset + lower_end_length, 0, z) lower_sw_point = OpenStudio::Point3d.new(left_end_offset, 0, z) # Identity matrix for setting space origins m = OpenStudio::Matrix.new(4, 4, 0) m[0, 0] = 1 m[1, 1] = 1 m[2, 2] = 1 m[3, 3] = 1 # Define polygons for a T-shape building with perimeter core zoning if perimeter_zone_depth > 0 perimeter_lower_ne_point = lower_ne_point + OpenStudio::Vector3d.new(perimeter_zone_depth, perimeter_zone_depth, 0) perimeter_upper_sw_point = upper_sw_point + OpenStudio::Vector3d.new(perimeter_zone_depth, perimeter_zone_depth, 0) perimeter_upper_nw_point = upper_nw_point + OpenStudio::Vector3d.new(perimeter_zone_depth, -perimeter_zone_depth, 0) perimeter_upper_ne_point = upper_ne_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, -perimeter_zone_depth, 0) perimeter_upper_se_point = upper_se_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, perimeter_zone_depth, 0) perimeter_lower_nw_point = lower_nw_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, perimeter_zone_depth, 0) perimeter_lower_se_point = lower_se_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, perimeter_zone_depth, 0) perimeter_lower_sw_point = lower_sw_point + OpenStudio::Vector3d.new(perimeter_zone_depth, perimeter_zone_depth, 0) west_lower_perimeter_polygon = OpenStudio::Point3dVector.new west_lower_perimeter_polygon << lower_sw_point west_lower_perimeter_polygon << lower_ne_point west_lower_perimeter_polygon << perimeter_lower_ne_point west_lower_perimeter_polygon << perimeter_lower_sw_point west_lower_perimeter_space = OpenStudio::Model::Space.fromFloorPrint(west_lower_perimeter_polygon, floor_to_floor_height, model) west_lower_perimeter_space = west_lower_perimeter_space.get m[0, 3] = lower_sw_point.x m[1, 3] = lower_sw_point.y m[2, 3] = lower_sw_point.z west_lower_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m)) west_lower_perimeter_space.setBuildingStory(story) west_lower_perimeter_space.setName("Story #{floor + 1} West Lower Perimeter Space") south_upper_left_perimeter_polygon = OpenStudio::Point3dVector.new south_upper_left_perimeter_polygon << lower_ne_point south_upper_left_perimeter_polygon << upper_sw_point south_upper_left_perimeter_polygon << perimeter_upper_sw_point south_upper_left_perimeter_polygon << perimeter_lower_ne_point south_upper_left_perimeter_space = OpenStudio::Model::Space.fromFloorPrint(south_upper_left_perimeter_polygon, floor_to_floor_height, model) south_upper_left_perimeter_space = south_upper_left_perimeter_space.get m[0, 3] = upper_sw_point.x m[1, 3] = upper_sw_point.y m[2, 3] = upper_sw_point.z south_upper_left_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m)) south_upper_left_perimeter_space.setBuildingStory(story) south_upper_left_perimeter_space.setName("Story #{floor + 1} South Upper Left Perimeter Space") west_upper_perimeter_polygon = OpenStudio::Point3dVector.new west_upper_perimeter_polygon << upper_sw_point west_upper_perimeter_polygon << upper_nw_point west_upper_perimeter_polygon << perimeter_upper_nw_point west_upper_perimeter_polygon << perimeter_upper_sw_point west_upper_perimeter_space = OpenStudio::Model::Space.fromFloorPrint(west_upper_perimeter_polygon, floor_to_floor_height, model) west_upper_perimeter_space = west_upper_perimeter_space.get m[0, 3] = upper_sw_point.x m[1, 3] = upper_sw_point.y m[2, 3] = upper_sw_point.z west_upper_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m)) west_upper_perimeter_space.setBuildingStory(story) west_upper_perimeter_space.setName("Story #{floor + 1} West Upper Perimeter Space") north_perimeter_polygon = OpenStudio::Point3dVector.new north_perimeter_polygon << upper_nw_point north_perimeter_polygon << upper_ne_point north_perimeter_polygon << perimeter_upper_ne_point north_perimeter_polygon << perimeter_upper_nw_point north_perimeter_space = OpenStudio::Model::Space.fromFloorPrint(north_perimeter_polygon, floor_to_floor_height, model) north_perimeter_space = north_perimeter_space.get m[0, 3] = perimeter_upper_nw_point.x m[1, 3] = perimeter_upper_nw_point.y m[2, 3] = perimeter_upper_nw_point.z north_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m)) north_perimeter_space.setBuildingStory(story) north_perimeter_space.setName("Story #{floor + 1} North Perimeter Space") east_upper_perimeter_polygon = OpenStudio::Point3dVector.new east_upper_perimeter_polygon << upper_ne_point east_upper_perimeter_polygon << upper_se_point east_upper_perimeter_polygon << perimeter_upper_se_point east_upper_perimeter_polygon << perimeter_upper_ne_point east_upper_perimeter_space = OpenStudio::Model::Space.fromFloorPrint(east_upper_perimeter_polygon, floor_to_floor_height, model) east_upper_perimeter_space = east_upper_perimeter_space.get m[0, 3] = perimeter_upper_se_point.x m[1, 3] = perimeter_upper_se_point.y m[2, 3] = perimeter_upper_se_point.z east_upper_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m)) east_upper_perimeter_space.setBuildingStory(story) east_upper_perimeter_space.setName("Story #{floor + 1} East Upper Perimeter Space") south_upper_right_perimeter_polygon = OpenStudio::Point3dVector.new south_upper_right_perimeter_polygon << upper_se_point south_upper_right_perimeter_polygon << lower_nw_point south_upper_right_perimeter_polygon << perimeter_lower_nw_point south_upper_right_perimeter_polygon << perimeter_upper_se_point south_upper_right_perimeter_space = OpenStudio::Model::Space.fromFloorPrint(south_upper_right_perimeter_polygon, floor_to_floor_height, model) south_upper_right_perimeter_space = south_upper_right_perimeter_space.get m[0, 3] = lower_nw_point.x m[1, 3] = lower_nw_point.y m[2, 3] = lower_nw_point.z south_upper_right_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m)) south_upper_right_perimeter_space.setBuildingStory(story) south_upper_right_perimeter_space.setName("Story #{floor + 1} South Upper Left Perimeter Space") east_lower_perimeter_polygon = OpenStudio::Point3dVector.new east_lower_perimeter_polygon << lower_nw_point east_lower_perimeter_polygon << lower_se_point east_lower_perimeter_polygon << perimeter_lower_se_point east_lower_perimeter_polygon << perimeter_lower_nw_point east_lower_perimeter_space = OpenStudio::Model::Space.fromFloorPrint(east_lower_perimeter_polygon, floor_to_floor_height, model) east_lower_perimeter_space = east_lower_perimeter_space.get m[0, 3] = perimeter_lower_se_point.x m[1, 3] = perimeter_lower_se_point.y m[2, 3] = perimeter_lower_se_point.z east_lower_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m)) east_lower_perimeter_space.setBuildingStory(story) east_lower_perimeter_space.setName("Story #{floor + 1} East Lower Perimeter Space") south_lower_perimeter_polygon = OpenStudio::Point3dVector.new south_lower_perimeter_polygon << lower_se_point south_lower_perimeter_polygon << lower_sw_point south_lower_perimeter_polygon << perimeter_lower_sw_point south_lower_perimeter_polygon << perimeter_lower_se_point south_lower_perimeter_space = OpenStudio::Model::Space.fromFloorPrint(south_lower_perimeter_polygon, floor_to_floor_height, model) south_lower_perimeter_space = south_lower_perimeter_space.get m[0, 3] = lower_sw_point.x m[1, 3] = lower_sw_point.y m[2, 3] = lower_sw_point.z south_lower_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m)) south_lower_perimeter_space.setBuildingStory(story) south_lower_perimeter_space.setName("Story #{floor + 1} South Lower Perimeter Space") north_core_polygon = OpenStudio::Point3dVector.new north_core_polygon << perimeter_upper_sw_point north_core_polygon << perimeter_upper_nw_point north_core_polygon << perimeter_upper_ne_point north_core_polygon << perimeter_upper_se_point north_core_polygon << perimeter_lower_nw_point north_core_polygon << perimeter_lower_ne_point north_core_space = OpenStudio::Model::Space.fromFloorPrint(north_core_polygon, floor_to_floor_height, model) north_core_space = north_core_space.get m[0, 3] = perimeter_upper_sw_point.x m[1, 3] = perimeter_upper_sw_point.y m[2, 3] = perimeter_upper_sw_point.z north_core_space.changeTransformation(OpenStudio::Transformation.new(m)) north_core_space.setBuildingStory(story) north_core_space.setName("Story #{floor + 1} North Core Space") south_core_polygon = OpenStudio::Point3dVector.new south_core_polygon << perimeter_lower_sw_point south_core_polygon << perimeter_lower_ne_point south_core_polygon << perimeter_lower_nw_point south_core_polygon << perimeter_lower_se_point south_core_space = OpenStudio::Model::Space.fromFloorPrint(south_core_polygon, floor_to_floor_height, model) south_core_space = south_core_space.get m[0, 3] = perimeter_lower_sw_point.x m[1, 3] = perimeter_lower_sw_point.y m[2, 3] = perimeter_lower_sw_point.z south_core_space.changeTransformation(OpenStudio::Transformation.new(m)) south_core_space.setBuildingStory(story) south_core_space.setName("Story #{floor + 1} South Core Space") else # Minimal zones north_polygon = OpenStudio::Point3dVector.new north_polygon << upper_sw_point north_polygon << upper_nw_point north_polygon << upper_ne_point north_polygon << upper_se_point north_polygon << lower_nw_point north_polygon << lower_ne_point north_space = OpenStudio::Model::Space.fromFloorPrint(north_polygon, floor_to_floor_height, model) north_space = north_space.get m[0, 3] = upper_sw_point.x m[1, 3] = upper_sw_point.y m[2, 3] = upper_sw_point.z north_space.changeTransformation(OpenStudio::Transformation.new(m)) north_space.setBuildingStory(story) north_space.setName("Story #{floor + 1} North Space") south_polygon = OpenStudio::Point3dVector.new south_polygon << lower_sw_point south_polygon << lower_ne_point south_polygon << lower_nw_point south_polygon << lower_se_point south_space = OpenStudio::Model::Space.fromFloorPrint(south_polygon, floor_to_floor_height, model) south_space = south_space.get m[0, 3] = lower_sw_point.x m[1, 3] = lower_sw_point.y m[2, 3] = lower_sw_point.z south_space.changeTransformation(OpenStudio::Transformation.new(m)) south_space.setBuildingStory(story) south_space.setName("Story #{floor + 1} South Space") end # Set vertical story position story.setNominalZCoordinate(z) end BTAP::Geometry.match_surfaces(model) return model end |
.create_shape_u(model, length = 40.0, left_width = 40.0, right_width = 40.0, left_end_length = 15.0, right_end_length = 15.0, left_end_offset = 25.0, num_floors = 3.0, floor_to_floor_height = 3.8, plenum_height = 1.0, perimeter_zone_depth = 4.57) ⇒ OpenStudio::Model::Model
Create a U shape in a model
1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 |
# File 'lib/openstudio-standards/geometry/create_shape.rb', line 1513 def self.create_shape_u(model, length = 40.0, left_width = 40.0, right_width = 40.0, left_end_length = 15.0, right_end_length = 15.0, left_end_offset = 25.0, num_floors = 3.0, floor_to_floor_height = 3.8, plenum_height = 1.0, perimeter_zone_depth = 4.57) if length <= 1e-4 OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', 'Length must be greater than 0.') return nil end if left_width <= 1e-4 OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', 'Left width must be greater than 0.') return nil end if left_end_length <= 1e-4 || left_end_length >= (length - 1e-4) OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', "Left end length must be greater than 0 and less than #{length}m.") return nil end if right_end_length <= 1e-4 || right_end_length >= (length - left_end_length - 1e-4) OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', "Right end length must be greater than 0 and less than #{length - left_end_length}m.") return nil end if left_end_offset <= 1e-4 || left_end_offset >= (left_width - 1e-4) OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', "Left end offset must be greater than 0 and less than #{left_width}m.") return nil end if right_width <= (left_width - left_end_offset - 1e-4) OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', "Right width must be greater than #{left_width - left_end_offset}m.") return nil end if num_floors <= 1e-4 OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', 'Number of floors must be greater than 0.') return nil end if floor_to_floor_height <= 1e-4 OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', 'Floor to floor height must be greater than 0.') return nil end if plenum_height < 0 OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', 'Plenum height must be greater than 0.') return nil end shortest_side = [length / 2, left_width, right_width, left_end_length, right_end_length, left_width - left_end_offset].min if perimeter_zone_depth < 0 || 2 * perimeter_zone_depth >= (shortest_side - 1e-4) OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create.Shape', "Perimeter zone depth must be greater than or equal to 0 and less than #{shortest_side / 2}m.") return nil end # Loop through the number of floors for floor in (0..num_floors - 1) z = floor_to_floor_height * floor # Create a new story within the building story = OpenStudio::Model::BuildingStory.new(model) story.setNominalFloortoFloorHeight(floor_to_floor_height) story.setName("Story #{floor + 1}") left_nw_point = OpenStudio::Point3d.new(0, left_width, z) left_ne_point = OpenStudio::Point3d.new(left_end_length, left_width, z) upper_sw_point = OpenStudio::Point3d.new(left_end_length, left_width - left_end_offset, z) upper_se_point = OpenStudio::Point3d.new(length - right_end_length, left_width - left_end_offset, z) right_nw_point = OpenStudio::Point3d.new(length - right_end_length, right_width, z) right_ne_point = OpenStudio::Point3d.new(length, right_width, z) lower_se_point = OpenStudio::Point3d.new(length, 0, z) lower_sw_point = OpenStudio::Point3d.new(0, 0, z) # Identity matrix for setting space origins m = OpenStudio::Matrix.new(4, 4, 0) m[0, 0] = 1 m[1, 1] = 1 m[2, 2] = 1 m[3, 3] = 1 # Define polygons for a U-shape building with perimeter core zoning if perimeter_zone_depth > 0 perimeter_left_nw_point = left_nw_point + OpenStudio::Vector3d.new(perimeter_zone_depth, -perimeter_zone_depth, 0) perimeter_left_ne_point = left_ne_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, -perimeter_zone_depth, 0) perimeter_upper_sw_point = upper_sw_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, -perimeter_zone_depth, 0) perimeter_upper_se_point = upper_se_point + OpenStudio::Vector3d.new(perimeter_zone_depth, -perimeter_zone_depth, 0) perimeter_right_nw_point = right_nw_point + OpenStudio::Vector3d.new(perimeter_zone_depth, -perimeter_zone_depth, 0) perimeter_right_ne_point = right_ne_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, -perimeter_zone_depth, 0) perimeter_lower_se_point = lower_se_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, perimeter_zone_depth, 0) perimeter_lower_sw_point = lower_sw_point + OpenStudio::Vector3d.new(perimeter_zone_depth, perimeter_zone_depth, 0) west_left_perimeter_polygon = OpenStudio::Point3dVector.new west_left_perimeter_polygon << lower_sw_point west_left_perimeter_polygon << left_nw_point west_left_perimeter_polygon << perimeter_left_nw_point west_left_perimeter_polygon << perimeter_lower_sw_point west_left_perimeter_space = OpenStudio::Model::Space.fromFloorPrint(west_left_perimeter_polygon, floor_to_floor_height, model) west_left_perimeter_space = west_left_perimeter_space.get m[0, 3] = lower_sw_point.x m[1, 3] = lower_sw_point.y m[2, 3] = lower_sw_point.z west_left_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m)) west_left_perimeter_space.setBuildingStory(story) west_left_perimeter_space.setName("Story #{floor + 1} West Left Perimeter Space") north_left_perimeter_polygon = OpenStudio::Point3dVector.new north_left_perimeter_polygon << left_nw_point north_left_perimeter_polygon << left_ne_point north_left_perimeter_polygon << perimeter_left_ne_point north_left_perimeter_polygon << perimeter_left_nw_point north_left_perimeter_space = OpenStudio::Model::Space.fromFloorPrint(north_left_perimeter_polygon, floor_to_floor_height, model) north_left_perimeter_space = north_left_perimeter_space.get m[0, 3] = perimeter_left_nw_point.x m[1, 3] = perimeter_left_nw_point.y m[2, 3] = perimeter_left_nw_point.z north_left_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m)) north_left_perimeter_space.setBuildingStory(story) north_left_perimeter_space.setName("Story #{floor + 1} North Left Perimeter Space") east_left_perimeter_polygon = OpenStudio::Point3dVector.new east_left_perimeter_polygon << left_ne_point east_left_perimeter_polygon << upper_sw_point east_left_perimeter_polygon << perimeter_upper_sw_point east_left_perimeter_polygon << perimeter_left_ne_point east_left_perimeter_space = OpenStudio::Model::Space.fromFloorPrint(east_left_perimeter_polygon, floor_to_floor_height, model) east_left_perimeter_space = east_left_perimeter_space.get m[0, 3] = perimeter_upper_sw_point.x m[1, 3] = perimeter_upper_sw_point.y m[2, 3] = perimeter_upper_sw_point.z east_left_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m)) east_left_perimeter_space.setBuildingStory(story) east_left_perimeter_space.setName("Story #{floor + 1} East Left Perimeter Space") north_lower_perimeter_polygon = OpenStudio::Point3dVector.new north_lower_perimeter_polygon << upper_sw_point north_lower_perimeter_polygon << upper_se_point north_lower_perimeter_polygon << perimeter_upper_se_point north_lower_perimeter_polygon << perimeter_upper_sw_point north_lower_perimeter_space = OpenStudio::Model::Space.fromFloorPrint(north_lower_perimeter_polygon, floor_to_floor_height, model) north_lower_perimeter_space = north_lower_perimeter_space.get m[0, 3] = perimeter_upper_sw_point.x m[1, 3] = perimeter_upper_sw_point.y m[2, 3] = perimeter_upper_sw_point.z north_lower_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m)) north_lower_perimeter_space.setBuildingStory(story) north_lower_perimeter_space.setName("Story #{floor + 1} North Lower Perimeter Space") west_right_perimeter_polygon = OpenStudio::Point3dVector.new west_right_perimeter_polygon << upper_se_point west_right_perimeter_polygon << right_nw_point west_right_perimeter_polygon << perimeter_right_nw_point west_right_perimeter_polygon << perimeter_upper_se_point west_right_perimeter_space = OpenStudio::Model::Space.fromFloorPrint(west_right_perimeter_polygon, floor_to_floor_height, model) west_right_perimeter_space = west_right_perimeter_space.get m[0, 3] = upper_se_point.x m[1, 3] = upper_se_point.y m[2, 3] = upper_se_point.z west_right_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m)) west_right_perimeter_space.setBuildingStory(story) west_right_perimeter_space.setName("Story #{floor + 1} West Right Perimeter Space") north_right_perimeter_polygon = OpenStudio::Point3dVector.new north_right_perimeter_polygon << right_nw_point north_right_perimeter_polygon << right_ne_point north_right_perimeter_polygon << perimeter_right_ne_point north_right_perimeter_polygon << perimeter_right_nw_point north_right_perimeter_space = OpenStudio::Model::Space.fromFloorPrint(north_right_perimeter_polygon, floor_to_floor_height, model) north_right_perimeter_space = north_right_perimeter_space.get m[0, 3] = perimeter_right_nw_point.x m[1, 3] = perimeter_right_nw_point.y m[2, 3] = perimeter_right_nw_point.z north_right_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m)) north_right_perimeter_space.setBuildingStory(story) north_right_perimeter_space.setName("Story #{floor + 1} North Right Perimeter Space") east_right_perimeter_polygon = OpenStudio::Point3dVector.new east_right_perimeter_polygon << right_ne_point east_right_perimeter_polygon << lower_se_point east_right_perimeter_polygon << perimeter_lower_se_point east_right_perimeter_polygon << perimeter_right_ne_point east_right_perimeter_space = OpenStudio::Model::Space.fromFloorPrint(east_right_perimeter_polygon, floor_to_floor_height, model) east_right_perimeter_space = east_right_perimeter_space.get m[0, 3] = perimeter_lower_se_point.x m[1, 3] = perimeter_lower_se_point.y m[2, 3] = perimeter_lower_se_point.z east_right_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m)) east_right_perimeter_space.setBuildingStory(story) east_right_perimeter_space.setName("Story #{floor + 1} East Right Perimeter Space") south_lower_perimeter_polygon = OpenStudio::Point3dVector.new south_lower_perimeter_polygon << lower_se_point south_lower_perimeter_polygon << lower_sw_point south_lower_perimeter_polygon << perimeter_lower_sw_point south_lower_perimeter_polygon << perimeter_lower_se_point south_lower_perimeter_space = OpenStudio::Model::Space.fromFloorPrint(south_lower_perimeter_polygon, floor_to_floor_height, model) south_lower_perimeter_space = south_lower_perimeter_space.get m[0, 3] = lower_sw_point.x m[1, 3] = lower_sw_point.y m[2, 3] = lower_sw_point.z south_lower_perimeter_space.changeTransformation(OpenStudio::Transformation.new(m)) south_lower_perimeter_space.setBuildingStory(story) south_lower_perimeter_space.setName("Story #{floor + 1} South Lower Perimeter Space") west_core_polygon = OpenStudio::Point3dVector.new west_core_polygon << perimeter_lower_sw_point west_core_polygon << perimeter_left_nw_point west_core_polygon << perimeter_left_ne_point west_core_polygon << perimeter_upper_sw_point west_core_space = OpenStudio::Model::Space.fromFloorPrint(west_core_polygon, floor_to_floor_height, model) west_core_space = west_core_space.get m[0, 3] = perimeter_lower_sw_point.x m[1, 3] = perimeter_lower_sw_point.y m[2, 3] = perimeter_lower_sw_point.z west_core_space.changeTransformation(OpenStudio::Transformation.new(m)) west_core_space.setBuildingStory(story) west_core_space.setName("Story #{floor + 1} West Core Space") south_core_polygon = OpenStudio::Point3dVector.new south_core_polygon << perimeter_upper_sw_point south_core_polygon << perimeter_upper_se_point south_core_polygon << perimeter_lower_se_point south_core_polygon << perimeter_lower_sw_point south_core_space = OpenStudio::Model::Space.fromFloorPrint(south_core_polygon, floor_to_floor_height, model) south_core_space = south_core_space.get m[0, 3] = perimeter_lower_sw_point.x m[1, 3] = perimeter_lower_sw_point.y m[2, 3] = perimeter_lower_sw_point.z south_core_space.changeTransformation(OpenStudio::Transformation.new(m)) south_core_space.setBuildingStory(story) south_core_space.setName("Story #{floor + 1} South Core Space") east_core_polygon = OpenStudio::Point3dVector.new east_core_polygon << perimeter_upper_se_point east_core_polygon << perimeter_right_nw_point east_core_polygon << perimeter_right_ne_point east_core_polygon << perimeter_lower_se_point east_core_space = OpenStudio::Model::Space.fromFloorPrint(east_core_polygon, floor_to_floor_height, model) east_core_space = east_core_space.get m[0, 3] = perimeter_upper_se_point.x m[1, 3] = perimeter_upper_se_point.y m[2, 3] = perimeter_upper_se_point.z east_core_space.changeTransformation(OpenStudio::Transformation.new(m)) east_core_space.setBuildingStory(story) east_core_space.setName("Story #{floor + 1} East Core Space") else # Minimal zones west_polygon = OpenStudio::Point3dVector.new west_polygon << lower_sw_point west_polygon << left_nw_point west_polygon << left_ne_point west_polygon << upper_sw_point west_space = OpenStudio::Model::Space.fromFloorPrint(west_polygon, floor_to_floor_height, model) west_space = west_space.get m[0, 3] = lower_sw_point.x m[1, 3] = lower_sw_point.y m[2, 3] = lower_sw_point.z west_space.changeTransformation(OpenStudio::Transformation.new(m)) west_space.setBuildingStory(story) west_space.setName("Story #{floor + 1} West Space") south_polygon = OpenStudio::Point3dVector.new south_polygon << lower_sw_point south_polygon << upper_sw_point south_polygon << upper_se_point south_polygon << lower_se_point south_space = OpenStudio::Model::Space.fromFloorPrint(south_polygon, floor_to_floor_height, model) south_space = south_space.get m[0, 3] = lower_sw_point.x m[1, 3] = lower_sw_point.y m[2, 3] = lower_sw_point.z south_space.changeTransformation(OpenStudio::Transformation.new(m)) south_space.setBuildingStory(story) south_space.setName("Story #{floor + 1} South Space") east_polygon = OpenStudio::Point3dVector.new east_polygon << upper_se_point east_polygon << right_nw_point east_polygon << right_ne_point east_polygon << lower_se_point east_space = OpenStudio::Model::Space.fromFloorPrint(east_polygon, floor_to_floor_height, model) east_space = east_space.get m[0, 3] = upper_se_point.x m[1, 3] = upper_se_point.y m[2, 3] = upper_se_point.z east_space.changeTransformation(OpenStudio::Transformation.new(m)) east_space.setBuildingStory(story) east_space.setName("Story #{floor + 1} East Space") end # Set vertical story position story.setNominalZCoordinate(z) end BTAP::Geometry.match_surfaces(model) return model end |
.create_sliced_bar_multi_polygons(space_types, length, width, footprint_origin_point, story_hash) ⇒ Hash
sliced bar multi creates and array of multiple sliced bar simple hashes
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 |
# File 'lib/openstudio-standards/geometry/create.rb', line 176 def self.(space_types, length, width, footprint_origin_point, story_hash) # total building floor area to calculate ratios from space type floor areas total_floor_area = 0.0 target_per_space_type = {} space_types.each do |space_type, space_type_hash| total_floor_area += space_type_hash[:floor_area] target_per_space_type[space_type] = space_type_hash[:floor_area] end # sort array by floor area, this hash will be altered to reduce floor area for each space type to 0 space_types_running_count = space_types.sort_by { |k, v| v[:floor_area] } # array entry for each story footprints = [] # variables for sliver check # re-evaluate what the default should be = OpenStudio.convert(3.0, 'ft', 'm').get # building width = width = * # loop through stories to populate footprints story_hash.each_with_index do |(k, v), i| # update the length and width for partial floors if i + 1 == story_hash.size area_multiplier = v[:partial_story_multiplier] edge_multiplier = Math.sqrt(area_multiplier) length *= edge_multiplier width *= edge_multiplier end # this will be populated for each building story target_footprint_area = v[:multiplier] * length * width current_footprint_area = 0.0 space_types_local_count = {} space_types_running_count.each do |space_type, space_type_hash| # next if floor area is full or space type is empty tol_value = 0.0001 next if current_footprint_area + tol_value >= target_footprint_area next if space_type_hash[:floor_area] <= tol_value # special test for when total floor area is smaller than valid_bar_area_min_m2, just make bar smaller that valid min and warn user if target_per_space_type[space_type] < sliver_override = true OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', "Floor area of #{space_type.name} results in a bar with smaller than target minimum width.") else sliver_override = false end # add entry for space type if it doesn't have one yet if !space_types_local_count.key?(space_type) if space_type_hash.key?(:children) space_type = space_type_hash[:children][:default][:space_type] # will re-using space type create issue space_types_local_count[space_type] = { floor_area: 0.0 } space_types_local_count[space_type][:children] = space_type_hash[:children] else space_types_local_count[space_type] = { floor_area: 0.0 } end end # if there is enough of this space type to fill rest of floor area remaining_in_footprint = target_footprint_area - current_footprint_area raw_footprint_area_used = [space_type_hash[:floor_area], remaining_in_footprint].min # add to local hash space_types_local_count[space_type][:floor_area] = raw_footprint_area_used / v[:multiplier].to_f # adjust balance ot running and local counts current_footprint_area += raw_footprint_area_used space_type_hash[:floor_area] -= raw_footprint_area_used # test if think sliver left on current floor. # fix by moving smallest space type to next floor and and the same amount more of the sliver space type to this story raw_footprint_area_used < && sliver_override == false ? (test_a = true) : (test_a = false) # test if what would be left of the current space type would result in a sliver on the next story. # fix by removing some of this space type so their is enough left for the next story, and replace the removed amount with the largest space type in the model (space_type_hash[:floor_area] < ) && (space_type_hash[:floor_area] > tol_value) ? (test_b = true) : (test_b = false) # identify very small slices and re-arrange spaces to different stories to avoid this if test_a # get first/smallest space type to move to another story first_space = space_types_local_count.first # adjustments running counter for space type being removed from this story space_types_running_count.each do |k2, v2| next if k2 != first_space[0] v2[:floor_area] += first_space[1][:floor_area] * v[:multiplier] end # adjust running count for current space type space_type_hash[:floor_area] -= first_space[1][:floor_area] * v[:multiplier] # add to local count for current space type space_types_local_count[space_type][:floor_area] += first_space[1][:floor_area] # remove from local count for removed space type space_types_local_count.shift elsif test_b # swap size swap_size = * 5.0 # currently equal to default perimeter zone depth of 15' # this prevents too much area from being swapped resulting in a negative number for floor area if swap_size > space_types_local_count[space_type][:floor_area] * v[:multiplier].to_f swap_size = space_types_local_count[space_type][:floor_area] * v[:multiplier].to_f end # adjust running count for current space type space_type_hash[:floor_area] += swap_size # remove from local count for current space type space_types_local_count[space_type][:floor_area] -= swap_size / v[:multiplier].to_f # adjust footprint used current_footprint_area -= swap_size # the next larger space type will be brought down to fill out the footprint without any additional code end end # creating footprint for story footprints << OpenstudioStandards::Geometry.(space_types_local_count, length, width, footprint_origin_point) end return footprints end |
.create_sliced_bar_simple_polygons(space_types, length, width, footprint_origin_point = OpenStudio::Point3d.new(0.0, 0.0, 0.0), perimeter_zone_depth = OpenStudio.convert(15.0, 'ft', 'm').get) ⇒ Hash
sliced bar simple creates a single sliced bar for space types passed in look at length and width to adjust slicing direction
317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 |
# File 'lib/openstudio-standards/geometry/create.rb', line 317 def self.(space_types, length, width, footprint_origin_point = OpenStudio::Point3d.new(0.0, 0.0, 0.0), perimeter_zone_depth = OpenStudio.convert(15.0, 'ft', 'm').get) hash_of_point_vectors = {} # key is name, value is a hash, one item of which is polygon. Another could be space type reverse_slice = false if length < width reverse_slice = true # OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "Reverse typical slice direction for bar because of aspect ratio less than 1.0.") end # determine if core and perimeter zoning can be used if !([length, width].min > perimeter_zone_depth * 2.5 && [length, width].min > perimeter_zone_depth * 2.5) perimeter_zone_depth = 0 # if any size is to small then just model floor as single zone, issue warning OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', 'Not modeling core and perimeter zones for some portion of the model.') end x_delta = footprint_origin_point.x - (length / 2.0) y_delta = footprint_origin_point.y - (width / 2.0) z = 0.0 # this represents the entire bar, not individual space type slices nw_point = OpenStudio::Point3d.new(x_delta, y_delta + width, z) sw_point = OpenStudio::Point3d.new(x_delta, y_delta, z) # used when length is less than width se_point = OpenStudio::Point3d.new(x_delta + length, y_delta, z) # total building floor area to calculate ratios from space type floor areas total_floor_area = 0.0 space_types.each do |space_type, space_type_hash| total_floor_area += space_type_hash[:floor_area] end # sort array by floor area but shift largest object to front space_types = space_types.sort_by { |k, v| v[:floor_area] } space_types.insert(0, space_types.delete_at(space_types.size - 1)) # .to_h # min and max bar end values = 0.75 = 1.5 # sort_by results in arrays with two items , first is key, second is hash value re_apply_largest_space_type_at_end = false max_reduction = nil # used when looping through section_hash_for_space_type if first space type needs to also be at far end of bar space_types.each do |space_type, space_type_hash| # setup end perimeter zones if needed start_perimeter_width_deduction = 0.0 end_perimeter_width_deduction = 0.0 if space_type == space_types.first[0] if [length, width].max * space_type_hash[:floor_area] / total_floor_area > * perimeter_zone_depth start_perimeter_width_deduction = perimeter_zone_depth end # see if last space type is too small for perimeter. If it is then save some of this space type if [length, width].max * space_types.last[1][:floor_area] / total_floor_area < perimeter_zone_depth * re_apply_largest_space_type_at_end = true end end if space_type == space_types.last[0] if [length, width].max * space_type_hash[:floor_area] / total_floor_area > * perimeter_zone_depth end_perimeter_width_deduction = perimeter_zone_depth end end non_end_adjusted_width = ([length, width].max * space_type_hash[:floor_area] / total_floor_area) - start_perimeter_width_deduction - end_perimeter_width_deduction # adjustment of end space type is too small and is replaced with largest space type if (space_type == space_types.first[0]) && re_apply_largest_space_type_at_end max_reduction = [perimeter_zone_depth, non_end_adjusted_width].min non_end_adjusted_width -= max_reduction end if (space_type == space_types.last[0]) && re_apply_largest_space_type_at_end end_perimeter_width_deduction = space_types.first[0] end_b_flag = true else end_b_flag = false end # populate data for core and perimeter of slice section_hash_for_space_type = {} section_hash_for_space_type['end_a'] = start_perimeter_width_deduction section_hash_for_space_type[''] = non_end_adjusted_width section_hash_for_space_type['end_b'] = end_perimeter_width_deduction # determine if this space+type is double loaded corridor, and if so what the perimeter zone depth should be based on building width # look at reverse_slice to see if length or width should be used to determine perimeter depth if space_type_hash.key?(:children) core_ratio = space_type_hash[:children][:circ][:orig_ratio] perim_ratio = space_type_hash[:children][:default][:orig_ratio] core_ratio_adj = core_ratio / (core_ratio + perim_ratio) perim_ratio_adj = perim_ratio / (core_ratio + perim_ratio) core_space_type = space_type_hash[:children][:circ][:space_type] perim_space_type = space_type_hash[:children][:default][:space_type] if reverse_slice custom_cor_val = length * core_ratio_adj custom_perim_val = (length - custom_cor_val) / 2.0 else custom_cor_val = width * core_ratio_adj custom_perim_val = (width - custom_cor_val) / 2.0 end actual_perim = custom_perim_val double_loaded_corridor = true else actual_perim = perimeter_zone_depth double_loaded_corridor = false end # may overwrite first_space_type_hash = space_types.first[1] if end_b_flag && first_space_type_hash.key?(:children) end_b_core_ratio = first_space_type_hash[:children][:circ][:orig_ratio] end_b_perim_ratio = first_space_type_hash[:children][:default][:orig_ratio] end_b_core_ratio_adj = end_b_core_ratio / (end_b_core_ratio + end_b_perim_ratio) end_b_perim_ratio_adj = end_b_perim_ratio / (end_b_core_ratio + end_b_perim_ratio) end_b_core_space_type = first_space_type_hash[:children][:circ][:space_type] end_b_perim_space_type = first_space_type_hash[:children][:default][:space_type] if reverse_slice end_b_custom_cor_val = length * end_b_core_ratio_adj end_b_custom_perim_val = (length - end_b_custom_cor_val) / 2.0 else end_b_custom_cor_val = width * end_b_core_ratio_adj end_b_custom_perim_val = (width - end_b_custom_cor_val) / 2.0 end end_b_actual_perim = end_b_custom_perim_val end_b_double_loaded_corridor = true else end_b_actual_perim = perimeter_zone_depth end_b_double_loaded_corridor = false end # loop through sections for space type (main and possibly one or two end perimeter sections) section_hash_for_space_type.each do |k, slice| # need to use different space type for end_b if end_b_flag && k == 'end_b' && space_types.first[1].key?(:children) slice = space_types.first[0] actual_perim = end_b_actual_perim double_loaded_corridor = end_b_double_loaded_corridor core_ratio = end_b_core_ratio perim_ratio = end_b_perim_ratio core_ratio_adj = end_b_core_ratio_adj perim_ratio_adj = end_b_perim_ratio_adj core_space_type = end_b_core_space_type perim_space_type = end_b_perim_space_type end if slice.class.to_s == 'OpenStudio::Model::SpaceType' || slice.class.to_s == 'OpenStudio::Model::Building' space_type = slice max_reduction = [perimeter_zone_depth, max_reduction].min slice = max_reduction end if slice == 0 next end if reverse_slice # create_bar at 90 degrees if aspect ration is less than 1.0 # typical order (sw,nw,ne,se) # order used here (se,sw,nw,ne) nw_point = (sw_point + OpenStudio::Vector3d.new(0, slice, 0)) ne_point = (se_point + OpenStudio::Vector3d.new(0, slice, 0)) if actual_perim > 0 && (actual_perim * 2.0) < length polygon_a = OpenStudio::Point3dVector.new polygon_a << se_point polygon_a << (se_point + OpenStudio::Vector3d.new(- actual_perim, 0, 0)) polygon_a << (ne_point + OpenStudio::Vector3d.new(- actual_perim, 0, 0)) polygon_a << ne_point if double_loaded_corridor hash_of_point_vectors["#{perim_space_type.name} A #{k}"] = {} hash_of_point_vectors["#{perim_space_type.name} A #{k}"][:space_type] = perim_space_type hash_of_point_vectors["#{perim_space_type.name} A #{k}"][:polygon] = polygon_a else hash_of_point_vectors["#{space_type.name} A #{k}"] = {} hash_of_point_vectors["#{space_type.name} A #{k}"][:space_type] = space_type hash_of_point_vectors["#{space_type.name} A #{k}"][:polygon] = polygon_a end polygon_b = OpenStudio::Point3dVector.new polygon_b << (se_point + OpenStudio::Vector3d.new(- actual_perim, 0, 0)) polygon_b << (sw_point + OpenStudio::Vector3d.new(actual_perim, 0, 0)) polygon_b << (nw_point + OpenStudio::Vector3d.new(actual_perim, 0, 0)) polygon_b << (ne_point + OpenStudio::Vector3d.new(- actual_perim, 0, 0)) if double_loaded_corridor hash_of_point_vectors["#{core_space_type.name} B #{k}"] = {} hash_of_point_vectors["#{core_space_type.name} B #{k}"][:space_type] = core_space_type hash_of_point_vectors["#{core_space_type.name} B #{k}"][:polygon] = polygon_b else hash_of_point_vectors["#{space_type.name} B #{k}"] = {} hash_of_point_vectors["#{space_type.name} B #{k}"][:space_type] = space_type hash_of_point_vectors["#{space_type.name} B #{k}"][:polygon] = polygon_b end polygon_c = OpenStudio::Point3dVector.new polygon_c << (sw_point + OpenStudio::Vector3d.new(actual_perim, 0, 0)) polygon_c << sw_point polygon_c << nw_point polygon_c << (nw_point + OpenStudio::Vector3d.new(actual_perim, 0, 0)) if double_loaded_corridor hash_of_point_vectors["#{perim_space_type.name} C #{k}"] = {} hash_of_point_vectors["#{perim_space_type.name} C #{k}"][:space_type] = perim_space_type hash_of_point_vectors["#{perim_space_type.name} C #{k}"][:polygon] = polygon_c else hash_of_point_vectors["#{space_type.name} C #{k}"] = {} hash_of_point_vectors["#{space_type.name} C #{k}"][:space_type] = space_type hash_of_point_vectors["#{space_type.name} C #{k}"][:polygon] = polygon_c end else polygon_a = OpenStudio::Point3dVector.new polygon_a << se_point polygon_a << sw_point polygon_a << nw_point polygon_a << ne_point hash_of_point_vectors["#{space_type.name} #{k}"] = {} hash_of_point_vectors["#{space_type.name} #{k}"][:space_type] = space_type hash_of_point_vectors["#{space_type.name} #{k}"][:polygon] = polygon_a end # update west points sw_point = nw_point se_point = ne_point else ne_point = nw_point + OpenStudio::Vector3d.new(slice, 0, 0) se_point = sw_point + OpenStudio::Vector3d.new(slice, 0, 0) if actual_perim > 0 && (actual_perim * 2.0) < width polygon_a = OpenStudio::Point3dVector.new polygon_a << sw_point polygon_a << (sw_point + OpenStudio::Vector3d.new(0, actual_perim, 0)) polygon_a << (se_point + OpenStudio::Vector3d.new(0, actual_perim, 0)) polygon_a << se_point if double_loaded_corridor hash_of_point_vectors["#{perim_space_type.name} A #{k}"] = {} hash_of_point_vectors["#{perim_space_type.name} A #{k}"][:space_type] = perim_space_type hash_of_point_vectors["#{perim_space_type.name} A #{k}"][:polygon] = polygon_a else hash_of_point_vectors["#{space_type.name} A #{k}"] = {} hash_of_point_vectors["#{space_type.name} A #{k}"][:space_type] = space_type hash_of_point_vectors["#{space_type.name} A #{k}"][:polygon] = polygon_a end polygon_b = OpenStudio::Point3dVector.new polygon_b << (sw_point + OpenStudio::Vector3d.new(0, actual_perim, 0)) polygon_b << (nw_point + OpenStudio::Vector3d.new(0, - actual_perim, 0)) polygon_b << (ne_point + OpenStudio::Vector3d.new(0, - actual_perim, 0)) polygon_b << (se_point + OpenStudio::Vector3d.new(0, actual_perim, 0)) if double_loaded_corridor hash_of_point_vectors["#{core_space_type.name} B #{k}"] = {} hash_of_point_vectors["#{core_space_type.name} B #{k}"][:space_type] = core_space_type hash_of_point_vectors["#{core_space_type.name} B #{k}"][:polygon] = polygon_b else hash_of_point_vectors["#{space_type.name} B #{k}"] = {} hash_of_point_vectors["#{space_type.name} B #{k}"][:space_type] = space_type hash_of_point_vectors["#{space_type.name} B #{k}"][:polygon] = polygon_b end polygon_c = OpenStudio::Point3dVector.new polygon_c << (nw_point + OpenStudio::Vector3d.new(0, - actual_perim, 0)) polygon_c << nw_point polygon_c << ne_point polygon_c << (ne_point + OpenStudio::Vector3d.new(0, - actual_perim, 0)) if double_loaded_corridor hash_of_point_vectors["#{perim_space_type.name} C #{k}"] = {} hash_of_point_vectors["#{perim_space_type.name} C #{k}"][:space_type] = perim_space_type hash_of_point_vectors["#{perim_space_type.name} C #{k}"][:polygon] = polygon_c else hash_of_point_vectors["#{space_type.name} C #{k}"] = {} hash_of_point_vectors["#{space_type.name} C #{k}"][:space_type] = space_type hash_of_point_vectors["#{space_type.name} C #{k}"][:polygon] = polygon_c end else polygon_a = OpenStudio::Point3dVector.new polygon_a << sw_point polygon_a << nw_point polygon_a << ne_point polygon_a << se_point hash_of_point_vectors["#{space_type.name} #{k}"] = {} hash_of_point_vectors["#{space_type.name} #{k}"][:space_type] = space_type hash_of_point_vectors["#{space_type.name} #{k}"][:polygon] = polygon_a end # update west points nw_point = ne_point sw_point = se_point end end end return hash_of_point_vectors end |
.create_space_from_polygon(model, space_origin, point_3d_vector, options = {}) ⇒ OpenStudio::Model::Space
add def to create a space from input, optionally take a name, space type, story and thermal zone.
746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 |
# File 'lib/openstudio-standards/geometry/create.rb', line 746 def self.create_space_from_polygon(model, space_origin, point_3d_vector, = {}) # set defaults to use if user inputs not passed in defaults = { 'name' => nil, 'space_type' => nil, 'story' => nil, 'make_thermal_zone' => nil, 'thermal_zone' => nil, 'thermal_zone_multiplier' => 1, 'floor_to_floor_height' => OpenStudio.convert(10.0, 'ft', 'm').get } # merge user inputs with defaults = defaults.merge() # Identity matrix for setting space origins m = OpenStudio::Matrix.new(4, 4, 0) m[0, 0] = 1 m[1, 1] = 1 m[2, 2] = 1 m[3, 3] = 1 # make space from floor print space = OpenStudio::Model::Space.fromFloorPrint(point_3d_vector, ['floor_to_floor_height'], model) space = space.get m[0, 3] = space_origin.x m[1, 3] = space_origin.y m[2, 3] = space_origin.z space.changeTransformation(OpenStudio::Transformation.new(m)) space.setBuildingStory(['story']) if !['name'].nil? space.setName(['name']) end if !['space_type'].nil? && ['space_type'].class.to_s == 'OpenStudio::Model::SpaceType' space.setSpaceType(['space_type']) end # create thermal zone if requested and assign if ['make_thermal_zone'] new_zone = OpenStudio::Model::ThermalZone.new(model) new_zone.setMultiplier(['thermal_zone_multiplier']) space.setThermalZone(new_zone) new_zone.setName("Zone #{space.name}") else if !['thermal_zone'].nil? then space.setThermalZone(['thermal_zone']) end end return space end |
.create_spaces_from_polygons(model, footprints, typical_story_height, effective_num_stories, footprint_origin_point = OpenStudio::Point3d.new(0.0, 0.0, 0.0), story_hash = {}) ⇒ Array<OpenStudio::Model::Space>
add option to create shading surfaces when using multiplier. Mainly important for non rectangular buildings where self shading would be an issue.
take diagram made by create_core_and_perimeter_polygons and make multi-story building
615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 |
# File 'lib/openstudio-standards/geometry/create.rb', line 615 def self.create_spaces_from_polygons(model, footprints, typical_story_height, effective_num_stories, footprint_origin_point = OpenStudio::Point3d.new(0.0, 0.0, 0.0), story_hash = {}) # default story hash is for three stories with mid-story multiplier, but user can pass in custom versions if story_hash.empty? if effective_num_stories > 2 story_hash['ground'] = { space_origin_z: footprint_origin_point.z, space_height: typical_story_height, multiplier: 1 } story_hash['mid'] = { space_origin_z: footprint_origin_point.z + typical_story_height + (typical_story_height * (effective_num_stories.ceil - 3) / 2.0), space_height: typical_story_height, multiplier: effective_num_stories - 2 } story_hash['top'] = { space_origin_z: footprint_origin_point.z + (typical_story_height * (effective_num_stories.ceil - 1)), space_height: typical_story_height, multiplier: 1 } elsif effective_num_stories > 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 * (effective_num_stories.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 end # hash of new spaces (only change boundary conditions for these) new_spaces = [] # loop through story_hash and polygons to generate all of the spaces story_hash.each_with_index do |(story_name, story_data), index| # make new story unless story at requested height already exists. story = nil model.getBuildingStorys.sort.each do |ext_story| if (ext_story.nominalZCoordinate.to_f - story_data[:space_origin_z].to_f).abs < 0.01 story = ext_story end end if story.nil? story = OpenStudio::Model::BuildingStory.new(model) # not used for anything story.setNominalFloortoFloorHeight(story_data[:space_height]) # not used for anything story.setNominalZCoordinate(story_data[:space_origin_z]) story.setName("Story #{story_name}") end # multiplier values for adjacent stories to be altered below as needed multiplier_story_above = 1 multiplier_story_below = 1 if index == 0 # bottom floor, only check above if story_hash.size > 1 multiplier_story_above = story_hash.values[index + 1][:multiplier] end elsif index == story_hash.size - 1 # top floor, check only below multiplier_story_below = story_hash.values[index + -1][:multiplier] else # mid floor, check above and below multiplier_story_above = story_hash.values[index + 1][:multiplier] multiplier_story_below = story_hash.values[index + -1][:multiplier] end # if adjacent story has multiplier > 1 then make appropriate surfaces adiabatic adiabatic_ceilings = false adiabatic_floors = false if story_data[:multiplier] > 1 adiabatic_ceilings = true adiabatic_floors = true elsif multiplier_story_above > 1 adiabatic_ceilings = true elsif multiplier_story_below > 1 adiabatic_floors = true end # get the right collection of polygons to make up footprint for each building story if index > footprints.size - 1 # use last footprint target_footprint = footprints.last else target_footprint = footprints[index] end target_footprint.each do |name, space_data| # gather options = { 'name' => "#{name} - #{story.name}", 'space_type' => space_data[:space_type], 'story' => story, 'make_thermal_zone' => true, 'thermal_zone_multiplier' => story_data[:multiplier], 'floor_to_floor_height' => story_data[:space_height] } # make space space = OpenstudioStandards::Geometry.create_space_from_polygon(model, space_data[:polygon].first, space_data[:polygon], ) new_spaces << space # set z origin to proper position space.setZOrigin(story_data[:space_origin_z]) # loop through celings and floors to hard asssign constructions and set boundary condition if adiabatic_ceilings || adiabatic_floors space.surfaces.each do |surface| if adiabatic_floors && (surface.surfaceType == 'Floor') if surface.construction.is_initialized surface.setConstruction(surface.construction.get) end surface.setOutsideBoundaryCondition('Adiabatic') end if adiabatic_ceilings && (surface.surfaceType == 'RoofCeiling') if surface.construction.is_initialized surface.setConstruction(surface.construction.get) end surface.setOutsideBoundaryCondition('Adiabatic') end end end end # @tofo in future add code to include plenums or raised floor to each/any story. end # any changes to wall boundary conditions will be handled by same code that calls this method. # this method doesn't need to know about basements and party walls. return new_spaces end |
.model_assign_spaces_to_building_stories(model) ⇒ Boolean
Assign each space in the model to a building story based on common z (height) values. If no story object is found for a particular height, create a new one and assign it to the space. Does not assign a story to plenum spaces.
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
# File 'lib/openstudio-standards/geometry/modify.rb', line 98 def self.model_assign_spaces_to_building_stories(model) # Make hash of spaces and min z values sorted_spaces = {} model.getSpaces.sort.each do |space| # Skip plenum spaces next if OpenstudioStandards::Space.space_plenum?(space) # loop through space surfaces to find min z value z_points = [] space.surfaces.each do |surface| surface.vertices.each do |vertex| z_points << vertex.z end end min_z = z_points.min + space.zOrigin sorted_spaces[space] = min_z end # Pre-sort spaces sorted_spaces = sorted_spaces.sort_by { |a| a[1] } # Take the sorted list and assign/make stories sorted_spaces.each do |space| space_obj = space[0] space_min_z = space[1] if space_obj.buildingStory.empty? tolerance = 0.3 story = OpenstudioStandards::Geometry.model_get_building_story_for_nominal_height(model, space_min_z, tolerance: tolerance) if story.nil? story = OpenStudio::Model::BuildingStory.new(model) story.setNominalZCoordinate(space_min_z) story.setName("Building Story #{space_min_z.round(1)}m") OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Model', "No story with a min z value of #{space_min_z.round(2)} m +/- #{tolerance} m was found, so a new story called #{story.name} was created.") end space_obj.setBuildingStory(story) OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Model', "Space #{space[0].name} was not assigned to a story by the user. It has been assigned to #{story.name}.") end end return true end |
.model_envelope_data(model) ⇒ Hash
full list of hash returns aren’t documented yet
gather envelope data for envelope simplification
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 |
# File 'lib/openstudio-standards/geometry/create_bar.rb', line 137 def self.model_envelope_data(model) OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', 'Gathering envelope data.') # hash to contain envelope data envelope_data_hash = {} # used for overhang and party wall orientation catigorization = { 'north_east' => 45.0, 'south_east' => 125.0, 'south_west' => 225.0, 'north_west' => 315.0 } # get building level inputs envelope_data_hash[:north_axis] = model.getBuilding.northAxis envelope_data_hash[:building_floor_area] = model.getBuilding.floorArea envelope_data_hash[:building_exterior_surface_area] = model.getBuilding.exteriorSurfaceArea envelope_data_hash[:building_exterior_wall_area] = model.getBuilding.exteriorWallArea envelope_data_hash[:building_exterior_roof_area] = envelope_data_hash[:building_exterior_surface_area] - envelope_data_hash[:building_exterior_wall_area] envelope_data_hash[:building_air_volume] = model.getBuilding.airVolume envelope_data_hash[:building_perimeter] = nil # will be applied for first story without ground walls # get bounding_box bounding_box = OpenStudio::BoundingBox.new model.getSpaces.sort.each do |space| space.surfaces.sort.each do |space_surface| bounding_box.addPoints(space.transformation * space_surface.vertices) end end min_x = bounding_box.minX.get min_y = bounding_box.minY.get min_z = bounding_box.minZ.get max_x = bounding_box.maxX.get max_y = bounding_box.maxY.get max_z = bounding_box.maxZ.get envelope_data_hash[:building_min_xyz] = [min_x, min_y, min_z] envelope_data_hash[:building_max_xyz] = [max_x, max_y, max_z] # add orientation specific wwr ext_surfaces_hash = OpenstudioStandards::Geometry.model_get_exterior_window_and_wall_area_by_orientation(model) envelope_data_hash[:building_wwr_n] = ext_surfaces_hash['north_window'] / ext_surfaces_hash['north_wall'] envelope_data_hash[:building_wwr_s] = ext_surfaces_hash['south_window'] / ext_surfaces_hash['south_wall'] envelope_data_hash[:building_wwr_e] = ext_surfaces_hash['east_window'] / ext_surfaces_hash['east_wall'] envelope_data_hash[:building_wwr_w] = ext_surfaces_hash['west_window'] / ext_surfaces_hash['west_wall'] envelope_data_hash[:stories] = {} # each entry will be hash with buildingStory as key and attributes has values envelope_data_hash[:space_types] = {} # each entry will be hash with spaceType as key and attributes has values # as rough estimate overhang area / glazing area should be close to projection factor assuming overhang is same width as windows # will only add building shading surfaces assoicated with a sub-surface. building_overhang_area_n = 0.0 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 = 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 story_multiplied_exterior_surface_area = OpenstudioStandards::Geometry.spaces_get_exterior_area(story.spaces) story_multiplied_exterior_wall_area = OpenstudioStandards::Geometry.spaces_get_exterior_wall_area(story.spaces) story_multiplied_exterior_roof_area = story_multiplied_exterior_surface_area - story_multiplied_exterior_wall_area story_has_ground_walls = [] story_has_adiabatic_walls = [] story_included_in_building_area = false # will be true if any spaces on story are inclued in building area story_max_z = nil # loop through spaces for story gathering information story.spaces.each do |space| # get min multiplier value multiplier = space.multiplier if story_min_multiplier.nil? || (story_min_multiplier > multiplier) story_min_multiplier = multiplier end # calculate footprint story_footprint = story_multiplied_floor_area / story_min_multiplier # see if part of floor area if space.partofTotalFloorArea story_included_in_building_area = true # add to space type ratio hash when space is included in building floor area if space.spaceType.is_initialized space_type = space.spaceType.get space_floor_area = space.floorArea * space.multiplier if envelope_data_hash[:space_types].key?(space_type) envelope_data_hash[:space_types][space_type][:floor_area] += space_floor_area else envelope_data_hash[:space_types][space_type] = {} envelope_data_hash[:space_types][space_type][:floor_area] = space_floor_area # make hash for heating and cooling setpoints envelope_data_hash[:space_types][space_type][:htg_setpoint] = {} envelope_data_hash[:space_types][space_type][:clg_setpoint] = {} end # add heating and cooling setpoints if space.thermalZone.is_initialized && space.thermalZone.get.thermostatSetpointDualSetpoint.is_initialized thermostat = space.thermalZone.get.thermostatSetpointDualSetpoint.get # log heating schedule if thermostat.heatingSetpointTemperatureSchedule.is_initialized htg_sch = thermostat.heatingSetpointTemperatureSchedule.get if envelope_data_hash[:space_types][space_type][:htg_setpoint].key?(htg_sch) envelope_data_hash[:space_types][space_type][:htg_setpoint][htg_sch] += space_floor_area else envelope_data_hash[:space_types][space_type][:htg_setpoint][htg_sch] = space_floor_area end else OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', "#{space.thermalZone.get.name} containing #{space.name} doesn't have a heating setpoint schedule.") end # log cooling schedule if thermostat.coolingSetpointTemperatureSchedule.is_initialized clg_sch = thermostat.coolingSetpointTemperatureSchedule.get if envelope_data_hash[:space_types][space_type][:clg_setpoint].key?(clg_sch) envelope_data_hash[:space_types][space_type][:clg_setpoint][clg_sch] += space_floor_area else envelope_data_hash[:space_types][space_type][:clg_setpoint][clg_sch] = space_floor_area end else OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', "#{space.thermalZone.get.name} containing #{space.name} doesn't have a heating setpoint schedule.") end else OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', "#{space.name} either isn't in a thermal zone or doesn't have a thermostat assigned") end else OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', "#{space.name} is included in the building floor area but isn't assigned a space type.") end end # check for walls with adiabatic and ground boundary condition space.surfaces.each do |surface| next if surface.surfaceType != 'Wall' if surface.outsideBoundaryCondition == 'Ground' story_has_ground_walls << surface elsif surface.outsideBoundaryCondition == 'Adiabatic' story_has_adiabatic_walls << surface end end # populate overhang values space.surfaces.each do |surface| surface.subSurfaces.each do |sub_surface| sub_surface.shadingSurfaceGroups.each do |shading_surface_group| shading_surface_group.shadingSurfaces.each do |shading_surface| absolute_azimuth = OpenStudio.convert(sub_surface.azimuth, 'rad', 'deg').get + sub_surface.space.get.directionofRelativeNorth + model.getBuilding.northAxis absolute_azimuth -= 360.0 until absolute_azimuth < 360.0 # add to hash based on orientation if (['north_east'] <= absolute_azimuth) && (absolute_azimuth < ['south_east']) # East overhang building_overhang_area_e += shading_surface.grossArea * space.multiplier elsif (['south_east'] <= absolute_azimuth) && (absolute_azimuth < ['south_west']) # South overhang building_overhang_area_s += shading_surface.grossArea * space.multiplier elsif (['south_west'] <= absolute_azimuth) && (absolute_azimuth < ['north_west']) # West overhang building_overhang_area_w += shading_surface.grossArea * space.multiplier else # North overhang building_overhang_area_n += shading_surface.grossArea * space.multiplier end end end end end # get max z space_z_max = OpenstudioStandards::Geometry.surfaces_get_z_values(space.surfaces.to_a).max + space.zOrigin if story_max_z.nil? || (story_max_z > space_z_max) story_max_z = space_z_max end end # populate hash for story data envelope_data_hash[:stories][story] = {} envelope_data_hash[:stories][story][:story_min_height] = story_min_z envelope_data_hash[:stories][story][:story_max_height] = story_max_z envelope_data_hash[:stories][story][:story_min_multiplier] = story_min_multiplier envelope_data_hash[:stories][story][:story_has_ground_walls] = story_has_ground_walls envelope_data_hash[:stories][story][:story_has_adiabatic_walls] = story_has_adiabatic_walls envelope_data_hash[:stories][story][:story_included_in_building_area] = story_included_in_building_area envelope_data_hash[:stories][story][:story_footprint] = story_footprint envelope_data_hash[:stories][story][:story_multiplied_floor_area] = story_multiplied_floor_area envelope_data_hash[:stories][story][:story_exterior_surface_area] = story_multiplied_exterior_surface_area envelope_data_hash[:stories][story][:story_multiplied_exterior_wall_area] = story_multiplied_exterior_wall_area envelope_data_hash[:stories][story][:story_multiplied_exterior_roof_area] = story_multiplied_exterior_roof_area # get perimeter and adiabatic walls that appear to be party walls perimeter_and_party_walls = OpenstudioStandards::Geometry.building_story_get_exterior_wall_perimeter(story, multiplier_adjustment: story_min_multiplier, bounding_box: bounding_box) envelope_data_hash[:stories][story][:story_perimeter] = perimeter_and_party_walls[:perimeter] envelope_data_hash[:stories][story][:story_party_walls] = [] east = false south = false west = false north = false perimeter_and_party_walls[:party_walls].each do |surface| absolute_azimuth = OpenStudio.convert(surface.azimuth, 'rad', 'deg').get + surface.space.get.directionofRelativeNorth + model.getBuilding.northAxis absolute_azimuth -= 360.0 until absolute_azimuth < 360.0 # add to hash based on orientation (initially added array of sourfaces, but swtiched to just true/false flag) if (['north_east'] <= absolute_azimuth) && (absolute_azimuth < ['south_east']) # East party walls east = true elsif (['south_east'] <= absolute_azimuth) && (absolute_azimuth < ['south_west']) # South party walls south = true elsif (['south_west'] <= absolute_azimuth) && (absolute_azimuth < ['north_west']) # West party walls west = true else # North party walls north = true end end if east then envelope_data_hash[:stories][story][:story_party_walls] << 'east' end if south then envelope_data_hash[:stories][story][:story_party_walls] << 'south' end if west then envelope_data_hash[:stories][story][:story_party_walls] << 'west' end if north then envelope_data_hash[:stories][story][:story_party_walls] << 'north' end # store perimeter from first story that doesn't have ground walls if story_has_ground_walls.empty? && envelope_data_hash[:building_perimeter].nil? 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['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.") end end return envelope_data_hash end |
.model_get_building_stories_above_ground(model) ⇒ Array<OpenStudio::Model::BuildingStory>
Returns an array of the above ground building stories in the model.
927 928 929 930 931 932 933 934 935 936 |
# File 'lib/openstudio-standards/geometry/information.rb', line 927 def self.model_get_building_stories_above_ground(model) above_ground_stories = [] model.getBuildingStorys.sort.each do |story| z = story.nominalZCoordinate if !z.empty? && z.to_f >= 0 above_ground_stories << story end end return above_ground_stories end |
.model_get_building_stories_below_ground(model) ⇒ Array<OpenStudio::Model::BuildingStory>
Returns an array of the below ground building stories in the model.
942 943 944 945 946 947 948 949 950 951 |
# File 'lib/openstudio-standards/geometry/information.rb', line 942 def self.model_get_building_stories_below_ground(model) below_ground_stories = [] model.getBuildingStorys.sort.each do |story| z = story.nominalZCoordinate if !z.empty? && z.to_f < 0 below_ground_stories << story end end return below_ground_stories end |
.model_get_building_story_for_nominal_height(model, minimum_height, tolerance: 0.3) ⇒ OpenStudio::Model::BuildingStory
Returns the building story associated with a given minimum height. This return the story that matches the minimum z value of any vertex of any surface of any space on the story, with the exception of plenum spaces.
910 911 912 913 914 915 916 917 918 919 920 921 |
# File 'lib/openstudio-standards/geometry/information.rb', line 910 def self.model_get_building_story_for_nominal_height(model, minimum_height, tolerance: 0.3) matched_story = nil model.getBuildingStorys.sort.each do |story| z = OpenstudioStandards::Geometry.building_story_get_minimum_height(story) if (minimum_height - z).abs < tolerance OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.Model', "The story with a min z value of #{minimum_height.round(2)} is #{story.name}.") matched_story = story end end return matched_story end |
.model_get_exterior_window_and_wall_area_by_orientation(model, spaces: []) ⇒ Hash
Returns the wall area and window area by orientation
1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 |
# File 'lib/openstudio-standards/geometry/information.rb', line 1029 def self.model_get_exterior_window_and_wall_area_by_orientation(model, spaces: []) # counters total_gross_ext_wall_area_north = 0.0 total_gross_ext_wall_area_south = 0.0 total_gross_ext_wall_area_east = 0.0 total_gross_ext_wall_area_west = 0.0 total_ext_window_area_north = 0.0 total_ext_window_area_south = 0.0 total_ext_window_area_east = 0.0 total_ext_window_area_west = 0.0 if spaces.empty? spaces = model.getSpaces end spaces.each do |space| # get surface area adjusting for zone multiplier zone = space.thermalZone if zone.empty? zone_multiplier = 1 # space is not in a thermal zone else zone_multiplier = zone.get.multiplier end space.surfaces.each do |s| next if s.surfaceType != 'Wall' next if s.outsideBoundaryCondition != 'Outdoors' surface_gross_area = s.grossArea * zone_multiplier # loop through sub surfaces and add area including multiplier ext_window_area = 0 s.subSurfaces.each do |sub_surface| ext_window_area += sub_surface.grossArea * sub_surface.multiplier * zone_multiplier end absolute_azimuth = OpenStudio.convert(s.azimuth, 'rad', 'deg').get + s.space.get.directionofRelativeNorth + model.getBuilding.northAxis absolute_azimuth -= 360.0 until absolute_azimuth < 360.0 # add to exterior wall counter if north or south if (absolute_azimuth >= 45.0) && (absolute_azimuth < 125.0) # east exterior walls total_gross_ext_wall_area_east += surface_gross_area total_ext_window_area_east += ext_window_area elsif (absolute_azimuth >= 125.0) && (absolute_azimuth < 225.0) # south exterior walls total_gross_ext_wall_area_south += surface_gross_area total_ext_window_area_south += ext_window_area elsif (absolute_azimuth >= 225.0) && (absolute_azimuth < 315.0) # west exterior walls total_gross_ext_wall_area_west += surface_gross_area total_ext_window_area_west += ext_window_area else # north exterior walls total_gross_ext_wall_area_north += surface_gross_area total_ext_window_area_north += ext_window_area end end end result = { 'north_wall' => total_gross_ext_wall_area_north, 'north_window' => total_ext_window_area_north, 'south_wall' => total_gross_ext_wall_area_south, 'south_window' => total_ext_window_area_south, 'east_wall' => total_gross_ext_wall_area_east, 'east_window' => total_ext_window_area_east, 'west_wall' => total_gross_ext_wall_area_west, 'west_window' => total_ext_window_area_west } return result end |
.model_get_exterior_window_to_wall_ratio(model, spaces: [], cardinal_direction: nil) ⇒ Double
Returns the window to wall ratio
961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 |
# File 'lib/openstudio-standards/geometry/information.rb', line 961 def self.model_get_exterior_window_to_wall_ratio(model, spaces: [], cardinal_direction: nil) # counters total_gross_ext_wall_area = 0.0 total_ext_window_area = 0.0 window_to_wall_ratio = 0.0 # get spaces if none provided if spaces.empty? spaces = model.getSpaces end # loop through each space and log window and wall areas spaces.each do |space| # get surface area adjusting for zone multiplier zone = space.thermalZone if zone.empty? # space is not in a thermal zone zone_multiplier = 1 else zone_multiplier = zone.get.multiplier end # loop through spaces and skip all that aren't exterior walls and don't match selected cardinal direction space.surfaces.each do |surface| next if surface.surfaceType != 'Wall' next if surface.outsideBoundaryCondition != 'Outdoors' # filter by cardinal direction if specified case cardinal_direction when 'N', 'n', 'North', 'north' next unless OpenstudioStandards::Geometry.surface_get_cardinal_direction(surface) == 'N' when 'E', 'e', 'East', 'east' next unless OpenstudioStandards::Geometry.surface_get_cardinal_direction(surface) == 'E' when 'S', 's', 'South', 'south' next unless OpenstudioStandards::Geometry.surface_get_cardinal_direction(surface) == 'S' when 'W', 'w', 'West', 'west' next unless OpenstudioStandards::Geometry.surface_get_cardinal_direction(surface) == 'W' end # Get wall and window area surface_gross_area = surface.grossArea * zone_multiplier # loop through sub surfaces and add area including multiplier ext_window_area = 0 surface.subSurfaces.each do |sub_surface| ext_window_area += sub_surface.grossArea * sub_surface.multiplier * zone_multiplier end total_gross_ext_wall_area += surface_gross_area total_ext_window_area += ext_window_area end end if total_gross_ext_wall_area > 0.0 window_to_wall_ratio = total_ext_window_area / total_gross_ext_wall_area end return window_to_wall_ratio end |
.model_get_perimeter(model) ⇒ Double
this doesn’t catch walls that are split that sit above floor surfaces that are not (e.g. main corridoor in secondary school model)
also odd with multi-height spaces
Calculates the exterior perimeter length, checking checks for edges shared by a ground exposed floor and exterior exposed wall.
1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 |
# File 'lib/openstudio-standards/geometry/information.rb', line 1106 def self.model_get_perimeter(model) perimeter = 0.0 model.getSpaces.sort.each do |space| # counter to use later edge_hash = {} edge_counter = 0 space.surfaces.sort.each do |surface| # get vertices vertex_hash = {} vertex_counter = 0 surface.vertices.each do |vertex| vertex_counter += 1 vertex_hash[vertex_counter] = [vertex.x, vertex.y, vertex.z] end # make edges counter = 0 vertex_hash.each do |k, v| edge_counter += 1 counter += 1 if vertex_hash.size == counter # different code for wrap around vertex edge_hash[edge_counter] = [v, vertex_hash[1], surface, surface.outsideBoundaryCondition, surface.surfaceType] else edge_hash[edge_counter] = [v, vertex_hash[counter + 1], surface, surface.outsideBoundaryCondition, surface.surfaceType] end end end # check edges for matches (need opposite vertices and proper boundary conditions) edge_hash.each do |k1, v1| next if v1[3] != 'Ground' # skip if not ground exposed floor next if v1[4] != 'Floor' edge_hash.each do |k2, v2| next if v2[3] != 'Outdoors' # skip if not exterior exposed wall (todo - update to handle basement) next if v2[4] != 'Wall' # see if edges have same geometry # found cases where the two lines below removed edges and resulted in lower than actual perimeter. Added new code with tolerance. # next if not v1[0] == v2[1] # next if not same geometry reversed # next if not v1[1] == v2[0] # these are three item array's add in tolerance for each array entry tolerance = 0.0001 test_a = true test_b = true 3.times.each do |i| if (v1[0][i] - v2[1][i]).abs > tolerance test_a = false end if (v1[1][i] - v2[0][i]).abs > tolerance test_b = false end end next if test_a != true next if test_b != true point_one = OpenStudio::Point3d.new(v1[0][0], v1[0][1], v1[0][2]) point_two = OpenStudio::Point3d.new(v1[1][0], v1[1][1], v1[1][2]) length = OpenStudio::Vector3d.new(point_one - point_two).length perimeter += length end end end return perimeter end |
.model_group_thermal_zones_by_building_story(model, thermal_zones) ⇒ Array<Array<OpenStudio::Model::ThermalZone>>
Group an array of zones into multiple arrays, one for each story in the building. Zones with spaces on multiple stories will be assigned to only one of the stories. Returns an empty array when the story doesn’t contain any of the zones.
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/openstudio-standards/geometry/group.rb', line 15 def self.model_group_thermal_zones_by_building_story(model, thermal_zones) story_zone_lists = [] zones_already_assigned = [] model.getBuildingStorys.sort.each do |story| # Get all the spaces on this story spaces = story.spaces # Get all the thermal zones that serve these spaces all_zones_on_story = [] spaces.each do |space| if space.thermalZone.is_initialized all_zones_on_story << space.thermalZone.get else OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Model', "Space #{space.name} has no thermal zone, it is not included in the simulation.") end end # Find thermal zones in the list that are on this story zones_on_story = [] thermal_zones.each do |zone| if all_zones_on_story.include?(zone) # Skip thermal zones that were already assigned to a story. # This can happen if a zone has multiple spaces on multiple stories. # Stairwells and atriums are typical scenarios. next if zones_already_assigned.include?(zone) zones_on_story << zone zones_already_assigned << zone end end unless zones_on_story.empty? story_zone_lists << zones_on_story end end return story_zone_lists end |
.model_group_thermal_zones_by_building_type(model, min_area_m2: 1858.0608) ⇒ Array<Hash>
Split all zones in the model into groups that are big enough to justify their own HVAC system type. Similar to the logic from 90.1 Appendix G, but without regard to the fuel type of the existing HVAC system (because the model may not have one).
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 |
# File 'lib/openstudio-standards/geometry/group.rb', line 163 def self.model_group_thermal_zones_by_building_type(model, min_area_m2: 1858.0608) min_area_ft2 = OpenStudio.convert(min_area_m2, 'm^2', 'ft^2').get # Get occupancy type, building type, fuel type, and area information for all zones, excluding unconditioned zones std = Standard.build('90.1-2019') # delete once space methods refactored zones = std.model_zones_with_occ_and_fuel_type(model, nil) # Ensure that there is at least one conditioned zone if zones.empty? OpenStudio.logFree(OpenStudio::Error, 'openstudio.prototype.Model', 'The building does not appear to have any conditioned zones. Make sure zones have thermostat with appropriate heating and cooling setpoint schedules.') return [] end # Group the zones by building type type_to_area = Hash.new { 0.0 } zones_grouped_by_bldg_type = zones.group_by { |z| z['bldg_type'] } # Determine the dominant building type by area zones_grouped_by_bldg_type.each do |bldg_type, zns| zns.each do |zn| type_to_area[bldg_type] += zn['area'] end end dom_bldg_type = type_to_area.sort_by { |k, v| v }.reverse[0][0] # Get the dominant building type group dom_bldg_type_group = zones_grouped_by_bldg_type[dom_bldg_type] # Check the non-dominant building type groups to see if they are big enough to trigger the building exception. # If they are, leave the group standing alone. # If they are not, add the zones in that group back to the dominant building type group. bldg_type_groups = [] zones_grouped_by_bldg_type.each do |bldg_type, zns| # Skip the dominant building type next if bldg_type == dom_bldg_type # Add up the floor area of the group area_m2 = 0 zns.each do |zn| area_m2 += zn['area'] end area_ft2 = OpenStudio.convert(area_m2, 'm^2', 'ft^2').get # If the non-dominant group is big enough, preserve that group. if area_ft2 > min_area_ft2 bldg_type_groups << [bldg_type, zns] OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "The portion of the building with a building type of #{bldg_type} is bigger than the minimum area of #{min_area_ft2.round} ft2. It will be assigned a separate HVAC system type.") # Otherwise, add the zones back to the dominant group. else dom_bldg_type_group += zns end end # Add the dominant building type group to the list bldg_type_groups << [dom_bldg_type, dom_bldg_type_group] # Calculate the area for each of the final groups # and replace the zone hashes with an array of zone objects final_groups = [] bldg_type_groups.each do |bldg_type, zns| # Sum the area and put all zones into an array area_m2 = 0.0 gp_zns = [] zns.each do |zn| area_m2 += zn['area'] gp_zns << zn['zone'] end area_ft2 = OpenStudio.convert(area_m2, 'm^2', 'ft^2').get # Determine the number of stories this group spans num_stories = OpenstudioStandards::Geometry.thermal_zones_get_number_of_stories_spanned(gp_zns) # Create a hash representing this group group = {} group['area_ft2'] = area_ft2 group['type'] = bldg_type group['stories'] = num_stories group['zones'] = gp_zns final_groups << group # Report out the final grouping OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "Final system type group: bldg_type = #{group['type']}, area = #{group['area_ft2'].round} ft2, num stories = #{group['stories']}, zones:") group['zones'].sort.each_slice(5) do |zone_list| zone_names = [] zone_list.each do |zone| zone_names << zone.name.get.to_s end OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "--- #{zone_names.join(', ')}") end end return final_groups end |
.model_group_thermal_zones_by_occupancy_type(model, min_area_m2: 1858.0608) ⇒ Array<Hash>
Split all zones in the model into groups that are big enough to justify their own HVAC system type. Similar to the logic from 90.1 Appendix G, but without regard to the fuel type of the existing HVAC system (because the model may not have one).
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
# File 'lib/openstudio-standards/geometry/group.rb', line 60 def self.model_group_thermal_zones_by_occupancy_type(model, min_area_m2: 1858.0608) min_area_ft2 = OpenStudio.convert(min_area_m2, 'm^2', 'ft^2').get # Get occupancy type, fuel type, and area information for all zones, excluding unconditioned zones. # Occupancy types are: # Residential # NonResidential # Use 90.1-2010 so that retail and publicassembly are not split out std = Standard.build('90.1-2019') # delete once space methods refactored zones = std.model_zones_with_occ_and_fuel_type(model, nil) # Ensure that there is at least one conditioned zone if zones.empty? OpenStudio.logFree(OpenStudio::Error, 'openstudio.prototype.Model', 'The building does not appear to have any conditioned zones. Make sure zones have thermostat with appropriate heating and cooling setpoint schedules.') return [] end # Group the zones by occupancy type type_to_area = Hash.new { 0.0 } zones_grouped_by_occ = zones.group_by { |z| z['occ'] } # Determine the dominant occupancy type by area zones_grouped_by_occ.each do |occ_type, zns| zns.each do |zn| type_to_area[occ_type] += zn['area'] end end dom_occ = type_to_area.sort_by { |k, v| v }.reverse[0][0] # Get the dominant occupancy type group dom_occ_group = zones_grouped_by_occ[dom_occ] # Check the non-dominant occupancy type groups to see if they are big enough to trigger the occupancy exception. # If they are, leave the group standing alone. # If they are not, add the zones in that group back to the dominant occupancy type group. occ_groups = [] zones_grouped_by_occ.each do |occ_type, zns| # Skip the dominant occupancy type next if occ_type == dom_occ # Add up the floor area of the group area_m2 = 0 zns.each do |zn| area_m2 += zn['area'] end area_ft2 = OpenStudio.convert(area_m2, 'm^2', 'ft^2').get # If the non-dominant group is big enough, preserve that group. if area_ft2 > min_area_ft2 occ_groups << [occ_type, zns] OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "The portion of the building with an occupancy type of #{occ_type} is bigger than the minimum area of #{min_area_ft2.round} ft2. It will be assigned a separate HVAC system type.") # Otherwise, add the zones back to the dominant group. else dom_occ_group += zns end end # Add the dominant occupancy group to the list occ_groups << [dom_occ, dom_occ_group] # Calculate the area for each of the final groups # and replace the zone hashes with an array of zone objects final_groups = [] occ_groups.each do |occ_type, zns| # Sum the area and put all zones into an array area_m2 = 0.0 gp_zns = [] zns.each do |zn| area_m2 += zn['area'] gp_zns << zn['zone'] end area_ft2 = OpenStudio.convert(area_m2, 'm^2', 'ft^2').get # Determine the number of stories this group spans num_stories = OpenstudioStandards::Geometry.thermal_zones_get_number_of_stories_spanned(gp_zns) # Create a hash representing this group group = {} group['area_ft2'] = area_ft2 group['type'] = occ_type group['stories'] = num_stories group['zones'] = gp_zns final_groups << group # Report out the final grouping OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "Final system type group: occ = #{group['type']}, area = #{group['area_ft2'].round} ft2, num stories = #{group['stories']}, zones:") group['zones'].sort.each_slice(5) do |zone_list| zone_names = [] zone_list.each do |zone| zone_names << zone.name.get.to_s end OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "--- #{zone_names.join(', ')}") end end return final_groups end |
.model_set_building_north_axis(model, north_axis) ⇒ Boolean
Set the model’s north axis (degrees from true North)
145 146 147 148 149 150 151 152 |
# File 'lib/openstudio-standards/geometry/modify.rb', line 145 def self.model_set_building_north_axis(model, north_axis) return false if north_axis.nil? building = model.getBuilding building.setNorthAxis(north_axis) return true end |
.model_sort_building_stories_and_get_min_multiplier(model) ⇒ Hash
sort building stories
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
# File 'lib/openstudio-standards/geometry/create_bar.rb', line 114 def self.model_sort_building_stories_and_get_min_multiplier(model) sorted_building_stories = {} # loop through stories model.getBuildingStorys.sort.each do |story| story_min_z = nil # loop through spaces in story. story.spaces.sort.each do |space| space_z_min = OpenstudioStandards::Geometry.surfaces_get_z_values(space.surfaces.to_a).min + space.zOrigin if story_min_z.nil? || (story_min_z > space_z_min) story_min_z = space_z_min end end sorted_building_stories[story] = story_min_z end return sorted_building_stories end |
.space_create_point_at_center_of_floor(space, z_offset_m) ⇒ OpenStudio::Point3d
method to create a point object at the center of a floor
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
# File 'lib/openstudio-standards/geometry/create.rb', line 12 def self.space_create_point_at_center_of_floor(space, z_offset_m) # find floors floor_surfaces = [] space.surfaces.each { |surface| floor_surfaces << surface if surface.surfaceType == 'Floor' } # this method only works for flat (non-inclined) floors bounding_box = OpenStudio::BoundingBox.new floor_surfaces.each { |floor| bounding_box.addPoints(floor.vertices) } xmin = bounding_box.minX.get ymin = bounding_box.minY.get zmin = bounding_box.minZ.get xmax = bounding_box.maxX.get ymax = bounding_box.maxY.get x_pos = (xmin + xmax) / 2 y_pos = (ymin + ymax) / 2 z_pos = zmin + z_offset_m point_on_floor = OpenstudioStandards::Geometry.surfaces_contain_point?(floor_surfaces, OpenStudio::Point3d.new(x_pos, y_pos, zmin)) if point_on_floor new_point = OpenStudio::Point3d.new(x_pos, y_pos, z_pos) else # don't make point, it doesn't appear to be inside of the space new_point = nil end return new_point end |
.space_get_adjacent_space_with_most_shared_wall_area(space, same_floor: true) ⇒ OpenStudio::Model::Space
Find the space that has the most wall area touching this space.
480 481 482 483 |
# File 'lib/openstudio-standards/geometry/information.rb', line 480 def self.space_get_adjacent_space_with_most_shared_wall_area(space, same_floor: true) adjacent_space = OpenstudioStandards::Geometry.space_get_adjacent_spaces_with_shared_wall_areas(space, same_floor: same_floor)[0][0] return adjacent_space end |
.space_get_adjacent_spaces_with_shared_wall_areas(space, same_floor: true) ⇒ Hash
Get a sorted array of tuples containing a list of spaces and connected area in descending order
409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 |
# File 'lib/openstudio-standards/geometry/information.rb', line 409 def self.space_get_adjacent_spaces_with_shared_wall_areas(space, same_floor: true) same_floor_spaces = [] spaces = [] space.surfaces.each do |surface| adj_surface = surface.adjacentSurface unless adj_surface.empty? space.model.getSpaces.sort.each do |other_space| next if other_space == space other_space.surfaces.each do |surf| if surf == adj_surface.get spaces << other_space end end end end end # If looking for only spaces adjacent on the same floor. if same_floor == true if space.buildingStory.empty? OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Information', "Cannot get adjacent spaces of space #{space.name} since space not set to BuildingStory.") return nil end spaces.each do |other_space| if space.buildingStory.empty? OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Information', "One or more adjecent spaces to space #{space.name} is not assigned to a BuildingStory. Ensure all spaces are assigned.") return nil end if other_space.buildingStory.get == space.buildingStory.get same_floor_spaces << other_space end end spaces = same_floor_spaces end # now sort by areas. area_index = [] array_hash = {} return array_hash if spaces.empty? # iterate through each surface in the space space.surfaces.each do |surface| # get the adjacent surface in another space. adj_surface = surface.adjacentSurface unless adj_surface.empty? # go through each of the adjacent spaces to find the matching surface/space. spaces.each_with_index do |other_space, index| next if other_space == space other_space.surfaces.each do |surf| if surf == adj_surface.get # initialize array index to zero for first time so += will work. area_index[index] = 0 if area_index[index].nil? area_index[index] += surf.grossArea array_hash[other_space] = area_index[index] end end end end end sorted_spaces = array_hash.sort_by { |_key, value| value }.reverse return sorted_spaces end |
.space_get_below_grade_wall_height(space) ⇒ Double
Finds heights of the first below grade walls and returns them as a numeric. Used when defining C Factor walls. Returns nil if the space is above grade.
490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 |
# File 'lib/openstudio-standards/geometry/information.rb', line 490 def self.space_get_below_grade_wall_height(space) # find height of first below-grade wall adjacent to the ground surface_height = nil space.surfaces.each do |surface| next unless surface.surfaceType == 'Wall' boundary_condition = surface.outsideBoundaryCondition next unless boundary_condition == 'OtherSideCoefficients' || boundary_condition.to_s.downcase.include?('ground') # calculate wall height as difference of maximum and minimum z values, assuming square, vertical walls z_values = [] surface.vertices.each do |vertex| z_values << vertex.z end surface_height = z_values.max - z_values.min end return surface_height end |
.space_get_envelope_area(space, multiplier: true) ⇒ Double
Calculate the space envelope area. According to the 90.1 definition, building envelope include:
-
“the elements of a building that separate conditioned spaces from the exterior”
-
“the elements of a building that separate conditioned space from unconditioned space or that enclose semiheated spaces through which thermal energy may be transferred to or from the exterior, to or from unconditioned spaces or to or from conditioned spaces.”
Outside boundary conditions currently supported:
-
Adiabatic
-
Surface
-
Outdoors
-
Foundation
-
Ground
-
GroundFCfactorMethod
-
OtherSideCoefficients
-
OtherSideConditionsModel
-
GroundSlabPreprocessorAverage
-
GroundSlabPreprocessorCore
-
GroundSlabPreprocessorPerimeter
-
GroundBasementPreprocessorAverageWall
-
GroundBasementPreprocessorAverageFloor
-
GroundBasementPreprocessorUpperWall
-
GroundBasementPreprocessorLowerWall
Surface type currently supported:
-
Floor
-
Wall
-
RoofCeiling
297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 |
# File 'lib/openstudio-standards/geometry/information.rb', line 297 def self.space_get_envelope_area(space, multiplier: true) area_m2 = 0.0 # Get the space conditioning type std = Standard.build('90.1-2019') # delete once space methods refactored space_cond_type = std.space_conditioning_category(space) # Loop through all surfaces in this space space.surfaces.sort.each do |surface| # Only account for spaces that are conditioned or semi-heated next unless space_cond_type != 'Unconditioned' surf_cnt = false # Conditioned space OR semi-heated space <-> exterior # Conditioned space OR semi-heated space <-> ground if surface.outsideBoundaryCondition == 'Outdoors' || surface.isGroundSurface surf_cnt = true end # Conditioned space OR semi-heated space <-> unconditioned spaces unless surf_cnt # @todo add a case for 'Zone' when supported if surface.outsideBoundaryCondition == 'Surface' adj_space = surface.adjacentSurface.get.space.get adj_space_cond_type = std.space_conditioning_category(adj_space) surf_cnt = true unless adj_space_cond_type != 'Unconditioned' end end if surf_cnt # This surface area_m2 += surface.netArea # Subsurfaces in this surface surface.subSurfaces.sort.each do |subsurface| area_m2 += subsurface.netArea end end end if multiplier area_m2 *= space.multiplier end return area_m2 end |
.space_get_exterior_wall_and_subsurface_and_roof_area(space, multiplier: false) ⇒ Double
Calculate the area of the exterior walls, including the area of the windows and doors on these walls, and the area of roofs.
379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 |
# File 'lib/openstudio-standards/geometry/information.rb', line 379 def self.space_get_exterior_wall_and_subsurface_and_roof_area(space, multiplier: false) area_m2 = 0.0 # 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' || surface.surfaceType == 'RoofCeiling' # This surface area_m2 += surface.netArea # Subsurfaces in this surface surface.subSurfaces.sort.each do |subsurface| area_m2 += subsurface.netArea end end if multiplier area_m2 *= space.multiplier end return area_m2 end |
.space_get_exterior_wall_and_subsurface_area(space, multiplier: false) ⇒ Double
Calculate the area of the exterior walls, including the area of the windows and doors on these walls.
349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 |
# File 'lib/openstudio-standards/geometry/information.rb', line 349 def self.space_get_exterior_wall_and_subsurface_area(space, multiplier: false) area_m2 = 0.0 # 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' # This surface area_m2 += surface.netArea # Subsurfaces in this surface surface.subSurfaces.sort.each do |subsurface| area_m2 += subsurface.netArea end end if multiplier area_m2 *= space.multiplier end return area_m2 end |
.space_get_f_floor_area(space) ⇒ Double
This function returns the space’s ground area. Assumes only one floor per space!
550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 |
# File 'lib/openstudio-standards/geometry/information.rb', line 550 def self.space_get_f_floor_area(space) # Find space's floors with ground contact floors = [] space.surfaces.each do |surface| if surface.surfaceType == 'Floor' && surface.outsideBoundaryCondition.to_s.downcase.include?('ground') floors << surface end end # If this space has no ground contact floors, return 0 return 0.0 if floors.empty? # Raise a warning for any space with more than 1 ground contact floor surface. if floors.length > 1 OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Information', "Space: #{space.name} has more than one ground contact floor. FFactorGroundFloorConstruction area in this space may be incorrect.") end # Get floor area floor = floors[0] area = floor.netArea return area end |
.space_get_f_floor_perimeter(space) ⇒ Double
This function returns the space’s ground perimeter length. Assumes only one floor per space!
515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 |
# File 'lib/openstudio-standards/geometry/information.rb', line 515 def self.space_get_f_floor_perimeter(space) # Find space's floors with ground contact floors = [] space.surfaces.each do |surface| if surface.surfaceType == 'Floor' && surface.outsideBoundaryCondition.to_s.downcase.include?('ground') floors << surface end end # If this space has no ground contact floors, return 0 return 0.0 if floors.empty? # Raise a warning for any space with more than 1 ground contact floor surface. if floors.length > 1 OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Information', "Space: #{space.name} has more than one ground contact floor. FFactorGroundFloorConstruction perimeter in this space may be incorrect.") end # cycle through surfaces in the space and get adjacency length to the floor floor = floors[0] perimeter = 0.0 space.surfaces.each do |surface| # find perimeter of floor by finding intersecting outdoor walls and measuring the intersection if surface.surfaceType == 'Wall' && surface.outsideBoundaryCondition == 'Outdoors' perimeter += OpenstudioStandards::Geometry.wall_and_floor_intersection_length(surface, floor) end end return perimeter end |
.spaces_get_exterior_area(spaces, multiplier: true) ⇒ Double
Get the total exterior area of selected spaces
597 598 599 600 601 602 603 604 |
# File 'lib/openstudio-standards/geometry/information.rb', line 597 def self.spaces_get_exterior_area(spaces, multiplier: true) total_area = 0.0 spaces.each do |space| space_multiplier = multiplier ? space.multiplier : 1.0 total_area += space.exteriorArea * space_multiplier end return total_area end |
.spaces_get_exterior_wall_area(spaces, multiplier: true) ⇒ Double
Get the total exterior wall area of selected spaces
611 612 613 614 615 616 617 618 |
# File 'lib/openstudio-standards/geometry/information.rb', line 611 def self.spaces_get_exterior_wall_area(spaces, multiplier: true) total_area = 0.0 spaces.each do |space| space_multiplier = multiplier ? space.multiplier : 1.0 total_area += space.exteriorWallArea * space_multiplier end return total_area end |
.spaces_get_floor_area(spaces, multiplier: true) ⇒ Double
Get the total floor area of selected spaces
583 584 585 586 587 588 589 590 |
# File 'lib/openstudio-standards/geometry/information.rb', line 583 def self.spaces_get_floor_area(spaces, multiplier: true) total_area = 0.0 spaces.each do |space| space_multiplier = multiplier ? space.multiplier : 1.0 total_area += space.floorArea * space_multiplier end return total_area end |
.sub_surface_create_point_at_specific_height(sub_surface, reference_floor, distance_from_window_m, height_above_subsurface_bottom_m) ⇒ OpenStudio::Point3d
method to create a point object from a sub surface
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
# File 'lib/openstudio-standards/geometry/create.rb', line 48 def self.sub_surface_create_point_at_specific_height(sub_surface, reference_floor, distance_from_window_m, height_above_subsurface_bottom_m) window_outward_normal = sub_surface.outwardNormal window_centroid = OpenStudio.getCentroid(sub_surface.vertices).get window_outward_normal.setLength(distance_from_window_m) vertex = window_centroid + window_outward_normal.reverseVector vertex_on_floorplane = reference_floor.plane.project(vertex) floor_outward_normal = reference_floor.outwardNormal floor_outward_normal.setLength(height_above_subsurface_bottom_m) floor_surfaces = [] space.surfaces.each { |surface| floor_surfaces << surface if surface.surfaceType == 'Floor' } point_on_floor = OpenstudioStandards::Geometry.surfaces_contain_point?(floor_surfaces, vertex_on_floorplane) if point_on_floor new_point = vertex_on_floorplane + floor_outward_normal.reverseVector else # don't make point, it doesn't appear to be inside of the space # nil new_point = vertex_on_floorplane + floor_outward_normal.reverseVector end return new_point end |
.sub_surface_reduce_area_by_percent_by_raising_sill(sub_surface, percent_reduction) ⇒ Boolean
Reduce the area of the subsurface by raising the sill height
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
# File 'lib/openstudio-standards/geometry/modify.rb', line 51 def self.sub_surface_reduce_area_by_percent_by_raising_sill(sub_surface, percent_reduction) # Find the min and max z values min_z_val = 99_999 max_z_val = -99_999 sub_surface.vertices.each do |vertex| # Min z value if vertex.z < min_z_val min_z_val = vertex.z end # Max z value if vertex.z > max_z_val max_z_val = vertex.z end end # Calculate the window height height = max_z_val - min_z_val # Calculate the new sill height z_delta = height * percent_reduction # Reset the z value of the lowest points within a certain threshold new_vertices = [] sub_surface.vertices.each do |vertex| if (vertex.z - min_z_val).abs < 0.025 new_vertices << (vertex + OpenStudio::Vector3d.new(0.0, 0.0, z_delta)) else new_vertices << vertex end end # Reset the vertices sub_surface.setVertices(new_vertices) return true end |
.sub_surface_reduce_area_by_percent_by_shrinking_toward_centroid(sub_surface, percent_reduction) ⇒ Boolean
Reduce the area of the subsurface by shrinking it toward the centroid
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
# File 'lib/openstudio-standards/geometry/modify.rb', line 14 def self.sub_surface_reduce_area_by_percent_by_shrinking_toward_centroid(sub_surface, percent_reduction) # if percent_reduction > 1=> percent increase instead of reduction mult = percent_reduction <= 1 ? 1 - percent_reduction : percent_reduction scale_factor = mult**0.5 # Get the centroid (Point3d) g = sub_surface.centroid # Create an array to collect the new vertices new_vertices = [] # Loop on vertices (Point3ds) sub_surface.vertices.each do |vertex| # Point3d - Point3d = Vector3d # Vector from centroid to vertex (GA, GB, GC, etc) centroid_vector = vertex - g # Resize the vector (done in place) according to scale_factor centroid_vector.setLength(centroid_vector.length * scale_factor) # Move the vertex toward the centroid vertex = g + centroid_vector new_vertices << vertex end # Assign the new vertices to the self sub_surface.setVertices(new_vertices) return true end |
.sub_surface_vertical_rectangle?(sub_surface) ⇒ Boolean
Determine if the sub surface is a vertical rectangle, meaning a rectangle where the bottom is parallel to the ground.
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 |
# File 'lib/openstudio-standards/geometry/information.rb', line 235 def self.sub_surface_vertical_rectangle?(sub_surface) # Get the vertices once verts = sub_surface.vertices # Check for 4 vertices return false unless verts.size == 4 # Check if the 2 lowest z-values # are the same z_vals = [] verts.each do |vertex| z_vals << vertex.z end z_vals = z_vals.sort return false unless z_vals[0] == z_vals[1] # Check if the diagonals are equal length diag_a = verts[0] - verts[2] diag_b = verts[1] - verts[3] return false unless diag_a.length == diag_b.length # If here, we have a rectangle return true end |
.surface_get_absolute_azimuth(surface) ⇒ Double
Calculate a surface’s absolute azimuth
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
# File 'lib/openstudio-standards/geometry/information.rb', line 136 def self.surface_get_absolute_azimuth(surface) # Get associated space space = surface.space.get # Get model object model = surface.model # Calculate azimuth surface_azimuth_rel_space = OpenStudio.convert(surface.azimuth, 'rad', 'deg').get space_dir_rel_north = space.directionofRelativeNorth building_dir_rel_north = model.getBuilding.northAxis surface_abs_azimuth = surface_azimuth_rel_space + space_dir_rel_north + building_dir_rel_north surface_abs_azimuth -= 360.0 until surface_abs_azimuth < 360.0 return surface_abs_azimuth end |
.surface_get_cardinal_direction(surface) ⇒ String
Determine a surface absolute cardinal direction
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
# File 'lib/openstudio-standards/geometry/information.rb', line 157 def self.surface_get_cardinal_direction(surface) # Get the surface's absolute azimuth surface_abs_azimuth = OpenstudioStandards::Geometry.surface_get_absolute_azimuth(surface) # Determine the surface's cardinal direction cardinal_direction = '' if (surface_abs_azimuth >= 0 && surface_abs_azimuth <= 45) || (surface_abs_azimuth > 315 && surface_abs_azimuth <= 360) cardinal_direction = 'N' elsif surface_abs_azimuth > 45 && surface_abs_azimuth <= 135 cardinal_direction = 'E' elsif surface_abs_azimuth > 135 && surface_abs_azimuth <= 225 cardinal_direction = 'S' elsif surface_abs_azimuth > 225 && surface_abs_azimuth <= 315 cardinal_direction = 'W' end return cardinal_direction end |
.surface_get_door_to_wall_ratio(surface) ⇒ Double
Calculate the door to wall ratio of a surface
121 122 123 124 125 126 127 128 129 130 |
# File 'lib/openstudio-standards/geometry/information.rb', line 121 def self.surface_get_door_to_wall_ratio(surface) surface_area = surface.grossArea surface_door_area = 0.0 surface.subSurfaces.sort.each do |ss| next unless ss.subSurfaceType == 'Door' surface_door_area += ss.netArea end return surface_door_area / surface_area end |
.surface_get_edges(surface) ⇒ Array<Array(OpenStudio::Point3D, OpenStudio::Point3D)>
Returns an array of OpenStudio::Point3D pairs of an OpenStudio::Model::Surface’s edges. Used to calculate surface intersections.
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
# File 'lib/openstudio-standards/geometry/information.rb', line 78 def self.surface_get_edges(surface) vertices = surface.vertices n_vertices = vertices.length # Create edge hash that keeps track of all edges in surface. An edge is defined here as an array of length 2 # containing two OpenStudio::Point3Ds that define the line segment representing a surface edge. # format edge_array[i] = [OpenStudio::Point3D, OpenStudio::Point3D] edge_array = [] # Iterate through each vertex in the surface and construct an edge for it for edge_counter in 0..n_vertices - 1 # If not the last vertex in surface if edge_counter < n_vertices - 1 edge_array << [vertices[edge_counter], vertices[edge_counter + 1]] else # Make index adjustments for final index in vertices array edge_array << [vertices[edge_counter], vertices[0]] end end return edge_array end |
.surface_get_window_to_wall_ratio(surface) ⇒ Double
Calculate the window to wall ratio of a surface
106 107 108 109 110 111 112 113 114 115 |
# File 'lib/openstudio-standards/geometry/information.rb', line 106 def self.surface_get_window_to_wall_ratio(surface) surface_area = surface.grossArea surface_fene_area = 0.0 surface.subSurfaces.sort.each do |ss| next unless ss.subSurfaceType == 'FixedWindow' || ss.subSurfaceType == 'OperableWindow' || ss.subSurfaceType == 'GlassDoor' surface_fene_area += ss.netArea end return surface_fene_area / surface_area end |
.surfaces_contain_point?(surfaces, point) ⇒ Boolean
Check if a point is contained on any surface in an array of surfaces
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 |
# File 'lib/openstudio-standards/geometry/information.rb', line 205 def self.surfaces_contain_point?(surfaces, point) on_surface = false surfaces.each do |surface| # Check if sensor is on floor plane (I need to loop through all floors) plane = surface.plane point_on_plane = plane.project(point) face_transform = OpenStudio::Transformation.alignFace(surface.vertices) face_vertices = face_transform * surface.vertices face_point_on_plane = face_transform * point_on_plane if OpenStudio.pointInPolygon(face_point_on_plane, face_vertices.reverse, 0.01) # initial_sensor location lands in this surface's polygon on_surface = true end end return on_surface end |
.surfaces_get_z_values(surfaces) ⇒ Array<Double>
return an array of z values for surfaces passed in. The values will be relative to the parent origin.
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 |
# File 'lib/openstudio-standards/geometry/information.rb', line 184 def self.surfaces_get_z_values(surfaces) z_values = [] # loop over all surfaces surfaces.each do |surface| # get the existing vertices vertices = surface.vertices vertices.each do |vertex| # push z value to array z_values << vertex.z end end return z_values end |
.thermal_zone_get_adjacent_zones_with_shared_walls(thermal_zone, same_floor: true) ⇒ Array<OpenStudio::Model::ThermalZone>
Return an array of zones that share a wall with the zone
629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 |
# File 'lib/openstudio-standards/geometry/information.rb', line 629 def self.thermal_zone_get_adjacent_zones_with_shared_walls(thermal_zone, same_floor: true) adjacent_zones = [] thermal_zone.spaces.each do |space| adj_spaces = OpenstudioStandards::Geometry.space_get_adjacent_spaces_with_shared_wall_areas(space, same_floor: same_floor) adj_spaces.each do |k, v| # skip if space is in current thermal zone. next unless space.thermalZone.is_initialized next if k.thermalZone.get == thermal_zone adjacent_zones << k.thermalZone.get end end adjacent_zones = adjacent_zones.uniq return adjacent_zones end |
.thermal_zones_get_number_of_stories_spanned(thermal_zones) ⇒ Integer
Determine the number of stories spanned by the supplied thermal zones. If all zones on one of the stories have an identical multiplier, assume that the multiplier is a floor multiplier and increase the number of stories accordingly. Stories do not have to be contiguous.
659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 |
# File 'lib/openstudio-standards/geometry/information.rb', line 659 def self.thermal_zones_get_number_of_stories_spanned(thermal_zones) # Get the story object for all zones stories = [] thermal_zones.each do |zone| zone.spaces.each do |space| story = space.buildingStory next if story.empty? stories << story.get end end # Reduce down to the unique set of stories stories = stories.uniq # Tally up stories including multipliers num_stories = 0 stories.each do |story| num_stories += OpenstudioStandards::Geometry.building_story_get_floor_multiplier(story) end return num_stories end |
.wall_and_floor_intersection_length(wall, floor) ⇒ Double
this calculation has a few assumptions:
This function returns the length of intersection between a wall and floor sharing space. Primarily used for FFactorGroundFloorConstruction exposed perimeter calculations.
-
Floors are flat. This means they have a constant z-axis value.
-
If a wall shares an edge with a floor, it’s assumed that edge intersects with only this floor.
-
The wall and floor share a common space. This space is assumed to only have one floor!
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/openstudio-standards/geometry/information.rb', line 31 def self.wall_and_floor_intersection_length(wall, floor) # Used for determining if two points are 'equal' if within this length tolerance = 0.0001 # Get floor and wall edges wall_edge_array = OpenstudioStandards::Geometry.surface_get_edges(wall) floor_edge_array = OpenstudioStandards::Geometry.surface_get_edges(floor) # Floor assumed flat and constant in x-y plane (i.e. a single z value) floor_z_value = floor_edge_array[0][0].z # Iterate through wall edges wall_edge_array.each do |wall_edge| wall_edge_p1 = wall_edge[0] wall_edge_p2 = wall_edge[1] # If points representing the wall surface edge have different z-coordinates, this edge is not parallel to the # floor and can be skipped if tolerance <= (wall_edge_p1.z - wall_edge_p2.z).abs next end # If wall edge is parallel to the floor, ensure it's on the same x-y plane as the floor. if tolerance <= (wall_edge_p1.z - floor_z_value).abs next end # If the edge is parallel with the floor and in the same x-y plane as the floor, assume an intersection the # length of the wall edge intersect_vector = wall_edge_p1 - wall_edge_p2 edge_vector = OpenStudio::Vector3d.new(intersect_vector.x, intersect_vector.y, intersect_vector.z) return(edge_vector.length) end # If no edges intersected, return 0 return 0.0 end |