lib/openstudio-standards/standards/Standards.Model.rb in openstudio-standards-0.1.15 vs lib/openstudio-standards/standards/Standards.Model.rb in openstudio-standards-0.2.0.rc1
- old
+ new
@@ -1,348 +1,268 @@
+class Standard
+ attr_accessor :space_multiplier_map
-# Loads the openstudio standards dataset.
-#
-# @return [Hash] a hash of standards data
-def load_openstudio_standards_json
- standards_files = []
- standards_files << 'OpenStudio_Standards_boilers.json'
- standards_files << 'OpenStudio_Standards_chillers.json'
- standards_files << 'OpenStudio_Standards_climate_zone_sets.json'
- standards_files << 'OpenStudio_Standards_climate_zones.json'
- standards_files << 'OpenStudio_Standards_construction_properties.json'
- standards_files << 'OpenStudio_Standards_construction_sets.json'
- standards_files << 'OpenStudio_Standards_constructions.json'
- standards_files << 'OpenStudio_Standards_curve_bicubics.json'
- standards_files << 'OpenStudio_Standards_curve_biquadratics.json'
- standards_files << 'OpenStudio_Standards_curve_cubics.json'
- standards_files << 'OpenStudio_Standards_curve_quadratics.json'
- standards_files << 'OpenStudio_Standards_ground_temperatures.json'
- standards_files << 'OpenStudio_Standards_heat_pumps_heating.json'
- standards_files << 'OpenStudio_Standards_heat_pumps.json'
- standards_files << 'OpenStudio_Standards_materials.json'
- standards_files << 'OpenStudio_Standards_motors.json'
- standards_files << 'OpenStudio_Standards_prototype_inputs.json'
- 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"
- standards_data = {}
- standards_files.sort.each do |standards_file|
- temp = ""
- begin
- temp = load_resource_relative("../../../data/standards/#{standards_file}", 'r:UTF-8')
- rescue NoMethodError
- File.open("#{standards_data_dir}/#{standards_file}", 'r:UTF-8') do |f|
- temp = f.read
- end
- end
- file_hash = JSON.load(temp)
- standards_data = standards_data.merge(file_hash)
+ def define_space_multiplier
+ return @space_multiplier_map
end
- # Check that standards data was loaded
- if standards_data.keys.size.zero?
- OpenStudio.logFree(OpenStudio::Error, 'OpenStudio Standards JSON data was not loaded correctly.')
- end
+ # @!group Model
- return standards_data
-end
-
-# open the class to add methods to apply HVAC efficiency standards
-class OpenStudio::Model::Model
- # Load the helper libraries for getting the autosized
- # values for each type of model object.
- require_relative 'Standards.AirTerminalSingleDuctParallelPIUReheat'
- require_relative 'Standards.BuildingStory'
- require_relative 'Standards.Fan'
- require_relative 'Standards.FanConstantVolume'
- require_relative 'Standards.FanVariableVolume'
- require_relative 'Standards.FanOnOff'
- require_relative 'Standards.FanZoneExhaust'
- require_relative 'Standards.ChillerElectricEIR'
- require_relative 'Standards.CoilDX'
- require_relative 'Standards.CoilCoolingDXTwoSpeed'
- require_relative 'Standards.CoilCoolingDXSingleSpeed'
- require_relative 'Standards.CoilHeatingDXSingleSpeed'
- require_relative 'Standards.BoilerHotWater'
- require_relative 'Standards.AirLoopHVAC'
- require_relative 'Standards.WaterHeaterMixed'
- require_relative 'Standards.Space'
- require_relative 'Standards.Construction'
- require_relative 'Standards.ThermalZone'
- require_relative 'Standards.Surface'
- require_relative 'Standards.SubSurface'
- require_relative 'Standards.ScheduleRuleset'
- require_relative 'Standards.ScheduleConstant'
- require_relative 'Standards.ScheduleCompact'
- require_relative 'Standards.SpaceType'
- require_relative 'Standards.PlanarSurface'
- require_relative 'Standards.PlantLoop'
- require_relative 'Standards.Pump'
- require_relative 'Standards.PumpConstantSpeed'
- require_relative 'Standards.PumpVariableSpeed'
- require_relative 'Standards.AirTerminalSingleDuctVAVReheat'
- require_relative 'Standards.CoolingTower'
- require_relative 'Standards.CoolingTowerSingleSpeed'
- require_relative 'Standards.CoolingTowerTwoSpeed'
- require_relative 'Standards.CoolingTowerVariableSpeed'
- require_relative 'Standards.ZoneHVACComponent'
- require_relative 'Standards.HeatExchangerSensLat'
- require_relative 'Standards.HeaderedPumpsConstantSpeed'
- require_relative 'Standards.HeaderedPumpsVariableSpeed'
-
# Creates a Performance Rating Method (aka Appendix G aka LEED) baseline building model
# based on the inputs currently in the model.
# the current model with this model.
#
# @note Per 90.1, the Performance Rating Method "does NOT offer an alternative
# compliance path for minimum standard compliance." This means you can't use
# this method for code compliance to get a permit.
# @param building_type [String] the building type
- # @param template [String] the template. Valid choices are 90.1-2004, 90.1-2007, 90.1-2010, 90.1-2013.
# @param climate_zone [String] the climate zone
# @param custom [String] the custom logic that will be applied during baseline creation. Valid choices are 'Xcel Energy CO EDA' or '90.1-2007 with addenda dn'.
# If nothing is specified, no custom logic will be applied; the process will follow the template logic explicitly.
# @param sizing_run_dir [String] the directory where the sizing runs will be performed
# @param debug [Boolean] If true, will report out more detailed debugging output
# @return [Bool] returns true if successful, false if not
- def create_prm_baseline_building(building_type, template, climate_zone, custom = nil, sizing_run_dir = Dir.pwd, debug = false)
- lookup_building_type = get_lookup_name(building_type)
+ def model_create_prm_baseline_building(model, building_type, climate_zone, custom = nil, sizing_run_dir = Dir.pwd, debug = false)
+ model.getBuilding.setName("#{template}-#{building_type}-#{climate_zone} PRM baseline created: #{Time.new}")
- getBuilding.setName("#{template}-#{building_type}-#{climate_zone} PRM baseline created: #{Time.new}")
-
# Remove external shading devices
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', '*** Removing External Shading Devices ***')
- remove_external_shading_devices
+ model_remove_external_shading_devices(model)
# Reduce the WWR and SRR, if necessary
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', '*** Adjusting Window and Skylight Ratios ***')
- apply_prm_baseline_window_to_wall_ratio(template, climate_zone)
- apply_prm_baseline_skylight_to_roof_ratio(template)
+ model_apply_prm_baseline_window_to_wall_ratio(model, climate_zone)
+ model_apply_prm_baseline_skylight_to_roof_ratio(model)
# Assign building stories to spaces in the building
# where stories are not yet assigned.
- assign_spaces_to_stories
+ model_assign_spaces_to_stories(model)
# Modify the internal loads in each space type,
# keeping user-defined schedules.
- OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.Model', '*** Changing Lighting Loads ***')
- getSpaceTypes.sort.each do |space_type|
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', '*** Changing Lighting Loads ***')
+ model.getSpaceTypes.sort.each do |space_type|
set_people = false
set_lights = true
set_electric_equipment = false
set_gas_equipment = false
set_ventilation = false
set_infiltration = false
- space_type.apply_internal_loads(template, set_people, set_lights, set_electric_equipment, set_gas_equipment, set_ventilation, set_infiltration)
+ space_type_apply_internal_loads(space_type, set_people, set_lights, set_electric_equipment, set_gas_equipment, set_ventilation, set_infiltration)
end
# If any of the lights are missing schedules, assign an
# always-off schedule to those lights. This is assumed to
# be the user's intent in the proposed model.
- getLightss.each do |lights|
+ model.getLightss.sort.each do |lights|
if lights.schedule.empty?
- lights.setSchedule(alwaysOffDiscreteSchedule)
+ lights.setSchedule(model.alwaysOffDiscreteSchedule)
end
end
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', '*** Adding Daylighting Controls ***')
# Run a sizing run to calculate VLT for layer-by-layer windows.
- # Only necessary for 90.1-2010 daylighting control determination.
- if template == '90.1-2010'
- if runSizingRun("#{sizing_run_dir}/SRVLT") == false
+ if model_create_prm_baseline_building_requires_vlt_sizing_run(model)
+ if model_run_sizing_run(model, "#{sizing_run_dir}/SRVLT") == false
return false
end
end
# Add daylighting controls to each space
- getSpaces.sort.each do |space|
- added = space.add_daylighting_controls(template, false, false)
+ model.getSpaces.sort.each do |space|
+ added = space_add_daylighting_controls(space, false, false)
end
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', '*** Applying Baseline Constructions ***')
# Modify some of the construction types as necessary
- apply_prm_construction_types(template)
+ model_apply_prm_construction_types(model)
# Set the construction properties of all the surfaces in the model
- apply_standard_constructions(template, climate_zone)
+ model_apply_standard_constructions(model, climate_zone)
# Get the groups of zones that define the
# baseline HVAC systems for later use.
# This must be done before removing the HVAC systems
# because it requires knowledge of proposed HVAC fuels.
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', '*** Grouping Zones by Fuel Type and Occupancy Type ***')
- sys_groups = prm_baseline_system_groups(template, custom)
+ sys_groups = model_prm_baseline_system_groups(model, custom)
# Remove all HVAC from model,
# excluding service water heating
- remove_prm_hvac
+ model_remove_prm_hvac(model)
# Modify the service water heating loops
# per the baseline rules
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', '*** Cleaning up Service Water Heating Loops ***')
- apply_baseline_swh_loops(template, building_type)
+ model_apply_baseline_swh_loops(model, building_type)
# Determine the baseline HVAC system type for each of
# the groups of zones and add that system type.
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', '*** Adding Baseline HVAC Systems ***')
sys_groups.each do |sys_group|
# Determine the primary baseline system type
- system_type = prm_baseline_system_type(template,
- climate_zone,
- sys_group['occ'],
- sys_group['fuel'],
- sys_group['area_ft2'],
- sys_group['stories'],
- custom)
+ system_type = model_prm_baseline_system_type(model,
+ climate_zone,
+ sys_group['occ'],
+ sys_group['fuel'],
+ sys_group['area_ft2'],
+ sys_group['stories'],
+ custom)
sys_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
# Add the system type for these zones
- add_prm_baseline_system(template,
- system_type[0],
- system_type[1],
- system_type[2],
- system_type[3],
- sys_group['zones'])
+ model_add_prm_baseline_system(model,
+ system_type[0],
+ system_type[1],
+ system_type[2],
+ system_type[3],
+ sys_group['zones'])
end
# Set the zone sizing SAT for each zone in the model
- getThermalZones.each(&:apply_prm_baseline_supply_temperatures)
+ model.getThermalZones.each do |zone|
+ thermal_zone_apply_prm_baseline_supply_temperatures(zone)
+ end
# Set the system sizing properties based on the zone sizing information
- getAirLoopHVACs.each(&:apply_prm_sizing_temperatures)
+ model.getAirLoopHVACs.each do |air_loop|
+ air_loop_hvac_apply_prm_sizing_temperatures(air_loop)
+ end
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', '*** Applying Baseline HVAC System Controls ***')
# SAT reset, economizers
- getAirLoopHVACs.sort.each do |air_loop|
- air_loop.apply_prm_baseline_controls(template, climate_zone)
+ model.getAirLoopHVACs.sort.each do |air_loop|
+ air_loop_hvac_apply_prm_baseline_controls(air_loop, climate_zone)
end
# Apply the minimum damper positions, assuming no DDC control of VAV terminals
- getAirLoopHVACs.sort.each do |air_loop|
- air_loop.apply_minimum_vav_damper_positions(template, false)
+ model.getAirLoopHVACs.sort.each do |air_loop|
+ air_loop_hvac_apply_minimum_vav_damper_positions(air_loop, false)
end
# Apply the baseline system temperatures
- getPlantLoops.sort.each do |plant_loop|
+ model.getPlantLoops.sort.each do |plant_loop|
# Skip the SWH loops
- next if plant_loop.swh_loop?
- plant_loop.apply_prm_baseline_temperatures(template)
+ next if plant_loop_swh_loop?(plant_loop)
+ plant_loop_apply_prm_baseline_temperatures(plant_loop)
end
# Set the heating and cooling sizing parameters
- apply_prm_sizing_parameters
+ model_apply_prm_sizing_parameters(model)
# Run sizing run with the HVAC equipment
- if runSizingRun("#{sizing_run_dir}/SR1") == false
+ if model_run_sizing_run(model, "#{sizing_run_dir}/SR1") == false
return false
end
# If there are any multizone systems, reset damper positions
# to achieve a 60% ventilation effectiveness minimum for the system
# following the ventilation rate procedure from 62.1
- apply_multizone_vav_outdoor_air_sizing(template)
+ model_apply_multizone_vav_outdoor_air_sizing(model)
# Set the baseline fan power for all airloops
- getAirLoopHVACs.sort.each do |air_loop|
- air_loop.apply_prm_baseline_fan_power(template)
+ model.getAirLoopHVACs.sort.each do |air_loop|
+ air_loop_hvac_apply_prm_baseline_fan_power(air_loop)
end
# Set the baseline fan power for all zone HVAC
- getZoneHVACComponents.sort.each do |zone_hvac|
- zone_hvac.apply_prm_baseline_fan_power(template)
+ model.getZoneHVACComponents.sort.each do |zone_hvac|
+ zone_hvac_component_apply_prm_baseline_fan_power(zone_hvac)
end
# Set the baseline number of boilers and chillers
- getPlantLoops.sort.each do |plant_loop|
+ model.getPlantLoops.sort.each do |plant_loop|
# Skip the SWH loops
- next if plant_loop.swh_loop?
- plant_loop.apply_prm_number_of_boilers(template)
- plant_loop.apply_prm_number_of_chillers(template)
+ next if plant_loop_swh_loop?(plant_loop)
+ plant_loop_apply_prm_number_of_boilers(plant_loop)
+ plant_loop_apply_prm_number_of_chillers(plant_loop)
end
# Set the baseline number of cooling towers
# Must be done after all chillers are added
- getPlantLoops.sort.each do |plant_loop|
+ model.getPlantLoops.sort.each do |plant_loop|
# Skip the SWH loops
- next if plant_loop.swh_loop?
- plant_loop.apply_prm_number_of_cooling_towers(template)
+ next if plant_loop_swh_loop?(plant_loop)
+ plant_loop_apply_prm_number_of_cooling_towers(plant_loop)
end
# Run sizing run with the new chillers, boilers, and
# cooling towers to determine capacities
- if runSizingRun("#{sizing_run_dir}/SR2") == false
+ if model_run_sizing_run(model, "#{sizing_run_dir}/SR2") == false
return false
end
# Set the pumping control strategy and power
# Must be done after sizing components
- getPlantLoops.sort.each do |plant_loop|
+ model.getPlantLoops.sort.each do |plant_loop|
# Skip the SWH loops
- next if plant_loop.swh_loop?
- plant_loop.apply_prm_baseline_pump_power(template)
- plant_loop.apply_prm_baseline_pumping_type(template)
+ next if plant_loop_swh_loop?(plant_loop)
+ plant_loop_apply_prm_baseline_pump_power(plant_loop)
+ plant_loop_apply_prm_baseline_pumping_type(plant_loop)
end
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', '*** Applying Prescriptive HVAC Controls and Equipment Efficiencies ***')
# Apply the HVAC efficiency standard
- apply_hvac_efficiency_standard(template, climate_zone)
+ model_apply_hvac_efficiency_standard(model, climate_zone)
+ # Fix EMS references.
+ # Temporary workaround for OS issue #2598
+ model_temp_fix_ems_references(model)
+
# Delete all the unused curves
- getCurves.sort.each do |curve|
- if curve.parent.empty?
- # OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.Model', "#{curve.name} is unused; it will be removed.")
- curve.remove
+ model.getCurves.sort.each do |curve|
+ if curve.directUseCount == 0
+ OpenStudio::logFree(OpenStudio::Debug, 'openstudio.standards.Model', "#{curve.name} is unused; it will be removed.")
+ model.removeObject(curve.handle)
end
end
# TODO: turn off self shading
# Set Solar Distribution to MinimalShadowing... problem is when you also have detached shading such as surrounding buildings etc
# It won't be taken into account, while it should: only self shading from the building itself should be turned off but to my knowledge there isn't a way to do this in E+
model_status = 'final'
- save(OpenStudio::Path.new("#{sizing_run_dir}/#{model_status}.osm"), true)
+ model.save(OpenStudio::Path.new("#{sizing_run_dir}/#{model_status}.osm"), true)
# Translate to IDF and save for debugging
forward_translator = OpenStudio::EnergyPlus::ForwardTranslator.new
- idf = forward_translator.translateModel(self)
+ idf = forward_translator.translateModel(model)
idf_path = OpenStudio::Path.new("#{sizing_run_dir}/#{model_status}.idf")
idf.save(idf_path, true)
return true
end
+ # Determine if there needs to be a sizing run after constructions
+ # are added so that EnergyPlus can calculate the VLTs of
+ # layer-by-layer glazing constructions. These VLT values are
+ # needed for the daylighting controls logic for some templates.
+ def model_create_prm_baseline_building_requires_vlt_sizing_run(model)
+ return false # Not required for most templates
+ end
+
# Determine the residential and nonresidential floor areas
# based on the space type properties for each space.
# For spaces with no space type, assume nonresidential.
#
# @return [Hash] keys are 'residential' and 'nonresidential', units are m^2
- def residential_and_nonresidential_floor_areas(template)
+ def model_residential_and_nonresidential_floor_areas(model)
res_area_m2 = 0
nonres_area_m2 = 0
- getSpaces.each do |space|
- if space.residential?(template)
+ model.getSpaces.sort.each do |space|
+ if thermal_zone_residential?(space)
res_area_m2 += space.floorArea
else
nonres_area_m2 += space.floorArea
end
end
@@ -356,11 +276,11 @@
# floor multiplier and increase the number of stories accordingly.
# Stories do not have to be contiguous.
#
# @param zones [Array<OpenStudio::Model::ThermalZone>] an array of zones
# @return [Integer] the number of stories spanned
- def num_stories_spanned(zones)
+ def model_num_stories_spanned(model, zones)
# Get the story object for all zones
stories = []
zones.each do |zone|
zone.spaces.each do |space|
story = space.buildingStory
@@ -373,34 +293,34 @@
stories = stories.uniq
# Tally up stories including multipliers
num_stories = 0
stories.each do |story|
- num_stories += story.floor_multiplier
+ num_stories += building_story_floor_multiplier(story)
end
return num_stories
end
# Categorize zones by occupancy type and fuel type,
# where the types depend on the standard.
#
# @return [Array<Hash>] an array of hashes, one for each zone,
# with the keys 'zone', 'type' (occ type), 'fuel', and 'area'
- def zones_with_occ_and_fuel_type(template, custom)
+ def model_zones_with_occ_and_fuel_type(model, custom)
zones = []
- getThermalZones.sort.each do |zone|
+ model.getThermalZones.sort.each do |zone|
# Skip plenums
- if zone.plenum?
+ if thermal_zone_plenum?(zone)
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "Zone #{zone.name} is a plenum. It will not be assigned a baseline system.")
next
end
# Skip unconditioned zones
- heated = zone.heated?
- cooled = zone.cooled?
+ heated = thermal_zone_heated?(zone)
+ cooled = thermal_zone_cooled?(zone)
if !heated && !cooled
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "Zone #{zone.name} is unconditioned. It will not be assigned a baseline system.")
next
end
@@ -411,47 +331,32 @@
# Floor area
zn_hash['area'] = zone.floorArea
# Occupancy type
- zn_hash['occ'] = zone.occupancy_type(template)
+ zn_hash['occ'] = thermal_zone_occupancy_type(zone)
# Fuel type
- zn_hash['fuel'] = zone.fossil_or_electric_type(custom)
+ zn_hash['fuel'] = thermal_zone_fossil_or_electric_type(zone, custom)
zones << zn_hash
end
return zones
end
# Determine the dominant and exceptional areas of the
# building based on fuel types and occupancy types.
#
- # @param template [String] the template. Valid choices are 90.1-2004, 90.1-2007, 90.1-2010, 90.1-2013.
# @return [Array<Hash>] an array of hashes of area information,
# with keys area_ft2, type, fuel, and zones (an array of zones)
- def prm_baseline_system_groups(template, custom)
-
+ def model_prm_baseline_system_groups(model, custom)
# Define the minimum area for the
# exception that allows a different
# system type in part of the building.
- # This is common across different versions
- # of 90.1
- exception_min_area_ft2 = nil
- case template
- when '90.1-2004', '90.1-2007', '90.1-2010'
- exception_min_area_ft2 = 20_000
- when '90.1-2013'
- exception_min_area_ft2 = 20_000
- # Customization - Xcel EDA Program Manual 2014
- # 3.2.1 Mechanical System Selection ii
- if custom == 'Xcel Energy CO EDA'
- exception_min_area_ft2 = 5000
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "Customization; per Xcel EDA Program Manual 2014 3.2.1 Mechanical System Selection ii, minimum area for non-predominant conditions reduced to #{exception_min_area_ft2} ft2.")
- end
- end
+ exception_min_area_m2 = model_prm_baseline_system_group_minimum_area(model, custom)
+ exception_min_area_ft2 = OpenStudio.convert(exception_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
@@ -462,15 +367,15 @@
# Fuel types are:
# fossil
# electric
# (and for Xcel Energy CO EDA)
# fossilandelectric
- zones = zones_with_occ_and_fuel_type(template, custom)
+ zones = model_zones_with_occ_and_fuel_type(model, custom)
# Ensure that there is at least one conditioned zone
if zones.size.zero?
- 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.")
+ 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
type_to_area = Hash.new { 0.0 }
@@ -488,33 +393,32 @@
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
+ # 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 > exception_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 exception area of #{exception_min_area_ft2.round} ft2. It will be assigned a separate HVAC system type.")
- # Otherwise, add the zones back to the dominant group.
+ # 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]
# Inside of each remaining occupancy group,
@@ -536,12 +440,12 @@
# Determine the dominant fuel type
# from the subset of the dominant area type zones
fuel_to_area = Hash.new { 0.0 }
zones_grouped_by_fuel = dom_occ_zns.group_by { |z| z['fuel'] }
- zones_grouped_by_fuel.each do |fuel, zns|
- zns.each do |zn|
+ zones_grouped_by_fuel.each do |fuel, zns_by_fuel|
+ zns_by_fuel.each do |zn|
fuel_to_area[fuel] += zn['area']
end
end
sorted_by_area = fuel_to_area.sort_by { |k, v| v }.reverse
@@ -551,11 +455,11 @@
# go to the next biggest
if dom_fuel == 'unconditioned'
if sorted_by_area.size > 1
dom_fuel = sorted_by_area[1][0]
else
- OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Model', "The fuel type was not able to be determined for any zones in this model. Run with debug messages enabled to see possible reasons.")
+ OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Model', 'The fuel type was not able to be determined for any zones in this model. Run with debug messages enabled to see possible reasons.')
return []
end
end
# Get the dominant fuel type group
@@ -569,70 +473,67 @@
dom_fuel_group['zones'] += nondom_occ_zns
# 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
+ # If they are not, add the zones in that group
# back to the dominant occupancy type group.
- zones_grouped_by_fuel.each do |fuel_type, zns|
+ zones_grouped_by_fuel.each do |fuel_type, zns_by_fuel|
# Skip the dominant occupancy type
next if fuel_type == dom_fuel
# Add up the floor area of the group
area_m2 = 0
- zns.each do |zn|
+ zns_by_fuel.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 > exception_min_area_ft2
group = {}
group['occ'] = occ_type
group['fuel'] = fuel_type
- group['zones'] = zns
+ group['zones'] = zns_by_fuel
occ_and_fuel_groups << group
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "The portion of the building with an occupancy type of #{occ_type} and fuel type of #{fuel_type} is bigger than the minimum exception area of #{exception_min_area_ft2.round} ft2. It will be assigned a separate HVAC system type.")
- # Otherwise, add the zones back to the dominant group.
+ # Otherwise, add the zones back to the dominant group.
else
- dom_fuel_group['zones'] += zns
+ dom_fuel_group['zones'] += zns_by_fuel
end
-
end
# Add the dominant occupancy group to the list
- occ_and_fuel_groups << dom_fuel_group
-
+ occ_and_fuel_groups << dom_fuel_group
end
# Moved heated-only zones into their own groups.
# Per the PNNL PRM RM, this must be done AFTER
# the dominant occ and fuel types are determined
# so that heated-only zone areas are part of
# the determination.
final_groups = []
occ_and_fuel_groups.each do |gp|
-
# Skip unconditioned groups
next if gp['fuel'] == 'unconditioned'
heated_only_zones = []
heated_cooled_zones = []
gp['zones'].each do |zn|
- if zn['zone'].heated? && !zn['zone'].cooled?
+ if thermal_zone_heated?(zn['zone']) && !thermal_zone_cooled?(zn['zone'])
heated_only_zones << zn
else
heated_cooled_zones << zn
end
end
gp['zones'] = heated_cooled_zones
# Add the group (less unheated zones) to the final list
final_groups << gp
-
+
# If there are any heated-only zones, create
# a new group for them.
- if heated_only_zones.size > 0
+ unless heated_only_zones.empty?
htd_only_group = {}
htd_only_group['occ'] = 'heatedonly'
htd_only_group['fuel'] = gp['fuel']
htd_only_group['zones'] = heated_only_zones
final_groups << htd_only_group
@@ -651,22 +552,21 @@
area_ft2 = OpenStudio.convert(area_m2, 'm^2', 'ft^2').get
gp['area_ft2'] = area_ft2
gp['zones'] = gp_zns
end
- # TODO Remove the secondary zones before
+ # TODO: Remove the secondary zones before
# determining the area used to pick the HVAC
# system, per PNNL PRM RM
-
-
+
# If there is any district heating or district cooling
# in the proposed building, the heating and cooling
# fuels in the entire baseline building are changed
# for the purposes of HVAC system assignment
all_htg_fuels = []
all_clg_fuels = []
- getThermalZones.each do |zone|
+ model.getThermalZones.sort.each do |zone|
all_htg_fuels += zone.heating_fuels
all_clg_fuels += zone.cooling_fuels
end
purchased_heating = false
@@ -684,19 +584,19 @@
# Categorize
district_fuel = nil
if purchased_heating && purchased_cooling
district_fuel = 'purchasedheatandcooling'
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "The proposed model included purchased heating and cooling. All baseline building system selection will be based on this information.")
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', 'The proposed model included purchased heating and cooling. All baseline building system selection will be based on this information.')
elsif purchased_heating && !purchased_cooling
district_fuel = 'purchasedheat'
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "The proposed model included purchased heating. All baseline building system selection will be based on this information.")
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', 'The proposed model included purchased heating. All baseline building system selection will be based on this information.')
elsif !purchased_heating && purchased_cooling
district_fuel = 'purchasedcooling'
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "The proposed model included purchased cooling. All baseline building system selection will be based on this information.")
- end
-
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', 'The proposed model included purchased cooling. All baseline building system selection will be based on this information.')
+ end
+
# Change the fuel in all final groups
# if district systems were found.
if district_fuel
final_groups.each do |gp|
gp['fuel'] = district_fuel
@@ -705,11 +605,11 @@
# Determine the number of stories spanned
# by each group and report out info.
final_groups.each do |group|
# Determine the number of stories this group spans
- num_stories = num_stories_spanned(group['zones'])
+ num_stories = model_num_stories_spanned(model, group['zones'])
group['stories'] = num_stories
# Report out the final grouping
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "Final system type group: occ = #{group['occ']}, fuel = #{group['fuel']}, area = #{group['area_ft2'].round} ft2, num stories = #{group['stories']}, zones:")
group['zones'].sort.each_slice(5) do |zone_list|
zone_names = []
@@ -721,14 +621,22 @@
end
return final_groups
end
+ # Determines the area of the building above which point
+ # the non-dominant area type gets it's own HVAC system type.
+ # @return [Double] the minimum area (m^2)
+ def model_prm_baseline_system_group_minimum_area(model, custom)
+ exception_min_area_ft2 = 20_000
+ exception_min_area_m2 = OpenStudio.convert(exception_min_area_ft2, 'ft^2', 'm^2').get
+ return exception_min_area_m2
+ end
+
# Determine the baseline system type given the
# inputs. Logic is different for different standards.
#
- # @param template [String] Valid choices are 90.1-2004,
# 90.1-2007, 90.1-2010, 90.1-2013
# @param area_type [String] Valid choices are residential,
# nonresidential, and heatedonly
# @param fuel_type [String] Valid choices are
# electric, fossil, fossilandelectric,
@@ -737,131 +645,20 @@
# @param num_stories [Integer] Number of stories
# @return [String] The system type. Possibilities are
# PTHP, PTAC, PSZ_AC, PSZ_HP, PVAV_Reheat, PVAV_PFP_Boxes,
# VAV_Reheat, VAV_PFP_Boxes, Gas_Furnace, Electric_Furnace
# @todo add 90.1-2013 systems 11-13
- def prm_baseline_system_type(template, climate_zone, area_type, fuel_type, area_ft2, num_stories, custom)
+ def model_prm_baseline_system_type(model, climate_zone, area_type, fuel_type, area_ft2, num_stories, custom)
# [type, central_heating_fuel, zone_heating_fuel, cooling_fuel]
system_type = [nil, nil, nil, nil]
- # Customization - Xcel EDA Program Manual 2014
- # Table 3.2.2 Baseline HVAC System Types
- if custom == 'Xcel Energy CO EDA'
- template = '90.1-2010'
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', 'Custom; per Xcel EDA Program Manual 2014 Table 3.2.2 Baseline HVAC System Types, the 90.1-2010 lookup for HVAC system types shall be used.')
- end
- if custom == "90.1-2007 with addenda dn"
- template = '90.1-2010'
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', 'Custom; per Addenda dn of 90.1-2007, System 10 and 11 (same as system 9 and 10 in 90.1-2010) will be used for heated only space.')
- end
-
# Get the row from TableG3.1.1A
- sys_num = nil
- case template
- when '90.1-2004', '90.1-2007'
- # Set the limit differently for
- # different codes
- limit_ft2 = 25_000
- limit_ft2 = 75_000 if template == '90.1-2004'
+ sys_num = model_prm_baseline_system_number(model, climate_zone, area_type, fuel_type, area_ft2, num_stories, custom)
- # Warn about heated only
- if area_type == 'heatedonly'
- OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Model', "Per Table G3.1.10.d, '(In the proposed building) Where no cooling system exists or no cooling system has been specified, the cooling system shall be identical to the system modeled in the baseline building design.' This requires that you go back and add a cooling system to the proposed model. This code cannot do that for you; you must do it manually.")
- end
+ # Modify the fuel type if called for by the standard
+ fuel_type = model_prm_baseline_system_change_fuel_type(model, fuel_type, climate_zone, custom)
- case area_type
- when 'residential'
- sys_num = '1_or_2'
- when 'nonresidential', 'heatedonly'
- # nonresidential and 3 floors or less and <25,000 ft2
- if num_stories <= 3 && area_ft2 < limit_ft2
- sys_num = '3_or_4'
- # nonresidential and 4 or 5 floors or 5 floors or less and 25,000 ft2 to 150,000 ft2
- elsif ((num_stories == 4 || num_stories == 5) && area_ft2 < limit_ft2) || (num_stories <= 5 && (area_ft2 >= limit_ft2 && area_ft2 <= 150_000))
- sys_num = '5_or_6'
- # nonresidential and more than 5 floors or >150,000 ft2
- elsif num_stories >= 5 || area_ft2 > 150_000
- sys_num = '7_or_8'
- end
- end
-
- when '90.1-2010'
-
- limit_ft2 = 25_000
-
- # Customization for Xcel EDA.
- # No special retail category
- # for regular 90.1-2010.
- unless custom == 'Xcel Energy CO EDA'
- if area_type == 'retail'
- area_type = 'nonresidential'
- end
- end
-
- case area_type
- when 'residential'
- sys_num = '1_or_2'
- when 'nonresidential'
- # nonresidential and 3 floors or less and <25,000 ft2
- if num_stories <= 3 && area_ft2 < limit_ft2
- sys_num = '3_or_4'
- # nonresidential and 4 or 5 floors or 5 floors or less and 25,000 ft2 to 150,000 ft2
- elsif ((num_stories == 4 || num_stories == 5) && area_ft2 < limit_ft2) || (num_stories <= 5 && (area_ft2 >= limit_ft2 && area_ft2 <= 150_000))
- sys_num = '5_or_6'
- # nonresidential and more than 5 floors or >150,000 ft2
- elsif num_stories >= 5 || area_ft2 > 150_000
- sys_num = '7_or_8'
- end
- when 'heatedonly'
- sys_num = '9_or_10'
- when 'retail'
- # Should only be hit by Xcel EDA
- sys_num = '3_or_4'
- end
-
- when '90.1-2013'
-
- limit_ft2 = 25_000
-
- case area_type
- when 'residential'
- sys_num = '1_or_2'
- when 'nonresidential'
- # nonresidential and 3 floors or less and <25,000 ft2
- if num_stories <= 3 && area_ft2 < limit_ft2
- sys_num = '3_or_4'
- # nonresidential and 4 or 5 floors or 5 floors or less and 25,000 ft2 to 150,000 ft2
- elsif ((num_stories == 4 || num_stories == 5) && area_ft2 < limit_ft2) || (num_stories <= 5 && (area_ft2 >= limit_ft2 && area_ft2 <= 150_000))
- sys_num = '5_or_6'
- # nonresidential and more than 5 floors or >150,000 ft2
- elsif num_stories >= 5 || area_ft2 > 150_000
- sys_num = '7_or_8'
- end
- when 'heatedonly'
- sys_num = '9_or_10'
- when 'retail'
- sys_num = '3_or_4'
- end
-
- end
-
- # For 90.1-2013 the fuel type is determined based on climate zone.
- # Don't change the fuel if it purchased heating or cooling.
- if template == '90.1-2013'
- if fuel_type == 'electric' || fuel_type == 'fossil'
- case climate_zone
- when 'ASHRAE 169-2006-1A',
- 'ASHRAE 169-2006-2A',
- 'ASHRAE 169-2006-3A'
- fuel_type = 'electric'
- else
- fuel_type = 'fossil'
- end
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "Heating fuel is #{fuel_type} for 90.1-2013, climate zone #{climate_zone}. This is independent of the heating fuel type in the proposed building, per G3.1.1-3. This is different than previous versions of 90.1.")
- end
- end
-
# Define the lookup by row and by fuel type
sys_lookup = Hash.new { |h, k| h[k] = Hash.new(&h.default_proc) }
# fossil, fossil and electric, purchased heat, purchased heat and cooling
sys_lookup['1_or_2']['fossil'] = ['PTAC', 'NaturalGas', nil, 'Electricity']
@@ -910,76 +707,105 @@
end
return system_type
end
+ # Determines which system number is used
+ # for the baseline system. Default is 90.1-2004 approach.
+ # @return [String] the system number: 1_or_2, 3_or_4,
+ # 5_or_6, 7_or_8, 9_or_10
+ def model_prm_baseline_system_number(model, climate_zone, area_type, fuel_type, area_ft2, num_stories, custom)
+ sys_num = nil
+ # Set the area limit
+ limit_ft2 = 75_000
+
+ # Warn about heated only
+ if area_type == 'heatedonly'
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Model', "Per Table G3.1.10.d, '(In the proposed building) Where no cooling system exists or no cooling system has been specified, the cooling system shall be identical to the system modeled in the baseline building design.' This requires that you go back and add a cooling system to the proposed model. This code cannot do that for you; you must do it manually.")
+ end
+
+ case area_type
+ when 'residential'
+ sys_num = '1_or_2'
+ when 'nonresidential', 'heatedonly'
+ # nonresidential and 3 floors or less and <25,000 ft2
+ if num_stories <= 3 && area_ft2 < limit_ft2
+ sys_num = '3_or_4'
+ # nonresidential and 4 or 5 floors or 5 floors or less and 25,000 ft2 to 150,000 ft2
+ elsif ((num_stories == 4 || num_stories == 5) && area_ft2 < limit_ft2) || (num_stories <= 5 && (area_ft2 >= limit_ft2 && area_ft2 <= 150_000))
+ sys_num = '5_or_6'
+ # nonresidential and more than 5 floors or >150,000 ft2
+ elsif num_stories >= 5 || area_ft2 > 150_000
+ sys_num = '7_or_8'
+ end
+ end
+
+ return sys_num
+ end
+
+ # Change the fuel type based on climate zone, depending on the standard.
+ # Defaults to no change.
+ # @return [String] the revised fuel type
+ def model_prm_baseline_system_change_fuel_type(model, fuel_type, climate_zone, custom = nil)
+ return fuel_type # Don't change fuel type for most templates
+ end
+
# Add the specified baseline system type to the
# specified zons based on the specified template.
# For some multi-zone system types, the standards require
# identifying zones whose loads or schedules
# are outliers and putting these systems on separate
# single-zone systems. This method does that.
#
- # @param template [String] Valid choices are 90.1-2004,
- # 90.1-2007, 90.1-2010, 90.1-2013
- # @param area_type [String] Valid choices are residential,
- # nonresidential, and heatedonly
- # @param heating_fuel_type [String] Valid choices are
- # electric and fossil
- # @param area_ft2 [Double] Area in ft^2
- # @param num_stories [Integer] Number of stories
# @param system_type [String] The system type. Valid choices are
# PTHP, PTAC, PSZ_AC, PSZ_HP, PVAV_Reheat, PVAV_PFP_Boxes,
# VAV_Reheat, VAV_PFP_Boxes, Gas_Furnace, Electric_Furnace,
# which are also returned by the method
# OpenStudio::Model::Model.prm_baseline_system_type.
# @param main_heat_fuel [String] main heating fuel. Valid choices are
# Electricity, NaturalGas, DistrictHeating
- # @param main_heat_fuel [String] zone heating/reheat fuel. Valid choices are
+ # @param zone_heat_fuel [String] zone heating/reheat fuel. Valid choices are
# Electricity, NaturalGas, DistrictHeating
- # @param main_heat_fuel [String] cooling fuel. Valid choices are
+ # @param cool_fuel [String] cooling fuel. Valid choices are
# Electricity, DistrictCooling
# @todo add 90.1-2013 systems 11-13
- def add_prm_baseline_system(template, system_type, main_heat_fuel, zone_heat_fuel, cool_fuel, zones)
- case template
- when '90.1-2004', '90.1-2007', '90.1-2010', '90.1-2013'
-
- case system_type
+ def model_add_prm_baseline_system(model, system_type, main_heat_fuel, zone_heat_fuel, cool_fuel, zones)
+ case system_type
when 'PTAC' # System 1
unless zones.empty?
# Retrieve the existing hot water loop
# or add a new one if necessary.
hot_water_loop = nil
- hot_water_loop = if getPlantLoopByName('Hot Water Loop').is_initialized
- getPlantLoopByName('Hot Water Loop').get
+ hot_water_loop = if model.getPlantLoopByName('Hot Water Loop').is_initialized
+ model.getPlantLoopByName('Hot Water Loop').get
else
- add_hw_loop(main_heat_fuel)
+ model_add_hw_loop(model, main_heat_fuel)
end
# Add a hot water PTAC to each zone
- add_ptac(template,
- nil,
- hot_water_loop,
- zones,
- 'ConstantVolume',
- 'Water',
- 'Single Speed DX AC')
+ model_add_ptac(model,
+ nil,
+ hot_water_loop,
+ zones,
+ 'ConstantVolume',
+ 'Water',
+ 'Single Speed DX AC')
end
when 'PTHP' # System 2
unless zones.empty?
# Add an air-source packaged terminal
# heat pump with electric supplemental heat
# to each zone.
- add_pthp(template,
- nil,
- zones,
- 'ConstantVolume')
+ model_add_pthp(model,
+ nil,
+ zones,
+ 'ConstantVolume')
end
when 'PSZ_AC' # System 3
@@ -988,485 +814,484 @@
heating_type = 'Gas'
# If district heating
hot_water_loop = nil
if main_heat_fuel == 'DistrictHeating'
heating_type = 'Water'
- hot_water_loop = if getPlantLoopByName('Hot Water Loop').is_initialized
- getPlantLoopByName('Hot Water Loop').get
+ hot_water_loop = if model.getPlantLoopByName('Hot Water Loop').is_initialized
+ model.getPlantLoopByName('Hot Water Loop').get
else
- add_hw_loop(main_heat_fuel)
+ model_add_hw_loop(model, main_heat_fuel)
end
end
cooling_type = 'Single Speed DX AC'
# If district cooling
chilled_water_loop = nil
if cool_fuel == 'DistrictCooling'
cooling_type = 'Water'
- chilled_water_loop = if getPlantLoopByName('Chilled Water Loop').is_initialized
- getPlantLoopByName('Chilled Water Loop').get
+ chilled_water_loop = if model.getPlantLoopByName('Chilled Water Loop').is_initialized
+ model.getPlantLoopByName('Chilled Water Loop').get
else
- add_chw_loop(template,
- 'const_pri',
- chiller_cooling_type = nil,
- chiller_condenser_type = nil,
- chiller_compressor_type = nil,
- cool_fuel,
- condenser_water_loop = nil,
- building_type = nil)
+ model_add_chw_loop(model,
+ 'const_pri',
+ chiller_cooling_type = nil,
+ chiller_condenser_type = nil,
+ chiller_compressor_type = nil,
+ cool_fuel,
+ condenser_water_loop = nil,
+ building_type = nil)
end
end
# Add a gas-fired PSZ-AC to each zone
# hvac_op_sch=nil means always on
# oa_damper_sch to nil means always open
- add_psz_ac(template,
- sys_name = nil,
- hot_water_loop,
- chilled_water_loop,
- zones,
- hvac_op_sch = nil,
- oa_damper_sch = nil,
- fan_location = 'DrawThrough',
- fan_type = 'ConstantVolume',
- heating_type,
- supplemental_heating_type = 'Gas', # Should we really add supplemental heating here?
- cooling_type,
- building_type = nil)
+ model_add_psz_ac(model,
+ sys_name = nil,
+ hot_water_loop,
+ chilled_water_loop,
+ zones,
+ hvac_op_sch = nil,
+ oa_damper_sch = nil,
+ fan_location = 'DrawThrough',
+ fan_type = 'ConstantVolume',
+ heating_type,
+ supplemental_heating_type = 'Gas', # Should we really add supplemental heating here?
+ cooling_type,
+ building_type = nil)
end
when 'PSZ_HP' # System 4
unless zones.empty?
# Add an air-source packaged single zone
# heat pump with electric supplemental heat
# to each zone.
- add_psz_ac(template,
- 'PSZ-HP',
- nil,
- nil,
- zones,
- nil,
- nil,
- 'DrawThrough',
- 'ConstantVolume',
- 'Single Speed Heat Pump',
- 'Electric',
- 'Single Speed Heat Pump',
- building_type = nil)
+ model_add_psz_ac(model,
+ 'PSZ-HP',
+ nil,
+ nil,
+ zones,
+ nil,
+ nil,
+ 'DrawThrough',
+ 'ConstantVolume',
+ 'Single Speed Heat Pump',
+ 'Electric',
+ 'Single Speed Heat Pump',
+ building_type = nil)
end
when 'PVAV_Reheat' # System 5
# Retrieve the existing hot water loop
# or add a new one if necessary.
hot_water_loop = nil
- hot_water_loop = if getPlantLoopByName('Hot Water Loop').is_initialized
- getPlantLoopByName('Hot Water Loop').get
+ hot_water_loop = if model.getPlantLoopByName('Hot Water Loop').is_initialized
+ model.getPlantLoopByName('Hot Water Loop').get
else
- add_hw_loop(main_heat_fuel)
+ model_add_hw_loop(model, main_heat_fuel)
end
# If district cooling
chilled_water_loop = nil
if cool_fuel == 'DistrictCooling'
- chilled_water_loop = if getPlantLoopByName('Chilled Water Loop').is_initialized
- getPlantLoopByName('Chilled Water Loop').get
+ chilled_water_loop = if model.getPlantLoopByName('Chilled Water Loop').is_initialized
+ model.getPlantLoopByName('Chilled Water Loop').get
else
- add_chw_loop(template,
- 'const_pri',
- chiller_cooling_type = nil,
- chiller_condenser_type = nil,
- chiller_compressor_type = nil,
- cool_fuel,
- condenser_water_loop = nil,
- building_type = nil)
+ model_add_chw_loop(model,
+ 'const_pri',
+ chiller_cooling_type = nil,
+ chiller_condenser_type = nil,
+ chiller_compressor_type = nil,
+ cool_fuel,
+ condenser_water_loop = nil,
+ building_type = nil)
end
end
# If electric zone heat
electric_reheat = false
if zone_heat_fuel == 'Electricity'
electric_reheat = true
end
# Group zones by story
- story_zone_lists = group_zones_by_story(zones)
+ story_zone_lists = model_group_zones_by_story(model, zones)
# For the array of zones on each story,
# separate the primary zones from the secondary zones.
# Add the baseline system type to the primary zones
# and add the suplemental system type to the secondary zones.
story_zone_lists.each do |story_group|
# Differentiate primary and secondary zones
- pri_sec_zone_lists = differentiate_primary_secondary_thermal_zones(story_group)
+ pri_sec_zone_lists = model_differentiate_primary_secondary_thermal_zones(model, story_group)
pri_zones = pri_sec_zone_lists['primary']
sec_zones = pri_sec_zone_lists['secondary']
# Add a PVAV with Reheat for the primary zones
stories = []
story_group[0].spaces.each do |space|
- stories << [space.buildingStory.get.name.get, space.buildingStory.get.minimum_z_value]
+ stories << [space.buildingStory.get.name.get, building_story_minimum_z_value(space.buildingStory.get)]
end
- story_name = stories.sort_by{ |nm, z| z }[0][0]
+ story_name = stories.sort_by { |nm, z| z }[0][0]
sys_name = "#{story_name} PVAV_Reheat (Sys5)"
# If and only if there are primary zones to attach to the loop
# counter example: floor with only one elevator machine room that get classified as sec_zones
unless pri_zones.empty?
- add_pvav(template,
- sys_name,
- pri_zones,
- nil,
- nil,
- electric_reheat,
- hot_water_loop,
- chilled_water_loop,
- nil,
- nil)
+ model_add_pvav(model,
+ sys_name,
+ pri_zones,
+ nil,
+ nil,
+ electric_reheat,
+ hot_water_loop,
+ chilled_water_loop,
+ nil,
+ nil)
end
# Add a PSZ_AC for each secondary zone
unless sec_zones.empty?
- add_prm_baseline_system(template, 'PSZ_AC', main_heat_fuel, zone_heat_fuel, cool_fuel, sec_zones)
+ model_add_prm_baseline_system(model, 'PSZ_AC', main_heat_fuel, zone_heat_fuel, cool_fuel, sec_zones)
end
end
when 'PVAV_PFP_Boxes' # System 6
# If district cooling
chilled_water_loop = nil
if cool_fuel == 'DistrictCooling'
- chilled_water_loop = if getPlantLoopByName('Chilled Water Loop').is_initialized
- getPlantLoopByName('Chilled Water Loop').get
+ chilled_water_loop = if model.getPlantLoopByName('Chilled Water Loop').is_initialized
+ model.getPlantLoopByName('Chilled Water Loop').get
else
- add_chw_loop(template,
- 'const_pri',
- chiller_cooling_type = nil,
- chiller_condenser_type = nil,
- chiller_compressor_type = nil,
- cool_fuel,
- condenser_water_loop = nil,
- building_type = nil)
+ model_add_chw_loop(model,
+ 'const_pri',
+ chiller_cooling_type = nil,
+ chiller_condenser_type = nil,
+ chiller_compressor_type = nil,
+ cool_fuel,
+ condenser_water_loop = nil,
+ building_type = nil)
end
end
# Group zones by story
- story_zone_lists = group_zones_by_story(zones)
+ story_zone_lists = model_group_zones_by_story(model, zones)
# For the array of zones on each story,
# separate the primary zones from the secondary zones.
# Add the baseline system type to the primary zones
# and add the suplemental system type to the secondary zones.
story_zone_lists.each do |story_group|
# Differentiate primary and secondary zones
- pri_sec_zone_lists = differentiate_primary_secondary_thermal_zones(story_group)
+ pri_sec_zone_lists = model_differentiate_primary_secondary_thermal_zones(model, story_group)
pri_zones = pri_sec_zone_lists['primary']
sec_zones = pri_sec_zone_lists['secondary']
# Add an VAV for the primary zones
stories = []
story_group[0].spaces.each do |space|
- stories << [space.buildingStory.get.name.get, space.buildingStory.get.minimum_z_value]
+ stories << [space.buildingStory.get.name.get, building_story_minimum_z_value(space.buildingStory.get)]
end
- story_name = stories.sort_by{ |nm, z| z }[0][0]
+ story_name = stories.sort_by { |nm, z| z }[0][0]
sys_name = "#{story_name} PVAV_PFP_Boxes (Sys6)"
# If and only if there are primary zones to attach to the loop
unless pri_zones.empty?
- add_pvav_pfp_boxes(template,
- sys_name,
- pri_zones,
- nil,
- nil,
- 0.62,
- 0.9,
- OpenStudio.convert(4.0, 'inH_{2}O', 'Pa').get,
- chilled_water_loop,
- nil)
+ model_add_pvav_pfp_boxes(model,
+ sys_name,
+ pri_zones,
+ nil,
+ nil,
+ 0.62,
+ 0.9,
+ OpenStudio.convert(4.0, 'inH_{2}O', 'Pa').get,
+ chilled_water_loop,
+ nil)
end
# Add a PSZ_HP for each secondary zone
unless sec_zones.empty?
- add_prm_baseline_system(template, 'PSZ_HP', main_heat_fuel, zone_heat_fuel, cool_fuel, sec_zones)
+ model_add_prm_baseline_system(model, 'PSZ_HP', main_heat_fuel, zone_heat_fuel, cool_fuel, sec_zones)
end
end
when 'VAV_Reheat' # System 7
# Retrieve the existing hot water loop
# or add a new one if necessary.
hot_water_loop = nil
- hot_water_loop = if getPlantLoopByName('Hot Water Loop').is_initialized
- getPlantLoopByName('Hot Water Loop').get
+ hot_water_loop = if model.getPlantLoopByName('Hot Water Loop').is_initialized
+ model.getPlantLoopByName('Hot Water Loop').get
else
- add_hw_loop(main_heat_fuel)
+ model_add_hw_loop(model, main_heat_fuel)
end
# Retrieve the existing chilled water loop
# or add a new one if necessary.
chilled_water_loop = nil
- if getPlantLoopByName('Chilled Water Loop').is_initialized
- chilled_water_loop = getPlantLoopByName('Chilled Water Loop').get
+ if model.getPlantLoopByName('Chilled Water Loop').is_initialized
+ chilled_water_loop = model.getPlantLoopByName('Chilled Water Loop').get
else
if cool_fuel == 'DistrictCooling'
- chilled_water_loop = add_chw_loop(template,
- 'const_pri',
- chiller_cooling_type = nil,
- chiller_condenser_type = nil,
- chiller_compressor_type = nil,
- cool_fuel,
- condenser_water_loop = nil,
- building_type = nil)
+ chilled_water_loop = model_add_chw_loop(model,
+ 'const_pri',
+ chiller_cooling_type = nil,
+ chiller_condenser_type = nil,
+ chiller_compressor_type = nil,
+ cool_fuel,
+ condenser_water_loop = nil,
+ building_type = nil)
else
- fan_type = 'TwoSpeed Fan'
- if template == '90.1-2013'
- fan_type = 'Variable Speed Fan'
- end
- condenser_water_loop = add_cw_loop(template,
- 'Open Cooling Tower',
- 'Propeller or Axial',
- fan_type,
- 1,
- 1,
- nil)
- chilled_water_loop = add_chw_loop(template,
- 'const_pri_var_sec',
- 'WaterCooled',
- chiller_condenser_type = nil,
- 'Rotary Screw',
- cooling_fuel = nil,
- condenser_water_loop,
- building_type = nil)
+ fan_type = model_cw_loop_cooling_tower_fan_type(model)
+ condenser_water_loop = model_add_cw_loop(model,
+ 'Open Cooling Tower',
+ 'Propeller or Axial',
+ fan_type,
+ 1,
+ 1,
+ nil)
+ chilled_water_loop = model_add_chw_loop(model,
+ 'const_pri_var_sec',
+ 'WaterCooled',
+ chiller_condenser_type = nil,
+ 'Rotary Screw',
+ cooling_fuel = nil,
+ condenser_water_loop,
+ building_type = nil)
end
end
# If electric zone heat
reheat_type = 'Water'
if zone_heat_fuel == 'Electricity'
reheat_type = 'Electricity'
end
# Group zones by story
- story_zone_lists = group_zones_by_story(zones)
+ story_zone_lists = model_group_zones_by_story(model, zones)
# For the array of zones on each story,
# separate the primary zones from the secondary zones.
# Add the baseline system type to the primary zones
# and add the suplemental system type to the secondary zones.
story_zone_lists.each do |story_group|
- # The group_zones_by_story NO LONGER returns empty lists when a given floor doesn't have any of the zones
+ # The model_group_zones_by_story(model) NO LONGER returns empty lists when a given floor doesn't have any of the zones
# So NO need to filter it out otherwise you get an error undefined method `spaces' for nil:NilClass
# next if zones.empty?
# Differentiate primary and secondary zones
- pri_sec_zone_lists = differentiate_primary_secondary_thermal_zones(story_group)
+ pri_sec_zone_lists = model_differentiate_primary_secondary_thermal_zones(model, story_group)
pri_zones = pri_sec_zone_lists['primary']
sec_zones = pri_sec_zone_lists['secondary']
# Add a VAV for the primary zones
stories = []
story_group[0].spaces.each do |space|
- stories << [space.buildingStory.get.name.get, space.buildingStory.get.minimum_z_value]
+ stories << [space.buildingStory.get.name.get, building_story_minimum_z_value(space.buildingStory.get)]
end
- story_name = stories.sort_by{ |nm, z| z }[0][0]
+ story_name = stories.sort_by { |nm, z| z }[0][0]
sys_name = "#{story_name} VAV_Reheat (Sys7)"
# If and only if there are primary zones to attach to the loop
# counter example: floor with only one elevator machine room that get classified as sec_zones
unless pri_zones.empty?
- add_vav_reheat(template,
- sys_name,
- hot_water_loop,
- chilled_water_loop,
- pri_zones,
- nil,
- nil,
- 0.62,
- 0.9,
- OpenStudio.convert(4.0, 'inH_{2}O', 'Pa').get,
- nil,
- reheat_type,
- nil)
+ model_add_vav_reheat(model,
+ sys_name,
+ hot_water_loop,
+ chilled_water_loop,
+ pri_zones,
+ nil,
+ nil,
+ 0.62,
+ 0.9,
+ OpenStudio.convert(4.0, 'inH_{2}O', 'Pa').get,
+ nil,
+ reheat_type,
+ nil)
end
# Add a PSZ_AC for each secondary zone
unless sec_zones.empty?
- add_prm_baseline_system(template, 'PSZ_AC', main_heat_fuel, zone_heat_fuel, cool_fuel, sec_zones)
+ model_add_prm_baseline_system(model, 'PSZ_AC', main_heat_fuel, zone_heat_fuel, cool_fuel, sec_zones)
end
end
when 'VAV_PFP_Boxes' # System 8
# Retrieve the existing chilled water loop
# or add a new one if necessary.
chilled_water_loop = nil
- if getPlantLoopByName('Chilled Water Loop').is_initialized
- chilled_water_loop = getPlantLoopByName('Chilled Water Loop').get
+ if model.getPlantLoopByName('Chilled Water Loop').is_initialized
+ chilled_water_loop = model.getPlantLoopByName('Chilled Water Loop').get
else
if cool_fuel == 'DistrictCooling'
- chilled_water_loop = add_chw_loop(template,
- 'const_pri',
- chiller_cooling_type = nil,
- chiller_condenser_type = nil,
- chiller_compressor_type = nil,
- cool_fuel,
- condenser_water_loop = nil,
- building_type = nil)
+ chilled_water_loop = model_add_chw_loop(model,
+ 'const_pri',
+ chiller_cooling_type = nil,
+ chiller_condenser_type = nil,
+ chiller_compressor_type = nil,
+ cool_fuel,
+ condenser_water_loop = nil,
+ building_type = nil)
else
- fan_type = 'TwoSpeed Fan'
- if template == '90.1-2013'
- fan_type = 'Variable Speed Fan'
- end
- condenser_water_loop = add_cw_loop(template,
- 'Open Cooling Tower',
- 'Propeller or Axial',
- fan_type,
- 1,
- 1,
- nil)
- chilled_water_loop = add_chw_loop(template,
- 'const_pri_var_sec',
- 'WaterCooled',
- chiller_condenser_type = nil,
- 'Rotary Screw',
- cool_fueling = nil,
- condenser_water_loop,
- building_type = nil)
+ fan_type = model_cw_loop_cooling_tower_fan_type(model)
+ condenser_water_loop = model_add_cw_loop(model,
+ 'Open Cooling Tower',
+ 'Propeller or Axial',
+ fan_type,
+ 1,
+ 1,
+ nil)
+ chilled_water_loop = model_add_chw_loop(model,
+ 'const_pri_var_sec',
+ 'WaterCooled',
+ chiller_condenser_type = nil,
+ 'Rotary Screw',
+ cool_fueling = nil,
+ condenser_water_loop,
+ building_type = nil)
end
end
# Group zones by story
- story_zone_lists = group_zones_by_story(zones)
+ story_zone_lists = model_group_zones_by_story(model, zones)
# For the array of zones on each story,
# separate the primary zones from the secondary zones.
# Add the baseline system type to the primary zones
# and add the suplemental system type to the secondary zones.
story_zone_lists.each do |story_group|
# Differentiate primary and secondary zones
- pri_sec_zone_lists = differentiate_primary_secondary_thermal_zones(story_group)
+ pri_sec_zone_lists = model_differentiate_primary_secondary_thermal_zones(model, story_group)
pri_zones = pri_sec_zone_lists['primary']
sec_zones = pri_sec_zone_lists['secondary']
# Add an VAV for the primary zones
stories = []
story_group[0].spaces.each do |space|
- stories << [space.buildingStory.get.name.get, space.buildingStory.get.minimum_z_value]
+ stories << [space.buildingStory.get.name.get, building_story_minimum_z_value(space.buildingStory.get)]
end
- story_name = stories.sort_by{ |nm, z| z }[0][0]
+ story_name = stories.sort_by { |nm, z| z }[0][0]
sys_name = "#{story_name} VAV_PFP_Boxes (Sys8)"
# If and only if there are primary zones to attach to the loop
unless pri_zones.empty?
- add_vav_pfp_boxes(template,
- sys_name,
- chilled_water_loop,
- pri_zones,
- nil,
- nil,
- 0.62,
- 0.9,
- OpenStudio.convert(4.0, 'inH_{2}O', 'Pa').get)
+ model_add_vav_pfp_boxes(model,
+ sys_name,
+ chilled_water_loop,
+ pri_zones,
+ nil,
+ nil,
+ 0.62,
+ 0.9,
+ OpenStudio.convert(4.0, 'inH_{2}O', 'Pa').get)
end
# Add a PSZ_HP for each secondary zone
unless sec_zones.empty?
- add_prm_baseline_system(template, 'PSZ_HP', main_heat_fuel, zone_heat_fuel, cool_fuel, sec_zones)
+ model_add_prm_baseline_system(model, 'PSZ_HP', main_heat_fuel, zone_heat_fuel, cool_fuel, sec_zones)
end
end
when 'Gas_Furnace' # System 9
unless zones.empty?
# If district heating
hot_water_loop = nil
if main_heat_fuel == 'DistrictHeating'
- hot_water_loop = if getPlantLoopByName('Hot Water Loop').is_initialized
- getPlantLoopByName('Hot Water Loop').get
+ hot_water_loop = if model.getPlantLoopByName('Hot Water Loop').is_initialized
+ model.getPlantLoopByName('Hot Water Loop').get
else
- add_hw_loop(main_heat_fuel)
+ model_add_hw_loop(model, main_heat_fuel)
end
end
# Add a System 9 - Gas Unit Heater to each zone
- add_unitheater(template,
- nil,
- zones,
- nil,
- 'ConstantVolume',
- OpenStudio.convert(0.2, 'inH_{2}O', 'Pa').get,
- main_heat_fuel,
- hot_water_loop,
- nil)
+ model_add_unitheater(model,
+ nil,
+ zones,
+ nil,
+ 'ConstantVolume',
+ OpenStudio.convert(0.2, 'inH_{2}O', 'Pa').get,
+ main_heat_fuel,
+ hot_water_loop,
+ nil)
end
when 'Electric_Furnace' # System 10
unless zones.empty?
# Add a System 10 - Electric Unit Heater to each zone
- add_unitheater(template,
- nil,
- zones,
- nil,
- 'ConstantVolume',
- OpenStudio.convert(0.2, 'inH_{2}O', 'Pa').get,
- main_heat_fuel,
- nil,
- nil)
+ model_add_unitheater(model,
+ nil,
+ zones,
+ nil,
+ 'ConstantVolume',
+ OpenStudio.convert(0.2, 'inH_{2}O', 'Pa').get,
+ main_heat_fuel,
+ nil,
+ nil)
end
else
OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Model', "System type #{system_type} is not a valid choice, nothing will be added to the model.")
+ return false
- end
-
end
+ return true
end
+ # Determines the fan type used by VAV_Reheat and VAV_PFP_Boxes systems.
+ # Defaults to two speed fan.
+ # @return [String] the fan type: TwoSpeed Fan, Variable Speed Fan
+ def model_baseline_system_vav_fan_type(model)
+ fan_type = 'TwoSpeed Fan'
+ return fan_type
+ end
+
# Looks through the model and creates an hash of what the baseline
# system type should be for each zone.
#
# @return [Hash] keys are zones, values are system type strings
# PTHP, PTAC, PSZ_AC, PSZ_HP, PVAV_Reheat, PVAV_PFP_Boxes,
# VAV_Reheat, VAV_PFP_Boxes, Gas_Furnace, Electric_Furnace
- def get_baseline_system_type_by_zone(template, climate_zone, custom = nil)
+ def model_get_baseline_system_type_by_zone(model, climate_zone, custom = nil)
zone_to_sys_type = {}
# Get the groups of zones that define the
# baseline HVAC systems for later use.
# This must be done before removing the HVAC systems
# because it requires knowledge of proposed HVAC fuels.
- sys_groups = prm_baseline_system_groups(template, custom)
+ sys_groups = model_prm_baseline_system_groups(model, custom)
# Assign building stories to spaces in the building
# where stories are not yet assigned.
- assign_spaces_to_stories
+ model_assign_spaces_to_stories(model)
# Determine the baseline HVAC system type for each of
# the groups of zones and add that system type.
sys_groups.each do |sys_group|
# Determine the primary baseline system type
- pri_system_type = prm_baseline_system_type(template,
- climate_zone,
- sys_group['occ'],
- sys_group['fuel'],
- sys_group['area_ft2'],
- sys_group['stories'],
- custom)[0]
+ pri_system_type = model_prm_baseline_system_type(model,
+ climate_zone,
+ sys_group['occ'],
+ sys_group['fuel'],
+ sys_group['area_ft2'],
+ sys_group['stories'],
+ custom)[0]
# Record the zone-by-zone system type assignments
- case template
- when '90.1-2004', '90.1-2007', '90.1-2010', '90.1-2013'
-
- case pri_system_type
+ case pri_system_type
when 'PTAC', 'PTHP', 'PSZ_AC', 'PSZ_HP', 'Gas_Furnace', 'Electric_Furnace'
sys_group['zones'].each do |zone|
zone_to_sys_type[zone] = pri_system_type
end
@@ -1481,39 +1306,36 @@
when 'PVAV_PFP_Boxes', 'VAV_PFP_Boxes'
sec_system_type = 'PSZ_HP'
end
# Group zones by story
- story_zone_lists = group_zones_by_story(sys_group['zones'])
+ story_zone_lists = model_group_zones_by_story(model, sys_group['zones'])
# For the array of zones on each story,
# separate the primary zones from the secondary zones.
# Add the baseline system type to the primary zones
# and add the suplemental system type to the secondary zones.
story_zone_lists.each do |zones|
# Differentiate primary and secondary zones
- pri_sec_zone_lists = differentiate_primary_secondary_thermal_zones(zones)
+ pri_sec_zone_lists = model_differentiate_primary_secondary_thermal_zones(model, zones)
# Record the primary zone system types
pri_sec_zone_lists['primary'].each do |zone|
zone_to_sys_type[zone] = pri_system_type
end
# Record the secondary zone system types
pri_sec_zone_lists['secondary'].each do |zone|
zone_to_sys_type[zone] = sec_system_type
end
end
-
- end
-
end
end
return zone_to_sys_type
end
# @param array_of_zones [Array] an array of Hashes for each zone,
# with the keys 'zone',
- def eliminate_outlier_zones(array_of_zones, key_to_inspect, tolerance, field_name, units)
+ def model_eliminate_outlier_zones(model, array_of_zones, key_to_inspect, tolerance, field_name, units)
# Sort the zones by the desired key
array_of_zones = array_of_zones.sort_by { |hsh| hsh[key_to_inspect] }
# Calculate the area-weighted average
total = 0.0
@@ -1560,11 +1382,11 @@
if biggest_delta > tolerance
zn_name = array_of_zones[biggest_delta_i]['zone'].name.get.to_s
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "For zone #{zn_name}, the #{field_name} of #{worst.round(1)} #{units} is more than #{tolerance} #{units} outside the area-weighted average of #{avg.round(1)} #{units}; it will be placed on its own secondary system.")
array_of_zones.delete_at(biggest_delta_i)
# Call method recursively if something was eliminated
- array_of_zones = eliminate_outlier_zones(array_of_zones, key_to_inspect, tolerance, field_name, units)
+ array_of_zones = model_eliminate_outlier_zones(model, array_of_zones, key_to_inspect, tolerance, field_name, units)
else
# OpenStudio::logFree(OpenStudio::Debug, 'openstudio.standards.Model', "#{worst.round(1)} - #{avg.round(1)} = #{biggest_delta.round(1)} #{units} < tolerance of #{tolerance} #{units}, stopping elimination process.")
OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.Model', "#{worst} - #{avg} = #{biggest_delta} #{units} < tolerance of #{tolerance} #{units}, stopping elimination process.")
end
@@ -1582,11 +1404,11 @@
# area-weighted average of all other zones
# on the system by more than 10 Btu/hr*ft^2.
#
# @return [Hash] A hash of two arrays of ThermalZones,
# where the keys are 'primary' and 'secondary'
- def differentiate_primary_secondary_thermal_zones(zones)
+ def model_differentiate_primary_secondary_thermal_zones(model, zones)
OpenStudio.logFree(OpenStudio::Info, 'openstudio.Standards.Model', 'Determining which zones are served by the primary vs. secondary HVAC system.')
# Determine the operational hours (proxy is annual
# full load lighting hours) for all zones
zone_data_1 = []
@@ -1618,18 +1440,18 @@
# Skip lights with no schedule
next if lights_sch.empty?
lights_sch = lights_sch.get
if lights_sch.to_ScheduleRuleset.is_initialized
lights_sch = lights_sch.to_ScheduleRuleset.get
- full_load_hrs = lights_sch.annual_equivalent_full_load_hrs
+ full_load_hrs = schedule_ruleset_annual_equivalent_full_load_hrs(lights_sch)
if full_load_hrs > 0
ann_op_hrs = full_load_hrs
break # Stop after the first schedule with more than 0 hrs
end
elsif lights_sch.to_ScheduleConstant.is_initialized
lights_sch = lights_sch.to_ScheduleConstant.get
- full_load_hrs = lights_sch.annual_equivalent_full_load_hrs
+ full_load_hrs = schedule_constant_annual_equivalent_full_load_hrs(lights_sch)
if full_load_hrs > 0
ann_op_hrs = full_load_hrs
break # Stop after the first schedule with more than 0 hrs
end
end
@@ -1643,11 +1465,11 @@
end
# Filter out any zones that operate differently by more
# than 40hrs/wk. This will be determined by a difference of more
# than (40 hrs/wk * 52 wks/yr) = 2080 annual full load hrs.
- zones_same_hrs = eliminate_outlier_zones(zone_data_1, 'wk_op_hrs', 40, 'weekly operating hrs', 'hrs')
+ zones_same_hrs = model_eliminate_outlier_zones(model, zone_data_1, 'wk_op_hrs', 40, 'weekly operating hrs', 'hrs')
# Get the internal loads for
# all remaining zones.
zone_data_2 = []
zones_same_hrs.each do |zn_data|
@@ -1657,20 +1479,20 @@
# Get the area
area_m2 = zone.floorArea * zone.multiplier
area_ft2 = OpenStudio.convert(area_m2, 'm^2', 'ft^2').get
data['area_ft2'] = area_ft2
# Get the internal loads
- int_load_w = zone.design_internal_load
+ int_load_w = thermal_zone_design_internal_load(zone)
# Normalize per-area
int_load_w_per_m2 = int_load_w / area_m2
int_load_btu_per_ft2 = OpenStudio.convert(int_load_w_per_m2, 'W/m^2', 'Btu/hr*ft^2').get
data['int_load_btu_per_ft2'] = int_load_btu_per_ft2
zone_data_2 << data
end
# Filter out any zones that are +/- 10 Btu/hr*ft^2 from the average
- pri_zn_data = eliminate_outlier_zones(zone_data_2, 'int_load_btu_per_ft2', 10, 'internal load', 'Btu/hr*ft^2')
+ pri_zn_data = model_eliminate_outlier_zones(model, zone_data_2, 'int_load_btu_per_ft2', 10, 'internal load', 'Btu/hr*ft^2')
# Get just the primary zones themselves
pri_zones = []
pri_zone_names = []
pri_zn_data.each do |zn_data|
@@ -1702,14 +1524,14 @@
# 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.
# Removes empty array (when the story doesn't contain any of the zones)
# @return [Array<Array<OpenStudio::Model::ThermalZone>>] array of arrays of zones
- def group_zones_by_story(zones)
+ def model_group_zones_by_story(model, zones)
story_zone_lists = []
zones_already_assigned = []
- getBuildingStorys.sort.each do |story|
+ 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 = []
@@ -1747,16 +1569,16 @@
# 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.
#
# @return [Bool] returns true if successful, false if not.
- def assign_spaces_to_stories
+ def model_assign_spaces_to_stories(model)
# Make hash of spaces and minz values
sorted_spaces = {}
- getSpaces.each do |space|
+ model.getSpaces.sort.each do |space|
# Skip plenum spaces
- next if space.plenum?
+ next if space_plenum?(space)
# loop through space surfaces to find min z value
z_points = []
space.surfaces.each do |surface|
surface.vertices.each do |vertex|
@@ -1766,18 +1588,18 @@
minz = z_points.min + space.zOrigin
sorted_spaces[space] = minz
end
# Pre-sort spaces
- sorted_spaces = sorted_spaces.sort { |a, b| a[1] <=> b[1] }
+ 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_minz = space[1]
if space_obj.buildingStory.empty?
- story = get_story_for_nominal_z_coordinate(space_minz)
+ story = model_get_story_for_nominal_z_coordinate(model, space_minz)
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
@@ -1788,37 +1610,36 @@
# Performance Rating Method (aka Appendix G aka LEED) and adds it to the model.
# This method creates and adds the constructions and their materials as well.
#
# @param category [String] the construction set category desired.
# Valid choices are Nonresidential, Residential, and Semiheated
- # @param template [String] the template. Valid choices are 90.1-2004, 90.1-2007, 90.1-2010, 90.1-2013.
# @return [OpenStudio::Model::DefaultConstructionSet] returns a default
# construction set populated with the specified constructions.
- def add_prm_construction_set(template, category)
+ def model_add_prm_construction_set(model, category)
construction_set = OpenStudio::Model::OptionalDefaultConstructionSet.new
# Find the climate zone set that this climate zone falls into
- climate_zone_set = find_climate_zone_set(clim, template)
+ climate_zone_set = model_find_climate_zone_set(model, clim)
unless climate_zone_set
return construction_set
end
# Get the object data
- data = find_object($os_standards['construction_sets'], 'template' => template, 'climate_zone_set' => climate_zone_set, 'building_type' => building_type, 'space_type' => spc_type, 'is_residential' => is_residential)
+ data = model_find_object(standards_data['construction_sets'], 'template' => template, 'climate_zone_set' => climate_zone_set, 'building_type' => building_type, 'space_type' => spc_type, 'is_residential' => is_residential)
unless data
- data = find_object($os_standards['construction_sets'], 'template' => template, 'climate_zone_set' => climate_zone_set, 'building_type' => building_type, 'space_type' => spc_type)
+ data = model_find_object(standards_data['construction_sets'], 'template' => template, 'climate_zone_set' => climate_zone_set, 'building_type' => building_type, 'space_type' => spc_type)
unless data
return construction_set
end
end
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "Adding construction set: #{template}-#{clim}-#{building_type}-#{spc_type}-is_residential#{is_residential}")
- name = make_name(template, clim, building_type, spc_type)
+ name = model_make_name(model, clim, building_type, spc_type)
# Create a new construction set and name it
- construction_set = OpenStudio::Model::DefaultConstructionSet.new(self)
+ construction_set = OpenStudio::Model::DefaultConstructionSet.new(model)
construction_set.setName(name)
# Specify the types of constructions
# Exterior surfaces constructions
exterior_floor_standards_construction_type = 'SteelFramed'
@@ -1835,135 +1656,135 @@
exterior_door_standards_construction_type = 'IEAD'
exterior_overhead_door_standards_construction_type = 'IEAD'
exterior_skylight_standards_construction_type = 'IEAD'
# Exterior surfaces constructions
- exterior_surfaces = OpenStudio::Model::DefaultSurfaceConstructions.new(self)
+ exterior_surfaces = OpenStudio::Model::DefaultSurfaceConstructions.new(model)
construction_set.setDefaultExteriorSurfaceConstructions(exterior_surfaces)
- exterior_surfaces.setFloorConstruction(find_and_add_construction(template,
- climate_zone_set,
- 'ExteriorFloor',
- exterior_floor_standards_construction_type,
- category))
-
- exterior_surfaces.setWallConstruction(find_and_add_construction(template,
- climate_zone_set,
- 'ExteriorWall',
- exterior_wall_standards_construction_type,
- category))
-
- exterior_surfaces.setRoofCeilingConstruction(find_and_add_construction(template,
+ exterior_surfaces.setFloorConstruction(model_find_and_add_construction(model,
climate_zone_set,
- 'ExteriorRoof',
- exterior_roof_standards_construction_type,
+ 'ExteriorFloor',
+ exterior_floor_standards_construction_type,
category))
+ exterior_surfaces.setWallConstruction(model_find_and_add_construction(model,
+ climate_zone_set,
+ 'ExteriorWall',
+ exterior_wall_standards_construction_type,
+ category))
+
+ exterior_surfaces.setRoofCeilingConstruction(model_find_and_add_construction(model,
+ climate_zone_set,
+ 'ExteriorRoof',
+ exterior_roof_standards_construction_type,
+ category))
+
# Interior surfaces constructions
- interior_surfaces = OpenStudio::Model::DefaultSurfaceConstructions.new(self)
+ interior_surfaces = OpenStudio::Model::DefaultSurfaceConstructions.new(model)
construction_set.setDefaultInteriorSurfaceConstructions(interior_surfaces)
construction_name = interior_floors
unless construction_name.nil?
- interior_surfaces.setFloorConstruction(add_construction(construction_name))
+ interior_surfaces.setFloorConstruction(model_add_construction(model, construction_name))
end
construction_name = interior_walls
unless construction_name.nil?
- interior_surfaces.setWallConstruction(add_construction(construction_name))
+ interior_surfaces.setWallConstruction(model_add_construction(model, construction_name))
end
construction_name = interior_ceilings
unless construction_name.nil?
- interior_surfaces.setRoofCeilingConstruction(add_construction(construction_name))
+ interior_surfaces.setRoofCeilingConstruction(model_add_construction(model, construction_name))
end
# Ground contact surfaces constructions
- ground_surfaces = OpenStudio::Model::DefaultSurfaceConstructions.new(self)
+ ground_surfaces = OpenStudio::Model::DefaultSurfaceConstructions.new(model)
construction_set.setDefaultGroundContactSurfaceConstructions(ground_surfaces)
- ground_surfaces.setFloorConstruction(find_and_add_construction(template,
- climate_zone_set,
- 'GroundContactFloor',
- ground_contact_floor_standards_construction_type,
- category))
+ ground_surfaces.setFloorConstruction(model_find_and_add_construction(model,
+ climate_zone_set,
+ 'GroundContactFloor',
+ ground_contact_floor_standards_construction_type,
+ category))
- ground_surfaces.setWallConstruction(find_and_add_construction(template,
- climate_zone_set,
- 'GroundContactWall',
- ground_contact_wall_standards_construction_type,
- category))
+ ground_surfaces.setWallConstruction(model_find_and_add_construction(model,
+ climate_zone_set,
+ 'GroundContactWall',
+ ground_contact_wall_standards_construction_type,
+ category))
# Exterior sub surfaces constructions
- exterior_subsurfaces = OpenStudio::Model::DefaultSubSurfaceConstructions.new(self)
+ exterior_subsurfaces = OpenStudio::Model::DefaultSubSurfaceConstructions.new(model)
construction_set.setDefaultExteriorSubSurfaceConstructions(exterior_subsurfaces)
if exterior_fixed_window_standards_construction_type && exterior_fixed_window_building_category
- exterior_subsurfaces.setFixedWindowConstruction(find_and_add_construction(template,
- climate_zone_set,
- 'ExteriorWindow',
- exterior_fixed_window_standards_construction_type,
- category))
+ exterior_subsurfaces.setFixedWindowConstruction(model_find_and_add_construction(model,
+ climate_zone_set,
+ 'ExteriorWindow',
+ exterior_fixed_window_standards_construction_type,
+ category))
end
if exterior_operable_window_standards_construction_type && exterior_operable_window_building_category
- exterior_subsurfaces.setOperableWindowConstruction(find_and_add_construction(template,
- climate_zone_set,
- 'ExteriorWindow',
- exterior_operable_window_standards_construction_type,
- category))
+ exterior_subsurfaces.setOperableWindowConstruction(model_find_and_add_construction(model,
+ climate_zone_set,
+ 'ExteriorWindow',
+ exterior_operable_window_standards_construction_type,
+ category))
end
if exterior_door_standards_construction_type && exterior_door_building_category
- exterior_subsurfaces.setDoorConstruction(find_and_add_construction(template,
- climate_zone_set,
- 'ExteriorDoor',
- exterior_door_standards_construction_type,
- category))
+ exterior_subsurfaces.setDoorConstruction(model_find_and_add_construction(model,
+ climate_zone_set,
+ 'ExteriorDoor',
+ exterior_door_standards_construction_type,
+ category))
end
construction_name = exterior_glass_doors
unless construction_name.nil?
- exterior_subsurfaces.setGlassDoorConstruction(add_construction(construction_name))
+ exterior_subsurfaces.setGlassDoorConstruction(model_add_construction(model, construction_name))
end
if exterior_overhead_door_standards_construction_type && exterior_overhead_door_building_category
- exterior_subsurfaces.setOverheadDoorConstruction(find_and_add_construction(template,
- climate_zone_set,
- 'ExteriorDoor',
- exterior_overhead_door_standards_construction_type,
- category))
+ exterior_subsurfaces.setOverheadDoorConstruction(model_find_and_add_construction(model,
+ climate_zone_set,
+ 'ExteriorDoor',
+ exterior_overhead_door_standards_construction_type,
+ category))
end
if exterior_skylight_standards_construction_type && exterior_skylight_building_category
- exterior_subsurfaces.setSkylightConstruction(find_and_add_construction(template,
- climate_zone_set,
- 'Skylight',
- exterior_skylight_standards_construction_type,
- category))
+ exterior_subsurfaces.setSkylightConstruction(model_find_and_add_construction(model,
+ climate_zone_set,
+ 'Skylight',
+ exterior_skylight_standards_construction_type,
+ category))
end
if construction_name == tubular_daylight_domes
- exterior_subsurfaces.setTubularDaylightDomeConstruction(add_construction(construction_name))
+ exterior_subsurfaces.setTubularDaylightDomeConstruction(model_add_construction(model, construction_name))
end
if construction_name == tubular_daylight_diffusers
- exterior_subsurfaces.setTubularDaylightDiffuserConstruction(add_construction(construction_name))
+ exterior_subsurfaces.setTubularDaylightDiffuserConstruction(model_add_construction(model, construction_name))
end
# Interior sub surfaces constructions
- interior_subsurfaces = OpenStudio::Model::DefaultSubSurfaceConstructions.new(self)
+ interior_subsurfaces = OpenStudio::Model::DefaultSubSurfaceConstructions.new(model)
construction_set.setDefaultInteriorSubSurfaceConstructions(interior_subsurfaces)
if construction_name == interior_fixed_windows
- interior_subsurfaces.setFixedWindowConstruction(add_construction(construction_name))
+ interior_subsurfaces.setFixedWindowConstruction(model_add_construction(model, construction_name))
end
if construction_name == interior_operable_windows
- interior_subsurfaces.setOperableWindowConstruction(add_construction(construction_name))
+ interior_subsurfaces.setOperableWindowConstruction(model_add_construction(model, construction_name))
end
if construction_name == interior_doors
- interior_subsurfaces.setDoorConstruction(add_construction(construction_name))
+ interior_subsurfaces.setDoorConstruction(model_add_construction(model, construction_name))
end
# Other constructions
if construction_name == interior_partitions
- construction_set.setInteriorPartitionConstruction(add_construction(construction_name))
+ construction_set.setInteriorPartitionConstruction(model_add_construction(model, construction_name))
end
if construction_name == space_shading
- construction_set.setSpaceShadingConstruction(add_construction(construction_name))
+ construction_set.setSpaceShadingConstruction(model_add_construction(model, construction_name))
end
if construction_name == building_shading
- construction_set.setBuildingShadingConstruction(add_construction(construction_name))
+ construction_set.setBuildingShadingConstruction(model_add_construction(model, construction_name))
end
if construction_name == site_shading
- construction_set.setSiteShadingConstruction(add_construction(construction_name))
+ construction_set.setSiteShadingConstruction(model_add_construction(model, construction_name))
end
# componentize the construction set
# construction_set_component = construction_set.createComponent
@@ -1976,109 +1797,112 @@
# Applies the multi-zone VAV outdoor air sizing requirements
# to all applicable air loops in the model.
#
# @note This must be performed before the sizing run because
# it impacts component sizes, which in turn impact efficiencies.
- def apply_multizone_vav_outdoor_air_sizing(template)
+ def model_apply_multizone_vav_outdoor_air_sizing(model)
OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', 'Started applying multizone vav OA sizing.')
# Multi-zone VAV outdoor air sizing
- getAirLoopHVACs.sort.each { |obj| obj.apply_multizone_vav_outdoor_air_sizing(template) }
+ model.getAirLoopHVACs.sort.each { |obj| air_loop_hvac_apply_multizone_vav_outdoor_air_sizing(obj) }
OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', 'Finished applying multizone vav OA sizing.')
end
# Applies the HVAC parts of the template to all objects in the model
# using the the template specified in the model.
- def apply_hvac_efficiency_standard(template, climate_zone)
+ def model_apply_hvac_efficiency_standard(model, climate_zone)
sql_db_vars_map = {}
OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', 'Started applying HVAC efficiency standards.')
# Air Loop Controls
- getAirLoopHVACs.sort.each { |obj| obj.apply_standard_controls(template, climate_zone) }
+ model.getAirLoopHVACs.sort.each { |obj| air_loop_hvac_apply_standard_controls(obj, climate_zone) }
+ # Plant Loop Controls
+ # TODO refactor: enable this code (missing before refactor)
+ # getPlantLoops.sort.each { |obj| plant_loop_apply_standard_controls(obj, template, climate_zone) }
+
##### Apply equipment efficiencies
# Fans
- getFanVariableVolumes.sort.each { |obj| obj.apply_standard_minimum_motor_efficiency(template, obj.brake_horsepower) }
- getFanConstantVolumes.sort.each { |obj| obj.apply_standard_minimum_motor_efficiency(template, obj.brake_horsepower) }
- getFanOnOffs.sort.each { |obj| obj.apply_standard_minimum_motor_efficiency(template, obj.brake_horsepower) }
- getFanZoneExhausts.sort.each { |obj| obj.apply_standard_minimum_motor_efficiency(template, obj.brake_horsepower) }
+ model.getFanVariableVolumes.sort.each { |obj| fan_apply_standard_minimum_motor_efficiency(obj, fan_brake_horsepower(obj)) }
+ model.getFanConstantVolumes.sort.each { |obj| fan_apply_standard_minimum_motor_efficiency(obj, fan_brake_horsepower(obj)) }
+ model.getFanOnOffs.sort.each { |obj| fan_apply_standard_minimum_motor_efficiency(obj, fan_brake_horsepower(obj)) }
+ model.getFanZoneExhausts.sort.each { |obj| fan_apply_standard_minimum_motor_efficiency(obj, fan_brake_horsepower(obj)) }
# Pumps
- getPumpConstantSpeeds.sort.each { |obj| obj.apply_standard_minimum_motor_efficiency(template) }
- getPumpVariableSpeeds.sort.each { |obj| obj.apply_standard_minimum_motor_efficiency(template) }
- getHeaderedPumpsConstantSpeeds.sort.each { |obj| obj.apply_standard_minimum_motor_efficiency(template) }
- getHeaderedPumpsVariableSpeeds.sort.each { |obj| obj.apply_standard_minimum_motor_efficiency(template) }
+ model.getPumpConstantSpeeds.sort.each { |obj| pump_apply_standard_minimum_motor_efficiency(obj) }
+ model.getPumpVariableSpeeds.sort.each { |obj| pump_apply_standard_minimum_motor_efficiency(obj) }
+ model.getHeaderedPumpsConstantSpeeds.sort.each { |obj| pump_apply_standard_minimum_motor_efficiency(obj) }
+ model.getHeaderedPumpsVariableSpeeds.sort.each { |obj| pump_apply_standard_minimum_motor_efficiency(obj) }
# Unitary HPs
# set DX HP coils before DX clg coils because when DX HP coils need to first
# pull the capacities of their paried DX clg coils, and this does not work
# correctly if the DX clg coil efficiencies have been set because they are renamed.
- getCoilHeatingDXSingleSpeeds.sort.each { |obj| sql_db_vars_map = obj.apply_efficiency_and_curves(template, sql_db_vars_map) }
+ model.getCoilHeatingDXSingleSpeeds.sort.each { |obj| sql_db_vars_map = coil_heating_dx_single_speed_apply_efficiency_and_curves(obj, sql_db_vars_map) }
# Unitary ACs
- getCoilCoolingDXTwoSpeeds.sort.each { |obj| sql_db_vars_map = obj.apply_efficiency_and_curves(template, sql_db_vars_map) }
- getCoilCoolingDXSingleSpeeds.sort.each { |obj| sql_db_vars_map = obj.apply_efficiency_and_curves(template, sql_db_vars_map) }
+ model.getCoilCoolingDXTwoSpeeds.sort.each { |obj| sql_db_vars_map = coil_cooling_dx_two_speed_apply_efficiency_and_curves(obj, sql_db_vars_map) }
+ model.getCoilCoolingDXSingleSpeeds.sort.each { |obj| sql_db_vars_map = coil_cooling_dx_single_speed_apply_efficiency_and_curves(obj, sql_db_vars_map) }
# Chillers
- clg_tower_objs = getCoolingTowerSingleSpeeds
- getChillerElectricEIRs.sort.each { |obj| obj.apply_efficiency_and_curves(template, clg_tower_objs) }
+ clg_tower_objs = model.getCoolingTowerSingleSpeeds
+ model.getChillerElectricEIRs.sort.each { |obj| chiller_electric_eir_apply_efficiency_and_curves(obj, clg_tower_objs) }
# Boilers
- getBoilerHotWaters.sort.each { |obj| obj.apply_efficiency_and_curves(template) }
+ model.getBoilerHotWaters.sort.each { |obj| boiler_hot_water_apply_efficiency_and_curves(obj) }
# Water Heaters
- getWaterHeaterMixeds.sort.each { |obj| obj.apply_efficiency(template) }
+ model.getWaterHeaterMixeds.sort.each { |obj| water_heater_mixed_apply_efficiency(obj) }
# Cooling Towers
- getCoolingTowerSingleSpeeds.sort.each { |obj| obj.apply_efficiency_and_curves(template) }
- getCoolingTowerTwoSpeeds.sort.each { |obj| obj.apply_efficiency_and_curves(template) }
- getCoolingTowerVariableSpeeds.sort.each { |obj| obj.apply_efficiency_and_curves(template) }
+ model.getCoolingTowerSingleSpeeds.sort.each { |obj| cooling_tower_single_speed_apply_efficiency_and_curves(obj) }
+ model.getCoolingTowerTwoSpeeds.sort.each { |obj| cooling_tower_two_speed_apply_efficiency_and_curves(obj) }
+ model.getCoolingTowerVariableSpeeds.sort.each { |obj| cooling_tower_variable_speed_apply_efficiency_and_curves(obj) }
# ERVs
- getHeatExchangerAirToAirSensibleAndLatents.each { |obj| obj.apply_efficiency(template) }
+ model.getHeatExchangerAirToAirSensibleAndLatents.each { |obj| heat_exchanger_air_to_air_sensible_and_latent_apply_efficiency(obj) }
OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', 'Finished applying HVAC efficiency standards.')
end
# Applies daylighting controls to each space in the model
# per the standard.
- def add_daylighting_controls(template)
+ def model_add_daylighting_controls(model)
OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', 'Started adding daylighting controls.')
# Add daylighting controls to each space
- getSpaces.sort.each do |space|
- added = space.add_daylighting_controls(template, false, false)
+ model.getSpaces.sort.each do |space|
+ added = space_add_daylighting_controls(space, false, false)
end
OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', 'Finished adding daylighting controls.')
end
# Apply the air leakage requirements to the model,
- # as described in PNNL section 5.2.1.6.
+ # as described in PNNL section 5.2.1.6. This method
+ # creates customized infiltration objects for each space
+ # and removes the SpaceType-level infiltration objects.
#
# base infiltration rates off of.
# @return [Bool] true if successful, false if not
# @todo This infiltration method is not used by the Reference
# buildings, fix this inconsistency.
- def apply_infiltration_standard(template)
+ def model_apply_infiltration_standard(model)
# Set the infiltration rate at each space
- getSpaces.sort.each do |space|
- space.apply_infiltration_rate(template)
+ model.getSpaces.sort.each do |space|
+ space_apply_infiltration_rate(space)
end
- case template
- when 'DOE Ref Pre-1980', 'DOE Ref 1980-2004'
- # "For 'DOE Ref Pre-1980' and 'DOE Ref 1980-2004', infiltration rates are not defined using this method, no changes have been made to the model.
- else
- # Remove infiltration rates set at the space type. Kind of redundant for NECB 2011
- getSpaceTypes.each do |space_type|
- space_type.spaceInfiltrationDesignFlowRates.each(&:remove)
- end
+ # Remove infiltration rates set at the space type
+ model.getSpaceTypes.sort.each do |space_type|
+ space_type.spaceInfiltrationDesignFlowRates.each(&:remove)
end
+
+ return true
end
# Method to search through a hash for the objects that meets the
# desired search criteria, as passed via a hash.
# Returns an Array (empty if nothing found) of matching objects.
@@ -2088,16 +1912,16 @@
# @param capacity [Double] capacity of the object in question. If capacity is supplied,
# the objects will only be returned if the specified capacity is between
# the minimum_capacity and maximum_capacity 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 = self.find_objects($os_standards['schedules'], {'name'=>schedule_name})
+ # rules = model_find_objects(self, standards_data['schedules'], {'name'=>schedule_name})
# if rules.size == 0
# OpenStudio::logFree(OpenStudio::Warn, 'openstudio.standards.Model', "Cannot find data for schedule: #{schedule_name}, will not be created.")
# return false #TODO change to return empty optional schedule:ruleset?
# end
- def find_objects(hash_of_objects, search_criteria, capacity = nil)
+ def model_find_objects(hash_of_objects, search_criteria, capacity = nil)
# matching_objects = hash_of_objects.clone
# #new
# puts "searching"
# puts search_criteria
# raise ("hash of objects is nil or empty. #{hash_of_objects}") if hash_of_objects.nil? || hash_of_objects.empty? || matching_objects[0].nil?
@@ -2129,18 +1953,23 @@
# old
desired_object = nil
search_criteria_matching_objects = []
matching_objects = []
+ if hash_of_objects.is_a?(Hash) and hash_of_objects.key?('table')
+ hash_of_objects = hash_of_objects['table']
+ end
+
# Compare each of the objects against the search criteria
hash_of_objects.each do |object|
meets_all_search_criteria = true
search_criteria.each do |key, value|
# Don't check non-existent search criteria
next unless object.key?(key)
# Stop as soon as one of the search criteria is not met
- if object[key] != value
+ # 'Any' is a special key that matches anything
+ unless object[key] == value || object[key] == 'Any'
meets_all_search_criteria = false
break
end
end
# Skip objects that don't meet all search criteria
@@ -2171,11 +2000,11 @@
end
# If no object was found, round the capacity down a little
# to avoid issues where the number fell between the limits
# in the json file.
if matching_objects.size.zero?
- capacity = capacity * 0.99
+ 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
next if object['minimum_capacity'].nil? || object['maximum_capacity'].nil?
@@ -2222,13 +2051,16 @@
# 'template' => template,
# 'number_of_poles' => 4.0,
# 'type' => 'Enclosed',
# }
# motor_properties = self.model.find_object(motors, search_criteria, 2.5)
- def find_object(hash_of_objects, search_criteria, capacity = nil, date = nil)
- # new_matching_objects = self.find_objects(hash_of_objects, search_criteria, capacity)
+ def model_find_object(hash_of_objects, search_criteria, capacity = nil, date = nil)
+ # new_matching_objects = model_find_objects(self, hash_of_objects, search_criteria, capacity)
+ if hash_of_objects.is_a?(Hash) and hash_of_objects.key?('table')
+ hash_of_objects = hash_of_objects['table']
+ end
desired_object = nil
search_criteria_matching_objects = []
matching_objects = []
# Compare each of the objects against the search criteria
@@ -2236,11 +2068,12 @@
meets_all_search_criteria = true
search_criteria.each do |key, value|
# Don't check non-existent search criteria
next unless object.key?(key)
# Stop as soon as one of the search criteria is not met
- if object[key] != value
+ # 'Any' is a special key that matches anything
+ unless object[key] == value || object[key] == 'Any'
meets_all_search_criteria = false
break
end
end
# Skip objects that don't meet all search criteria
@@ -2271,11 +2104,11 @@
end
# If no object was found, round the capacity down a little
# to avoid issues where the number fell between the limits
# in the json file.
if matching_objects.size.zero?
- capacity = capacity * 0.99
+ 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
next if object['minimum_capacity'].nil? || object['maximum_capacity'].nil?
@@ -2319,34 +2152,33 @@
return desired_object
end
# Create constant ScheduleRuleset
#
- # @param [double] constant value
- # @param [string] name
+ # @param value [double] the value to use, 24-7, 365
+ # @param name [string] the name of the schedule
# @return schedule
- def add_constant_schedule_ruleset(value,name = nil)
- schedule = OpenStudio::Model::ScheduleRuleset.new(self)
- if not name.nil?
+ def model_add_constant_schedule_ruleset(model, value, name = nil)
+ schedule = OpenStudio::Model::ScheduleRuleset.new(model)
+ unless 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
# @todo make return an OptionalScheduleRuleset
- def add_schedule(schedule_name)
+ def model_add_schedule(model, schedule_name)
return nil if schedule_name.nil? || schedule_name == ''
# First check model and return schedule if it already exists
- getSchedules.each do |schedule|
+ model.getSchedules.sort.each do |schedule|
if schedule.name.get.to_s == schedule_name
OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.Model', "Already added schedule: #{schedule_name}")
return schedule
end
end
@@ -2354,18 +2186,20 @@
require 'date'
# OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.Model', "Adding schedule: #{schedule_name}")
# Find all the schedule rules that match the name
- rules = find_objects($os_standards['schedules'], 'name' => schedule_name)
+ rules = model_find_objects(standards_data['schedules'], 'name' => schedule_name)
if rules.size.zero?
- OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Model', "Cannot find data for schedule: #{schedule_name}, will not be created.")
- return false # TODO: change to return empty optional schedule:ruleset?
+ OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Model', "Cannot find data for schedule: #{schedule_name}, will not be created.")
+ sch_ruleset = OpenStudio::Model::ScheduleRuleset.new(model)
+ sch_ruleset.setName("NOT ACTUALLY #{schedule_name}")
+ return sch_ruleset
end
# Make a schedule ruleset
- sch_ruleset = OpenStudio::Model::ScheduleRuleset.new(self)
+ sch_ruleset = OpenStudio::Model::ScheduleRuleset.new( model )
sch_ruleset.setName(schedule_name.to_s)
# Loop through the rules, making one for each row in the spreadsheet
rules.each do |rule|
day_types = rule['day_types']
@@ -2373,34 +2207,33 @@
end_date = DateTime.parse(rule['end_date'])
sch_type = rule['type']
values = rule['values']
# Day Type choices: Wkdy, Wknd, Mon, Tue, Wed, Thu, Fri, Sat, Sun, WntrDsn, SmrDsn, Hol
-
# Default
if day_types.include?('Default')
day_sch = sch_ruleset.defaultDaySchedule
day_sch.setName("#{schedule_name} Default")
- add_vals_to_sch(day_sch, sch_type, values)
+ model_add_vals_to_sch(model, day_sch, sch_type, values)
end
# Winter Design Day
if day_types.include?('WntrDsn')
- day_sch = OpenStudio::Model::ScheduleDay.new(self)
+ 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")
- add_vals_to_sch(day_sch, sch_type, values)
+ model_add_vals_to_sch(model, day_sch, sch_type, values)
end
# Summer Design Day
if day_types.include?('SmrDsn')
- day_sch = OpenStudio::Model::ScheduleDay.new(self)
+ 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")
- add_vals_to_sch(day_sch, sch_type, values)
+ model_add_vals_to_sch(model, day_sch, sch_type, values)
end
# Other days (weekdays, weekends, etc)
if day_types.include?('Wknd') ||
day_types.include?('Wkdy') ||
@@ -2414,11 +2247,11 @@
# Make the Rule
sch_rule = OpenStudio::Model::ScheduleRule.new(sch_ruleset)
day_sch = sch_rule.daySchedule
day_sch.setName("#{schedule_name} #{day_types} Day")
- add_vals_to_sch(day_sch, sch_type, values)
+ model_add_vals_to_sch(model, day_sch, sch_type, values)
# 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))
@@ -2442,42 +2275,40 @@
sch_rule.setApplyWednesday(true) if day_types.include?('Wed')
sch_rule.setApplyThursday(true) if day_types.include?('Thu')
sch_rule.setApplyFriday(true) if day_types.include?('Fri')
sch_rule.setApplySaturday(true) if day_types.include?('Sat')
sch_rule.setApplySunday(true) if day_types.include?('Sun')
-
end
end # Next rule
-
return sch_ruleset
end
# Create a material from the openstudio standards dataset.
# @todo make return an OptionalMaterial
- def add_material(material_name)
+ def model_add_material(model, material_name)
# First check model and return material if it already exists
- getMaterials.each do |material|
+ model.getMaterials.sort.each do |material|
if material.name.get.to_s == material_name
OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.Model', "Already added material: #{material_name}")
return material
end
end
# OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.Model', "Adding material: #{material_name}")
# Get the object data
- data = find_object($os_standards['materials'], 'name' => material_name)
+ data = model_find_object(standards_data['materials'], 'name' => material_name)
unless data
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Model', "Cannot find data for material: #{material_name}, will not be created.")
return false # TODO: change to return empty optional material
end
material = nil
material_type = data['material_type']
if material_type == 'StandardOpaqueMaterial'
- material = OpenStudio::Model::StandardOpaqueMaterial.new(self)
+ 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)
material.setConductivity(OpenStudio.convert(data['conductivity'].to_f, 'Btu*in/hr*ft^2*R', 'W/m*K').get)
@@ -2486,11 +2317,11 @@
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'
- material = OpenStudio::Model::MasslessOpaqueMaterial.new(self)
+ 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.setConductivity(OpenStudio.convert(data['conductivity'].to_f, 'Btu*in/hr*ft^2*R', 'W/m*K').get)
material.setDensity(OpenStudio.convert(data['density'].to_f, 'lb/ft^3', 'kg/m^3').get)
@@ -2498,32 +2329,32 @@
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'
- material = OpenStudio::Model::AirGap.new(self)
+ 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'
- material = OpenStudio::Model::Gas.new(self)
+ 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'
- material = OpenStudio::Model::SimpleGlazing.new(self)
+ material = OpenStudio::Model::SimpleGlazing.new(model)
material.setName(material_name)
material.setUFactor(OpenStudio.convert(data['u_factor'].to_f, 'Btu/hr*ft^2*R', 'W/m^2*K').get)
material.setSolarHeatGainCoefficient(data['solar_heat_gain_coefficient'].to_f)
material.setVisibleTransmittance(data['visible_transmittance'].to_f)
elsif material_type == 'StandardGlazing'
- material = OpenStudio::Model::StandardGlazing.new(self)
+ 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)
material.setSolarTransmittanceatNormalIncidence(data['solar_transmittance_at_normal_incidence'].to_f)
@@ -2552,51 +2383,47 @@
end
# Create a construction from the openstudio standards dataset.
# If construction_props are specified, modifies the insulation layer accordingly.
# @todo make return an OptionalConstruction
- def add_construction(construction_name, construction_props = nil)
+ def model_add_construction(model, construction_name, construction_props = nil)
# First check model and return construction if it already exists
- getConstructions.each do |construction|
+ model.getConstructions.sort.each do |construction|
if construction.name.get.to_s == construction_name
OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.Model', "Already added construction: #{construction_name}")
return construction
end
end
OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.Model', "Adding construction: #{construction_name}")
# Get the object data
- data = find_object($os_standards['constructions'], 'name' => construction_name)
+ data = model_find_object(standards_data['constructions'], 'name' => construction_name)
unless data
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Model', "Cannot find data for construction: #{construction_name}, will not be created.")
return OpenStudio::Model::OptionalConstruction.new
end
# Make a new construction and set the standards details
- construction = OpenStudio::Model::Construction.new(self)
+ construction = OpenStudio::Model::Construction.new(model)
construction.setName(construction_name)
standards_info = construction.standardsInformation
intended_surface_type = data['intended_surface_type']
- unless intended_surface_type
- intended_surface_type = ''
- end
+ intended_surface_type ||= ''
standards_info.setIntendedSurfaceType(intended_surface_type)
standards_construction_type = data['standards_construction_type']
- unless standards_construction_type
- standards_construction_type = ''
- end
+ standards_construction_type ||= ''
standards_info.setStandardsConstructionType(standards_construction_type)
# TODO: could put construction rendering color in the spreadsheet
# Add the material layers to the construction
layers = OpenStudio::Model::MaterialVector.new
data['materials'].each do |material_name|
- material = add_material(material_name)
+ material = model_add_material(model, material_name)
if material
layers << material
end
end
construction.setLayers(layers)
@@ -2606,386 +2433,422 @@
if construction_props
# Determine the target U-value, C-factor, and F-factor
target_u_value_ip = construction_props['assembly_maximum_u_value']
target_f_factor_ip = construction_props['assembly_maximum_f_factor']
target_c_factor_ip = construction_props['assembly_maximum_c_factor']
+ target_shgc = construction_props['assembly_maximum_solar_heat_gain_coefficient']
+ u_includes_int_film = construction_props['u_value_includes_interior_film_coefficient']
+ u_includes_ext_film = construction_props['u_value_includes_exterior_film_coefficient']
OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.Model', "#{data['intended_surface_type']} u_val #{target_u_value_ip} f_fac #{target_f_factor_ip} c_fac #{target_c_factor_ip}")
- if target_u_value_ip && !(data['intended_surface_type'] == 'ExteriorWindow' || data['intended_surface_type'] == 'Skylight')
+ if target_u_value_ip
- # Set the U-Value
- construction.set_u_value(target_u_value_ip.to_f, data['insulation_layer'], data['intended_surface_type'], true)
+ # Handle Opaque and Fenestration Constructions differently
+ if construction.isFenestration && construction_simple_glazing?(construction)
+ # Set the U-Value and SHGC
+ construction_set_glazing_u_value(construction, target_u_value_ip.to_f, data['intended_surface_type'], u_includes_int_film, u_includes_ext_film)
+ construction_set_glazing_shgc(construction, target_shgc.to_f)
+ else # if !data['intended_surface_type'] == 'ExteriorWindow' && !data['intended_surface_type'] == 'Skylight'
+ # Set the U-Value
+ construction_set_u_value(construction, target_u_value_ip.to_f, data['insulation_layer'], data['intended_surface_type'], u_includes_int_film, u_includes_ext_film)
+ # else
+ # OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "Not modifying U-value for #{data['intended_surface_type']} u_val #{target_u_value_ip} f_fac #{target_f_factor_ip} c_fac #{target_c_factor_ip}")
+ end
elsif target_f_factor_ip && data['intended_surface_type'] == 'GroundContactFloor'
# Set the F-Factor (only applies to slabs on grade)
# TODO figure out what the prototype buildings did about ground heat transfer
- # construction.set_slab_f_factor(target_f_factor_ip.to_f, data['insulation_layer'])
- construction.set_u_value(0.0, data['insulation_layer'], data['intended_surface_type'], true)
+ # construction_set_slab_f_factor(construction, target_f_factor_ip.to_f, data['insulation_layer'])
+ construction_set_u_value(construction, 0.0, data['insulation_layer'], data['intended_surface_type'], u_includes_int_film, u_includes_ext_film)
elsif target_c_factor_ip && data['intended_surface_type'] == 'GroundContactWall'
# Set the C-Factor (only applies to underground walls)
# TODO figure out what the prototype buildings did about ground heat transfer
- # construction.set_underground_wall_c_factor(target_c_factor_ip.to_f, data['insulation_layer'])
- construction.set_u_value(0.0, data['insulation_layer'], data['intended_surface_type'], true)
+ # construction_set_underground_wall_c_factor(construction, target_c_factor_ip.to_f, data['insulation_layer'])
+ construction_set_u_value(construction, 0.0, data['insulation_layer'], data['intended_surface_type'], u_includes_int_film, u_includes_ext_film)
end
end
+ # # Check if the construction with the modified name was already in the model.
+ # # If it was, delete this new construction and return the copy already in the model.
+ # m = construction.name.get.to_s.match(/\s(\d+)/)
+ # if m
+ # revised_cons_name = construction.name.get.to_s.gsub(/\s\d+/,'')
+ # model.getConstructions.sort.each do |exist_construction|
+ # if exist_construction.name.get.to_s == revised_cons_name
+ # OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.Model', "Already added construction: #{construction_name}")
+ # # Remove the recently added construction
+ # lyrs = construction.layers
+ # # Erase the layers in the construction
+ # construction.setLayers([])
+ # # Delete unused materials
+ # lyrs.uniq.each do |lyr|
+ # if lyr.directUseCount.zero?
+ # OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Model', "Removing Material: #{lyr.name}")
+ # lyr.remove
+ # end
+ # end
+ # construction.remove # Remove the construction
+ # return exist_construction
+ # end
+ # end
+ # end
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "Adding construction #{construction.name}.")
return construction
end
# Helper method to find a particular construction and add it to the model
# after modifying the insulation value if necessary.
- def find_and_add_construction(template, climate_zone_set, intended_surface_type, standards_construction_type, building_category)
+ def model_find_and_add_construction(model, climate_zone_set, intended_surface_type, standards_construction_type, building_category)
# Get the construction properties,
# which specifies properties by construction category by climate zone set.
# AKA the info in Tables 5.5-1-5.5-8
- props = find_object($os_standards['construction_properties'], 'template' => template,
- 'climate_zone_set' => climate_zone_set,
- 'intended_surface_type' => intended_surface_type,
- 'standards_construction_type' => standards_construction_type,
- 'building_category' => building_category)
+ props = model_find_object(standards_data['construction_properties'], 'template' => template,
+ 'climate_zone_set' => climate_zone_set,
+ 'intended_surface_type' => intended_surface_type,
+ 'standards_construction_type' => standards_construction_type,
+ 'building_category' => building_category)
if !props
OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Model', "Could not find construction properties for: #{template}-#{climate_zone_set}-#{intended_surface_type}-#{standards_construction_type}-#{building_category}.")
# Return an empty construction
- construction = OpenStudio::Model::Construction.new(self)
+ construction = OpenStudio::Model::Construction.new(model)
construction.setName('Could not find construction properties set to Adiabatic ')
- almost_adiabatic = OpenStudio::Model::MasslessOpaqueMaterial.new(self, 'Smooth', 500)
+ almost_adiabatic = OpenStudio::Model::MasslessOpaqueMaterial.new(model, 'Smooth', 500)
construction.insertLayer(0, almost_adiabatic)
return construction
else
- OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.Model', "Construction properties for: #{template}-#{climate_zone_set}-#{intended_surface_type}-#{standards_construction_type}-#{building_category} = #{props}.")
+ # OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "Construction properties for: #{template}-#{climate_zone_set}-#{intended_surface_type}-#{standards_construction_type}-#{building_category} = #{props}.")
end
# Make sure that a construction is specified
if props['construction'].nil?
OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Model', "No typical construction is specified for construction properties of: #{template}-#{climate_zone_set}-#{intended_surface_type}-#{standards_construction_type}-#{building_category}. Make sure it is entered in the spreadsheet.")
# Return an empty construction
- construction = OpenStudio::Model::Construction.new(self)
+ construction = OpenStudio::Model::Construction.new(model)
construction.setName('No typical construction was specified')
return construction
end
# Add the construction, modifying properties as necessary
- construction = add_construction(props['construction'], props)
+ construction = model_add_construction(model, props['construction'], props)
return construction
end
# Create a construction set from the openstudio standards dataset.
# Returns an Optional DefaultConstructionSet
- def add_construction_set(template, clim, building_type, spc_type, is_residential)
+ def model_add_construction_set(model, clim, building_type, spc_type, is_residential)
construction_set = OpenStudio::Model::OptionalDefaultConstructionSet.new
# Find the climate zone set that this climate zone falls into
- climate_zone_set = find_climate_zone_set(clim, template)
+ climate_zone_set = model_find_climate_zone_set(model, clim)
unless climate_zone_set
return construction_set
end
# Get the object data
- data = find_object($os_standards['construction_sets'], 'template' => template, 'climate_zone_set' => climate_zone_set, 'building_type' => building_type, 'space_type' => spc_type, 'is_residential' => is_residential)
+ data = model_find_object(standards_data['construction_sets'], 'template' => template, 'climate_zone_set' => climate_zone_set, 'building_type' => building_type, 'space_type' => spc_type, 'is_residential' => is_residential)
unless data
- data = find_object($os_standards['construction_sets'], 'template' => template, 'climate_zone_set' => climate_zone_set, 'building_type' => building_type, 'space_type' => spc_type)
-
+ data = model_find_object(standards_data['construction_sets'], 'template' => template, 'climate_zone_set' => climate_zone_set, 'building_type' => building_type, 'space_type' => spc_type)
unless data
-
- # for debugging (maria)
- # puts "data = #{data}"
-
+ # if nothing matches say that we could not find it.
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', "Construction set for template =#{template}, climate zone set =#{climate_zone_set}, building type = #{building_type}, space type = #{spc_type}, is residential = #{is_residential} was not found in standards_data['construction_sets']")
return construction_set
end
-
end
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "Adding construction set: #{template}-#{clim}-#{building_type}-#{spc_type}-is_residential#{is_residential}")
- name = make_name(template, clim, building_type, spc_type)
+ name = model_make_name(model, clim, building_type, spc_type)
# Create a new construction set and name it
- construction_set = OpenStudio::Model::DefaultConstructionSet.new(self)
+ construction_set = OpenStudio::Model::DefaultConstructionSet.new(model)
construction_set.setName(name)
# Exterior surfaces constructions
- exterior_surfaces = OpenStudio::Model::DefaultSurfaceConstructions.new(self)
+ exterior_surfaces = OpenStudio::Model::DefaultSurfaceConstructions.new(model)
construction_set.setDefaultExteriorSurfaceConstructions(exterior_surfaces)
if data['exterior_floor_standards_construction_type'] && data['exterior_floor_building_category']
- exterior_surfaces.setFloorConstruction(find_and_add_construction(template,
- climate_zone_set,
- 'ExteriorFloor',
- data['exterior_floor_standards_construction_type'],
- data['exterior_floor_building_category']))
+ exterior_surfaces.setFloorConstruction(model_find_and_add_construction(model,
+ climate_zone_set,
+ 'ExteriorFloor',
+ data['exterior_floor_standards_construction_type'],
+ data['exterior_floor_building_category']))
end
if data['exterior_wall_standards_construction_type'] && data['exterior_wall_building_category']
- exterior_surfaces.setWallConstruction(find_and_add_construction(template,
- climate_zone_set,
- 'ExteriorWall',
- data['exterior_wall_standards_construction_type'],
- data['exterior_wall_building_category']))
+ exterior_surfaces.setWallConstruction(model_find_and_add_construction(model,
+ climate_zone_set,
+ 'ExteriorWall',
+ data['exterior_wall_standards_construction_type'],
+ data['exterior_wall_building_category']))
end
if data['exterior_roof_standards_construction_type'] && data['exterior_roof_building_category']
- exterior_surfaces.setRoofCeilingConstruction(find_and_add_construction(template,
- climate_zone_set,
- 'ExteriorRoof',
- data['exterior_roof_standards_construction_type'],
- data['exterior_roof_building_category']))
+ exterior_surfaces.setRoofCeilingConstruction(model_find_and_add_construction(model,
+ climate_zone_set,
+ 'ExteriorRoof',
+ data['exterior_roof_standards_construction_type'],
+ data['exterior_roof_building_category']))
end
# Interior surfaces constructions
- interior_surfaces = OpenStudio::Model::DefaultSurfaceConstructions.new(self)
+ interior_surfaces = OpenStudio::Model::DefaultSurfaceConstructions.new(model)
construction_set.setDefaultInteriorSurfaceConstructions(interior_surfaces)
construction_name = data['interior_floors']
unless construction_name.nil?
- interior_surfaces.setFloorConstruction(add_construction(construction_name))
+ interior_surfaces.setFloorConstruction(model_add_construction(model, construction_name))
end
construction_name = data['interior_walls']
unless construction_name.nil?
- interior_surfaces.setWallConstruction(add_construction(construction_name))
+ interior_surfaces.setWallConstruction(model_add_construction(model, construction_name))
end
construction_name = data['interior_ceilings']
unless construction_name.nil?
- interior_surfaces.setRoofCeilingConstruction(add_construction(construction_name))
+ interior_surfaces.setRoofCeilingConstruction(model_add_construction(model, construction_name))
end
# Ground contact surfaces constructions
- ground_surfaces = OpenStudio::Model::DefaultSurfaceConstructions.new(self)
+ ground_surfaces = OpenStudio::Model::DefaultSurfaceConstructions.new(model)
construction_set.setDefaultGroundContactSurfaceConstructions(ground_surfaces)
if data['ground_contact_floor_standards_construction_type'] && data['ground_contact_floor_building_category']
- ground_surfaces.setFloorConstruction(find_and_add_construction(template,
- climate_zone_set,
- 'GroundContactFloor',
- data['ground_contact_floor_standards_construction_type'],
- data['ground_contact_floor_building_category']))
+ ground_surfaces.setFloorConstruction(model_find_and_add_construction(model,
+ climate_zone_set,
+ 'GroundContactFloor',
+ data['ground_contact_floor_standards_construction_type'],
+ data['ground_contact_floor_building_category']))
end
if data['ground_contact_wall_standards_construction_type'] && data['ground_contact_wall_building_category']
- ground_surfaces.setWallConstruction(find_and_add_construction(template,
- climate_zone_set,
- 'GroundContactWall',
- data['ground_contact_wall_standards_construction_type'],
- data['ground_contact_wall_building_category']))
+ ground_surfaces.setWallConstruction(model_find_and_add_construction(model,
+ climate_zone_set,
+ 'GroundContactWall',
+ data['ground_contact_wall_standards_construction_type'],
+ data['ground_contact_wall_building_category']))
end
if data['ground_contact_ceiling_standards_construction_type'] && data['ground_contact_ceiling_building_category']
- ground_surfaces.setRoofCeilingConstruction(find_and_add_construction(template,
- climate_zone_set,
- 'GroundContactRoof',
- data['ground_contact_ceiling_standards_construction_type'],
- data['ground_contact_ceiling_building_category']))
+ ground_surfaces.setRoofCeilingConstruction(model_find_and_add_construction(model,
+ climate_zone_set,
+ 'GroundContactRoof',
+ data['ground_contact_ceiling_standards_construction_type'],
+ data['ground_contact_ceiling_building_category']))
end
# Exterior sub surfaces constructions
- exterior_subsurfaces = OpenStudio::Model::DefaultSubSurfaceConstructions.new(self)
+ exterior_subsurfaces = OpenStudio::Model::DefaultSubSurfaceConstructions.new(model)
construction_set.setDefaultExteriorSubSurfaceConstructions(exterior_subsurfaces)
if data['exterior_fixed_window_standards_construction_type'] && data['exterior_fixed_window_building_category']
- exterior_subsurfaces.setFixedWindowConstruction(find_and_add_construction(template,
- climate_zone_set,
- 'ExteriorWindow',
- data['exterior_fixed_window_standards_construction_type'],
- data['exterior_fixed_window_building_category']))
+ exterior_subsurfaces.setFixedWindowConstruction(model_find_and_add_construction(model,
+ climate_zone_set,
+ 'ExteriorWindow',
+ data['exterior_fixed_window_standards_construction_type'],
+ data['exterior_fixed_window_building_category']))
end
if data['exterior_operable_window_standards_construction_type'] && data['exterior_operable_window_building_category']
- exterior_subsurfaces.setOperableWindowConstruction(find_and_add_construction(template,
- climate_zone_set,
- 'ExteriorWindow',
- data['exterior_operable_window_standards_construction_type'],
- data['exterior_operable_window_building_category']))
+ exterior_subsurfaces.setOperableWindowConstruction(model_find_and_add_construction(model,
+ climate_zone_set,
+ 'ExteriorWindow',
+ data['exterior_operable_window_standards_construction_type'],
+ data['exterior_operable_window_building_category']))
end
if data['exterior_door_standards_construction_type'] && data['exterior_door_building_category']
- exterior_subsurfaces.setDoorConstruction(find_and_add_construction(template,
- climate_zone_set,
- 'ExteriorDoor',
- data['exterior_door_standards_construction_type'],
- data['exterior_door_building_category']))
+ exterior_subsurfaces.setDoorConstruction(model_find_and_add_construction(model,
+ climate_zone_set,
+ 'ExteriorDoor',
+ data['exterior_door_standards_construction_type'],
+ data['exterior_door_building_category']))
end
construction_name = data['exterior_glass_doors']
unless construction_name.nil?
- exterior_subsurfaces.setGlassDoorConstruction(add_construction(construction_name))
+ exterior_subsurfaces.setGlassDoorConstruction(model_add_construction(model, construction_name))
end
if data['exterior_overhead_door_standards_construction_type'] && data['exterior_overhead_door_building_category']
- exterior_subsurfaces.setOverheadDoorConstruction(find_and_add_construction(template,
- climate_zone_set,
- 'ExteriorDoor',
- data['exterior_overhead_door_standards_construction_type'],
- data['exterior_overhead_door_building_category']))
+ exterior_subsurfaces.setOverheadDoorConstruction(model_find_and_add_construction(model,
+ climate_zone_set,
+ 'ExteriorDoor',
+ data['exterior_overhead_door_standards_construction_type'],
+ data['exterior_overhead_door_building_category']))
end
if data['exterior_skylight_standards_construction_type'] && data['exterior_skylight_building_category']
- exterior_subsurfaces.setSkylightConstruction(find_and_add_construction(template,
- climate_zone_set,
- 'Skylight',
- data['exterior_skylight_standards_construction_type'],
- data['exterior_skylight_building_category']))
+ exterior_subsurfaces.setSkylightConstruction(model_find_and_add_construction(model,
+ climate_zone_set,
+ 'Skylight',
+ data['exterior_skylight_standards_construction_type'],
+ data['exterior_skylight_building_category']))
end
if (construction_name = data['tubular_daylight_domes'])
- exterior_subsurfaces.setTubularDaylightDomeConstruction(add_construction(construction_name))
+ exterior_subsurfaces.setTubularDaylightDomeConstruction(model_add_construction(model, construction_name))
end
if (construction_name = data['tubular_daylight_diffusers'])
- exterior_subsurfaces.setTubularDaylightDiffuserConstruction(add_construction(construction_name))
+ exterior_subsurfaces.setTubularDaylightDiffuserConstruction(model_add_construction(model, construction_name))
end
# Interior sub surfaces constructions
- interior_subsurfaces = OpenStudio::Model::DefaultSubSurfaceConstructions.new(self)
+ interior_subsurfaces = OpenStudio::Model::DefaultSubSurfaceConstructions.new(model)
construction_set.setDefaultInteriorSubSurfaceConstructions(interior_subsurfaces)
if (construction_name = data['interior_fixed_windows'])
- interior_subsurfaces.setFixedWindowConstruction(add_construction(construction_name))
+ interior_subsurfaces.setFixedWindowConstruction(model_add_construction(model, construction_name))
end
if (construction_name = data['interior_operable_windows'])
- interior_subsurfaces.setOperableWindowConstruction(add_construction(construction_name))
+ interior_subsurfaces.setOperableWindowConstruction(model_add_construction(model, construction_name))
end
if (construction_name = data['interior_doors'])
- interior_subsurfaces.setDoorConstruction(add_construction(construction_name))
+ interior_subsurfaces.setDoorConstruction(model_add_construction(model, construction_name))
end
# Other constructions
if (construction_name = data['interior_partitions'])
- construction_set.setInteriorPartitionConstruction(add_construction(construction_name))
+ construction_set.setInteriorPartitionConstruction(model_add_construction(model, construction_name))
end
if (construction_name = data['space_shading'])
- construction_set.setSpaceShadingConstruction(add_construction(construction_name))
+ construction_set.setSpaceShadingConstruction(model_add_construction(model, construction_name))
end
if (construction_name = data['building_shading'])
- construction_set.setBuildingShadingConstruction(add_construction(construction_name))
+ construction_set.setBuildingShadingConstruction(model_add_construction(model, construction_name))
end
if (construction_name = data['site_shading'])
- construction_set.setSiteShadingConstruction(add_construction(construction_name))
+ construction_set.setSiteShadingConstruction(model_add_construction(model, construction_name))
end
# componentize the construction set
# construction_set_component = construction_set.createComponent
# Return the construction set
return OpenStudio::Model::OptionalDefaultConstructionSet.new(construction_set)
end
- def add_curve(curve_name)
- # OpenStudio::logFree(OpenStudio::Info, "openstudio.prototype.addCurve", "Adding curve '#{curve_name}' to the model.")
-
- success = false
-
- curve_biquadratics = $os_standards['curve_biquadratics']
- curve_quadratics = $os_standards['curve_quadratics']
- curve_bicubics = $os_standards['curve_bicubics']
- curve_cubics = $os_standards['curve_cubics']
-
- # Make biquadratic curves
- curve_data = find_object(curve_biquadratics, 'name' => curve_name)
- if curve_data
- curve = OpenStudio::Model::CurveBiquadratic.new(self)
- curve.setName(curve_data['name'])
- curve.setCoefficient1Constant(curve_data['coeff_1'])
- curve.setCoefficient2x(curve_data['coeff_2'])
- curve.setCoefficient3xPOW2(curve_data['coeff_3'])
- curve.setCoefficient4y(curve_data['coeff_4'])
- curve.setCoefficient5yPOW2(curve_data['coeff_5'])
- curve.setCoefficient6xTIMESY(curve_data['coeff_6'])
- curve.setMinimumValueofx(curve_data['min_x'])
- curve.setMaximumValueofx(curve_data['max_x'])
- curve.setMinimumValueofy(curve_data['min_y'])
- curve.setMaximumValueofy(curve_data['max_y'])
- if curve_data['min_out']
- curve.setMinimumCurveOutput(curve_data['min_out'])
+ # Adds a curve from the OpenStudio-Standards dataset to the model
+ # based on the curve name.
+ def model_add_curve(model, curve_name)
+ # First check model and return curve if it already exists
+ existing_curves = []
+ existing_curves += model.getCurveLinears
+ existing_curves += model.getCurveCubics
+ existing_curves += model.getCurveQuadratics
+ existing_curves += model.getCurveBicubics
+ existing_curves += model.getCurveBiquadratics
+ existing_curves.sort.each do |curve|
+ if curve.name.get.to_s == curve_name
+ OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.Model', "Already added curve: #{curve_name}")
+ return curve
end
- if curve_data['max_out']
- curve.setMaximumCurveOutput(curve_data['max_out'])
- end
- success = true
- return curve
end
- # Make quadratic curves
- curve_data = find_object(curve_quadratics, 'name' => curve_name)
- if curve_data
- curve = OpenStudio::Model::CurveQuadratic.new(self)
- curve.setName(curve_data['name'])
- curve.setCoefficient1Constant(curve_data['coeff_1'])
- curve.setCoefficient2x(curve_data['coeff_2'])
- curve.setCoefficient3xPOW2(curve_data['coeff_3'])
- curve.setMinimumValueofx(curve_data['min_x'])
- curve.setMaximumValueofx(curve_data['max_x'])
- if curve_data['min_out']
- curve.setMinimumCurveOutput(curve_data['min_out'])
- end
- if curve_data['max_out']
- curve.setMaximumCurveOutput(curve_data['max_out'])
- end
- success = true
- return curve
- end
+ # OpenStudio::logFree(OpenStudio::Info, "openstudio.prototype.addCurve", "Adding curve '#{curve_name}' to the model.")
- # Make cubic curves
- curve_data = find_object(curve_cubics, 'name' => curve_name)
- if curve_data
- curve = OpenStudio::Model::CurveCubic.new(self)
- curve.setName(curve_data['name'])
- curve.setCoefficient1Constant(curve_data['coeff_1'])
- curve.setCoefficient2x(curve_data['coeff_2'])
- curve.setCoefficient3xPOW2(curve_data['coeff_3'])
- curve.setCoefficient4xPOW3(curve_data['coeff_4'])
- curve.setMinimumValueofx(curve_data['min_x'])
- curve.setMaximumValueofx(curve_data['max_x'])
- if curve_data['min_out']
- curve.setMinimumCurveOutput(curve_data['min_out'])
- end
- if curve_data['max_out']
- curve.setMaximumCurveOutput(curve_data['max_out'])
- end
- success = true
- return curve
+ # Find curve data
+ data = model_find_object(standards_data['curves'], 'name' => curve_name)
+ if data.nil?
+ OpenStudio::logFree(OpenStudio::Warn, "openstudio.Model.Model", "Could not find a curve called '#{curve_name}' in the standards.")
+ return nil
end
- # Make bicubic curves
- curve_data = find_object(curve_bicubics, 'name' => curve_name)
- if curve_data
- curve = OpenStudio::Model::CurveBicubic.new(self)
- curve.setName(curve_data['name'])
- curve.setCoefficient1Constant(curve_data['coeff_1'])
- curve.setCoefficient2x(curve_data['coeff_2'])
- curve.setCoefficient3xPOW2(curve_data['coeff_3'])
- curve.setCoefficient4y(curve_data['coeff_4'])
- curve.setCoefficient5yPOW2(curve_data['coeff_5'])
- curve.setCoefficient6xTIMESY(curve_data['coeff_6'])
- curve.setCoefficient7xPOW3(curve_data['coeff_7'])
- curve.setCoefficient8yPOW3(curve_data['coeff_8'])
- curve.setCoefficient9xPOW2TIMESY(curve_data['coeff_9'])
- curve.setCoefficient10xTIMESYPOW2(curve_data['coeff_10'])
- curve.setMinimumValueofx(curve_data['min_x'])
- curve.setMaximumValueofx(curve_data['max_x'])
- curve.setMinimumValueofy(curve_data['min_y'])
- curve.setMaximumValueofy(curve_data['max_y'])
- if curve_data['min_out']
- curve.setMinimumCurveOutput(curve_data['min_out'])
- end
- if curve_data['max_out']
- curve.setMaximumCurveOutput(curve_data['max_out'])
- end
- success = true
+ # Make the correct type of curve
+ case data['form']
+ when 'Linear'
+ curve = OpenStudio::Model::CurveLinear.new(model)
+ curve.setName(data['name'])
+ curve.setCoefficient1Constant(data['coeff_1'])
+ curve.setCoefficient2x(data['coeff_2'])
+ curve.setMinimumValueofx(data['minimum_independent_variable_1']) if data['minimum_independent_variable_1']
+ curve.setMaximumValueofx(data['maximum_independent_variable_1']) if data['maximum_independent_variable_1']
+ curve.setMinimumCurveOutput(data['minimum_dependent_variable_output']) if data['minimum_dependent_variable_output']
+ curve.setMaximumCurveOutput(data['maximum_dependent_variable_output']) if data['maximum_dependent_variable_output']
return curve
- end
-
- # Return false if the curve was not created
- if success == false
- # OpenStudio::logFree(OpenStudio::Warn, "openstudio.prototype.addCurve", "Could not find a curve called '#{curve_name}' in the standards.")
+ when 'Cubic'
+ curve = OpenStudio::Model::CurveCubic.new(model)
+ curve.setName(data['name'])
+ curve.setCoefficient1Constant(data['coeff_1'])
+ curve.setCoefficient2x(data['coeff_2'])
+ curve.setCoefficient3xPOW2(data['coeff_3'])
+ curve.setCoefficient4xPOW3(data['coeff_4'])
+ curve.setMinimumValueofx(data['minimum_independent_variable_1']) if data['minimum_independent_variable_1']
+ curve.setMaximumValueofx(data['maximum_independent_variable_1']) if data['maximum_independent_variable_1']
+ curve.setMinimumCurveOutput(data['minimum_dependent_variable_output']) if data['minimum_dependent_variable_output']
+ curve.setMaximumCurveOutput(data['maximum_dependent_variable_output']) if data['maximum_dependent_variable_output']
+ return curve
+ when 'Quadratic'
+ curve = OpenStudio::Model::CurveQuadratic.new(model)
+ curve.setName(data['name'])
+ curve.setCoefficient1Constant(data['coeff_1'])
+ curve.setCoefficient2x(data['coeff_2'])
+ curve.setCoefficient3xPOW2(data['coeff_3'])
+ curve.setMinimumValueofx(data['minimum_independent_variable_1']) if data['minimum_independent_variable_1']
+ curve.setMaximumValueofx(data['maximum_independent_variable_1']) if data['maximum_independent_variable_1']
+ curve.setMinimumCurveOutput(data['minimum_dependent_variable_output']) if data['minimum_dependent_variable_output']
+ curve.setMaximumCurveOutput(data['maximum_dependent_variable_output']) if data['maximum_dependent_variable_output']
+ return curve
+ when 'BiCubic'
+ curve = OpenStudio::Model::CurveBicubic.new(model)
+ curve.setName(data['name'])
+ curve.setCoefficient1Constant(data['coeff_1'])
+ curve.setCoefficient2x(data['coeff_2'])
+ curve.setCoefficient3xPOW2(data['coeff_3'])
+ curve.setCoefficient4y(data['coeff_4'])
+ curve.setCoefficient5yPOW2(data['coeff_5'])
+ curve.setCoefficient6xTIMESY(data['coeff_6'])
+ curve.setCoefficient7xPOW3(data['coeff_7'])
+ curve.setCoefficient8yPOW3(data['coeff_8'])
+ curve.setCoefficient9xPOW2TIMESY(data['coeff_9'])
+ curve.setCoefficient10xTIMESYPOW2(data['coeff_10'])
+ curve.setMinimumValueofx(data['minimum_independent_variable_1']) if data['minimum_independent_variable_1']
+ curve.setMaximumValueofx(data['maximum_independent_variable_1']) if data['maximum_independent_variable_1']
+ curve.setMinimumValueofy(data['minimum_independent_variable_2']) if data['minimum_independent_variable_2']
+ curve.setMaximumValueofy(data['maximum_independent_variable_2']) if data['maximum_independent_variable_2']
+ curve.setMinimumCurveOutput(data['minimum_dependent_variable_output']) if data['minimum_dependent_variable_output']
+ curve.setMaximumCurveOutput(data['maximum_dependent_variable_output']) if data['maximum_dependent_variable_output']
+ return curve
+ when 'BiQuadratic'
+ curve = OpenStudio::Model::CurveBiquadratic.new(model)
+ curve.setName(data['name'])
+ curve.setCoefficient1Constant(data['coeff_1'])
+ curve.setCoefficient2x(data['coeff_2'])
+ curve.setCoefficient3xPOW2(data['coeff_3'])
+ curve.setCoefficient4y(data['coeff_4'])
+ curve.setCoefficient5yPOW2(data['coeff_5'])
+ curve.setCoefficient6xTIMESY(data['coeff_6'])
+ curve.setMinimumValueofx(data['minimum_independent_variable_1']) if data['minimum_independent_variable_1']
+ curve.setMaximumValueofx(data['maximum_independent_variable_1']) if data['maximum_independent_variable_1']
+ curve.setMinimumValueofy(data['minimum_independent_variable_2']) if data['minimum_independent_variable_2']
+ curve.setMaximumValueofy(data['maximum_independent_variable_2']) if data['maximum_independent_variable_2']
+ curve.setMinimumCurveOutput(data['minimum_dependent_variable_output']) if data['minimum_dependent_variable_output']
+ curve.setMaximumCurveOutput(data['maximum_dependent_variable_output']) if data['maximum_dependent_variable_output']
+ return curve
+ when 'BiLinear'
+ curve = OpenStudio::Model::CurveBiquadratic.new(model)
+ curve.setName(data['name'])
+ curve.setCoefficient1Constant(data['coeff_1'])
+ curve.setCoefficient2x(data['coeff_2'])
+ curve.setCoefficient4y(data['coeff_3'])
+ curve.setMinimumValueofx(data['minimum_independent_variable_1']) if data['minimum_independent_variable_1']
+ curve.setMaximumValueofx(data['maximum_independent_variable_1']) if data['maximum_independent_variable_1']
+ curve.setMinimumValueofy(data['minimum_independent_variable_2']) if data['minimum_independent_variable_2']
+ curve.setMaximumValueofy(data['maximum_independent_variable_2']) if data['maximum_independent_variable_2']
+ curve.setMinimumCurveOutput(data['minimum_dependent_variable_output']) if data['minimum_dependent_variable_output']
+ curve.setMaximumCurveOutput(data['maximum_dependent_variable_output']) if data['maximum_dependent_variable_output']
+ return curve
+ else
+ OpenStudio::logFree(OpenStudio::Error, "openstudio.Model.Model", "#{curve_name}' has an invalid form: #{data['form']}', cannot create this curve.")
return nil
end
end
# Get the full path to the weather file that is specified in the model.
#
# @return [OpenStudio::OptionalPath]
- def get_full_weather_file_path
+ def model_get_full_weather_file_path(model)
full_epw_path = OpenStudio::OptionalPath.new
- if weatherFile.is_initialized
- epw_path = weatherFile.get.path
+ if model.weatherFile.is_initialized
+ epw_path = model.weatherFile.get.path
if epw_path.is_initialized
if File.exist?(epw_path.get.to_s)
full_epw_path = OpenStudio::OptionalPath.new(epw_path.get)
else
# If this is an always-run Measure, need to check a different path
@@ -3009,22 +2872,21 @@
# Method to gather prototype simulation results for a specific climate zone, building type, and template
#
# @param climate_zone [String] string for the ASHRAE climate zone.
# @param building_type [String] string for prototype building type.
- # @param template [String] string for prototype template to target.
# @return [Hash] Returns a hash with data presented in various bins. Returns nil if no search results
- def process_results_for_datapoint(climate_zone, building_type, template)
+ def model_process_results_for_datapoint(model, climate_zone, building_type)
# 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"
# Load the legacy idf results JSON file into a ruby hash
temp = ''
begin
- temp = load_resource_relative("../../../data/standards/legacy_idf_results.json", 'r:UTF-8')
- rescue NoMethodError
+ temp = load_resource_relative('../../../data/standards/legacy_idf_results.json', 'r:UTF-8')
+ rescue NoMethodError
temp = File.read("#{standards_data_dir}/legacy_idf_results.json")
end
legacy_idf_results = JSON.parse(temp)
# List of all fuel types
@@ -3088,13 +2950,13 @@
# Keep track of floor area for prototype buildings.
# This is used to calculate EUI's to compare against non prototype buildings
# Areas taken from scorecard Excel Files
#
- # @param [Sting] building type
+ # @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 find_prototype_floor_area(building_type)
+ def model_find_prototype_floor_area(model, building_type)
if building_type == 'FullServiceRestaurant' # 5502 ft^2
result = 511
elsif building_type == 'Hospital' # 241,410 ft^2 (including basement)
result = 22_422
elsif building_type == 'LargeHotel' # 122,132 ft^2
@@ -3136,50 +2998,50 @@
return result
end
# this is used by other methods to get the clinzte aone and building type from a model.
# it has logic to break office into small, medium or large based on building area that can be turned off
- # @param [bool] re-map small office or leave it alone
+ # @param remap_office [bool] re-map small office or leave it alone
# @return [hash] key for climate zone and building type, both values are strings
- def get_building_climate_zone_and_building_type(remap_office = true)
+ def model_get_building_climate_zone_and_building_type(model, remap_office = true)
# get climate zone from model
# get ashrae climate zone from model
climate_zone = ''
- getClimateZones.climateZones.each do |cz|
+ model.getClimateZones.climateZones.each do |cz|
if cz.institution == 'ASHRAE'
- if cz.value == '7'||cz.value == '8'
- climate_zone = "ASHRAE 169-2006-#{cz.value}A"
- else
- climate_zone = "ASHRAE 169-2006-#{cz.value}"
- end
+ climate_zone = if cz.value == '7' || cz.value == '8'
+ "ASHRAE 169-2006-#{cz.value}A"
+ else
+ "ASHRAE 169-2006-#{cz.value}"
+ end
next
end
end
# get building type from model
building_type = ''
- if getBuilding.standardsBuildingType.is_initialized
- building_type = getBuilding.standardsBuildingType.get
+ if model.getBuilding.standardsBuildingType.is_initialized
+ building_type = model.getBuilding.standardsBuildingType.get
end
# map office building type to small medium or large
if building_type == 'Office' && remap_office
- open_studio_area = getBuilding.floorArea
- building_type = self.remap_office(open_studio_area)
+ open_studio_area = model.getBuilding.floorArea
+ building_type = model_remap_office(model, 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
+ # @param floor_area [Double] floor area (m^2)
# @return [String] SmallOffice, MediumOffice, LargeOffice
- def remap_office(floor_area)
+ def model_remap_office(model, 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
@@ -3193,22 +3055,21 @@
# 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
# @return [Double] EUI (MJ/m^2) for target template for given OSM. Returns nil if can't calculate EUI
- def find_target_eui(template)
- building_data = get_building_climate_zone_and_building_type
+ def model_find_target_eui(model)
+ building_data = model_get_building_climate_zone_and_building_type(model)
climate_zone = building_data['climate_zone']
building_type = building_data['building_type']
# look up results
- target_consumption = process_results_for_datapoint(climate_zone, building_type, template)
+ target_consumption = model_process_results_for_datapoint(model, climate_zone, building_type)
# lookup target floor area for prototype buildings
- target_floor_area = find_prototype_floor_area(building_type)
+ target_floor_area = model_find_prototype_floor_area(model, building_type)
if target_consumption['total_legacy_energy_val'] > 0
if target_floor_area > 0
result = target_consumption['total_legacy_energy_val'] / target_floor_area
else
@@ -3225,22 +3086,21 @@
# 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
# @return [Hash] EUI (MJ/m^2) This will return a hash of end uses. key is end use, value is eui
- def find_target_eui_by_end_use(template)
- building_data = get_building_climate_zone_and_building_type
+ def model_find_target_eui_by_end_use(model)
+ building_data = model_get_building_climate_zone_and_building_type(model)
climate_zone = building_data['climate_zone']
building_type = building_data['building_type']
# look up results
- target_consumption = process_results_for_datapoint(climate_zone, building_type, template)
+ target_consumption = model_process_results_for_datapoint(model, climate_zone, building_type)
# lookup target floor area for prototype buildings
- target_floor_area = find_prototype_floor_area(building_type)
+ target_floor_area = model_find_prototype_floor_area(model, building_type)
if target_consumption['total_legacy_energy_val'] > 0
if target_floor_area > 0
result = {}
target_consumption['total_energy_by_end_use'].each do |end_use, consumption|
@@ -3296,15 +3156,15 @@
# Skylight
# TubularDaylightDome
# TubularDaylightDiffuser
# return [Array<OpenStudio::Model::ConstructionBase>]
# an array of all constructions.
- def find_constructions(boundary_condition, type)
+ def model_find_constructions(model, boundary_condition, type)
constructions = []
# From default construction sets
- getDefaultConstructionSets.each do |const_set|
+ model.getDefaultConstructionSets.sort.each do |const_set|
ext_surfs = const_set.defaultExteriorSurfaceConstructions
int_surfs = const_set.defaultInteriorSurfaceConstructions
gnd_surfs = const_set.defaultGroundContactSurfaceConstructions
ext_subsurfs = const_set.defaultExteriorSubSurfaceConstructions
int_subsurfs = const_set.defaultInteriorSubSurfaceConstructions
@@ -3326,57 +3186,57 @@
ext_subsurfs = ext_subsurfs.get
int_subsurfs = int_subsurfs.get
case type
# Exterior Surfaces
- when 'ExteriorWall', 'AtticWall'
- constructions << ext_surfs.wallConstruction
- when 'ExteriorFloor'
- constructions << ext_surfs.floorConstruction
- when 'ExteriorRoof', 'AtticRoof'
- constructions << ext_surfs.roofCeilingConstruction
+ when 'ExteriorWall', 'AtticWall'
+ constructions << ext_surfs.wallConstruction
+ when 'ExteriorFloor'
+ constructions << ext_surfs.floorConstruction
+ when 'ExteriorRoof', 'AtticRoof'
+ constructions << ext_surfs.roofCeilingConstruction
# Interior Surfaces
- when 'InteriorWall', 'DemisingWall', 'InteriorPartition'
- constructions << int_surfs.wallConstruction
- when 'InteriorFloor', 'AtticFloor', 'DemisingFloor'
- constructions << int_surfs.floorConstruction
- when 'InteriorCeiling', 'DemisingRoof'
- constructions << int_surfs.roofCeilingConstruction
+ when 'InteriorWall', 'DemisingWall', 'InteriorPartition'
+ constructions << int_surfs.wallConstruction
+ when 'InteriorFloor', 'AtticFloor', 'DemisingFloor'
+ constructions << int_surfs.floorConstruction
+ when 'InteriorCeiling', 'DemisingRoof'
+ constructions << int_surfs.roofCeilingConstruction
# Ground Contact Surfaces
- when 'GroundContactWall'
- constructions << gnd_surfs.wallConstruction
- when 'GroundContactFloor'
- constructions << gnd_surfs.floorConstruction
- when 'GroundContactRoof'
- constructions << gnd_surfs.roofCeilingConstruction
+ when 'GroundContactWall'
+ constructions << gnd_surfs.wallConstruction
+ when 'GroundContactFloor'
+ constructions << gnd_surfs.floorConstruction
+ when 'GroundContactRoof'
+ constructions << gnd_surfs.roofCeilingConstruction
# Exterior SubSurfaces
- when 'ExteriorWindow'
- constructions << ext_subsurfs.fixedWindowConstruction
- constructions << ext_subsurfs.operableWindowConstruction
- when 'ExteriorDoor'
- constructions << ext_subsurfs.doorConstruction
- when 'GlassDoor'
- constructions << ext_subsurfs.glassDoorConstruction
- when 'OverheadDoor'
- constructions << ext_subsurfs.overheadDoorConstruction
- when 'Skylight'
- constructions << ext_subsurfs.skylightConstruction
- when 'TubularDaylightDome'
- constructions << ext_subsurfs.tubularDaylightDomeConstruction
- when 'TubularDaylightDiffuser'
- constructions << ext_subsurfs.tubularDaylightDiffuserConstruction
+ when 'ExteriorWindow'
+ constructions << ext_subsurfs.fixedWindowConstruction
+ constructions << ext_subsurfs.operableWindowConstruction
+ when 'ExteriorDoor'
+ constructions << ext_subsurfs.doorConstruction
+ when 'GlassDoor'
+ constructions << ext_subsurfs.glassDoorConstruction
+ when 'OverheadDoor'
+ constructions << ext_subsurfs.overheadDoorConstruction
+ when 'Skylight'
+ constructions << ext_subsurfs.skylightConstruction
+ when 'TubularDaylightDome'
+ constructions << ext_subsurfs.tubularDaylightDomeConstruction
+ when 'TubularDaylightDiffuser'
+ constructions << ext_subsurfs.tubularDaylightDiffuserConstruction
# Interior SubSurfaces
- when 'InteriorWindow'
- constructions << int_subsurfs.fixedWindowConstruction
- constructions << int_subsurfs.operableWindowConstruction
- when 'InteriorDoor'
- constructions << int_subsurfs.doorConstruction
+ when 'InteriorWindow'
+ constructions << int_subsurfs.fixedWindowConstruction
+ constructions << int_subsurfs.operableWindowConstruction
+ when 'InteriorDoor'
+ constructions << int_subsurfs.doorConstruction
end
end
# Hard-assigned surfaces
- getSurfaces.each do |surf|
+ model.getSurfaces.sort.each do |surf|
next unless surf.outsideBoundaryCondition == boundary_condition
surf_type = surf.surfaceType
if surf_type == 'Floor' || surf_type == 'Wall'
next unless type.include?(surf_type)
elsif surf_type == 'RoofCeiling'
@@ -3384,11 +3244,11 @@
end
constructions << surf.construction
end
# Hard-assigned subsurfaces
- getSubSurfaces.each do |surf|
+ model.getSubSurfaces.sort.each do |surf|
next unless surf.outsideBoundaryCondition == boundary_condition
surf_type = surf.subSurfaceType
if surf_type == 'FixedWindow' || surf_type == 'OperableWindow'
next unless type == 'ExteriorWindow'
elsif surf_type == 'Door'
@@ -3419,14 +3279,13 @@
# constructions. Clone the existing constructions and set their
# intended surface type and standards construction type per
# the PRM. For some standards, this will involve making
# modifications. For others, it will not.
#
- # @param template [String] valid choices are 90.1-2004,
# 90.1-2007, 90.1-2010, 90.1-2013
# @return [Bool] returns true if successful, false if not
- def apply_prm_construction_types(template)
+ def model_apply_prm_construction_types(model)
types_to_modify = []
# Possible boundary conditions are
# Adiabatic
# Surface
@@ -3483,44 +3342,35 @@
# Glass with Curb
# Plastic with Curb
# Without Curb
# Create an array of types
+ types_to_modify << ['Outdoors', 'ExteriorWall', 'SteelFramed']
+ types_to_modify << ['Outdoors', 'ExteriorRoof', 'IEAD']
+ types_to_modify << ['Outdoors', 'ExteriorFloor', 'SteelFramed']
+ types_to_modify << ['Ground', 'GroundContactFloor', 'Unheated']
+ types_to_modify << ['Ground', 'GroundContactWall', 'Mass']
- case template
- when 'NECB 2011'
- BTAP::Compliance::NECB2011.set_all_construction_sets_to_necb!(self, runner = nil)
- return true
- else
- case template
- when '90.1-2004', '90.1-2007', '90.1-2010', '90.1-2013'
- types_to_modify << ['Outdoors', 'ExteriorWall', 'SteelFramed']
- types_to_modify << ['Outdoors', 'ExteriorRoof', 'IEAD']
- types_to_modify << ['Outdoors', 'ExteriorFloor', 'SteelFramed']
- types_to_modify << ['Ground', 'GroundContactFloor', 'Unheated']
- types_to_modify << ['Ground', 'GroundContactWall', 'Mass']
- end
- # Modify all constructions of each type
- types_to_modify.each do |boundary_cond, surf_type, const_type|
- constructions = find_constructions(boundary_cond, surf_type)
+ # Modify all constructions of each type
+ types_to_modify.each do |boundary_cond, surf_type, const_type|
+ constructions = model_find_constructions(model, boundary_cond, surf_type)
- constructions.sort.each do |const|
- standards_info = const.standardsInformation
- standards_info.setIntendedSurfaceType(surf_type)
- standards_info.setStandardsConstructionType(const_type)
- end
+ constructions.sort.each do |const|
+ standards_info = const.standardsInformation
+ standards_info.setIntendedSurfaceType(surf_type)
+ standards_info.setStandardsConstructionType(const_type)
end
- return true
end
- return false
+
+ return true
end
# Apply the standard construction to each surface in the
# model, based on the construction type currently assigned.
#
# @return [Bool] true if successful, false if not
- def apply_standard_constructions(template, climate_zone)
+ def model_apply_standard_constructions(model, climate_zone)
types_to_modify = []
# Possible boundary conditions are
# Adiabatic
# Surface
@@ -3539,48 +3389,44 @@
# Skylight
# TubularDaylightDome
# TubularDaylightDiffuser
# Create an array of surface types
- # each standard applies to.
- case template
- when '90.1-2004', '90.1-2007', '90.1-2010', '90.1-2013'
- types_to_modify << ['Outdoors', 'Floor']
- types_to_modify << ['Outdoors', 'Wall']
- types_to_modify << ['Outdoors', 'RoofCeiling']
- types_to_modify << ['Outdoors', 'FixedWindow']
- types_to_modify << ['Outdoors', 'OperableWindow']
- types_to_modify << ['Outdoors', 'Door']
- types_to_modify << ['Outdoors', 'GlassDoor']
- types_to_modify << ['Outdoors', 'OverheadDoor']
- types_to_modify << ['Outdoors', 'Skylight']
- types_to_modify << ['Ground', 'Floor']
- types_to_modify << ['Ground', 'Wall']
- end
+ types_to_modify << ['Outdoors', 'Floor']
+ types_to_modify << ['Outdoors', 'Wall']
+ types_to_modify << ['Outdoors', 'RoofCeiling']
+ types_to_modify << ['Outdoors', 'FixedWindow']
+ types_to_modify << ['Outdoors', 'OperableWindow']
+ types_to_modify << ['Outdoors', 'Door']
+ types_to_modify << ['Outdoors', 'GlassDoor']
+ types_to_modify << ['Outdoors', 'OverheadDoor']
+ types_to_modify << ['Outdoors', 'Skylight']
+ types_to_modify << ['Ground', 'Floor']
+ types_to_modify << ['Ground', 'Wall']
# Find just those surfaces
surfaces_to_modify = []
types_to_modify.each do |boundary_condition, surface_type|
# Surfaces
- getSurfaces.each do |surf|
+ model.getSurfaces.sort.each do |surf|
next unless surf.outsideBoundaryCondition == boundary_condition
next unless surf.surfaceType == surface_type
surfaces_to_modify << surf
end
# SubSurfaces
- getSubSurfaces.each do |surf|
+ model.getSubSurfaces.sort.each do |surf|
next unless surf.outsideBoundaryCondition == boundary_condition
next unless surf.subSurfaceType == surface_type
surfaces_to_modify << surf
end
end
# Modify these surfaces
prev_created_consts = {}
surfaces_to_modify.sort.each do |surf|
- prev_created_consts = surf.apply_standard_construction(template, climate_zone, prev_created_consts)
+ prev_created_consts = planar_surface_apply_standard_construction(surf, climate_zone, prev_created_consts)
end
# List the unique array of constructions
if prev_created_consts.size.zero?
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Model', 'None of the constructions in your proposed model have both Intended Surface Type and Standards Construction Type')
@@ -3593,19 +3439,18 @@
return true
end
# Returns standards data for selected construction
#
- # @param [string] target template for lookup
- # @param [string] intended_surface_type template for lookup
- # @param [string] standards_construction_type template for lookup
- # @param [string] building_category template for lookup
+ # @param intended_surface_type [string] the surface type
+ # @param standards_construction_type [string] the type of construction
+ # @param building_category [string] the type of building
# @return [hash] hash of construction properties
- def get_construction_properties(template, intended_surface_type, standards_construction_type, building_category = 'Nonresidential')
+ def model_get_construction_properties(model, intended_surface_type, standards_construction_type, building_category = 'Nonresidential')
# get climate_zone_set
- climate_zone = get_building_climate_zone_and_building_type['climate_zone']
- climate_zone_set = find_climate_zone_set(climate_zone, template)
+ climate_zone = model_get_building_climate_zone_and_building_type(model)['climate_zone']
+ climate_zone_set = model_find_climate_zone_set(model, climate_zone)
# populate search hash
search_criteria = {
'template' => template,
'climate_zone_set' => climate_zone_set,
@@ -3613,11 +3458,11 @@
'standards_construction_type' => standards_construction_type,
'building_category' => building_category
}
# switch to use this but update test in standards and measures to load this outside of the method
- construction_properties = find_object($os_standards['construction_properties'], search_criteria)
+ construction_properties = model_find_object(standards_data['construction_properties'], search_criteria)
return construction_properties
end
# Reduces the WWR to the values specified by the PRM. WWR reduction
@@ -3629,11 +3474,11 @@
# @todo support 90.1-2004 requirement that windows be modeled as
# horizontal bands. Currently just using existing window geometry,
# and shrinking as necessary if WWR is above limit.
# @todo support semiheated spaces as a separate WWR category
# @todo add window frame area to calculation of WWR
- def apply_prm_baseline_window_to_wall_ratio(template, climate_zone)
+ def model_apply_prm_baseline_window_to_wall_ratio(model, climate_zone)
# Loop through all spaces in the model, and
# per the PNNL PRM Reference Manual, find the areas
# of each space conditioning category (res, nonres, semi-heated)
# separately. Include space multipliers.
nr_wall_m2 = 0.001 # Avoids divide by zero errors later
@@ -3644,11 +3489,11 @@
sh_wind_m2 = 0
total_wall_m2 = 0.001
total_subsurface_m2 = 0.0
# Store the space conditioning category for later use
space_cats = {}
- getSpaces.each do |space|
+ model.getSpaces.sort.each do |space|
# Loop through all surfaces in this space
wall_area_m2 = 0
wind_area_m2 = 0
space.surfaces.sort.each do |surface|
# Skip non-outdoor surfaces
@@ -3657,72 +3502,63 @@
next unless surface.surfaceType.casecmp('wall').zero?
# This wall's gross area (including window area)
wall_area_m2 += surface.grossArea * space.multiplier
# Subsurfaces in this surface
surface.subSurfaces.sort.each do |ss|
- if 'NECB 2011' == template
- wind_area_m2 += ss.netArea * space.multiplier
- elsif ss.subSurfaceType == 'FixedWindow' || ss.subSurfaceType == 'OperableWindow'
- wind_area_m2 += ss.netArea * space.multiplier
- else
- next
- end
+ next unless ss.subSurfaceType == 'FixedWindow' || ss.subSurfaceType == 'OperableWindow'
+ wind_area_m2 += ss.netArea * space.multiplier
end
end
# Determine the space category
# TODO This should really use the heating/cooling loads
# from the proposed building. However, in an attempt
# to avoid another sizing run just for this purpose,
# conditioned status is based on heating/cooling
# setpoints. If heated-only, will be assumed Semiheated.
# The full-bore method is on the next line in case needed.
- # cat = space.conditioning_category(template, climate_zone)
- cooled = space.cooled?
- heated = space.heated?
+ # cat = thermal_zone_conditioning_category(space, template, climate_zone)
+ cooled = space_cooled?(space)
+ heated = space_heated?(space)
cat = 'Unconditioned'
# Unconditioned
if !heated && !cooled
cat = 'Unconditioned'
- # Heated-Only
+ # Heated-Only
elsif heated && !cooled
cat = 'Semiheated'
- # Heated and Cooled
+ # Heated and Cooled
else
- res = space.residential?(template)
+ res = space_residential?(space)
cat = if res
'ResConditioned'
else
'NonResConditioned'
end
end
space_cats[space] = cat
# Add to the correct category
case cat
- when 'Unconditioned'
- next # Skip unconditioned spaces
- when 'NonResConditioned'
- nr_wall_m2 += wall_area_m2
- nr_wind_m2 += wind_area_m2
- when 'ResConditioned'
- res_wall_m2 += wall_area_m2
- res_wind_m2 += wind_area_m2
- when 'Semiheated'
- sh_wall_m2 += wall_area_m2
- sh_wind_m2 += wind_area_m2
+ when 'Unconditioned'
+ next # Skip unconditioned spaces
+ when 'NonResConditioned'
+ nr_wall_m2 += wall_area_m2
+ nr_wind_m2 += wind_area_m2
+ when 'ResConditioned'
+ res_wall_m2 += wall_area_m2
+ res_wind_m2 += wind_area_m2
+ when 'Semiheated'
+ sh_wall_m2 += wall_area_m2
+ sh_wind_m2 += wind_area_m2
end
- # keep track of totals for NECB
- total_wall_m2 += wall_area_m2
- total_subsurface_m2 += wind_area_m2 # this contains doors as well.
end
# Calculate the WWR of each category
wwr_nr = ((nr_wind_m2 / nr_wall_m2) * 100.0).round(1)
wwr_res = ((res_wind_m2 / res_wall_m2) * 100).round(1)
wwr_sh = ((sh_wind_m2 / sh_wall_m2) * 100).round(1)
- fdwr = ((total_subsurface_m2 / total_wall_m2) * 100).round(1) # used by NECB 2011
# Convert to IP and report
nr_wind_ft2 = OpenStudio.convert(nr_wind_m2, 'm^2', 'ft^2').get
nr_wall_ft2 = OpenStudio.convert(nr_wall_m2, 'm^2', 'ft^2').get
@@ -3738,58 +3574,30 @@
# WWR limit
wwr_lim = 40.0
# Check against WWR limit
- red_nr = wwr_nr > wwr_lim ? true : false
- red_res = wwr_res > wwr_lim ? true : false
- red_sh = wwr_sh > wwr_lim ? true : false
+ red_nr = wwr_nr > wwr_lim
+ red_res = wwr_res > wwr_lim
+ red_sh = wwr_sh > wwr_lim
- case template
- when 'NECB 2011'
- # NECB FDWR limit
- hdd = BTAP::Environment::WeatherFile.new(weatherFile.get.path.get).hdd18
- fdwr_lim = (BTAP::Compliance::NECB2011.max_fwdr(hdd) * 100.0).round(1)
+ # Stop here unless windows need reducing
+ return true unless red_nr || red_res || red_sh
- # Stop here unless windows / doors need reducing
- return true unless fdwr > fdwr_lim
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "Reducing the size of all windows (by raising sill height) to reduce window area down to the limit of #{wwr_lim.round}%.")
- # Determine the factors by which to reduce the window / door area
- mult = fdwr_lim / fdwr
- # Reduce the window area if any of the categories necessary
- getSpaces.each do |space|
- # Loop through all surfaces in this space
- space.surfaces.sort.each do |surface|
- # Skip non-outdoor surfaces
- next unless surface.outsideBoundaryCondition == 'Outdoors'
- # Skip non-walls
- next unless surface.surfaceType == 'Wall'
- # Subsurfaces in this surface
- surface.subSurfaces.sort.each do |ss|
- # Reduce the size of the window
- red = 1.0 - mult
- ss.reduce_area_by_percent_by_raising_sill(red)
- end
- end
- end
- else # all other template types
- # Stop here unless windows need reducing
- return true unless red_nr || red_res || red_sh
+ # Determine the factors by which to reduce the window area
+ mult_nr_red = wwr_lim / wwr_nr
+ mult_res_red = wwr_lim / wwr_res
+ mult_sh_red = wwr_lim / wwr_sh
- # Determine the factors by which to reduce the window area
- mult_nr_red = wwr_lim / wwr_nr
- mult_res_red = wwr_lim / wwr_res
- mult_sh_red = wwr_lim / wwr_sh
+ # Reduce the window area if any of the categories necessary
+ model.getSpaces.sort.each do |space|
+ # Determine the space category
+ # from the previously stored values
+ cat = space_cats[space]
- # Reduce the window area if any of the categories necessary
- getSpaces.each do |space|
- # Determine the space category
- # from the previously stored values
- cat = space_cats[space]
-
- # Get the correct multiplier
- case cat
+ # Get the correct multiplier
+ case cat
when 'Unconditioned'
next # Skip unconditioned spaces
when 'NonResConditioned'
next unless red_nr
mult = mult_nr_red
@@ -3797,46 +3605,44 @@
next unless red_res
mult = mult_res_red
when 'Semiheated'
next unless red_sh
mult = mult_sh_red
- end
+ end
- # 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.casecmp('wall').zero?
- # Subsurfaces in this surface
- surface.subSurfaces.sort.each do |ss|
- next unless ss.subSurfaceType == 'FixedWindow' || ss.subSurfaceType == 'OperableWindow'
- # Reduce the size of the window
- # If a vertical rectangle, raise sill height to avoid
- # impacting daylighting areas, otherwise
- # reduce toward centroid.
- red = 1.0 - mult
- if ss.vertical_rectangle?
- ss.reduce_area_by_percent_by_raising_sill(red)
- else
- ss.reduce_area_by_percent_by_shrinking_toward_centroid(red)
- end
+ # 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.casecmp('wall').zero?
+ # Subsurfaces in this surface
+ surface.subSurfaces.sort.each do |ss|
+ next unless ss.subSurfaceType == 'FixedWindow' || ss.subSurfaceType == 'OperableWindow'
+ # Reduce the size of the window
+ # If a vertical rectangle, raise sill height to avoid
+ # impacting daylighting areas, otherwise
+ # reduce toward centroid.
+ red = 1.0 - mult
+ if sub_surface_vertical_rectangle?(ss)
+ sub_surface_reduce_area_by_percent_by_raising_sill(ss, red)
+ else
+ sub_surface_reduce_area_by_percent_by_shrinking_toward_centroid(ss, red)
end
end
end
-
end
return true
end
# Reduces the SRR to the values specified by the PRM. SRR reduction
# will be done by shrinking vertices toward the centroid.
#
# @todo support semiheated spaces as a separate SRR category
# @todo add skylight frame area to calculation of SRR
- def apply_prm_baseline_skylight_to_roof_ratio(template)
+ def model_apply_prm_baseline_skylight_to_roof_ratio(model)
# Loop through all spaces in the model, and
# per the PNNL PRM Reference Manual, find the areas
# of each space conditioning category (res, nonres, semi-heated)
# separately. Include space multipliers.
nr_wall_m2 = 0.001 # Avoids divide by zero errors later
@@ -3845,11 +3651,11 @@
res_sky_m2 = 0
sh_wall_m2 = 0.001
sh_sky_m2 = 0
total_roof_m2 = 0.001
total_subsurface_m2 = 0
- getSpaces.each do |space|
+ model.getSpaces.sort.each do |space|
# Loop through all surfaces in this space
wall_area_m2 = 0
sky_area_m2 = 0
space.surfaces.sort.each do |surface|
# Skip non-outdoor surfaces
@@ -3858,35 +3664,35 @@
next unless surface.surfaceType == 'RoofCeiling'
# This wall's gross area (including skylight area)
wall_area_m2 += surface.grossArea * space.multiplier
# Subsurfaces in this surface
surface.subSurfaces.sort.each do |ss|
- next unless 'NECB 2011' == template || (ss.subSurfaceType == 'Skylight')
+ next unless ss.subSurfaceType == 'Skylight'
sky_area_m2 += ss.netArea * space.multiplier
end
end
# Determine the space category
cat = 'NonRes'
- if space.residential?(template)
+ if space_residential?(space)
cat = 'Res'
end
# if space.is_semiheated
# cat = 'Semiheated'
# end
# Add to the correct category
case cat
- when 'NonRes'
- nr_wall_m2 += wall_area_m2
- nr_sky_m2 += sky_area_m2
- when 'Res'
- res_wall_m2 += wall_area_m2
- res_sky_m2 += sky_area_m2
- when 'Semiheated'
- sh_wall_m2 += wall_area_m2
- sh_sky_m2 += sky_area_m2
+ when 'NonRes'
+ nr_wall_m2 += wall_area_m2
+ nr_sky_m2 += sky_area_m2
+ when 'Res'
+ res_wall_m2 += wall_area_m2
+ res_sky_m2 += sky_area_m2
+ when 'Semiheated'
+ sh_wall_m2 += wall_area_m2
+ sh_sky_m2 += sky_area_m2
end
total_roof_m2 += wall_area_m2
total_subsurface_m2 += sky_area_m2
end
@@ -3896,140 +3702,115 @@
srr_sh = ((sh_sky_m2 / sh_wall_m2) * 100).round(1)
srr = ((total_subsurface_m2 / total_roof_m2) * 100.0).round(1)
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "The skylight to roof ratios (SRRs) are: NonRes: #{srr_nr.round}%, Res: #{srr_res.round}%.")
# SRR limit
- srr_lim = nil
- case template
- when '90.1-2004', '90.1-2007', '90.1-2010', 'NECB 2011'
- srr_lim = 5.0
- when '90.1-2013'
- srr_lim = 3.0
- end
+ srr_lim = model_prm_skylight_to_roof_ratio_limit(model)
# Check against SRR limit
- red_nr = srr_nr > srr_lim ? true : false
- red_res = srr_res > srr_lim ? true : false
- red_sh = srr_sh > srr_lim ? true : false
+ red_nr = srr_nr > srr_lim
+ red_res = srr_res > srr_lim
+ red_sh = srr_sh > srr_lim
- case template
- when 'NECB 2011'
- # Stop here unless windows need reducing
- return true unless srr > srr_lim
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "Reducing the size of all windows (by raising sill height) to reduce window area down to the limit of #{srr_lim.round}%.")
- # Determine the factors by which to reduce the window / door area
- mult = srr_lim / srr
+ # Stop here unless skylights need reducing
+ return true unless red_nr || red_res || red_sh
- # Reduce the subsurface areas
- getSpaces.each do |space|
- # Loop through all surfaces in this space
- space.surfaces.sort.each do |surface|
- # Skip non-outdoor surfaces
- next unless surface.outsideBoundaryCondition == 'Outdoors'
- # Skip non-walls
- next unless surface.surfaceType == 'RoofCeiling'
- # Subsurfaces in this surface
- surface.subSurfaces.sort.each do |ss|
- # Reduce the size of the subsurface
- red = 1.0 - mult
- ss.reduce_area_by_percent_by_shrinking_toward_centroid(red)
- end
- end
- end
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "Reducing the size of all skylights equally down to the limit of #{srr_lim.round}%.")
- else
+ # Determine the factors by which to reduce the skylight area
+ mult_nr_red = srr_lim / srr_nr
+ mult_res_red = srr_lim / srr_res
+ # mult_sh_red = srr_lim / srr_sh
- # Stop here unless skylights need reducing
- return true unless red_nr || red_res || red_sh
+ # Reduce the skylight area if any of the categories necessary
+ model.getSpaces.sort.each do |space|
+ # Determine the space category
+ cat = 'NonRes'
+ if space_residential?(space)
+ cat = 'Res'
+ end
+ # if space.is_semiheated
+ # cat = 'Semiheated'
+ # end
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "Reducing the size of all skylights equally down to the limit of #{srr_lim.round}%.")
-
- # Determine the factors by which to reduce the skylight area
- mult_nr_red = srr_lim / srr_nr
- mult_res_red = srr_lim / srr_res
- # mult_sh_red = srr_lim / srr_sh
-
- # Reduce the skylight area if any of the categories necessary
- getSpaces.each do |space|
- # Determine the space category
- cat = 'NonRes'
- if space.residential?(template)
- cat = 'Res'
- end
- # if space.is_semiheated
- # cat = 'Semiheated'
- # end
-
- # Skip spaces whose skylights don't need to be reduced
- case cat
+ # Skip spaces whose skylights don't need to be reduced
+ case cat
when 'NonRes'
next unless red_nr
mult = mult_nr_red
when 'Res'
next unless red_res
mult = mult_res_red
when 'Semiheated'
next unless red_sh
- # mult = mult_sh_red
- end
+ # mult = mult_sh_red
+ end
- # Loop through all surfaces in this space
- space.surfaces.sort.each do |surface|
- # Skip non-outdoor surfaces
- next unless surface.outsideBoundaryCondition == 'Outdoors'
- # Skip non-walls
- next unless surface.surfaceType == 'RoofCeiling'
- # Subsurfaces in this surface
- surface.subSurfaces.sort.each do |ss|
- next unless ss.subSurfaceType == 'Skylight'
- # Reduce the size of the skylight
- red = 1.0 - mult
- ss.reduce_area_by_percent_by_shrinking_toward_centroid(red)
+ # Loop through all surfaces in this space
+ space.surfaces.sort.each do |surface|
+ # Skip non-outdoor surfaces
+ next unless surface.outsideBoundaryCondition == 'Outdoors'
+ # Skip non-walls
+ next unless surface.surfaceType == 'RoofCeiling'
+ # Subsurfaces in this surface
+ surface.subSurfaces.sort.each do |ss|
+ next unless ss.subSurfaceType == 'Skylight'
+ # Reduce the size of the skylight
+ red = 1.0 - mult
+ sub_surface_reduce_area_by_percent_by_shrinking_toward_centroid(ss, red)
end
- end
end
- end # template case
+ end
+
return true
end
+ # Determines the skylight to roof ratio limit for a given standard
+ # @return [Double] the skylight to roof ratio, as a percent: 5.0 = 5%
+ # 5% by default.
+ def model_prm_skylight_to_roof_ratio_limit(model)
+ srr_lim = 5.0
+ return srr_lim
+ end
+
# Remove all HVAC that will be replaced during the
# performance rating method baseline generation.
# This does not include plant loops that serve
# WaterUse:Equipment or Fan:ZoneExhaust
#
# @return [Bool] true if successful, false if not
- def remove_prm_hvac
+ def model_remove_prm_hvac(model)
# Plant loops
- getPlantLoops.each do |loop|
+ model.getPlantLoops.sort.each do |loop|
# Don't remove service water heating loops
- next if loop.swh_loop?
+ next if plant_loop_swh_loop?(loop)
loop.remove
end
# Air loops
- getAirLoopHVACs.each(&:remove)
+ model.getAirLoopHVACs.each(&:remove)
# Zone equipment
- getThermalZones.each do |zone|
+ model.getThermalZones.sort.each do |zone|
zone.equipment.each do |zone_equipment|
next if zone_equipment.to_FanZoneExhaust.is_initialized
zone_equipment.remove
end
end
# Outdoor VRF units (not in zone, not in loops)
- getAirConditionerVariableRefrigerantFlows.each(&:remove)
+ model.getAirConditionerVariableRefrigerantFlows.each(&:remove)
return true
end
# Remove external shading devices.
# Site shading will not be impacted.
# @return [Bool] returns true if successful, false if not.
- def remove_external_shading_devices
+ def model_remove_external_shading_devices(model)
shading_surfaces_removed = 0
- getShadingSurfaceGroups.each do |shade_group|
+ model.getShadingSurfaceGroups.sort.each do |shade_group|
# Skip Site shading
next if shade_group.shadingSurfaceType == 'Site'
# Space shading surfaces should be removed
shading_surfaces_removed += shade_group.shadingSurfaces.size
shade_group.remove
@@ -4039,43 +3820,42 @@
return true
end
# Changes the sizing parameters to the PRM specifications.
- def apply_prm_sizing_parameters
-
+ def model_apply_prm_sizing_parameters(model)
clg = 1.15
htg = 1.25
- sizing_params = getSizingParameters
+ sizing_params = model.getSizingParameters
sizing_params.setHeatingSizingFactor(htg)
sizing_params.setCoolingSizingFactor(clg)
OpenStudio.logFree(OpenStudio::Info, 'openstudio.prototype.Model', "Set sizing factors to #{htg} for heating and #{clg} for cooling.")
end
-
+
# Helper method to get the story object that
# cooresponds to a specific minimum z value.
# Makes a new story if none found at this height.
#
#
# @param minz [Double] the z value (height) of the
# desired story, in meters.
# @param tolerance [Double] tolerance for comparison, in m.
# Default is 0.3 m ~1ft
# @return [OpenStudio::Model::BuildingStory] the story
- def get_story_for_nominal_z_coordinate(minz, tolerance = 0.3)
- getBuildingStorys.each do |story|
- z = story.minimum_z_value
+ def model_get_story_for_nominal_z_coordinate(model, minz, tolerance = 0.3)
+ model.getBuildingStorys.sort.each do |story|
+ z = building_story_minimum_z_value(story)
if (minz - z).abs < tolerance
OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.Model', "The story with a min z value of #{minz.round(2)} is #{story.name}.")
return story
end
end
- story = OpenStudio::Model::BuildingStory.new(self)
+ story = OpenStudio::Model::BuildingStory.new(model)
story.setNominalZCoordinate(minz)
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Model', "No story with a min z value of #{minz.round(2)} m +/- #{tolerance} m was found, so a new story called #{story.name} was created.")
return story
end
@@ -4087,15 +3867,15 @@
# Will return an array of hashes. Many may have one array entry.
# all values other than block size are gallons.
#
# @return [Array] array of hashes. Each array entry based on different capacity
# specific to building type. Array will be empty for some building types.
- def find_ashrae_hot_water_demand
+ def model_find_ashrae_hot_water_demand(model)
# TODO: - for types not in table use standards area normalized swh values
# get building type
- building_data = get_building_climate_zone_and_building_type
+ building_data = model_get_building_climate_zone_and_building_type(model)
building_type = building_data['building_type']
result = []
if building_type == 'FullServiceRestaurant'
result << { units: 'meal', block: nil, max_hourly: 1.5, max_daily: 11.0, avg_day_unit: 2.4 }
@@ -4139,33 +3919,33 @@
# Returns average daily hot water consumption for residential buildings
# gal/day from ICC IECC 2015 Residential Standard Reference Design
# from Table R405.5.2(1)
#
# @return [Double] gal/day
- def find_icc_iecc_2015_hot_water_demand(units_per_bldg, bedrooms_per_unit)
+ def model_find_icc_iecc_2015_hot_water_demand(model, units_per_bldg, bedrooms_per_unit)
swh_gal_per_day = units_per_bldg * (30.0 + (10.0 * bedrooms_per_unit))
return swh_gal_per_day
end
# Returns average daily internal loads for residential buildings
# from Table R405.5.2(1)
#
# @return [Hash] mech_vent_cfm, infiltration_ach, igain_btu_per_day, internal_mass_lbs
- def find_icc_iecc_2015_internal_loads(units_per_bldg, bedrooms_per_unit)
+ def model_find_icc_iecc_2015_internal_loads(model, units_per_bldg, bedrooms_per_unit)
# get total and conditioned floor area
- total_floor_area = getBuilding.floorArea
- if getBuilding.conditionedFloorArea.is_initialized
- conditioned_floor_area = getBuilding.conditionedFloorArea.get
+ total_floor_area = model.getBuilding.floorArea
+ if model.getBuilding.conditionedFloorArea.is_initialized
+ conditioned_floor_area = model.getBuilding.conditionedFloorArea.get
else
OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Model', 'Cannot find conditioned floor area, will use total floor area.')
conditioned_floor_area = total_floor_area
end
# get climate zone value
climate_zone_value = ''
- getClimateZones.climateZones.each do |cz|
+ model.getClimateZones.climateZones.each do |cz|
if cz.institution == 'ASHRAE'
climate_zone_value = cz.value
next
end
end
@@ -4183,11 +3963,11 @@
return internal_loads
end
# Helper method to make a shortened version of a name
# that will be readable in a GUI.
- def make_name(template, clim, building_type, spc_type)
+ def model_make_name(model, clim, building_type, spc_type)
clim = clim.gsub('ClimateZone ', 'CZ')
if clim == 'CZ1-8'
clim = ''
end
@@ -4248,62 +4028,54 @@
return result
end
# Helper method to find out which climate zone set contains a specific climate zone.
# Returns climate zone set name as String if success, nil if not found.
- def find_climate_zone_set(clim, template)
+ def model_find_climate_zone_set(model, clim)
result = nil
- possible_climate_zones = []
- $os_standards['climate_zone_sets'].each do |climate_zone_set|
+ possible_climate_zone_sets = []
+ standards_data['climate_zone_sets'].each do |climate_zone_set|
if climate_zone_set['climate_zones'].include?(clim)
- possible_climate_zones << climate_zone_set['name']
+ possible_climate_zone_sets << climate_zone_set['name']
end
end
# Check the results
- if possible_climate_zones.size.zero?
+ if possible_climate_zone_sets.size.zero?
OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Model', "Cannot find a climate zone set containing #{clim}")
- elsif possible_climate_zones.size > 2
+ elsif possible_climate_zone_sets.size > 2
OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Model', "Found more than 2 climate zone sets containing #{clim}; will return last matching cliimate zone set.")
end
- # For Pre-1980 and 1980-2004, use the most specific climate zone set.
- # For example, 2A and 2 both contain 2A, so use 2A.
- # For 2004-2013, use least specific climate zone set.
- # For example, 2A and 2 both contain 2A, so use 2.
- case template
- when 'DOE Ref Pre-1980', 'DOE Ref 1980-2004'
- result = possible_climate_zones.sort.last
- when '90.1-2007', '90.1-2010', '90.1-2013', 'NECB 2011'
- result = possible_climate_zones.sort.first
- when '90.1-2004'
- result = if possible_climate_zones.include? 'ClimateZone 3'
- possible_climate_zones.sort.last
- else
- possible_climate_zones.sort.first
- end
- when 'ICC IECC 2015', 'OEESC 2014'
- result = possible_climate_zones.sort.first
- end
+ # Get the climate zone from the possible set
+ climate_zone_set = model_get_climate_zone_set_from_list(model, possible_climate_zone_sets)
# Check that a climate zone set was found
- if result.nil?
+ if climate_zone_set.nil?
OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Model', "Cannot find a climate zone set when #{template}")
end
- return result
+ return climate_zone_set
end
+ # Determine which climate zone to use.
+ # Defaults to the least specific climate zone set.
+ # For example, 2A and 2 both contain 2A, so use 2.
+ def model_get_climate_zone_set_from_list(model, possible_climate_zone_sets)
+ climate_zone_set = possible_climate_zone_sets.sort.first
+ return climate_zone_set
+ end
+
# This method ensures that all spaces with spacetypes defined contain at least
# a standardSpaceType appropriate for the template. So, if any space
- # with a space type defined does not have a NECB spacetype, or is undefined, an error will stop
+ # with a space type defined does not have a Stnadard spacetype, or is undefined, an error will stop
# with information that the spacetype needs to be defined.
- def validate_standards_spacetypes_in_model(template)
+ def model_validate_standards_spacetypes_in_model(model)
error_string = ''
# populate search hash
- getSpaces.each do |space|
+ model.getSpaces.sort.each do |space|
unless space.spaceType.empty?
if space.spaceType.get.standardsSpaceType.empty? || space.spaceType.get.standardsBuildingType.empty?
error_string << "Space: #{space.name} has SpaceType of #{space.spaceType.get.name} but the standardSpaceType or standardBuildingType is undefined. Please use an appropriate standardSpaceType for #{template}\n"
next
else
@@ -4311,19 +4083,19 @@
'template' => template,
'building_type' => space.spaceType.get.standardsBuildingType.get,
'space_type' => space.spaceType.get.standardsSpaceType.get
}
# lookup space type properties
- space_type_properties = @model.find_object($os_standards['space_types'], search_criteria)
+ space_type_properties = model_find_object(standards_data['space_types'], search_criteria)
if space_type_properties.nil?
error_string << "Could not find spacetype of criteria : #{search_criteria}. Please ensure you have a valid standardSpaceType and stantdardBuildingType defined.\n"
space_type_properties = {}
end
end
end
end
- if '' == error_string
+ if error_string == ''
return true
else
OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Model', error_string)
return false
end
@@ -4331,32 +4103,29 @@
# 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
-
+ def model_create_story_hash(model)
story_hash = {}
# loop through stories
- self.getBuildingStorys.each do |story|
-
+ model.getBuildingStorys.sort.each do |story|
# skip of story doesn't have any spaces
- next if story.spaces.size == 0
+ next if story.spaces.empty?
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
+ next if space.surfaces.empty?
# get space multiplier
story_zone_multipliers << space.multiplier
# space part of floor area check
@@ -4375,105 +4144,94 @@
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
+ next if surface.surfaceType != 'Wall'
+ next if surface.outsideBoundaryCondition != 'Ground' # TODO: - make more flexible for slab/basement model.modeling
story_ground_wall_area += surface.grossArea
-
end
# skip if surface had no vertices
- next if z_points.size == 0
+ next if z_points.empty?
# update story min_z
space_min_z = z_points.min + space.zOrigin
- if story_min_z.nil? or story_min_z > space_min_z
+ if story_min_z.nil? || (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]}
+ 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
-
+ def model_effective_num_stories(model)
below_grade = 0
above_grade = 0
- # call create_story_hash
- story_hash = self.create_story_hash
+ # call model_create_story_hash(model)
+ story_hash = model_create_story_hash(model)
- story_hash.each do |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
+ next if hash[:part_of_floor_area].empty?
# 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]
+ if above_grade.zero? && (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)
-
+ def model_create_space_type_hash(model, 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|
-
+ model.getSpaceTypes.sort.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
+ if stds_bldg_type.is_initialized && stds_space_type.is_initialized && !space_type.spaces.empty?
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
@@ -4484,44 +4242,43 @@
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
+ space_type.spaces.sort.each do |space|
+ next unless 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
+ 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
+ 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
+ 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
+ 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
+ elsif stds_bldg_type == 'StripMall'
+ avg_unit_size = OpenStudio.convert(22_500.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)
+ 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)
+ 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
@@ -4530,22 +4287,30 @@
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
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "For #{space_type.name}, floor area = #{OpenStudio.convert(floor_area, 'm^2', 'ft^2').get.round} ft^2.") unless floor_area == 0.0
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "For #{space_type.name}, number of spaces = #{effective_num_spaces}.") unless effective_num_spaces == 0.0
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "For #{space_type.name}, number of units = #{num_units}.") unless num_units == 0.0
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "For #{space_type.name}, number of people = #{num_people.round}.") unless num_people == 0.0
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "For #{space_type.name}, number of students = #{num_students}.") unless num_students == 0.0
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "For #{space_type.name}, number of beds = #{num_beds}.") unless num_beds == 0.0
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "For #{space_type.name}, number of meals = #{num_meals}.") unless num_meals.nil?
+
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
+ return space_type_hash.sort.to_h
end
private
# Helper method to fill in hourly values
- def add_vals_to_sch(day_sch, sch_type, values)
+ def model_add_vals_to_sch(model, day_sch, sch_type, values)
if sch_type == 'Constant'
day_sch.addValue(OpenStudio::Time.new(0, 24, 0, 0), values[0])
elsif sch_type == 'Hourly'
(0..23).each do |i|
next if values[i] == values[i + 1]
@@ -4558,91 +4323,159 @@
# Modify the existing service water heating loops
# to match the baseline required heating type.
# @return [Bool] return true if successful, false if not
# @author Julien Marrec
- def apply_baseline_swh_loops(template, building_type)
-
- getPlantLoops.each do |plant_loop|
+ def model_apply_baseline_swh_loops(model, building_type)
+ model.getPlantLoops.sort.each do |plant_loop|
# Skip non service water heating loops
- next unless plant_loop.swh_loop?
+ next unless plant_loop_swh_loop?(plant_loop)
# Rename the loop to avoid accidentally hooking
# up the HVAC systems to this loop later.
- plant_loop.setName("Service Water Heating Loop")
+ plant_loop.setName('Service Water Heating Loop')
- htg_fuels, combination_system, storage_capacity, total_heating_capacity = plant_loop.swh_system_type
+ htg_fuels, combination_system, storage_capacity, total_heating_capacity = plant_loop_swh_system_type(plant_loop)
# htg_fuels.size == 0 shoudln't happen
electric = true
if htg_fuels.include?('NaturalGas') ||
- htg_fuels.include?('PropaneGas') ||
- htg_fuels.include?('FuelOil#1') ||
- htg_fuels.include?('FuelOil#2') ||
- htg_fuels.include?('Coal') ||
- htg_fuels.include?('Diesel') ||
- htg_fuels.include?('Gasoline')
+ htg_fuels.include?('PropaneGas') ||
+ htg_fuels.include?('FuelOil#1') ||
+ htg_fuels.include?('FuelOil#2') ||
+ htg_fuels.include?('Coal') ||
+ htg_fuels.include?('Diesel') ||
+ htg_fuels.include?('Gasoline')
electric = false
end
- # Per Table G3.1 11.e, if the baseline system was a combination of
+ # Per Table G3.1 11.e, if the baseline system was a combination of
# heating and service water heating, delete all heating equipment
# and recreate a WaterHeater:Mixed.
if combination_system
plant_loop.supplyComponents.each do |component|
-
# Get the object type
obj_type = component.iddObjectType.valueName.to_s
next if ['OS_Node', 'OS_Pump_ConstantSpeed', 'OS_Pump_VariableSpeed', 'OS_Connector_Splitter', 'OS_Connector_Mixer', 'OS_Pipe_Adiabatic'].include?(obj_type)
component.remove
-
end
- water_heater = OpenStudio::Model::WaterHeaterMixed.new(self)
- water_heater.setName("Baseline Water Heater")
+ water_heater = OpenStudio::Model::WaterHeaterMixed.new(model)
+ water_heater.setName('Baseline Water Heater')
water_heater.setHeaterMaximumCapacity(total_heating_capacity)
water_heater.setTankVolume(storage_capacity)
plant_loop.addSupplyBranchForComponent(water_heater)
if electric
# G3.1.11.b: If electric, WaterHeater:Mixed with electric resistance
- water_heater.setHeaterFuelType("Electricity")
+ water_heater.setHeaterFuelType('Electricity')
water_heater.setHeaterThermalEfficiency(1.0)
else
# TODO: for now, just get the first fuel that isn't Electricity
# A better way would be to count the capacities associated
# with each fuel type and use the preponderant one
- fuels = htg_fuels - ["Electricity"]
+ fuels = htg_fuels - ['Electricity']
fossil_fuel_type = fuels[0]
water_heater.setHeaterFuelType(fossil_fuel_type)
water_heater.setHeaterThermalEfficiency(0.8)
end
- # If it's not a combination heating and service water heating system
- # just change the fuel type of all water heaters on the system
- # to electric resistance if it's electric
+ # If it's not a combination heating and service water heating system
+ # just change the fuel type of all water heaters on the system
+ # to electric resistance if it's electric
else
if electric
plant_loop.supplyComponents.each do |component|
next unless component.to_WaterHeaterMixed.is_initialized
water_heater = component.to_WaterHeaterMixed.get
# G3.1.11.b: If electric, WaterHeater:Mixed with electric resistance
- water_heater.setHeaterFuelType("Electricity")
+ water_heater.setHeaterFuelType('Electricity')
water_heater.setHeaterThermalEfficiency(1.0)
end
end
end
-
end
# Set the water heater fuel types if it's 90.1-2013
- getWaterHeaterMixeds.each do |water_heater|
- water_heater.apply_prm_baseline_fuel_type(template, building_type)
+ model.getWaterHeaterMixeds.sort.each do |water_heater|
+ water_heater_mixed_apply_prm_baseline_fuel_type(water_heater, building_type)
end
-
+
return true
-
end
+ # This method goes through certain types of EnergyManagementSystem
+ # variables and replaces UIDs with object names. This should
+ # be done by the forward translator, and this code should be
+ # removed after this bug is fixed:
+ # https://github.com/NREL/OpenStudio/issues/2598
+ #
+ # @todo remove this method after OpenStudio issue #2598 is fixed.
+ def model_temp_fix_ems_references(model)
+ # Internal Variables
+ model.getEnergyManagementSystemInternalVariables.sort.each do |var|
+ # Get the reference field value
+ ref = var.internalDataIndexKeyName
+ # Convert to UUID
+ uid = OpenStudio.toUUID(ref)
+ # Get the model object with this UID
+ obj = model.getModelObject(uid)
+ # If it exists, replace the UID with the object name
+ if obj.is_initialized
+ var.setInternalDataIndexKeyName(obj.get.name.get)
+ end
+ end
+
+ return true
+ end
+
+ # Loads a osm as a starting point.
+ #
+ # @return [Bool] returns true if successful, false if not
+ def load_initial_osm(osm_file)
+ # Load the geometry .osm
+ unless File.exist?(osm_file)
+ raise("The initial osm path: #{osm_file} does not exist.")
+ end
+ osm_model_path = OpenStudio::Path.new(osm_file.to_s)
+ # Upgrade version if required.
+ version_translator = OpenStudio::OSVersion::VersionTranslator.new
+ model = version_translator.loadModel(osm_model_path).get
+ if model.getBuildingStorys.empty?
+ OpenStudio.logFree(OpenStudio::Error, 'openstudio.model.Model', "Please assign Spaces to BuildingStorys in the geometry model: #{osm_model_path}.")
+ end
+ if model.getThermalZones.empty?
+ OpenStudio.logFree(OpenStudio::Error, 'openstudio.model.Model', "Please assign Spaces to ThermalZones in the geometry model: #{osm_model_path}.")
+ end
+ if model.getBuilding.standardsNumberOfStories.empty?
+ OpenStudio.logFree(OpenStudio::Error, 'openstudio.model.Model', "Please define Building.standardsNumberOfStories in the geometry model #{osm_model_path}.")
+ end
+ if model.getBuilding.standardsNumberOfAboveGroundStories.empty?
+ OpenStudio.logFree(OpenStudio::Error, 'openstudio.model.Model', "Please define Building.standardsNumberOfAboveStories in the geometry model#{osm_model_path}.")
+ end
+
+ if @space_type_map.nil? || @space_type_map.empty?
+ @space_type_map = get_space_type_maps_from_model(model)
+ if @space_type_map.nil? || @space_type_map.empty?
+ OpenStudio.logFree(OpenStudio::Error, 'openstudio.model.Model', "Please assign SpaceTypes in the geometry model: #{osm_model_path} or in standards database #{@space_type_map}.")
+ else
+ @space_type_map = @space_type_map.sort.to_h
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', "Loaded space type map from osm file: #{osm_model_path}")
+ end
+ end
+
+ # ensure that model is intersected correctly.
+ model.getSpaces.each { |space1| model.getSpaces.each { |space2| space1.intersectSurfaces(space2) } }
+ # Get multipliers from TZ in model. Need this for HVAC contruction.
+ @space_multiplier_map = {}
+ model.getSpaces.sort.each do |space|
+ @space_multiplier_map[space.name.get] = space.multiplier if space.multiplier > 1
+ end
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', 'Finished adding geometry')
+ unless @space_multiplier_map.empty?
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', "Found mulitpliers for space #{@space_multiplier_map}")
+ end
+ return model
+ end
end