lib/openstudio-standards/standards/Standards.Model.rb in openstudio-standards-0.1.11 vs lib/openstudio-standards/standards/Standards.Model.rb in openstudio-standards-0.1.12

- old
+ new

@@ -24,10 +24,13 @@ standards_files << 'OpenStudio_Standards_schedules.json' standards_files << 'OpenStudio_Standards_space_types.json' standards_files << 'OpenStudio_Standards_templates.json' standards_files << 'OpenStudio_Standards_unitary_acs.json' standards_files << 'OpenStudio_Standards_heat_rejection.json' + standards_files << 'OpenStudio_Standards_exterior_lighting.json' + standards_files << 'OpenStudio_Standards_parking.json' + standards_files << 'OpenStudio_Standards_entryways.json' # standards_files << 'OpenStudio_Standards_unitary_hps.json' # Combine the data from the JSON files into a single hash top_dir = File.expand_path('../../..', File.dirname(__FILE__)) standards_data_dir = "#{top_dir}/data/standards" @@ -2314,10 +2317,26 @@ end return desired_object end + # Create constant ScheduleRuleset + # + # @param [double] constant value + # @param [string] name + # @return schedule + def add_constant_schedule_ruleset(value,name = nil) + schedule = OpenStudio::Model::ScheduleRuleset.new(self) + if not name.nil? + schedule.setName(name) + schedule.defaultDaySchedule.setName("#{name} Default") + end + schedule.defaultDaySchedule.addValue(OpenStudio::Time.new(0, 24, 0, 0), value) + + return schedule + end + # Create a schedule from the openstudio standards dataset and # add it to the model. # # @param schedule_name [String} name of the schedule # @return [ScheduleRuleset] the resulting schedule ruleset @@ -3135,32 +3154,40 @@ building_type = '' if getBuilding.standardsBuildingType.is_initialized building_type = getBuilding.standardsBuildingType.get end - # prototype small office approx 500 m^2 - # prototype medium office approx 5000 m^2 - # prototype large office approx 50,000 m^2 # map office building type to small medium or large if building_type == 'Office' && remap_office open_studio_area = getBuilding.floorArea - building_type = if open_studio_area < 2750 - 'SmallOffice' - elsif open_studio_area < 25_250 - 'MediumOffice' - else - 'LargeOffice' - end + building_type = self.remap_office(open_studio_area) end results = {} results['climate_zone'] = climate_zone results['building_type'] = building_type return results end + # remap office to one of the protptye buildings + # @param [Double] floor area + # @return [String] SmallOffice, MediumOffice, LargeOffice + def remap_office(floor_area) + # prototype small office approx 500 m^2 + # prototype medium office approx 5000 m^2 + # prototype large office approx 50,000 m^2 + # map office building type to small medium or large + building_type = if floor_area < 2750 + 'SmallOffice' + elsif floor_area < 25_250 + 'MediumOffice' + else + 'LargeOffice' + end + end + # user needs to pass in template as string. The building type and climate zone will come from the model. # If the building type or ASHRAE climate zone is not set in the model this will return nil # If the lookup doesn't find matching simulation results this wil return nil # # @param [String] target prototype template for eui lookup @@ -4295,10 +4322,221 @@ OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Model', error_string) return false end end + # Create sorted hash of stories with data need to determine effective number of stories above and below grade + # the key should be the story object, which would allow other measures the ability to for example loop through spaces of the bottom story + # + # @return [hash] hash of space types with data in value necessary to determine effective number of stories above and below grade + def create_story_hash + + story_hash = {} + + # loop through stories + self.getBuildingStorys.each do |story| + + # skip of story doesn't have any spaces + next if story.spaces.size == 0 + + story_min_z = nil + story_zone_multipliers = [] + story_spaces_part_of_floor_area = [] + story_spaces_not_part_of_floor_area = [] + story_ext_wall_area = 0.0 + story_ground_wall_area = 0.0 + + # loop through space surfaces to find min z value + story.spaces.each do |space| + + # skip of space doesn't have any geometry + next if space.surfaces.size == 0 + + # get space multiplier + story_zone_multipliers << space.multiplier + + # space part of floor area check + if space.partofTotalFloorArea + story_spaces_part_of_floor_area << space + else + story_spaces_not_part_of_floor_area << space + end + + # update exterior wall area (not sure if this is net or gross) + story_ext_wall_area += space.exteriorWallArea + + space_min_z = nil + z_points = [] + space.surfaces.each do |surface| + surface.vertices.each do |vertex| + z_points << vertex.z + end + + # update count of ground wall areas + next if not surface.surfaceType == "Wall" + next if not surface.outsideBoundaryCondition == "Ground" # todo - make more flexible for slab/basement modeling + story_ground_wall_area += surface.grossArea + + end + + # skip if surface had no vertices + next if z_points.size == 0 + + # update story min_z + space_min_z = z_points.min + space.zOrigin + if story_min_z.nil? or story_min_z > space_min_z + story_min_z = space_min_z + end + + end + + # update story hash + story_hash[story] = {} + story_hash[story][:min_z] = story_min_z + story_hash[story][:multipliers] = story_zone_multipliers + story_hash[story][:part_of_floor_area] = story_spaces_part_of_floor_area + story_hash[story][:not_part_of_floor_area] = story_spaces_not_part_of_floor_area + story_hash[story][:ext_wall_area] = story_ext_wall_area + story_hash[story][:ground_wall_area] = story_ground_wall_area + + end + + # sort hash by min_z low to high + story_hash = story_hash.sort_by{|k,v| v[:min_z]} + + # reassemble into hash after sorting + hash = {} + story_hash.each do |story, props| + hash[story] = props + end + + return hash + + end + + # populate this method + # Determine the effective number of stories above and below grade + # + # @return hash with effective_num_stories_below_grade and effective_num_stories_above_grade + def effective_num_stories + + below_grade = 0 + above_grade = 0 + + # call create_story_hash + story_hash = self.create_story_hash + + story_hash.each do |story,hash| + + # skip if no spaces in story are included in the building area + next if hash[:part_of_floor_area].size == 0 + + # only count as below grade if ground wall area is greater than ext wall area and story below is also below grade + if above_grade == 0 and hash[:ground_wall_area] > hash[:ext_wall_area] + below_grade += 1 * hash[:multipliers].min + else + above_grade += 1 * hash[:multipliers].min + end + + end + + # populate hash + effective_num_stories = {} + effective_num_stories[:below_grade] = below_grade + effective_num_stories[:above_grade] = above_grade + effective_num_stories[:story_hash] = story_hash + + return effective_num_stories + + end + + # create space_type_hash with info such as effective_num_spaces, num_units, num_meds, num_meals + # + # @param template [String] + # @param trust_effective_num_spaces [Bool] defaults to false - set to true if modeled every space as a real rpp, vs. space as collection of rooms + # @return [hash] hash of space types with misc information + # @todo - add code when determining number of units to makeuse of trust_effective_num_spaces arg + def create_space_type_hash(template,trust_effective_num_spaces = false) + + # assumed class size to deduct teachers from occupant count for classrooms + typical_class_size = 20.0 + + space_type_hash = {} + self.getSpaceTypes.each do |space_type| + + # get standards info + stds_bldg_type = space_type.standardsBuildingType + stds_space_type = space_type.standardsSpaceType + if stds_bldg_type.is_initialized and stds_space_type.is_initialized and space_type.spaces.size > 0 + stds_bldg_type = stds_bldg_type.get + stds_space_type = stds_space_type.get + effective_num_spaces = 0 + floor_area = 0.0 + num_people = 0.0 + num_students = 0.0 + num_units = 0.0 + num_beds = 0.0 + num_people_bldg_total = nil # may need this in future, not same as sumo of people for all space types. + num_meals = nil + # determine num_elevators in another method + # determine num_parking_spots in another method + + # loop through spaces to get mis values + space_type.spaces.each do |space| + next if not space.partofTotalFloorArea + effective_num_spaces += space.multiplier + floor_area += space.floorArea * space.multiplier + num_people += space.numberOfPeople * space.multiplier + + end + + # determine number of units + if stds_bldg_type == "SmallHotel" && stds_space_type.include?("GuestRoom") # doesn't always == GuestRoom so use include? + avg_unit_size = OpenStudio::convert(354.2,"ft^2","m^2").get # calculated from prototype + num_units = floor_area / avg_unit_size + elsif stds_bldg_type == "LargeHotel" && stds_space_type.include?("GuestRoom") + avg_unit_size = OpenStudio::convert(279.7,"ft^2","m^2").get # calculated from prototype + num_units = floor_area / avg_unit_size + elsif stds_bldg_type == "MidriseApartment" && stds_space_type.include?("Apartment") + avg_unit_size = OpenStudio::convert(949.9,"ft^2","m^2").get # calculated from prototype + num_units = floor_area / avg_unit_size + elsif stds_bldg_type == "HighriseApartment" && stds_space_type.include?("Apartment") + avg_unit_size = OpenStudio::convert(949.9,"ft^2","m^2").get # calculated from prototype + num_units = floor_area / avg_unit_size + elsif stds_bldg_type == "StripMall" + avg_unit_size = OpenStudio::convert(22500.0/10.0,"ft^2","m^2").get # calculated from prototype + num_units = floor_area / avg_unit_size + end + + # determine number of beds + if stds_bldg_type == "Hospital" && ["PatRoom","ICU_PatRm","ICU_Open"].include?(stds_space_type) + num_beds = num_people + end + + # determine number of students + if ["PrimarySchool","SecondarySchool"].include?(stds_bldg_type) && stds_space_type == "Classroom" + num_students += num_people * ((typical_class_size - 1.0)/typical_class_size) + end + + space_type_hash[space_type] = {} + space_type_hash[space_type][:stds_bldg_type] = stds_bldg_type + space_type_hash[space_type][:stds_space_type] = stds_space_type + space_type_hash[space_type][:effective_num_spaces] = effective_num_spaces + space_type_hash[space_type][:floor_area] = floor_area + space_type_hash[space_type][:num_people] = num_people + space_type_hash[space_type][:num_students] = num_students + space_type_hash[space_type][:num_units] = num_units + space_type_hash[space_type][:num_beds] = num_beds + + else + OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "Cannot identify standards buidling type and space type for #{space_type.name}, it won't be added to space_type_hash.") + end + end + + return space_type_hash + end + private # Helper method to fill in hourly values def add_vals_to_sch(day_sch, sch_type, values) if sch_type == 'Constant' @@ -4399,6 +4637,7 @@ end return true end + end