lib/openstudio-standards/standards/Standards.Model.rb in openstudio-standards-0.4.0 vs lib/openstudio-standards/standards/Standards.Model.rb in openstudio-standards-0.5.0.rc1
- old
+ new
@@ -1,6 +1,7 @@
require 'csv'
+require 'date'
class Standard
attr_accessor :space_multiplier_map
attr_accessor :standards_data
@@ -16,44 +17,41 @@
# Creates a Performance Rating Method (aka Appendix G aka LEED) baseline building model
# Method used for 90.1-2016 and onward
#
# @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 user_model [OpenStudio::model::Model] User specified OpenStudio model
+ # @param model [OpenStudio::Model::Model] User specified OpenStudio model
# @param climate_zone [String] the climate zone
# @param hvac_building_type [String] the building type for baseline HVAC system determination (90.1-2016 and onward)
# @param wwr_building_type [String] the building type for baseline WWR determination (90.1-2016 and onward)
# @param swh_building_type [String] the building type for baseline SWH determination (90.1-2016 and onward)
# @param output_dir [String] the directory where the PRM generations will be performed
- # @param run_all_orients [Boolean] indicate weather a baseline model should be created for all 4 orientations: same as user model, +90 deg, +180 deg, +270 deg
# @param debug [Boolean] If true, will report out more detailed debugging output
- # @return [Bool] returns true if successful, false if not
-
- # Method used for 90.1-2016 and onward
+ # @return [Boolean] returns true if successful, false if not
def model_create_prm_stable_baseline_building(model, climate_zone, hvac_building_type, wwr_building_type, swh_building_type, output_dir = Dir.pwd, unmet_load_hours_check = true, debug = false)
- model_create_prm_any_baseline_building(model, '', climate_zone, hvac_building_type, wwr_building_type, swh_building_type, true, false, output_dir, true, unmet_load_hours_check, debug)
+ model_create_prm_any_baseline_building(model, '', climate_zone, hvac_building_type, wwr_building_type, swh_building_type, true, true, false, output_dir, true, unmet_load_hours_check, debug)
end
# Creates a Performance Rating Method (aka Appendix G aka LEED) baseline building model
# Method used for 90.1-2013 and prior
- # @param user_model [OpenStudio::model::Model] User specified OpenStudio model
+ # @param model [OpenStudio::Model::Model] User specified OpenStudio model
# @param building_type [String] the building type
# @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
+ # @param debug [Boolean] if true, will report out more detailed debugging output
def model_create_prm_baseline_building(model, building_type, climate_zone, custom = nil, sizing_run_dir = Dir.pwd, debug = false)
- model_create_prm_any_baseline_building(model, building_type, climate_zone, 'All others', 'All others', 'All others', false, custom, sizing_run_dir, false, false, debug)
+ model_create_prm_any_baseline_building(model, building_type, climate_zone, 'All others', 'All others', 'All others', false, false, custom, sizing_run_dir, false, false, debug)
end
- # Creates a Performance Rating Method (aka Appendix G aka LEED) baseline building model
- # based on the inputs currently in the model.
+ # Creates a Performance Rating Method (aka 90.1-Appendix G) baseline building model
+ # based on the inputs currently in the user 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 user_model [OpenStudio::model::Model] User specified OpenStudio model
+ # @param user_model [OpenStudio::Model::Model] User specified OpenStudio model
# @param building_type [String] the building type
# @param climate_zone [String] the climate zone
# @param hvac_building_type [String] the building type for baseline HVAC system determination (90.1-2016 and onward)
# @param wwr_building_type [String] the building type for baseline WWR determination (90.1-2016 and onward)
# @param swh_building_type [String] the building type for baseline SWH determination (90.1-2016 and onward)
@@ -61,31 +59,73 @@
# @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 run_all_orients [Boolean] indicate weather a baseline model should be created for all 4 orientations: same as user model, +90 deg, +180 deg, +270 deg
# @param debug [Boolean] If true, will report out more detailed debugging output
- # @return [Bool] returns true if successful, false if not
- def model_create_prm_any_baseline_building(user_model, building_type, climate_zone, hvac_building_type = 'All others', wwr_building_type = 'All others', swh_building_type = 'All others', model_deep_copy = false, custom = nil, sizing_run_dir = Dir.pwd, run_all_orients = false, unmet_load_hours_check = true, debug = false)
+ # @return [Boolean] returns true if successful, false if not
+ def model_create_prm_any_baseline_building(user_model, building_type, climate_zone, hvac_building_type = 'All others', wwr_building_type = 'All others', swh_building_type = 'All others', model_deep_copy = false, create_proposed_model = false, custom = nil, sizing_run_dir = Dir.pwd, run_all_orients = false, unmet_load_hours_check = true, debug = false)
+ if create_proposed_model
+ # Perform a user model design day run only to make sure
+ # that the user model is valid, i.e. can run without major
+ # errors
+ if !model_run_sizing_run(user_model, "#{sizing_run_dir}/USER-SR")
+ OpenStudio.logFree(OpenStudio::Warn, 'prm.log',
+ "The user model is not a valid OpenStudio model. Baseline and proposed model(s) won't be created.")
+ prm_raise(false,
+ sizing_run_dir,
+ "The user model is not a valid OpenStudio model. Baseline and proposed model(s) won't be created.")
+ end
+
+ # Check if proposed HVAC system is autosized
+ if model_is_hvac_autosized(user_model)
+ OpenStudio.logFree(OpenStudio::Warn, 'prm.log',
+ "The user model's HVAC system is partly autosized.")
+ end
+
+ # Generate proposed model from the user-provided model
+ proposed_model = model_create_prm_proposed_building(user_model)
+ end
+
# Check proposed model unmet load hours
if unmet_load_hours_check
- # Run proposed model; need annual simulation to get unmet load hours
- if model_run_simulation_and_log_errors(user_model, run_dir = "#{sizing_run_dir}/PROP")
- umlh = model_get_unmet_load_hours(user_model)
+ # Run user model; need annual simulation to get unmet load hours
+ if model_run_simulation_and_log_errors(proposed_model, run_dir = "#{sizing_run_dir}/PROP")
+ umlh = model_get_unmet_load_hours(proposed_model)
if umlh > 300
- OpenStudio.logFree(OpenStudio::Error, 'prm.log', "Proposed model unmet load hours exceed 300. Baseline model(s) won't be created.")
- raise "Proposed model unmet load hours exceed 300. Baseline model(s) won't be created."
+ OpenStudio.logFree(OpenStudio::Warn, 'prm.log',
+ "Proposed model unmet load hours (#{umlh}) exceed 300. Baseline model(s) won't be created.")
+ prm_raise(false,
+ sizing_run_dir,
+ "Proposed model unmet load hours exceed 300. Baseline model(s) won't be created.")
end
else
- OpenStudio.logFree(OpenStudio::Error, 'prm.log', 'Simulation failed. Check the model to make sure no severe errors.')
- raise 'Simulation on proposed model failed. Baseline generation is stopped.'
+ OpenStudio.logFree(OpenStudio::Error, 'prm.log',
+ 'Simulation failed. Check the model to make sure no severe errors.')
+ prm_raise(false,
+ sizing_run_dir,
+ 'Simulation on proposed model failed. Baseline generation is stopped.')
end
end
+ if create_proposed_model
+ # Make the run directory if it doesn't exist
+ unless Dir.exist?(sizing_run_dir)
+ FileUtils.mkdir_p(sizing_run_dir)
+ end
+ # Save proposed model
+ proposed_model.save(OpenStudio::Path.new("#{sizing_run_dir}/proposed_final.osm"), true)
+ forward_translator = OpenStudio::EnergyPlus::ForwardTranslator.new
+ idf = forward_translator.translateModel(proposed_model)
+ idf_path = OpenStudio::Path.new("#{sizing_run_dir}/proposed_final.idf")
+ idf.save(idf_path, true)
+ end
# User data process
# bldg_type_hvac_zone_hash could be an empty hash if all zones in the models are unconditioned
+ # TODO - move this portion to the top of the function
bldg_type_hvac_zone_hash = {}
- handle_user_input_data(user_model, climate_zone, hvac_building_type, wwr_building_type, swh_building_type, bldg_type_hvac_zone_hash)
+ handle_user_input_data(user_model, climate_zone, sizing_run_dir, hvac_building_type, wwr_building_type, swh_building_type, bldg_type_hvac_zone_hash)
+
# Define different orientation from original orientation
# for each individual baseline models
# Need to run proposed model sizing simulation if no sql data is available
degs_from_org = run_all_orientations(run_all_orients, user_model) ? [0, 90, 180, 270] : [0]
@@ -173,22 +213,31 @@
set_ventilation = false
set_infiltration = false
# For PRM, it only applies lights for now.
space_type_apply_internal_loads(space_type, set_people, set_lights, set_electric_equipment, set_gas_equipment, set_ventilation, set_infiltration)
end
+
# Modify the lighting schedule to handle lighting occupancy sensors
# Modify the upper limit value of fractional schedule to avoid the fatal error caused by schedule value higher than 1
space_type_light_sch_change(model)
+ # Modify electric equipment computer room schedule
+ model.getSpaces.sort.each do |space|
+ space_add_prm_computer_room_equipment_schedule(space)
+ end
+
model_apply_baseline_exterior_lighting(model)
# Modify the elevator motor peak power
model_add_prm_elevators(model)
# Calculate infiltration as per 90.1 PRM rules
- model_baseline_apply_infiltration_standard(model, climate_zone)
+ model_apply_standard_infiltration(model)
+ # Apply user outdoor air specs as per 90.1 PRM rules exceptions
+ model_apply_userdata_outdoor_air(model)
+
# 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.
model.getLightss.sort.each do |lights|
if lights.schedule.empty?
lights.setSchedule(model.alwaysOffDiscreteSchedule)
@@ -254,15 +303,11 @@
end
end
end
# Compute and marke DCV related information before deleting proposed model HVAC systems
- model_mark_zone_dcv_existence(model)
- model_add_dcv_user_exception_properties(model)
- model_add_dcv_requirement_properties(model)
- model_add_apxg_dcv_properties(model)
- model_raise_user_model_dcv_errors(model)
+ model_evaluate_dcv_requirements(model)
# Remove all HVAC from model, excluding service water heating
model_remove_prm_hvac(model)
# Remove all EMS objects from the model
@@ -440,11 +485,11 @@
# @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 = degs > 0 ? "final_#{degs}" : 'final'
+ model_status = degs > 0 ? "baseline_final_#{degs}" : 'baseline_final'
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(model)
@@ -519,16 +564,142 @@
end
return true
end
+ # Creates a Performance Rating Method (aka 90.1-Appendix G) proposed building model
+ # based on the inputs currently in the user model.
+ #
+ # @param user_model [OpenStudio::model::Model] User specified OpenStudio model
+ # @return [OpenStudio::model::Model] returns the proposed building model corresponding to a user model
+ def model_create_prm_proposed_building(user_model)
+ # Create copy of the user model
+ proposed_model = BTAP::FileIO.deep_copy(user_model)
+
+ # Get user building level data
+ building_name = proposed_model.building.get.name.get
+ user_buildings = @standards_data.key?('userdata_building') ? @standards_data['userdata_building'] : nil
+
+ # If needed, modify user model infiltration
+ if user_buildings
+ user_building_index = user_buildings.index { |user_building| building_name.include? user_building['name'] }
+ # TODO: Move the user data processing section
+ infiltration_modeled_from_field_verification_results = 'false'
+ if user_building_index && user_buildings[user_building_index]['infiltration_modeled_from_field_verification_results']
+ infiltration_modeled_from_field_verification_results = user_buildings[user_building_index]['infiltration_modeled_from_field_verification_results'].to_s.downcase
+ end
+
+ # Calculate total infiltration flow rate per envelope area
+ building_envelope_area_m2 = model_building_envelope_area(proposed_model)
+ curr_tot_infil_m3_per_s_per_envelope_area = model_current_building_envelope_infiltration_at_75pa(proposed_model, building_envelope_area_m2)
+ curr_tot_infil_cfm_per_envelope_area = OpenStudio.convert(curr_tot_infil_m3_per_s_per_envelope_area, 'm^3/s*m^2', 'cfm/ft^2').get
+
+ # Warn users if the infiltration modeling in the user/proposed model is not based on field verification
+ # If not modeled based on field verification, it should be modeled as 0.6 cfm/ft2
+ unless infiltration_modeled_from_field_verification_results.casecmp('true')
+ if curr_tot_infil_cfm_per_envelope_area < 0.6
+ OpenStudio.logFree(OpenStudio::Info, 'prm.log', "The user model's I_75Pa is estimated to be #{curr_tot_infil_cfm_per_envelope_area} m3/s per m2 of total building envelope")
+ end
+ end
+
+ # Modify model to follow the PRM infiltration modeling method
+ model_apply_standard_infiltration(proposed_model, curr_tot_infil_cfm_per_envelope_area)
+ end
+
+ # If needed, remove all non-adiabatic pipes of SWH loops
+ proposed_model.getPlantLoops.sort.each do |plant_loop|
+ # Skip non service water heating loops
+ next unless plant_loop_swh_loop?(plant_loop)
+
+ plant_loop_adiabatic_pipes_only(plant_loop)
+ end
+
+ # TODO: Once data refactoring has been completed lookup values from the database;
+ # For now, hard-code LPD for selected spaces. Current Standards Space Type
+ # of OS:SpaceType is the PRM interior lighting space type. These values are
+ # from Table 9.6.1 as required by Section G3.1.6.e.
+ proposed_lpd_residential_spaces = {
+ 'dormitory - living quarters' => 0.5, # "primary_space_type": "Dormitory—Living Quarters",
+ 'apartment - hardwired' => 0.6, # "primary_space_type": "Dwelling Unit"
+ 'guest room' => 0.41 # "primary_space_type": "Guest Room",
+ }
+
+ # Make proposed model space related adjustments
+ proposed_model.getSpaces.each do |space|
+ # If needed, modify computer equipment schedule
+ # Section G3.1.3.16
+ space_add_prm_computer_room_equipment_schedule(space)
+
+ # If needed, modify lighting power denstities in residential spaces/zones
+ # Section G3.1.6.e
+ standard_space_type = prm_get_optional_handler(space, @sizing_run_dir, 'spaceType', 'standardsSpaceType').downcase
+ user_spaces = @standards_data.key?('userdata_space') ? @standards_data['userdata_space'] : nil
+ if ['dormitory - living quarters', 'apartment - hardwired', 'guest room'].include?(standard_space_type)
+ user_spaces.each do |user_data|
+ if user_data['name'].to_s == space.name.to_s && user_data['has_residential_exception'].to_s.downcase != 'yes'
+ # Get LPDs
+ lpd_w_per_m2 = space.lightingPowerPerFloorArea
+ ref_space_lpd_per_ft2 = proposed_lpd_residential_spaces[standard_space_type]
+ ref_space_lpd_per_m2 = OpenStudio.convert(ref_space_lpd_per_ft2, 'W/ft^2', 'W/m^2').get
+ # Set new LPD
+ space.setLightingPowerPerFloorArea([lpd_w_per_m2, ref_space_lpd_per_m2].max)
+ end
+ end
+ end
+ end
+
+ return proposed_model
+ end
+
+ # Determine whether or not the HVAC system in a model is autosized
+ #
+ # As it is not realistic expectation to have all autosizable
+ # fields hard input, the method relies on autosizable field
+ # of prime movers (fans, pumps) and heating/cooling devices
+ # in the models (boilers, chillers, coils)
+ #
+ # @param model [OpenStudio::Model::Model] OpenStudio model object
+ # @return [Boolean] returns true if the HVAC system is likely autosized, false otherwise
+ def model_is_hvac_autosized(model)
+ is_hvac_autosized = false
+ model.modelObjects.each do |obj|
+ obj_type = obj.iddObjectType.valueName.to_s.downcase
+
+ # Check if the object needs to be checked for autosizing
+ obj_to_be_checked_for_autosizing = false
+ if obj_type.include?('chiller') || obj_type.include?('boiler') || obj_type.include?('coil') || obj_type.include?('fan') || obj_type.include?('pump') || obj_type.include?('waterheater')
+ if !obj_type.include?('controller')
+ obj_to_be_checked_for_autosizing = true
+ end
+ end
+
+ # Check for autosizing
+ if obj_to_be_checked_for_autosizing
+ casted_obj = model_cast_model_object(obj)
+
+ next if casted_obj.nil?
+
+ casted_obj.methods.each do |method|
+ if method.to_s.include?('is') && method.to_s.include?('Autosized')
+ if casted_obj.public_send(method) == true
+ is_hvac_autosized = true
+ OpenStudio.logFree(OpenStudio::Info, 'prm.log', "The #{method.to_s.sub('is', '').sub('Autosized', '').sub(':', '')} field of the #{obj_type} named #{casted_obj.name} is autosized. It should be hard sized.")
+ end
+ end
+ end
+ end
+ end
+
+ return is_hvac_autosized
+ 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.
#
# @param model [OpenStudio::Model::Model] OpenStudio model object
- # @return [Bool] returns true if required, false if not
+ # @return [Boolean] returns true if required, false if not
def model_create_prm_baseline_building_requires_vlt_sizing_run(model)
return false # Not required for most templates
end
# Determine if there is a need for a proposed model sizing run.
@@ -594,11 +765,11 @@
end
# Add design day schedule objects for space loads,
# not used for 2013 and earlier
# @author Xuechen (Jerry) Lei, PNNL
- # @param model [OpenStudio::model::Model] OpenStudio model object
+ # @param model [OpenStudio::Model::Model] OpenStudio model object
#
def model_apply_prm_baseline_sizing_schedule(model)
return true
end
@@ -668,12 +839,18 @@
# with keys area_ft2, type, fuel, and zones (an array of zones)
def model_prm_baseline_system_groups(model, custom, bldg_type_hvac_zone_hash = nil)
# Define the minimum area for the
# exception that allows a different
# system type in part of the building.
- 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
+ if custom == 'Xcel Energy CO EDA'
+ # Customization - Xcel EDA Program Manual 2014
+ # 3.2.1 Mechanical System Selection ii
+ 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.")
+ else
+ exception_min_area_ft2 = 20_000
+ end
# Get occupancy type, fuel type, and area information for all zones,
# excluding unconditioned zones.
# Occupancy types are:
# Residential
@@ -863,20 +1040,26 @@
# 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 = []
+
+ # error if HVACComponent heating fuels method is not available
+ if model.version < OpenStudio::VersionString.new('3.6.0')
+ OpenStudio.logFree(OpenStudio::Error, 'openstudio.Standards.Model', 'Required HVACComponent methods .heatingFuelTypes and .coolingFuelTypes are not available in pre-OpenStudio 3.6.0 versions. Use a more recent version of OpenStudio.')
+ end
+
model.getThermalZones.sort.each do |zone|
- all_htg_fuels += zone.heating_fuels
- all_clg_fuels += zone.cooling_fuels
+ all_htg_fuels += zone.heatingFuelTypes.map(&:valueName)
+ all_clg_fuels += zone.coolingFuelTypes.map(&:valueName)
end
purchased_heating = false
purchased_cooling = false
# Purchased heating
- if all_htg_fuels.include?('DistrictHeating')
+ if all_htg_fuels.include?('DistrictHeating') || all_htg_fuels.include?('DistrictHeatingWater') || all_htg_fuels.include?('DistrictHeatingSteam')
purchased_heating = true
end
# Purchased cooling
if all_clg_fuels.include?('DistrictCooling')
@@ -921,17 +1104,23 @@
return final_groups
end
# Before deleting proposed HVAC components, determine for each zone if it has district heating
- # @return [Hash] of boolean with zone name as key
+ # @return [Hash] Hash of boolean with zone name as key
def model_get_district_heating_zones(model)
has_district_hash = {}
model.getThermalZones.sort.each do |zone|
has_district_hash['building'] = false
- htg_fuels = zone.heating_fuels
- if htg_fuels.include?('DistrictHeating')
+
+ # error if HVACComponent heating fuels method is not available
+ if model.version < OpenStudio::VersionString.new('3.6.0')
+ OpenStudio.logFree(OpenStudio::Error, 'openstudio.Standards.Model', 'Required HVACComponent method .heatingFuelTypes is not available in pre-OpenStudio 3.6.0 versions. Use a more recent version of OpenStudio.')
+ end
+
+ htg_fuels = zone.heatingFuelTypes.map(&:valueName)
+ if htg_fuels.include?('DistrictHeating') || htg_fuels.include?('DistrictHeatingWater') || htg_fuels.include?('DistrictHeatingSteam')
has_district_hash[zone.name] = true
has_district_hash['building'] = true
else
has_district_hash[zone.name] = false
end
@@ -939,18 +1128,25 @@
return has_district_hash
end
# Get list of heat types across a list of zones
# @param zones [array of objects] array of zone objects
- # @return [string] concatenated string showing different fuel types in a group of zones
+ # @return [String concatenated string showing different fuel types in a group of zones
def get_group_heat_types(model, zones)
heat_list = ''
has_district_heat = false
has_fuel_heat = false
has_elec_heat = false
+
+ # error if HVACComponent heating fuels method is not available
+ if model.version < OpenStudio::VersionString.new('3.6.0')
+ OpenStudio.logFree(OpenStudio::Error, 'openstudio.Standards.Model', 'Required HVACComponent method .heatingFuelTypes is not available in pre-OpenStudio 3.6.0 versions. Use a more recent version of OpenStudio.')
+ end
+
zones.each do |zone|
- if zone.heating_fuels.include?('DistrictHeating')
+ htg_fuels = zone.heatingFuelTypes.map(&:valueName)
+ if htg_fuels.include?('DistrictHeating') || htg_fuels.include?('DistrictHeatingWater') || htg_fuels.include?('DistrictHeatingSteam')
has_district_heat = true
end
other_heat = thermal_zone_fossil_or_electric_type(zone, '')
if other_heat == 'fossil'
has_fuel_heat = true
@@ -971,20 +1167,20 @@
end
# Store fan operation schedule for each zone before deleting HVAC objects
# @author Doug Maddox, PNNL
# @param model [object]
- # @return [hash] of zoneName:fan_schedule_8760
+ # @return [Hash] of zoneName:fan_schedule_8760
def get_fan_schedule_for_each_zone(model)
fan_sch_names = {}
# Start with air loops
model.getAirLoopHVACs.sort.each do |air_loop_hvac|
fan_schedule_8760 = []
# Check for availability managers
# Assume only AvailabilityManagerScheduled will control fan schedule
- # TODO: also check AvailabilityManagerScheduledOn
+ # @todo also check AvailabilityManagerScheduledOn
avail_mgrs = air_loop_hvac.availabilityManagers
# if avail_mgrs.is_initialized
if !avail_mgrs.nil?
avail_mgrs.each do |avail_mgr|
# avail_mgr = avail_mgr.get
@@ -1097,11 +1293,11 @@
# Convert from schedule object to array of hourly values for entire year
# Array will include extra 24 values for leap year
# Array will also include extra 24 values at end for holiday day type
# @author: Doug Maddox, PNNL
- # @TODO: consider moving this to Standards.Schedule.rb
+ # @todo consider moving this to Standards.Schedule.rb
# @param: model [Object]
# @param: fan_schedule [Object]
# @return: [Array<String>] annual hourly values from schedule
def get_8760_values_from_schedule(model, fan_schedule)
sch_object_type = fan_schedule.iddObjectType.valueName.to_s
@@ -1116,54 +1312,47 @@
# First convert to ScheduleRuleset
sch_translator = ScheduleTranslator.new(model, fan_schedule)
fan_schedule_ruleset = sch_translator.convert_schedule_compact_to_schedule_ruleset
fan_8760 = get_8760_values_from_schedule_ruleset(model, fan_schedule_ruleset)
when 'OS_Schedule_Year'
- # TODO: add function for ScheduleYear
+ # @todo add function for ScheduleYear
# fan_8760 = get_8760_values_from_schedule_year(model, fan_schedule)
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Model', 'Automated baseline measure does not support use of Schedule Year')
end
return fan_8760
end
- # Determines the area of the building above which point
- # the non-dominant area type gets it's own HVAC system type.
- #
- # @param model [OpenStudio::Model::Model] OpenStudio model object
- # @param custom [String] custom fuel 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.
#
# 90.1-2007, 90.1-2010, 90.1-2013
#
# @param model [OpenStudio::Model::Model] OpenStudio model object
# @param climate_zone [String] ASHRAE climate zone, e.g. 'ASHRAE 169-2013-4A'
- # @param sys_group [hash] Hash defining a group of zones that have the same Appendix G system type
+ # @param sys_group [Hash] Hash defining a group of zones that have the same Appendix G system type
# @param custom [String] custom fuel type
# @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 model_prm_baseline_system_type(model, climate_zone, sys_group, custom, hvac_building_type = nil, district_heat_zones = nil)
area_type = sys_group['occ']
fuel_type = sys_group['fuel']
area_ft2 = sys_group['area_ft2']
num_stories = sys_group['stories']
- # [type, central_heating_fuel, zone_heating_fuel, cooling_fuel]
+ # [type, central_heating_fuel, zone_heating_fuel, cooling_fuel]
system_type = [nil, nil, nil, nil]
# Get the row from TableG3.1.1A
sys_num = model_prm_baseline_system_number(model, climate_zone, area_type, fuel_type, area_ft2, num_stories, custom)
# 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)
+ if custom == 'Xcel Energy CO EDA'
+ # fuel type remains unchanged
+ 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 rules for heating fuel type (based on proposed model) rules apply.')
+ else
+ fuel_type = model_prm_baseline_system_change_fuel_type(model, fuel_type, climate_zone)
+ 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
@@ -1259,47 +1448,16 @@
#
# @param model [OpenStudio::Model::Model] OpenStudio model object
# @param fuel_type [String] Valid choices are electric, fossil, fossilandelectric,
# purchasedheat, purchasedcooling, purchasedheatandcooling
# @param climate_zone [String] ASHRAE climate zone, e.g. 'ASHRAE 169-2013-4A'
- # @param custom [String] custom fuel type
# @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
+ def model_prm_baseline_system_change_fuel_type(model, fuel_type, climate_zone)
+ # Don't change fuel type for most templates
+ return fuel_type
end
- # Determine whether heating type is fuel or electric
- # @param hvac_building_type [String] Key for lookup of baseline system type
- # @param climate_zone [String] full name of climate zone
- # @return [String] fuel or electric
- def find_prm_heat_type(hvac_building_type, climate_zone)
- climate_code = get_climate_zone_code(climate_zone)
- heat_type_props = model_find_object(standards_data['prm_heat_type'],
- 'template' => template,
- 'hvac_building_type' => hvac_building_type,
- 'climate_zone' => climate_code)
- if !heat_type_props
- # try again with wild card for climate
- heat_type_props = model_find_object(standards_data['prm_heat_type'],
- 'template' => template,
- 'hvac_building_type' => hvac_building_type,
- 'climate_zone' => 'any')
- end
- if !heat_type_props
- # try again with wild card for building type
- heat_type_props = model_find_object(standards_data['prm_heat_type'],
- 'template' => template,
- 'hvac_building_type' => 'all others',
- 'climate_zone' => climate_code)
- end
- if !heat_type_props
- OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Model', "Could not find baseline heat type for: #{template}-#{hvac_building_type}-#{climate_zone}.")
- else
- return heat_type_props['heat_type']
- end
- end
-
# Get ASHRAE ID code for climate zone
# @param climate_zone [String] full name of climate zone
# @return [String] ASHRAE ID code for climate zone
def get_climate_zone_code(climate_zone)
cz_codes = []
@@ -1339,15 +1497,15 @@
#
# @param model [OpenStudio::Model::Model] OpenStudio model object
# @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 zone_heat_fuel [String] zone heating/reheat fuel. Valid choices are Electricity, NaturalGas, DistrictHeating
+ # @param main_heat_fuel [String] main heating fuel. Valid choices are Electricity, NaturalGas, DistrictHeating, DistrictHeatingWater, DistrictHeatingSteam
+ # @param zone_heat_fuel [String] zone heating/reheat fuel. Valid choices are Electricity, NaturalGas, DistrictHeating, DistrictHeatingWater, DistrictHeatingSteam
# @param cool_fuel [String] cooling fuel. Valid choices are Electricity, DistrictCooling
# @param zones [Array<OpenStudio::Model::ThermalZone>] an array of zones
- # @return [Bool] returns true if successful, false if not
+ # @return [Boolean] returns true if successful, false if not
# @todo Add 90.1-2013 systems 11-13
def model_add_prm_baseline_system(model, system_type, main_heat_fuel, zone_heat_fuel, cool_fuel, zones, zone_fan_scheds)
case system_type
when 'PTAC' # System 1
unless zones.empty?
@@ -1379,11 +1537,11 @@
when 'PSZ_AC' # System 3
unless zones.empty?
heating_type = 'Gas'
# if district heating
hot_water_loop = nil
- if main_heat_fuel == 'DistrictHeating'
+ if main_heat_fuel.include?('DistrictHeating')
heating_type = 'Water'
hot_water_loop = if model.getPlantLoopByName('Hot Water Loop').is_initialized
model.getPlantLoopByName('Hot Water Loop').get
else
model_add_hw_loop(model, main_heat_fuel)
@@ -1479,16 +1637,18 @@
system_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?
- model_add_pvav(model,
- pri_zones,
- system_name: system_name,
- hot_water_loop: hot_water_loop,
- chilled_water_loop: chilled_water_loop,
- electric_reheat: electric_reheat)
+ air_loop = model_add_pvav(model,
+ pri_zones,
+ system_name: system_name,
+ hot_water_loop: hot_water_loop,
+ chilled_water_loop: chilled_water_loop,
+ electric_reheat: electric_reheat)
+ model_system_outdoor_air_sizing_vrp_method(air_loop)
+ air_loop_hvac_apply_vav_damper_action(air_loop)
model_create_multizone_fan_schedule(model, zone_op_hrs, pri_zones, system_name)
end
# Add a PSZ_AC for each secondary zone
unless sec_zones.empty?
@@ -1616,19 +1776,21 @@
unless pri_zones.empty?
# if the loop configuration is primary / secondary loop
if chilled_water_loop.additionalProperties.hasFeature('secondary_loop_name')
chilled_water_loop = model.getPlantLoopByName(chilled_water_loop.additionalProperties.getFeatureAsString('secondary_loop_name').get).get
end
- model_add_vav_reheat(model,
- pri_zones,
- system_name: system_name,
- reheat_type: reheat_type,
- hot_water_loop: hot_water_loop,
- chilled_water_loop: chilled_water_loop,
- fan_efficiency: 0.62,
- fan_motor_efficiency: 0.9,
- fan_pressure_rise: 4.0)
+ air_loop = model_add_vav_reheat(model,
+ pri_zones,
+ system_name: system_name,
+ reheat_type: reheat_type,
+ hot_water_loop: hot_water_loop,
+ chilled_water_loop: chilled_water_loop,
+ fan_efficiency: 0.62,
+ fan_motor_efficiency: 0.9,
+ fan_pressure_rise: 4.0)
+ model_system_outdoor_air_sizing_vrp_method(air_loop)
+ air_loop_hvac_apply_vav_damper_action(air_loop)
model_create_multizone_fan_schedule(model, zone_op_hrs, pri_zones, system_name)
end
# Add a PSZ_AC for each secondary zone
unless sec_zones.empty?
@@ -1706,11 +1868,11 @@
when 'Gas_Furnace' # System 9
unless zones.empty?
# If district heating
hot_water_loop = nil
- if main_heat_fuel == 'DistrictHeating'
+ if main_heat_fuel.include?('DistrictHeating')
hot_water_loop = if model.getPlantLoopByName('Hot Water Loop').is_initialized
model.getPlantLoopByName('Hot Water Loop').get
else
model_add_hw_loop(model, main_heat_fuel)
end
@@ -1735,11 +1897,11 @@
end
when 'SZ_CV' # System 12 (gas or district heat) or System 13 (electric resistance heat)
unless zones.empty?
hot_water_loop = nil
- if zone_heat_fuel == 'DistrictHeating' || zone_heat_fuel == 'NaturalGas'
+ if zone_heat_fuel.include?('DistrictHeating') || zone_heat_fuel == 'NaturalGas'
heating_type = 'Water'
hot_water_loop = if model.getPlantLoopByName('Hot Water Loop').is_initialized
model.getPlantLoopByName('Hot Water Loop').get
else
model_add_hw_loop(model, main_heat_fuel)
@@ -2142,12 +2304,12 @@
return value_avg
end
# For a multizone system, create the fan schedule based on zone occupancy/fan schedules
# @author Doug Maddox, PNNL
- # @param model
- # @param zone_fan_scheds [Hash] of hash of zoneName:8760FanSchedPerZone
+ # @param model [OpenStudio::Model::Model] OpenStudio model object
+ # @param zone_op_hrs [Hash] hash of zoneName zone_op_hrs
# @param pri_zones [Array<String>] names of zones served by the multizone system
# @param system_name [String] name of air loop
def model_create_multizone_fan_schedule(model, zone_op_hrs, pri_zones, system_name)
# Not applicable if not stable baseline
return
@@ -2202,11 +2364,11 @@
# Assign each space in the model to a building story based on common z (height) values.
# If no story object is found for a particular height, create a new one and assign it to the space.
# Does not assign a story to plenum spaces.
#
# @param model [OpenStudio::Model::Model] OpenStudio model object
- # @return [Bool] returns true if successful, false if not
+ # @return [Boolean] returns true if successful, false if not
def model_assign_spaces_to_stories(model)
# Make hash of spaces and minz values
sorted_spaces = {}
model.getSpaces.sort.each do |space|
# Skip plenum spaces
@@ -2242,11 +2404,11 @@
# 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.
#
# @param model [OpenStudio::Model::Model] OpenStudio model object
- # @return [Bool] returns true if successful, false if not
+ # @return [Boolean] returns true if successful, false if not
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
model.getAirLoopHVACs.sort.each { |obj| air_loop_hvac_apply_multizone_vav_outdoor_air_sizing(obj) }
@@ -2256,14 +2418,14 @@
# Applies the HVAC parts of the template to all objects in the model using the the template specified in the model.
#
# @param model [OpenStudio::Model::Model] OpenStudio model object
# @param climate_zone [String] ASHRAE climate zone, e.g. 'ASHRAE 169-2013-4A'
- # @param apply_controls [Bool] toggle whether to apply air loop and plant loop controls
+ # @param apply_controls [Boolean] toggle whether to apply air loop and plant loop controls
# @param sql_db_vars_map [Hash] hash map
- # @param necb_ref_hp [Bool] for compatability with NECB ruleset only.
- # @return [Bool] returns true if successful, false if not
+ # @param necb_ref_hp [Boolean] for compatability with NECB ruleset only.
+ # @return [Boolean] returns true if successful, false if not
def model_apply_hvac_efficiency_standard(model, climate_zone, apply_controls: true, sql_db_vars_map: nil, necb_ref_hp: false)
sql_db_vars_map = {} if sql_db_vars_map.nil?
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "Started applying HVAC efficiency standards for #{template} template.")
@@ -2343,11 +2505,11 @@
end
# Applies daylighting controls to each space in the model per the standard.
#
# @param model [OpenStudio::Model::Model] OpenStudio model object
- # @return [Bool] returns true if successful, false if not
+ # @return [Boolean] returns true if successful, false if not
def model_add_daylighting_controls(model)
OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', 'Started adding daylighting controls.')
# Add daylighting controls to each space
model.getSpaces.sort.each do |space|
@@ -2357,21 +2519,22 @@
OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', 'Finished adding daylighting controls.')
return true
end
# For backward compatibility, infiltration standard not used for 2013 and earlier
- # @return [Bool] true if successful, false if not
- def model_baseline_apply_infiltration_standard(model, climate_zone)
+ #
+ # @return [Boolean] true if successful, false if not
+ def model_apply_standard_infiltration(model, specific_space_infiltration_rate_75_pa = nil)
return true
end
# Apply the air leakage requirements to the model, 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.
#
# @param model [OpenStudio::Model::Model] OpenStudio model object
- # @return [Bool] returns true if successful, false if not
+ # @return [Boolean] returns true if successful, false if not
# @todo This infiltration method is not used by the Reference buildings, fix this inconsistency.
def model_apply_infiltration_standard(model)
# Set the infiltration rate at each space
model.getSpaces.sort.each do |space|
space_apply_infiltration_rate(space)
@@ -2783,11 +2946,11 @@
# Create ScheduleTypeLimits
#
# @param model [OpenStudio::Model::Model] OpenStudio model object
# @param standard_sch_type_limit [String] the name of a standard schedule type limit with predefined limits
- # options are Temperature, Humidity Ratio, Fractional, OnOff, and Activity
+ # options are Dimensionless, Temperature, Humidity Ratio, Fractional, OnOff, and Activity
# @param name [String] the name of the schedule type limits
# @param lower_limit_value [double] the lower limit value for the schedule type
# @param upper_limit_value [double] the upper limit value for the schedule type
# @param numeric_type [String] the numeric type, options are Continuous or Discrete
# @param unit_type [String] the unit type, options are defined in EnergyPlus I/O reference
@@ -2821,10 +2984,18 @@
schedule_type_limits.setNumericType('Continuous')
schedule_type_limits.setUnitType('Temperature')
end
else
case standard_sch_type_limit.downcase
+ when 'dimensionless'
+ schedule_type_limits = OpenStudio::Model::ScheduleTypeLimits.new(model)
+ schedule_type_limits.setName('Dimensionless')
+ schedule_type_limits.setLowerLimitValue(0.0)
+ schedule_type_limits.setUpperLimitValue(1000.0)
+ schedule_type_limits.setNumericType('Continuous')
+ schedule_type_limits.setUnitType('Dimensionless')
+
when 'temperature'
schedule_type_limits = OpenStudio::Model::ScheduleTypeLimits.new(model)
schedule_type_limits.setName('Temperature')
schedule_type_limits.setLowerLimitValue(0.0)
schedule_type_limits.setUpperLimitValue(100.0)
@@ -3235,11 +3406,11 @@
frame.setFrameWidth(frame_with_m)
frame.setFrameConductance(frame_conductance_si)
skylights_frame_added = 0
model.getSubSurfaces.each do |sub_surface|
next unless sub_surface.outsideBoundaryCondition == 'Outdoors' && sub_surface.subSurfaceType == 'Skylight'
-
+
if model.version < OpenStudio::VersionString.new('3.1.0')
# window frame setting before https://github.com/NREL/OpenStudio/issues/2895 was fixed
sub_surface.setString(8, frame.name.get.to_s)
skylights_frame_added += 1
else
@@ -3364,11 +3535,11 @@
#
# @param model [OpenStudio::Model::Model] OpenStudio model object
# @param climate_zone [String] ASHRAE climate zone, e.g. 'ASHRAE 169-2013-4A'
# @param building_type [String] the building type
# @param spc_type [String] the space type
- # @param is_residential [Bool] true if the building is residential
+ # @param is_residential [Boolean] true if the building is residential
# @return [OpenStudio::Model::OptionalDefaultConstructionSet] an optional default construction set
def model_add_construction_set(model, climate_zone, building_type, spc_type, is_residential)
construction_set = OpenStudio::Model::OptionalDefaultConstructionSet.new
# Find the climate zone set that this climate zone falls into
@@ -3968,12 +4139,12 @@
# This is used by other methods to get the climate zone 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 model [OpenStudio::Model::Model] OpenStudio model object
- # @param remap_office [bool] re-map small office or leave it alone
- # @return [hash] key for climate zone, building type, and standards template. All values are strings.
+ # @param remap_office [Boolean] re-map small office or leave it alone
+ # @return [Hash] key for climate zone, building type, and standards template. All values are strings.
def model_get_building_properties(model, remap_office = true)
# get climate zone from model
climate_zone = model_standards_climate_zone(model)
# get building type from model
@@ -4251,11 +4422,11 @@
# 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.
#
# 90.1-2007, 90.1-2010, 90.1-2013
# @param model [OpenStudio::Model::Model] OpenStudio model object
- # @return [Bool] returns true if successful, false if not
+ # @return [Boolean] returns true if successful, false if not
def model_apply_prm_construction_types(model)
types_to_modify = []
# Possible boundary conditions are
# Adiabatic
@@ -4337,11 +4508,11 @@
# Apply the standard construction to each surface in the model, based on the construction type currently assigned.
#
# @param model [OpenStudio::Model::Model] OpenStudio model object
# @param climate_zone [String] ASHRAE climate zone, e.g. 'ASHRAE 169-2013-4A'
- # @return [Bool] returns true if successful, false if not
+ # @return [Boolean] returns true if successful, false if not
def model_apply_standard_constructions(model, climate_zone, wwr_building_type: nil, wwr_info: {})
types_to_modify = []
# Possible boundary conditions are
# Adiabatic
@@ -4479,11 +4650,11 @@
# WWR reduction will be done by moving vertices inward toward centroid.
# This causes the least impact on the daylighting area calculations and controls placement.
#
# @param model [OpenStudio::Model::Model] OpenStudio model object
# @param climate_zone [String] ASHRAE climate zone, e.g. 'ASHRAE 169-2013-4A'
- # @return [Bool] returns true if successful, false if not
+ # @return [Boolean] returns true if successful, false if not
# @todo add proper support for 90.1-2013 with all those building type specific values
# @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
@@ -4582,11 +4753,11 @@
if model_create_prm_baseline_building_requires_proposed_model_sizing_run(model)
# For PRM 90.1-2019 and onward, determine space category
# based on sizing run results
cat = space_conditioning_category(space)
else
- # TODO: This should really use the heating/cooling loads from the proposed building.
+ # @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 = thermal_zone_conditioning_category(space, template, climate_zone)
@@ -4733,25 +4904,24 @@
# If a vertical rectangle, raise sill height to avoid
# impacting daylighting areas, otherwise
# reduce toward centroid.
#
# daylighting control isn't modeled
- surface_wwr = surface_get_wwr_of_a_surface(surface)
- red = model_get_wwr_reduction_ratio(mult,
- surface_wwr: surface_wwr,
- surface_dr: surface_get_door_ratio_of_a_surface(surface),
- wwr_building_type: bat,
- wwr_target: wwr_lim / 100, # divide by 100 to revise it to decimals
- total_wall_m2: total_wall_area,
- total_wall_with_fene_m2: total_wall_with_fene_area,
- total_fene_m2: total_fene_area,
- total_plenum_wall_m2: total_plenum_wall_area)
+ red = surface_get_wwr_reduction_ratio(mult,
+ surface,
+ wwr_building_type: bat,
+ wwr_target: wwr_lim / 100, # divide by 100 to revise it to decimals
+ total_wall_m2: total_wall_area,
+ total_wall_with_fene_m2: total_wall_with_fene_area,
+ total_fene_m2: total_fene_area,
+ total_plenum_wall_m2: total_plenum_wall_area)
if red < 0.0
# surface with fenestration to its maximum but adjusted by door areas when need to add windows in surfaces no fenestration
# turn negative to positive to get the correct adjustment factor.
red = -red
+ surface_wwr = surface_get_wwr(surface)
residual_fene += (0.9 - red * surface_wwr) * surface.grossArea
end
surface_adjust_fenestration_in_a_surface(surface, red, model)
end
@@ -4766,11 +4936,11 @@
end
# Reduces the SRR to the values specified by the PRM. SRR reduction will be done by shrinking vertices toward the centroid.
#
# @param model [OpenStudio::Model::Model] OpenStudio model object
- # @return [Bool] returns true if successful, false if not
+ # @return [Boolean] returns true if successful, false if not
# @todo support semiheated spaces as a separate SRR category
# @todo add skylight frame area to calculation of SRR
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
@@ -4911,11 +5081,11 @@
end
# Apply baseline values to exterior lights objects
# Only implemented for stable baseline
#
- # @param model [OpenStudio::model::Model] OpenStudio model object
+ # @param model [OpenStudio::Model::Model] OpenStudio model object
def model_apply_baseline_exterior_lighting(model)
return false
end
# Function to add baseline elevators based on user data
@@ -4927,11 +5097,11 @@
# 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
#
# @param model [OpenStudio::Model::Model] OpenStudio model object
- # @return [Bool] returns true if successful, false if not
+ # @return [Boolean] returns true if successful, false if not
def model_remove_prm_hvac(model)
# Plant loops
model.getPlantLoops.sort.each do |loop|
# Don't remove service water heating loops
next if plant_loop_swh_loop?(loop)
@@ -4983,11 +5153,11 @@
end
# Remove EMS objects that may be orphaned from removing HVAC
#
# @param model [OpenStudio::Model::Model] OpenStudio model object
- # @return [Bool] returns true if successful, false if not
+ # @return [Boolean] returns true if successful, false if not
def model_remove_prm_ems_objects(model)
model.getEnergyManagementSystemActuators.each(&:remove)
model.getEnergyManagementSystemConstructionIndexVariables.each(&:remove)
model.getEnergyManagementSystemCurveOrTableIndexVariables.each(&:remove)
model.getEnergyManagementSystemGlobalVariables.each(&:remove)
@@ -5004,11 +5174,11 @@
end
# Remove external shading devices. Site shading will not be impacted.
#
# @param model [OpenStudio::Model::Model] OpenStudio model object
- # @return [Bool] returns true if successful, false if not
+ # @return [Boolean] returns true if successful, false if not
def model_remove_external_shading_devices(model)
shading_surfaces_removed = 0
model.getShadingSurfaceGroups.sort.each do |shade_group|
# Skip Site shading
next if shade_group.shadingSurfaceType == 'Site'
@@ -5024,11 +5194,11 @@
end
# Changes the sizing parameters to the PRM specifications.
#
# @param model [OpenStudio::Model::Model] OpenStudio model object
- # @return [Bool] returns true if successful, false if not
+ # @return [Boolean] returns true if successful, false if not
def model_apply_prm_sizing_parameters(model)
clg = 1.15
htg = 1.25
sizing_params = model.getSizingParameters
@@ -5301,11 +5471,11 @@
# 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 Stnadard spacetype, or is undefined, an error will stop
# with information that the spacetype needs to be defined.
#
# @param model [OpenStudio::Model::Model] OpenStudio model object
- # @return [Bool] returns true if successful, false if not
+ # @return [Boolean] returns true if successful, false if not
def model_validate_standards_spacetypes_in_model(model)
error_string = ''
# populate search hash
model.getSpaces.sort.each do |space|
unless space.spaceType.empty?
@@ -5454,11 +5624,11 @@
end
# create space_type_hash with info such as effective_num_spaces, num_units, num_meds, num_meals
#
# @param model [OpenStudio::Model::Model] OpenStudio model object
- # @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
+ # @param trust_effective_num_spaces [Boolean] 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 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
@@ -5565,11 +5735,11 @@
# This method only reduces subsurface sizes at most.
#
# @param model [OpenStudio::Model::Model] OpenStudio model object
# @param ratio [Double] ratio
# @param surface_type [String] surface type
- # @return [Bool] returns true if successful, false if not
+ # @return [Boolean] returns true if successful, false if not
def apply_limit_to_subsurface_ratio(model, ratio, surface_type = 'Wall')
fdwr = get_outdoor_subsurface_ratio(model, surface_type)
if fdwr <= ratio
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "Building FDWR of #{fdwr} is already lower than limit of #{ratio.round}%.")
return true
@@ -5600,11 +5770,11 @@
# Converts the climate zone in the model into the format used by the openstudio-standards lookup tables.
# For example,
# institution: ASHRAE, value: 6A becomes: ASHRAE 169-2013-6A.
# institution: CEC, value: 3 becomes: CEC T24-CEC3.
#
- # @param model [OpenStudio::Model::Model] the model
+ # @param model [OpenStudio::Model::Model] OpenStudio model object
# @return [String] the string representation of the climate zone,
# empty string if no climate zone is present in the model.
def model_standards_climate_zone(model)
climate_zone = ''
model.getClimateZones.climateZones.each do |cz|
@@ -5628,13 +5798,13 @@
# Sets the climate zone object in the model using
# the correct institution based on the climate zone specified
# in the format used by the openstudio-standards lookups.
# Clears out any climate zones previously added to the model.
#
- # @param model [OpenStudio::Model::Model] the model
+ # @param model [OpenStudio::Model::Model] OpenStudio model object
# @param climate_zone [String] ASHRAE climate zone, e.g. 'ASHRAE 169-2013-4A'
- # @return [Bool] returns true if successful, false if not
+ # @return [Boolean] returns true if successful, false if not
def model_set_climate_zone(model, climate_zone)
# Remove previous climate zones from the model
model.getClimateZones.clear
# Split the string into the correct institution and value
if climate_zone.include? 'ASHRAE 169-2006-'
@@ -5649,11 +5819,11 @@
end
# This method return the building ratio of subsurface_area / surface_type_area
# where surface_type can be "Wall" or "RoofCeiling"
#
- # @param model [OpenStudio::Model::Model] the model
+ # @param model [OpenStudio::Model::Model] OpenStudio model object
# @param surface_type [String] surface type
# @return [Double] surface ratio
def get_outdoor_subsurface_ratio(model, surface_type = 'Wall')
surface_area = 0.0
sub_surface_area = 0
@@ -5678,11 +5848,11 @@
end
# Loads a osm as a starting point.
#
# @param osm_file [String] path to the .osm file, relative to the /data folder
- # @return [Bool] returns true if successful, false if not
+ # @return [Boolean] 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
@@ -5695,12 +5865,12 @@
return model
end
# validate that model contains objects
#
- # @param model [OpenStudio::Model::Model] the model
- # @return [Bool] returns true if valid, false if not
+ # @param model [OpenStudio::Model::Model] OpenStudio model object
+ # @return [Boolean] returns true if valid, false if not
def validate_initial_model(model)
is_valid = true
if model.getBuildingStorys.empty?
OpenStudio.logFree(OpenStudio::Error, 'openstudio.model.Model', 'Please assign Spaces to BuildingStorys the geometry model.')
is_valid = false
@@ -5745,11 +5915,11 @@
# Determines how ventilation for the standard is specified.
# When 'Sum', all min OA flow rates are added up. Commonly used by 90.1.
# When 'Maximum', only the biggest OA flow rate. Used by T24.
#
- # @param model [OpenStudio::Model::Model] the model
+ # @param model [OpenStudio::Model::Model] OpenStudio model object
# @return [String] the ventilation method, either Sum or Maximum
def model_ventilation_method(model)
building_data = model_get_building_properties(model)
building_type = building_data['building_type']
if building_type != 'Laboratory' # Laboratory has multiple criteria on ventilation, pick the greatest
@@ -5762,12 +5932,12 @@
end
# Removes all of the unused ResourceObjects
# (Curves, ScheduleDay, Material, etc.) from the model.
#
- # @param model [OpenStudio::Model::Model] the model
- # @return [Bool] returns true if successful, false if not
+ # @param model [OpenStudio::Model::Model] OpenStudio model object
+ # @return [Boolean] returns true if successful, false if not
def model_remove_unused_resource_objects(model)
start_size = model.objects.size
model.getResourceObjects.sort.each do |obj|
if obj.directUseCount.zero?
OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.Model', "#{obj.name} is unused; it will be removed.")
@@ -5787,14 +5957,14 @@
# In future this could create different hours of operation for residential vs. non-residential, by building type, story, or space type.
# However this measure is a stop gap to convert old generic schedules to parametric schedules.
# Future new schedules should be designed as paramtric from the start and would not need to run through this inference process
#
# @author David Goldwasser
- # @param model [OpenStudio::Model::Model] the model
+ # @param model [OpenStudio::Model::Model] OpenStudio model object
# @param fraction_of_daily_occ_range [Double] fraction above/below daily min range required to start and end hours of operation
- # @param invert_res [Bool] if true will reverse hours of operation for residential space types
- # @param gen_occ_profile [Bool] if true creates a merged occupancy schedule for diagnostic purposes. This schedule is added to the model but no specifically returned by this method
+ # @param invert_res [Boolean] if true will reverse hours of operation for residential space types
+ # @param gen_occ_profile [Boolean] if true creates a merged occupancy schedule for diagnostic purposes. This schedule is added to the model but no specifically returned by this method
# @return [ScheduleRuleset] schedule that is assigned to the building as default hours of operation
def model_infer_hours_of_operation_building(model, fraction_of_daily_occ_range: 0.25, invert_res: true, gen_occ_profile: false)
# create an array of non-residential and residential spaces
res_spaces = []
non_res_spaces = []
@@ -5941,14 +6111,14 @@
# inputs. Inputs include one or more load profile formulas. Data is stored in model attributes for downstream
# application. This should impact all ScheduleRuleset objects in the model. Plant and Air loop hoours of operations
# should be traced back to a space or spaces.
#
# @author David Goldwasser
- # @param model [OpenStudio::Model::Model] the model
+ # @param model [OpenStudio::Model::Model] OpenStudio model object
# @param step_ramp_logic [String] type of step logic to use
- # @param infer_hoo_for_non_assigned_objects [Bool] attempt to get hoo for objects like swh with and exterior lighting
- # @param gather_data_only [Bool] false (stops method before changes made if true)
+ # @param infer_hoo_for_non_assigned_objects [Boolean] attempt to get hoo for objects like swh with and exterior lighting
+ # @param gather_data_only [Boolean] false (stops method before changes made if true)
# @param hoo_var_method [String] accepts hours and fractional. Any other value value will result in hoo variables not being applied
# @return [Hash] schedule is key, value is hash of number of objects
def model_setup_parametric_schedules(model, step_ramp_logic: nil, infer_hoo_for_non_assigned_objects: true, gather_data_only: false, hoo_var_method: 'hours')
parametric_inputs = {}
default_sch_type = OpenStudio::Model::DefaultScheduleType.new('HoursofOperationSchedule')
@@ -6104,14 +6274,14 @@
# for summer and winter design days.
#
# @note This measure will replace any prior chagnes made to ScheduleRule objects with new ScheduleRule values from
# profile formulas
# @author David Goldwasser
- # @param model [OpenStudio::Model::Model] the model
+ # @param model [OpenStudio::Model::Model] OpenStudio model object
# @param ramp_frequency [Double] ramp frequency in minutes. If nil method will match simulation timestep
- # @param infer_hoo_for_non_assigned_objects [Bool] # attempt to get hoo for objects like swh with and exterior lighting
- # @param error_on_out_of_order [Bool] true will error if applying formula creates out of order values
+ # @param infer_hoo_for_non_assigned_objects [Boolean] # attempt to get hoo for objects like swh with and exterior lighting
+ # @param error_on_out_of_order [Boolean] true will error if applying formula creates out of order values
# @return [Array] of modified ScheduleRuleset objects
def model_apply_parametric_schedules(model, ramp_frequency: nil, infer_hoo_for_non_assigned_objects: true, error_on_out_of_order: true)
# get ramp frequency (fractional hour) from timestep
if ramp_frequency.nil?
steps_per_hour = if model.getSimulationControl.timestep.is_initialized
@@ -6146,14 +6316,18 @@
return parametric_schedules
end
private
+ def model_apply_userdata_outdoor_air(model)
+ return true
+ end
+
# This function checks whether it is required to adjust the window to wall ratio based on the model WWR and wwr limit.
- # @param wwr_limit [Float] return wwr_limit
+ # @param wwr_limit [Double] window to wall ratio limit
# @param wwr_list [Array] list of wwr of zone conditioning category in a building area type category - residential, nonresidential and semiheated
- # @return require_adjustment [Boolean] True, require adjustment, false not require adjustment.
+ # @return [Boolean] True, require adjustment, false not require adjustment.
def model_does_require_wwr_adjustment?(wwr_limit, wwr_list)
require_adjustment = false
wwr_list.each do |wwr|
require_adjustment = true unless wwr > wwr_limit
end
@@ -6162,35 +6336,35 @@
# The function is used for codes that requires to adjusted wwr based on building categories for all other types
#
# @param bat [String] building area type category
# @param wwr_list [Array] list of wwr of zone conditioning category in a building area type category - residential, nonresidential and semiheated
- # @return wwr_limit [Float] return adjusted wwr_limit
+ # @return [Double] return adjusted wwr_limit
def model_get_bat_wwr_target(bat, wwr_list)
return 40.0
end
# Readjusted the WWR for surfaces previously has no windows to meet the
# overall WWR requirement.
# This function shall only be called if the maximum WWR value for surfaces with fenestration is lower than 90% due to
# accommodating the total door surface areas
#
- # @param residual_ratio: [Float] the ratio of residual surfaces among the total wall surface area with no fenestrations
+ # @param residual_ratio [Double] the ratio of residual surfaces among the total wall surface area with no fenestrations
# @param space [OpenStudio::Model:Space] a space
# @param model [OpenStudio::Model::Model] openstudio model
- # @return [Bool] return true if successful, false if not
+ # @return [Boolean] returns true if successful, false if not
def model_readjust_surface_wwr(residual_ratio, space, model)
return true
end
# Helper method to fill in hourly values
#
- # @param model [OpenStudio::Model::Model] the model
+ # @param model [OpenStudio::Model::Model] OpenStudio model object
# @param day_sch [OpenStudio::Model::ScheduleDay] schedule day object
# @param sch_type [String] Constant or Hourly
# @param values [Array<Double>]
- # @return [Bool] returns true if successful, false if not
+ # @return [Boolean] returns true if successful, false if not
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|
@@ -6203,13 +6377,13 @@
end
end
# Modify the existing service water heating loops to match the baseline required heating type.
#
- # @param model [OpenStudio::Model::Model] the model
+ # @param model [OpenStudio::Model::Model] OpenStudio model object
# @param building_type [String] the building type
- # @return [Bool] returns true if successful, false if not
+ # @return [Boolean] returns true if successful, false if not
# @author Julien Marrec
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?(plant_loop)
@@ -6291,12 +6465,12 @@
# 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
#
- # @param model [OpenStudio::Model::Model] the model
- # @return [Bool] returns true if successful, false if not
+ # @param model [OpenStudio::Model::Model] OpenStudio model object
+ # @return [Boolean] returns true if successful, false if not
# @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
@@ -6434,11 +6608,11 @@
# pass array of space types or spaces
#
# @author David Goldwasser
# @param space_space_types [Array] array of spaces or space types
# @param parametric_inputs [Hash]
- # @param gather_data_only [Bool]
+ # @param gather_data_only [Boolean]
# @return [Hash]
def gather_inputs_parametric_space_space_type_schedules(space_space_types, parametric_inputs, gather_data_only)
space_space_types.each do |space_type|
# get hours of operation for space type once
next if space_type.class == 'OpenStudio::Model::SpaceTypes' && space_type.floorArea == 0
@@ -6493,11 +6667,11 @@
#
# @author David Goldwasser
# @param load_inst [OpenStudio::Model::SpaceLoadInstance]
# @param parametric_inputs [Hash]
# @param hours_of_operation [Hash]
- # @param gather_data_only [Bool]
+ # @param gather_data_only [Boolean]
# @return [Hash]
def gather_inputs_parametric_load_inst_schedules(load_inst, parametric_inputs, hours_of_operation, gather_data_only)
if load_inst.class.to_s == 'OpenStudio::Model::People'
opt_sch = load_inst.numberofPeopleSchedule
elsif load_inst.class.to_s == 'OpenStudio::Model::DesignSpecificationOutdoorAir'
@@ -6519,13 +6693,13 @@
# @author David Goldwasser
# @param sch [OpenStudio::Model::Schedule]
# @param load_inst [OpenStudio::Model::SpaceLoadInstance]
# @param parametric_inputs [Hash]
# @param hours_of_operation [Hash]
- # @param ramp [Bool]
+ # @param ramp [Boolean]
# @param min_ramp_dur_hr [Double]
- # @param gather_data_only [Bool]
+ # @param gather_data_only [Boolean]
# @param hoo_var_method [String] accepts hours and fractional. Any other value value will result in hoo variables not being applied
# @return [Hash]
def gather_inputs_parametric_schedules(sch, load_inst, parametric_inputs, hours_of_operation, ramp: true, min_ramp_dur_hr: 2.0, gather_data_only: false, hoo_var_method: 'hours')
if parametric_inputs.key?(sch)
if hours_of_operation != parametric_inputs[sch][:hoo_inputs] # don't warn if the hours of operation between old and new schedule are equivalent
@@ -6841,12 +7015,12 @@
return parametric_inputs
end
# Retrieves the lowest story in a model
#
- # @param model [OpenStudio::model::Model] OpenStudio model object
- # @return [OpenStudio::model::BuildingStory] Lowest story included in the model
+ # @param model [OpenStudio::Model::Model] OpenStudio model object
+ # @return [OpenStudio::Model::BuildingStory] Lowest story included in the model
def find_lowest_story(model)
min_z_story = 1E+10
lowest_story = nil
model.getSpaces.sort.each do |space|
story = space.buildingStory.get
@@ -6860,11 +7034,11 @@
return lowest_story
end
# Utility function that returns the min and max value in a design day schedule.
#
- # TODO: move this to Standards.Schedule.rb
+ # @todo move this to Standards.Schedule.rb
# @param schedule [OpenStudio::Model::Schedule] can be ScheduleCompact, ScheduleRuleset, ScheduleConstant
# @param type [String] 'heating' for winter design day, 'cooling' for summer design day
# @return [Hash] Hash has two keys, min and max. if failed, return 999.9 for min and max.
def search_min_max_value_from_design_day_schedule(schedule, type = 'winter')
if schedule.is_initialized
@@ -6886,12 +7060,12 @@
return { 'min' => 999.9, 'max' => 999.9 }
end
# Identifies non mechanically cooled ("nmc") systems, if applicable
#
- # @param model [OpenStudio::model::Model] OpenStudio model object
- # @return zone_nmc_sys_type [Hash] Zone to nmc system type mapping
+ # @param model [OpenStudio::Model::Model] OpenStudio model object
+ # @return [Hash] Zone to nmc system type mapping
def model_identify_non_mechanically_cooled_systems(model)
return true
end
# Indicate if fan power breakdown (supply, return, and relief)
@@ -6906,10 +7080,11 @@
# The method calculates the window to wall ratio (assuming all spaces are conditioned)
# and select the range based on the calculated window to wall ratio
# @param model [OpenStudio::Model::Model] OpenStudio model object
# @param wwr_parameter [Hash] parameters to choose min and max percent of surfaces,
# could be different set in different standard
+ # @return [Hash] Hash of minimum_percent_of_surface and maximum_percent_of_surface
def model_get_percent_of_surface_range(model, wwr_parameter = {})
return { 'minimum_percent_of_surface' => nil, 'maximum_percent_of_surface' => nil }
end
# Default SAT reset type
@@ -6920,119 +7095,87 @@
return 'warmest_zone'
end
# Calculate the window to wall ratio reduction factor
#
- # @param multiplier [Float] multiplier of the wwr
- # @param surface_wwr [Float] the surface window to wall ratio
- # @param surface_dr [Float] the surface door to wall ratio
- # @param wwr_building_type[String] building type for wwr
- # @param wwr_target [Float] target window to wall ratio
- # @param total_wall_m2 [Float] total wall area of the category in m2.
- # @param total_wall_with_fene_m2 [Float] total wall area of the category with fenestrations in m2.
- # @param total_fene_m2 [Float] total fenestration area
- # @param total_plenum_wall_m2 [Float] total sqaure meter of a plenum
- # @return [Float] reduction factor
- def model_get_wwr_reduction_ratio(multiplier,
- surface_wwr: 0.0,
- surface_dr: 0.0,
- wwr_building_type: 'All others',
- wwr_target: 0.0,
- total_wall_m2: 0.0,
- total_wall_with_fene_m2: 0.0,
- total_fene_m2: 0.0,
- total_plenum_wall_m2: 0.0)
+ # @param multiplier [Double] multiplier of the wwr
+ # @param surface [OpenStudio::Model:Surface] OpenStudio Surface object
+ # @param wwr_target [Double] target window to wall ratio
+ # @param total_wall_m2 [Double] total wall area of the category in m2.
+ # @param total_wall_with_fene_m2 [Double] total wall area of the category with fenestrations in m2.
+ # @param total_fene_m2 [Double] total fenestration area
+ # @param total_plenum_wall_m2 [Double] total sqaure meter of a plenum
+ # @return [Double] reduction factor
+ def surface_get_wwr_reduction_ratio(multiplier,
+ surface,
+ wwr_building_type: 'All others',
+ wwr_target: 0.0,
+ total_wall_m2: 0.0,
+ total_wall_with_fene_m2: 0.0,
+ total_fene_m2: 0.0,
+ total_plenum_wall_m2: 0.0)
return 1.0 - multiplier
end
# A template method that handles the loading of user input data from multiple sources
# include data source from:
# 1. user data csv files
# 2. data from measure and OpenStudio interface
# @param [OpenStudio:model:Model] model
# @param [String] climate_zone
+ # @param [String] sizing_run_dir
# @param [String] default_hvac_building_type
# @param [String] default_wwr_building_type
# @param [String] default_swh_building_type
# @param [Hash] bldg_type_hvac_zone_hash A hash maps building type for hvac to a list of thermal zones
- # @return True
- def handle_user_input_data(model, climate_zone, default_hvac_building_type, default_wwr_building_type, default_swh_building_type, bldg_type_hvac_zone_hash)
+ # @return [Boolean] returns true
+ def handle_user_input_data(model, climate_zone, sizing_run_dir, default_hvac_building_type, default_wwr_building_type, default_swh_building_type, bldg_type_hvac_zone_hash)
return true
end
# Template method for adding a setpoint manager for a coil control logic to a heating coil.
# ASHRAE 90.1-2019 Appendix G.
#
# @param model [OpenStudio::Model::Model] OpenStudio model
- # @param thermalZones Array([OpenStudio::Model::ThermalZone]) thermal zone array
- # @param coil Heating Coils
- # @return [Boolean] true
+ # @param thermal_zones [Array<OpenStudio::Model::ThermalZone>] thermal zone array
+ # @param coil [OpenStudio::Model::StraightComponent] heating coil
+ # @return [Boolean] returns true if successful, false if not
def model_set_central_preheat_coil_spm(model, thermal_zones, coil)
return true
end
- # Template method for adding zone additional property "zone DCV implemented in user model"
+ # Template method for evaluate DCV requirements in the user model
#
- # @author Xuechen (Jerry) Lei, PNNL
# @param model [OpenStudio::Model::Model] OpenStudio model
- def model_mark_zone_dcv_existence(model)
+ # @return [Boolean] returns true if successful, false if not
+ def model_evaluate_dcv_requirements(model)
return true
end
# Check whether the baseline model generation needs to run all four orientations
# The default shall be true
#
- # @param [Boolean] run_all_orients: user inputs to indicate whether it is required to run all orientations
- # @param [OpenStudio::Model::Model] OpenStudio model
+ # @param run_all_orients [Boolean] user inputs to indicate whether it is required to run all orientations
+ # @param user_model [OpenStudio::Model::Model] OpenStudio model
+ # @return [Boolean] return True if all orientation need to be run, False if not
def run_all_orientations(run_all_orients, user_model)
return run_all_orients
end
- # Template method for reading user data and adding to zone additional properties
- #
- # @author Xuechen (Jerry) Lei, PNNL
- # @param model [OpenStudio::Model::Model] OpenStudio model
- def model_add_dcv_user_exception_properties(model)
- return true
- end
-
- # Template method for raising user model DCV warning and errors
- #
- # @author Xuechen (Jerry) Lei, PNNL
- # @param model [OpenStudio::Model::Model] OpenStudio model
- def model_raise_user_model_dcv_errors(model)
- return true
- end
-
- # Template method for adding zone additional property "airloop dcv required by 901" and "zone dcv required by 901"
- #
- # @author Xuechen (Jerry) Lei, PNNL
- # @param model [OpenStudio::Model::Model] OpenStudio model
- def model_add_dcv_requirement_properties(model)
- return true
- end
-
- # Template method for checking if zones in the baseline model should have DCV based on 90.1 2019 G3.1.2.5.
- # Zone additional property 'apxg no need to have DCV' added
- #
- # @author Xuechen (Jerry) Lei, PNNL
- # @param model [OpenStudio::Model::Model] OpenStudio model
- def model_add_apxg_dcv_properties(model)
- return true
- end
-
# Template method for setting DCV in baseline HVAC system if required
#
# @author Xuechen (Jerry) Lei, PNNL
# @param model [OpenStudio::Model::Model] OpenStudio model
+ # @return [Boolean] returns true if successful, false if not
def model_set_baseline_demand_control_ventilation(model, climate_zone)
return true
end
# Identify the return air type associated with each thermal zone
#
# @param model [OpenStudio::Model::Model] OpenStudio model object
+ # @return [Boolean] returns true if successful, false if not
def model_identify_return_air_type(model)
# air-loop based system
model.getThermalZones.each do |zone|
# Conditioning category won't include indirectly conditioned thermal zones
cond_cat = thermal_zone_conditioning_category(zone, model_standards_climate_zone(model))
@@ -7098,14 +7241,20 @@
# Catch all
if return_air_type.nil?
return_air_type = 'ducted_return_or_direct_to_unit'
end
+ # error if zone design air flow rate is not available
+ if zone.model.version < OpenStudio::VersionString.new('3.6.0')
+ OpenStudio.logFree(OpenStudio::Error, 'openstudio.Standards.Model', 'Required ThermalZone method .autosizedDesignAirFlowRate is not available in pre-OpenStudio 3.6.0 versions. Use a more recent version of OpenStudio.')
+ end
+
zone.additionalProperties.setFeature('return_air_type', return_air_type)
zone.additionalProperties.setFeature('plenum', return_plenum) unless return_plenum.nil?
- zone.additionalProperties.setFeature('proposed_model_zone_design_air_flow', zone.designAirFlowRate.to_f)
+ zone.additionalProperties.setFeature('proposed_model_zone_design_air_flow', zone.autosizedDesignAirFlowRate.to_f)
end
+ return true
end
# Determine the baseline return air type associated with each zone
#
# @param model [OpenStudio::Model::model] OpenStudio model object
@@ -7171,12 +7320,13 @@
end
# Add reporting tolerances. Default values are based on the suggestions from the PRM-RM.
#
# @param model [OpenStudio::Model::Model] OpenStudio Model
- # @param heating_tolerance_deg_f [Float] Tolerance for time heating setpoint not met in degree F
- # @param cooling_tolerance_deg_f [Float] Tolerance for time cooling setpoint not met in degree F
+ # @param heating_tolerance_deg_f [Double] Tolerance for time heating setpoint not met in degree F
+ # @param cooling_tolerance_deg_f [Double] Tolerance for time cooling setpoint not met in degree F
+ # @return [Boolean] returns true if successful, false if not
def model_add_reporting_tolerances(model, heating_tolerance_deg_f: 1.0, cooling_tolerance_deg_f: 1.0)
reporting_tolerances = model.getOutputControlReportingTolerances
heating_tolerance_deg_c = OpenStudio.convert(heating_tolerance_deg_f, 'R', 'K').get
cooling_tolerance_deg_c = OpenStudio.convert(cooling_tolerance_deg_f, 'R', 'K').get
reporting_tolerances.setToleranceforTimeHeatingSetpointNotMet(heating_tolerance_deg_c)
@@ -7185,30 +7335,30 @@
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
# @param model [OpenStudio::Model::Model] OpenStudio model object
# @param climate_zone [String] ASHRAE climate zone, e.g. 'ASHRAE 169-2013-4A'
- # @return [Bool] returns true if successful, false if not
+ # @return [Boolean] returns true if successful, false if not
def model_apply_constructions(model, climate_zone, wwr_building_type, wwr_info)
model_apply_standard_constructions(model, climate_zone, wwr_building_type: nil, wwr_info: {})
return true
end
# Generate baseline log to a specific file directory
# @param file_directory [String] file directory
+ # @return [Boolean] returns true if successful, false if not
def generate_baseline_log(file_directory)
return true
end
# Update ground temperature profile based on the weather file specified in the model
#
# @param model [OpenStudio::Model::Model] OpenStudio model object
# @param climate_zone [String] ASHRAE climate zone, e.g. 'ASHRAE 169-2013-4A'
- # @return [Bool] returns true if successful, false if not
+ # @return [Boolean] returns true if successful, false if not
def model_update_ground_temperature_profile(model, climate_zone)
return true
end
end