lib/openstudio-standards/standards/Standards.Model.rb in openstudio-standards-0.6.0.rc2 vs lib/openstudio-standards/standards/Standards.Model.rb in openstudio-standards-0.6.3

- old
+ new

@@ -1,11 +1,10 @@ require 'csv' require 'date' class Standard - attr_accessor :space_multiplier_map - attr_accessor :standards_data + attr_accessor :space_multiplier_map, :standards_data # returns the space multiplier map # @return [Hash] space multiplier map def define_space_multiplier @@ -121,13 +120,12 @@ 'Simulation on proposed model failed. Baseline generation is stopped.') end end if create_proposed_model # Make the run directory if it doesn't exist - unless Dir.exist?(sizing_run_dir) - FileUtils.mkdir_p(sizing_run_dir) - end + FileUtils.mkdir_p(sizing_run_dir) + # Save proposed model proposed_model.save(OpenStudio::Path.new("#{sizing_run_dir}/proposed_final.osm"), true) forward_translator = OpenStudio::EnergyPlus::ForwardTranslator.new idf = forward_translator.translateModel(proposed_model) idf_path = OpenStudio::Path.new("#{sizing_run_dir}/proposed_final.idf") @@ -824,11 +822,11 @@ # (and for Xcel Energy CO EDA) # fossilandelectric zones = model_zones_with_occ_and_fuel_type(model, custom) # Ensure that there is at least one conditioned zone - if zones.size.zero? + if zones.empty? OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.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 @@ -1153,15 +1151,15 @@ # Note: testing showed that the fan object schedule is not used by OpenStudio # Instead, get the fan schedule from the air_loop_hvac object # fan_object = nil # fan_object = get_fan_object_for_airloop(model, air_loop_hvac) fan_object = 'nothing' - if !fan_object.nil? + if fan_object.nil? + OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "Failed to retreive fan object for AirLoop #{air_loop_hvac.name}") + else # fan_schedule = fan_object.availabilitySchedule fan_schedule = air_loop_hvac.availabilitySchedule - else - OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "Failed to retreive fan object for AirLoop #{air_loop_hvac.name}") end fan_schedule_8760 = OpenstudioStandards::Schedules.schedule_get_hourly_values(fan_schedule) end # Assign this schedule to each zone on this air loop @@ -1197,13 +1195,11 @@ # @author Doug Maddox, PNNL # @param model [object] # @param air_loop [object] # @return [object] supply fan of zone equipment component def get_fan_object_for_airloop(model, air_loop) - if !air_loop.supplyFan.empty? - fan_component = air_loop.supplyFan.get - else + if air_loop.supplyFan.empty? # Check if system has unitary wrapper air_loop.supplyComponents.each do |component| # Get the object type, getting the internal coil # type if inside a unitary system. obj_type = component.iddObjectType.valueName.to_s @@ -1225,10 +1221,12 @@ if !fan_component.nil? break end end + else + fan_component = air_loop.supplyFan.get end # Get the fan object for this fan fan_obj_type = fan_component.iddObjectType.valueName.to_s case fan_obj_type @@ -2360,11 +2358,11 @@ # @param volume [Double] Equipment storage capacity in gallons. # @param capacity_per_volume [Double] Equipment capacity per storage capacity in Btu/h/gal. # @return [Array] returns an array of hashes, one hash per object. Array is empty if no results. # @example Find all the schedule rules that match the name # rules = model_find_objects(standards_data['schedules'], 'name' => schedule_name) - # if rules.size.zero? + # if rules.empty? # OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Model', "Cannot find data for schedule: #{schedule_name}, will not be created.") # return false # end def model_find_objects(hash_of_objects, search_criteria, capacity = nil, date = nil, area = nil, num_floors = nil, fan_motor_bhp = nil, volume = nil, capacity_per_volume = nil) matching_objects = [] @@ -2401,22 +2399,21 @@ matching_objects = matching_objects.reject { |object| !object.key?('minimum_capacity') || !object.key?('maximum_capacity') } # Skip objects that don't have values specified for minimum_capacity and maximum_capacity matching_objects = matching_objects.reject { |object| object['minimum_capacity'].nil? || object['maximum_capacity'].nil? } - # Round up if capacity is an integer - if capacity == capacity.round - capacity += (capacity * 0.01) - end + # Convert to a float in case not already + capacity = capacity.to_f + # Skip objects whose the minimum capacity is below or maximum capacity above the specified capacity - matching_capacity_objects = matching_objects.reject { |object| capacity.to_f <= object['minimum_capacity'].to_f || capacity.to_f > object['maximum_capacity'].to_f } + matching_capacity_objects = matching_objects.reject { |object| capacity <= object['minimum_capacity'].to_f || capacity > object['maximum_capacity'].to_f } # If no object was found, round the capacity down in case the number fell between the limits in the json file. - if matching_capacity_objects.size.zero? + if matching_capacity_objects.empty? capacity *= 0.99 # Skip objects whose minimum capacity is below or maximum capacity above the specified capacity - matching_objects = matching_objects.reject { |object| capacity.to_f <= object['minimum_capacity'].to_f || capacity.to_f > object['maximum_capacity'].to_f } + matching_objects = matching_objects.reject { |object| capacity <= object['minimum_capacity'].to_f || capacity > object['maximum_capacity'].to_f } else matching_objects = matching_capacity_objects end end @@ -2430,11 +2427,11 @@ # Skip objects whose the minimum volume is below or maximum volume above the specified volume matching_volume_objects = matching_objects.reject { |object| volume.to_f < object['minimum_storage'].to_f || volume.to_f > object['maximum_storage'].to_f } # If no object was found, round the volume down in case the number fell between the limits in the json file. - if matching_volume_objects.size.zero? + if matching_volume_objects.empty? volume *= 0.99 # Skip objects whose minimum volume is below or maximum volume above the specified volume matching_objects = matching_objects.reject { |object| volume.to_f <= object['minimum_storage'].to_f || volume.to_f >= object['maximum_storage'].to_f } else matching_objects = matching_volume_objects @@ -2451,11 +2448,11 @@ # Skip objects whose the minimum capacity_per_volume is below or maximum capacity_per_volume above the specified capacity_per_volume matching_capacity_per_volume_objects = matching_objects.reject { |object| capacity_per_volume.to_f <= object['minimum_capacity_per_storage'].to_f || capacity_per_volume.to_f >= object['maximum_capacity_per_storage'].to_f } # If no object was found, round the volume down in case the number fell between the limits in the json file. - if matching_capacity_per_volume_objects.size.zero? + if matching_capacity_per_volume_objects.empty? capacity_per_volume *= 0.99 # Skip objects whose minimum capacity_per_volume is below or maximum capacity_per_volume above the specified capacity_per_volume matching_objects = matching_objects.reject { |object| capacity_per_volume.to_f <= object['minimum_capacity_per_storage'].to_f || capacity_per_volume.to_f >= object['maximum_capacity_per_storage'].to_f } else matching_objects = matching_capacity_per_volume_objects @@ -2475,11 +2472,11 @@ # Filter based on motor type matching_capacity_objects = matching_capacity_objects.select { |object| object['type'].downcase == search_criteria['type'].downcase } if search_criteria.keys.include?('type') # If no object was found, round the fan_motor_bhp down in case the number fell between the limits in the json file. - if matching_capacity_objects.size.zero? + if matching_capacity_objects.empty? fan_motor_bhp *= 0.99 # Skip objects whose minimum capacity is below or maximum capacity above the specified fan_motor_bhp matching_objects = matching_objects.reject { |object| fan_motor_bhp.to_f <= object['minimum_capacity'].to_f || fan_motor_bhp.to_f > object['maximum_capacity'].to_f } else matching_objects = matching_capacity_objects @@ -2521,11 +2518,11 @@ # Skip objects whose minimum floors is below or maximum floors is above num_floors matching_objects = matching_objects.reject { |object| num_floors.to_f < object['minimum_floors'].to_f || num_floors.to_f > object['maximum_floors'].to_f } end # Check the number of matching objects found - if matching_objects.size.zero? + if matching_objects.empty? OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.Model', "Find objects search criteria returned no results. Search criteria: #{search_criteria}. Called from #{caller(0)[1]}.") end return matching_objects end @@ -2553,11 +2550,11 @@ # motor_properties = self.model.find_object(motors, search_criteria, capacity: 2.5) def model_find_object(hash_of_objects, search_criteria, capacity = nil, date = nil, area = nil, num_floors = nil, fan_motor_bhp = nil, volume = nil, capacity_per_volume = nil) matching_objects = model_find_objects(hash_of_objects, search_criteria, capacity, date, area, num_floors, fan_motor_bhp, volume, capacity_per_volume) # Check the number of matching objects found - if matching_objects.size.zero? + if matching_objects.empty? desired_object = nil OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.Model', "Find object search criteria returned no results. Search criteria: #{search_criteria}. Called from #{caller(0)[1]}") elsif matching_objects.size == 1 desired_object = matching_objects[0] else @@ -2582,11 +2579,11 @@ # @param num_floors [Double] capacity of the object in question. If num_floors is supplied, # the objects will only be returned if the specified num_floors is between the minimum_floors and maximum_floors values. # @return [Array] returns an array of hashes, one hash per object. Array is empty if no results. # @example Find all the schedule rules that match the name # rules = model_find_objects(standards_data['schedules'], 'name' => schedule_name) - # if rules.size.zero? + # if rules.empty? # OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Model', "Cannot find data for schedule: #{schedule_name}, will not be created.") # return false # end def standards_lookup_table_many(table_name:, search_criteria: {}, capacity: nil, date: nil, area: nil, num_floors: nil) desired_object = nil @@ -2631,19 +2628,18 @@ matching_objects = matching_objects.reject { |object| !object.key?('minimum_capacity') || !object.key?('maximum_capacity') } # Skip objects that don't have values specified for minimum_capacity and maximum_capacity matching_objects = matching_objects.reject { |object| object['minimum_capacity'].nil? || object['maximum_capacity'].nil? } - # Round up if capacity is an integer - if capacity == capacity.round - capacity += (capacity * 0.01) - end + # Convert to a float in case not already + capacity = capacity.to_f + # Skip objects whose the minimum capacity is below or maximum capacity above the specified capacity - matching_capacity_objects = matching_objects.reject { |object| capacity.to_f <= object['minimum_capacity'].to_f || capacity.to_f > object['maximum_capacity'].to_f } + matching_capacity_objects = matching_objects.reject { |object| capacity <= object['minimum_capacity'].to_f || capacity > object['maximum_capacity'].to_f } # If no object was found, round the capacity down in case the number fell between the limits in the json file. - if matching_capacity_objects.size.zero? + if matching_capacity_objects.empty? capacity *= 0.99 search_criteria_matching_objects.each do |object| # Skip objects that don't have fields for minimum_capacity and maximum_capacity next if !object.key?('minimum_capacity') || !object.key?('maximum_capacity') # Skip objects that don't have values specified for minimum_capacity and maximum_capacity @@ -2698,11 +2694,11 @@ # Skip objects whose minimum floors is below or maximum floors is above num_floors matching_objects = matching_objects.reject { |object| num_floors.to_f < object['minimum_floors'].to_f || num_floors.to_f > object['maximum_floors'].to_f } end # Check the number of matching objects found - if matching_objects.size.zero? + if matching_objects.empty? OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.Model', "Find objects search criteria returned no results. Search criteria: #{search_criteria}. Called from #{caller(0)[1]}.") end return matching_objects end @@ -2730,11 +2726,11 @@ search_criteria: search_criteria, capacity: capacity, date: date) # Check the number of matching objects found - if matching_objects.size.zero? + if matching_objects.empty? desired_object = nil OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.Model', "Find object search criteria returned no results. Search criteria: #{search_criteria}. Called from #{caller(0)[1]}") elsif matching_objects.size == 1 desired_object = matching_objects[0] else @@ -2766,11 +2762,11 @@ # OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.Model', "Adding schedule: #{schedule_name}") # Find all the schedule rules that match the name rules = model_find_objects(standards_data['schedules'], 'name' => schedule_name) - if rules.size.zero? + if rules.empty? OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Model', "Cannot find data for schedule: #{schedule_name}, will not be created.") return model.alwaysOnDiscreteSchedule end # Make a schedule ruleset @@ -2789,28 +2785,43 @@ # Default if day_types.include?('Default') day_sch = sch_ruleset.defaultDaySchedule day_sch.setName("#{schedule_name} Default") model_add_vals_to_sch(model, day_sch, sch_type, values) + if model.version < OpenStudio::VersionString.new('3.8.0') + day_sch.setInterpolatetoTimestep(false) + else + day_sch.setInterpolatetoTimestep('No') + end end # Winter Design Day if day_types.include?('WntrDsn') day_sch = OpenStudio::Model::ScheduleDay.new(model) sch_ruleset.setWinterDesignDaySchedule(day_sch) day_sch = sch_ruleset.winterDesignDaySchedule day_sch.setName("#{schedule_name} Winter Design Day") model_add_vals_to_sch(model, day_sch, sch_type, values) + if model.version < OpenStudio::VersionString.new('3.8.0') + day_sch.setInterpolatetoTimestep(false) + else + day_sch.setInterpolatetoTimestep('No') + end end # Summer Design Day if day_types.include?('SmrDsn') day_sch = OpenStudio::Model::ScheduleDay.new(model) sch_ruleset.setSummerDesignDaySchedule(day_sch) day_sch = sch_ruleset.summerDesignDaySchedule day_sch.setName("#{schedule_name} Summer Design Day") model_add_vals_to_sch(model, day_sch, sch_type, values) + if model.version < OpenStudio::VersionString.new('3.8.0') + day_sch.setInterpolatetoTimestep(false) + else + day_sch.setInterpolatetoTimestep('No') + end end # Other days (weekdays, weekends, etc) if day_types.include?('Wknd') || day_types.include?('Wkdy') || @@ -2825,10 +2836,15 @@ # Make the Rule sch_rule = OpenStudio::Model::ScheduleRule.new(sch_ruleset) day_sch = sch_rule.daySchedule day_sch.setName("#{schedule_name} #{day_types} Day") model_add_vals_to_sch(model, day_sch, sch_type, values) + if model.version < OpenStudio::VersionString.new('3.8.0') + day_sch.setInterpolatetoTimestep(false) + else + day_sch.setInterpolatetoTimestep('No') + end # Set the dates when the rule applies sch_rule.setStartDate(OpenStudio::Date.new(OpenStudio::MonthOfYear.new(start_date.month.to_i), start_date.day.to_i)) sch_rule.setEndDate(OpenStudio::Date.new(OpenStudio::MonthOfYear.new(end_date.month.to_i), end_date.day.to_i)) @@ -2882,21 +2898,22 @@ u_factor = nil shgc = nil vt = nil material_name.split.each_with_index do |item, i| prop_value = material_name.split[i + 1].to_f - if item == 'U' + case item + when 'U' unless u_factor.nil? OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Model', "Multiple U-Factor values have been identified for #{material_name}: previous = #{u_factor}, new = #{prop_value}. Please check the material name. New U-Factor will be used.") end u_factor = prop_value - elsif item == 'SHGC' + when 'SHGC' unless shgc.nil? OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Model', "Multiple SHGC values have been identified for #{material_name}: previous = #{shgc}, new = #{prop_value}. Please check the material name. New SHGC will be used.") end shgc = prop_value - elsif item == 'VT' + when 'VT' unless vt.nil? OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Model', "Multiple VT values have been identified for #{material_name}: previous = #{vt}, new = #{prop_value}. Please check the material name. New SHGC will be used.") end vt = prop_value end @@ -2929,11 +2946,12 @@ end material_type = data['material_type'] end material = nil - if material_type == 'StandardOpaqueMaterial' + case material_type + when 'StandardOpaqueMaterial' material = OpenStudio::Model::StandardOpaqueMaterial.new(model) material.setName(material_name) material.setRoughness(data['roughness'].to_s) material.setThickness(OpenStudio.convert(data['thickness'].to_f, 'in', 'm').get) @@ -2942,41 +2960,41 @@ material.setSpecificHeat(OpenStudio.convert(data['specific_heat'].to_f, 'Btu/lb*R', 'J/kg*K').get) material.setThermalAbsorptance(data['thermal_absorptance'].to_f) material.setSolarAbsorptance(data['solar_absorptance'].to_f) material.setVisibleAbsorptance(data['visible_absorptance'].to_f) - elsif material_type == 'MasslessOpaqueMaterial' + when 'MasslessOpaqueMaterial' material = OpenStudio::Model::MasslessOpaqueMaterial.new(model) material.setName(material_name) material.setThermalResistance(OpenStudio.convert(data['resistance'].to_f, 'hr*ft^2*R/Btu', 'm^2*K/W').get) material.setThermalConductivity(OpenStudio.convert(data['conductivity'].to_f, 'Btu*in/hr*ft^2*R', 'W/m*K').get) material.setThermalAbsorptance(data['thermal_absorptance'].to_f) material.setSolarAbsorptance(data['solar_absorptance'].to_f) material.setVisibleAbsorptance(data['visible_absorptance'].to_f) - elsif material_type == 'AirGap' + when 'AirGap' material = OpenStudio::Model::AirGap.new(model) material.setName(material_name) material.setThermalResistance(OpenStudio.convert(data['resistance'].to_f, 'hr*ft^2*R/Btu*in', 'm*K/W').get) - elsif material_type == 'Gas' + when 'Gas' material = OpenStudio::Model::Gas.new(model) material.setName(material_name) material.setThickness(OpenStudio.convert(data['thickness'].to_f, 'in', 'm').get) material.setGasType(data['gas_type'].to_s) - elsif material_type == 'SimpleGlazing' + when 'SimpleGlazing' material = OpenStudio::Model::SimpleGlazing.new(model) material.setName(material_name) material.setUFactor(OpenStudio.convert(u_factor.to_f, 'Btu/hr*ft^2*R', 'W/m^2*K').get) material.setSolarHeatGainCoefficient(shgc.to_f) material.setVisibleTransmittance(vt.to_f) - elsif material_type == 'StandardGlazing' + when 'StandardGlazing' material = OpenStudio::Model::StandardGlazing.new(model) material.setName(material_name) material.setOpticalDataType(data['optical_data_type'].to_s) material.setThickness(OpenStudio.convert(data['thickness'].to_f, 'in', 'm').get) @@ -3849,57 +3867,52 @@ # @param model [OpenStudio::Model::Model] OpenStudio model object # @param building_type [String] the building type # @return [Double] floor area (m^2) of prototype building for building type passed in. # Returns nil if unexpected building type def model_find_prototype_floor_area(model, building_type) - if building_type == 'FullServiceRestaurant' # 5502 ft^2 + case building_type + when 'FullServiceRestaurant' # 5502 ft^2 result = 511 - elsif building_type == 'Hospital' # 241,410 ft^2 (including basement) + when 'Hospital' # 241,410 ft^2 (including basement) result = 22_422 - elsif building_type == 'LargeHotel' # 122,132 ft^2 + when 'LargeHotel' # 122,132 ft^2 result = 11_345 - elsif building_type == 'LargeOffice' # 498,600 ft^2 + when 'LargeOffice', 'LargeOfficeDetailed' # 498,600 ft^2 result = 46_320 - elsif building_type == 'MediumOffice' # 53,600 ft^2 + when 'MediumOffice', 'MediumOfficeDetailed' # 53,600 ft^2 result = 4982 - elsif building_type == 'LargeOfficeDetailed' # 498,600 ft^2 - result = 46_320 - elsif building_type == 'MediumOfficeDetailed' # 53,600 ft^2 - result = 4982 - elsif building_type == 'MidriseApartment' # 33,700 ft^2 + when 'MidriseApartment' # 33,700 ft^2 result = 3135 - elsif building_type == 'Office' + when 'Office' result = nil # @todo there shouldn't be a prototype building for this OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Model', 'Measures calling this should choose between SmallOffice, MediumOffice, and LargeOffice') - elsif building_type == 'Outpatient' # 40.950 ft^2 + when 'Outpatient' # 40.950 ft^2 result = 3804 - elsif building_type == 'PrimarySchool' # 73,960 ft^2 + when 'PrimarySchool' # 73,960 ft^2 result = 6871 - elsif building_type == 'QuickServiceRestaurant' # 2500 ft^2 + when 'QuickServiceRestaurant' # 2500 ft^2 result = 232 - elsif building_type == 'Retail' # 24,695 ft^2 + when 'Retail' # 24,695 ft^2 result = 2294 - elsif building_type == 'SecondarySchool' # 210,900 ft^2 + when 'SecondarySchool' # 210,900 ft^2 result = 19_592 - elsif building_type == 'SmallHotel' # 43,200 ft^2 + when 'SmallHotel' # 43,200 ft^2 result = 4014 - elsif building_type == 'SmallOffice' # 5500 ft^2 + when 'SmallOffice', 'SmallOfficeDetailed' # 5500 ft^2 result = 511 - elsif building_type == 'SmallOfficeDetailed' # 5500 ft^2 - result = 511 - elsif building_type == 'StripMall' # 22,500 ft^2 + when 'StripMall' # 22,500 ft^2 result = 2090 - elsif building_type == 'SuperMarket' # 45,002 ft2 (from legacy reference idf file) + when 'SuperMarket' # 45,002 ft2 (from legacy reference idf file) result = 4181 - elsif building_type == 'Warehouse' # 49,495 ft^2 (legacy ref shows 52,045, but I wil calc using 49,495) + when 'Warehouse' # 49,495 ft^2 (legacy ref shows 52,045, but I wil calc using 49,495) result = 4595 - elsif building_type == 'SmallDataCenterLowITE' || building_type == 'SmallDataCenterHighITE' # 600 ft^2 + when 'SmallDataCenterLowITE', 'SmallDataCenterHighITE' # 600 ft^2 result = 56 - elsif building_type == 'LargeDataCenterLowITE' || building_type == 'LargeDataCenterHighITE' # 6000 ft^2 + when 'LargeDataCenterLowITE', 'LargeDataCenterHighITE' # 6000 ft^2 result = 557 - elsif building_type == 'Laboratory' # 90000 ft^2 + when 'Laboratory' # 90000 ft^2 result = 8361 else OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Model', "Didn't find expected building type. As a result can't determine floor prototype floor area") result = nil end @@ -4195,11 +4208,11 @@ surfaces_to_modify.sort.each do |surf| prev_created_consts = planar_surface_apply_standard_construction(surf, climate_zone, prev_created_consts, wwr_building_type, wwr_info, surface_category[surf]) end # List the unique array of constructions - if prev_created_consts.size.zero? + if prev_created_consts.empty? OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Model', 'None of the constructions in your proposed model have both Intended Surface Type and Standards Construction Type') else prev_created_consts.each do |surf_type, construction| OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "For #{surf_type.join(' ')}, applied #{construction.name}.") end @@ -4309,11 +4322,13 @@ space.additionalProperties.setFeature('building_type_for_wwr', std_spc_type) end # Initialize intermediate variables if space type hasn't # been encountered yet - if !bat_win_wall_info.key?(std_spc_type) + if bat_win_wall_info.key?(std_spc_type) + bat = bat_win_wall_info[std_spc_type] + else bat_win_wall_info[std_spc_type] = {} bat = bat_win_wall_info[std_spc_type] # Loop through all spaces in the model, and # per the PNNL PRM Reference Manual, find the areas @@ -4331,12 +4346,10 @@ bat.store('sh_fene_only_wall_m2', 0.001) bat.store('sh_wind_m2', 0) bat.store('sh_plenum_wall_m2', 0.001) bat.store('total_wall_m2', 0.001) bat.store('total_plenum_m2', 0.001) - else - bat = bat_win_wall_info[std_spc_type] end # Loop through all surfaces in this space wall_area_m2 = 0 wind_area_m2 = 0 @@ -4533,11 +4546,11 @@ if red < 0.0 # surface with fenestration to its maximum but adjusted by door areas when need to add windows in surfaces no fenestration # turn negative to positive to get the correct adjustment factor. red = -red surface_wwr = OpenstudioStandards::Geometry.surface_get_window_to_wall_ratio(surface) - residual_fene += (0.9 - red * surface_wwr) * surface.grossArea + residual_fene += (0.9 - (red * surface_wwr)) * surface.grossArea end surface_adjust_fenestration_in_a_surface(surface, red, model) end if residual_fene > 0.0 @@ -4725,13 +4738,11 @@ end # Air loops model.getAirLoopHVACs.each do |air_loop| # Don't remove airloops representing non-mechanically cooled systems - if !air_loop.additionalProperties.hasFeature('non_mechanically_cooled') - air_loop.remove - else + if air_loop.additionalProperties.hasFeature('non_mechanically_cooled') # Remove heating coil on air_loop.supplyComponents.each do |supply_comp| # Remove standalone heating coils if supply_comp.iddObjectType.valueName.to_s.include?('OS_Coil_Heating') supply_comp.remove @@ -4744,10 +4755,12 @@ unitary_system.resetCoolingCoil htg_coil.remove end end end + else + air_loop.remove end end # Zone equipment model.getThermalZones.sort.each do |zone| @@ -4840,44 +4853,33 @@ # get building type building_data = model_get_building_properties(model) building_type = building_data['building_type'] result = [] - if building_type == 'FullServiceRestaurant' + case building_type + when 'FullServiceRestaurant' result << { units: 'meal', block: nil, max_hourly: 1.5, max_daily: 11.0, avg_day_unit: 2.4 } - elsif building_type == 'Hospital' + when 'Hospital', 'Outpatient', 'Retail', 'StripMall', 'SuperMarket', 'Warehouse', 'SmallDataCenterLowITE', 'SmallDataCenterHighITE', 'LargeDataCenterLowITE', 'LargeDataCenterHighITE', 'Laboratory' OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Model', "No SWH rules of thumbs for #{building_type}.") - elsif ['LargeHotel', 'SmallHotel'].include? building_type + when 'LargeHotel', 'SmallHotel' result << { units: 'unit', block: 20, max_hourly: 6.0, max_daily: 35.0, avg_day_unit: 24.0 } result << { units: 'unit', block: 60, max_hourly: 5.0, max_daily: 25.0, avg_day_unit: 14.0 } result << { units: 'unit', block: 100, max_hourly: 4.0, max_daily: 15.0, avg_day_unit: 10.0 } - elsif building_type == 'MidriseApartment' + when 'MidriseApartment' result << { units: 'unit', block: 20, max_hourly: 12.0, max_daily: 80.0, avg_day_unit: 42.0 } result << { units: 'unit', block: 50, max_hourly: 10.0, max_daily: 73.0, avg_day_unit: 40.0 } result << { units: 'unit', block: 75, max_hourly: 8.5, max_daily: 66.0, avg_day_unit: 38.0 } result << { units: 'unit', block: 100, max_hourly: 7.0, max_daily: 60.0, avg_day_unit: 37.0 } result << { units: 'unit', block: 200, max_hourly: 5.0, max_daily: 50.0, avg_day_unit: 35.0 } - elsif ['Office', 'LargeOffice', 'MediumOffice', 'SmallOffice', 'LargeOfficeDetailed', 'MediumOfficeDetailed', 'SmallOfficeDetailed'].include? building_type + when 'Office', 'LargeOffice', 'MediumOffice', 'SmallOffice', 'LargeOfficeDetailed', 'MediumOfficeDetailed', 'SmallOfficeDetailed' result << { units: 'person', block: nil, max_hourly: 0.4, max_daily: 2.0, avg_day_unit: 1.0 } - elsif building_type == 'Outpatient' - OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Model', "No SWH rules of thumbs for #{building_type}.") - elsif building_type == 'PrimarySchool' + when 'PrimarySchool' result << { units: 'student', block: nil, max_hourly: 0.6, max_daily: 1.5, avg_day_unit: 0.6 } - elsif building_type == 'QuickServiceRestaurant' + when 'QuickServiceRestaurant' result << { units: 'meal', block: nil, max_hourly: 0.7, max_daily: 6.0, avg_day_unit: 0.7 } - elsif building_type == 'Retail' - OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Model', "No SWH rules of thumbs for #{building_type}.") - elsif building_type == 'SecondarySchool' + when 'SecondarySchool' result << { units: 'student', block: nil, max_hourly: 1.0, max_daily: 3.6, avg_day_unit: 1.8 } - elsif building_type == 'StripMall' - OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Model', "No SWH rules of thumbs for #{building_type}.") - elsif building_type == 'SuperMarket' - OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Model', "No SWH rules of thumbs for #{building_type}.") - elsif building_type == 'Warehouse' - OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Model', "No SWH rules of thumbs for #{building_type}.") - elsif ['SmallDataCenterLowITE', 'SmallDataCenterHighITE', 'LargeDataCenterLowITE', 'LargeDataCenterHighITE', 'Laboratory'].include? building_type - OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Model', "No SWH rules of thumbs for #{building_type}.") else OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Model', "Didn't find expected building type. As a result can't determine hot water demand recommendations") end return result @@ -4915,17 +4917,17 @@ # get climate zone value climate_zone = OpenstudioStandards::Weather.model_get_climate_zone(model) internal_loads = {} - internal_loads['mech_vent_cfm'] = units_per_bldg * (0.01 * conditioned_floor_area + 7.5 * (bedrooms_per_unit + 1.0)) + internal_loads['mech_vent_cfm'] = units_per_bldg * ((0.01 * conditioned_floor_area) + (7.5 * (bedrooms_per_unit + 1.0))) internal_loads['infiltration_ach'] = if ['1A', '1B', '2A', '2B'].include? climate_zone_value 5.0 else 3.0 end - internal_loads['igain_btu_per_day'] = units_per_bldg * (17_900.0 + 23.8 * conditioned_floor_area + 4104.0 * bedrooms_per_unit) + internal_loads['igain_btu_per_day'] = units_per_bldg * (17_900.0 + (23.8 * conditioned_floor_area) + (4104.0 * bedrooms_per_unit)) internal_loads['internal_mass_lbs'] = total_floor_area * 8.0 return internal_loads end @@ -4940,59 +4942,60 @@ climate_zone = climate_zone.gsub('ClimateZone ', 'CZ') if climate_zone == 'CZ1-8' climate_zone = '' end - if building_type == 'FullServiceRestaurant' + case building_type + when 'FullServiceRestaurant' building_type = 'FullSrvRest' - elsif building_type == 'Hospital' + when 'Hospital' building_type = 'Hospital' - elsif building_type == 'LargeHotel' + when 'LargeHotel' building_type = 'LrgHotel' - elsif building_type == 'LargeOffice' + when 'LargeOffice' building_type = 'LrgOffice' - elsif building_type == 'MediumOffice' + when 'MediumOffice' building_type = 'MedOffice' - elsif building_type == 'MidriseApartment' + when 'MidriseApartment' building_type = 'MidApt' - elsif building_type == 'HighriseApartment' + when 'HighriseApartment' building_type = 'HighApt' - elsif building_type == 'Office' + when 'Office' building_type = 'Office' - elsif building_type == 'Outpatient' + when 'Outpatient' building_type = 'Outpatient' - elsif building_type == 'PrimarySchool' + when 'PrimarySchool' building_type = 'PriSchl' - elsif building_type == 'QuickServiceRestaurant' + when 'QuickServiceRestaurant' building_type = 'QckSrvRest' - elsif building_type == 'Retail' + when 'Retail' building_type = 'Retail' - elsif building_type == 'SecondarySchool' + when 'SecondarySchool' building_type = 'SecSchl' - elsif building_type == 'SmallHotel' + when 'SmallHotel' building_type = 'SmHotel' - elsif building_type == 'SmallOffice' + when 'SmallOffice' building_type = 'SmOffice' - elsif building_type == 'StripMall' + when 'StripMall' building_type = 'StMall' - elsif building_type == 'SuperMarket' + when 'SuperMarket' building_type = 'SpMarket' - elsif building_type == 'Warehouse' + when 'Warehouse' building_type = 'Warehouse' - elsif building_type == 'SmallDataCenterLowITE' + when 'SmallDataCenterLowITE' building_type = 'SmDCLowITE' - elsif building_type == 'SmallDataCenterHighITE' + when 'SmallDataCenterHighITE' building_type = 'SmDCHighITE' - elsif building_type == 'LargeDataCenterLowITE' + when 'LargeDataCenterLowITE' building_type = 'LrgDCLowITE' - elsif building_type == 'LargeDataCenterHighITE' + when 'LargeDataCenterHighITE' building_type = 'LrgDCHighITE' - elsif building_type == 'Laboratory' + when 'Laboratory' building_type = 'Laboratory' - elsif building_type == 'TallBuilding' + when 'TallBuilding' building_type = 'TallBldg' - elsif building_type == 'SuperTallBuilding' + when 'SuperTallBuilding' building_type = 'SpTallBldg' end parts = [template] @@ -5028,11 +5031,11 @@ possible_climate_zone_sets << climate_zone_set['name'] end end # Check the results - if possible_climate_zone_sets.size.zero? + if possible_climate_zone_sets.empty? OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Model', "Cannot find a climate zone set containing #{climate_zone}. Make sure to use ASHRAE standards with ASHRAE climate zones and DEER or CA Title 24 standards with CEC climate zones.") elsif possible_climate_zone_sets.size > 2 OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Model', "Found more than 2 climate zone sets containing #{climate_zone}; will return last matching climate zone set.") end @@ -5281,21 +5284,19 @@ avg_unit_size = OpenStudio.convert(354.2, '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) + if ((stds_bldg_type == 'Hospital') && ['PatRoom', 'ICU_PatRm', 'ICU_Open'].include?(stds_space_type)) || + ((stds_bldg_type == 'Hsp') && ['PatientRoom', 'HspSurgOutptLab', 'HspNursing'].include?(stds_space_type)) num_beds = num_people - elsif stds_bldg_type == 'Hsp' && ['PatientRoom', 'HspSurgOutptLab', 'HspNursing'].include?(stds_space_type) - num_beds = num_people end # determine number of students - if ['PrimarySchool', 'SecondarySchool'].include?(stds_bldg_type) && stds_space_type == 'Classroom' + if ['PrimarySchool', 'SecondarySchool', 'EPr', 'ESe', 'ERC', 'EUn', 'ECC'].include?(stds_bldg_type) && + (stds_space_type == 'Classroom') num_students += num_people * ((typical_class_size - 1.0) / typical_class_size) - elsif ['EPr', 'ESe', 'ERC', 'EUn', 'ECC'].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 @@ -5460,14 +5461,15 @@ # @param model [OpenStudio::Model::Model] OpenStudio model object # @return [String] the ventilation method, either Sum or Maximum def model_ventilation_method(model) building_data = model_get_building_properties(model) building_type = building_data['building_type'] - if building_type != 'Laboratory' # Laboratory has multiple criteria on ventilation, pick the greatest - ventilation_method = 'Sum' - else + if building_type == 'Laboratory' + # Laboratory has multiple criteria on ventilation, pick the greatest ventilation_method = 'Maximum' + else + ventilation_method = 'Sum' end return ventilation_method end @@ -5953,27 +5955,25 @@ # Determine return configuration if (heating_equipment_type == 'ZoneHVACComponent') && (cooling_equipment_type == 'ZoneHVACComponent') return_air_type = 'ducted_return_or_direct_to_unit' else # Check heating air loop first - unless heating_equipment.nil? - if heating_equipment.to_StraightComponent.is_initialized - air_loop = heating_equipment.to_StraightComponent.get.airLoopHVAC.get - return_plenum = air_loop_hvac_return_air_plenum(air_loop) - return_air_type = return_plenum.nil? ? 'ducted_return_or_direct_to_unit' : 'return_plenum' - return_plenum = return_plenum.nil? ? nil : return_plenum.name.to_s - end + if !heating_equipment.nil? && heating_equipment.to_StraightComponent.is_initialized + air_loop = heating_equipment.to_StraightComponent.get.airLoopHVAC.get + return_plenum = air_loop_hvac_return_air_plenum(air_loop) + return_air_type = return_plenum.nil? ? 'ducted_return_or_direct_to_unit' : 'return_plenum' + return_plenum = return_plenum.nil? ? nil : return_plenum.name.to_s end # Check cooling air loop second; Assume that return air plenum is the dominant case - unless cooling_equipment.nil? - if (return_air_type != 'return_plenum') && cooling_equipment.to_StraightComponent.is_initialized - air_loop = cooling_equipment.to_StraightComponent.get.airLoopHVAC.get - return_plenum = air_loop_hvac_return_air_plenum(air_loop) - return_air_type = return_plenum.nil? ? 'ducted_return_or_direct_to_unit' : 'return_plenum' - return_plenum = return_plenum.nil? ? nil : return_plenum.name.to_s - end + if !cooling_equipment.nil? && + (return_air_type != 'return_plenum') && + cooling_equipment.to_StraightComponent.is_initialized + air_loop = cooling_equipment.to_StraightComponent.get.airLoopHVAC.get + return_plenum = air_loop_hvac_return_air_plenum(air_loop) + return_air_type = return_plenum.nil? ? 'ducted_return_or_direct_to_unit' : 'return_plenum' + return_plenum = return_plenum.nil? ? nil : return_plenum.name.to_s end end # Catch all if return_air_type.nil? @@ -6042,10 +6042,10 @@ if zone_return_air_type[return_type] > return_air_types_score return_air_type = return_type return_air_types_score = zone_return_air_type[return_type] end if return_air_type == 'return_plenum' - zone_return_air_type['plenum'].keys.each do |p| + zone_return_air_type['plenum'].each_key do |p| if zone_return_air_type['plenum'][p] > plenum_score plenum = p plenum_score = zone_return_air_type['plenum'][p] end end