class DEER
This abstract class holds methods that many versions of DEER
share. If a method in this class is redefined by a subclass, the implementation in the subclass is used. @abstract
Public Class Methods
Standard::new
# File lib/openstudio-standards/standards/deer/deer.rb, line 6 def initialize super() load_standards_database end
Public Instance Methods
Determine if the standard has an exception for demand control ventilation when an energy recovery device is present. Unlike ASHRAE 90.1, Title 24 does not have an ERV exception to DCV. This method is a copy of what is in Standards.AirLoopHVAC.rb and ensures ERVs will not prevent DCV from being applied to DEER
models.
# File lib/openstudio-standards/standards/deer/deer.AirLoopHVAC.rb, line 31 def air_loop_hvac_dcv_required_when_erv(air_loop_hvac) dcv_required_when_erv_present = true return dcv_required_when_erv_present end
Determines the OA flow rates above which an economizer is required. Two separate rates, one for systems with an economizer and another for systems without. The small numbers here are to reflect that there is not a minimum airflow requirement in Title 24. @return [Array<Double>] [min_oa_without_economizer_cfm, min_oa_with_economizer_cfm]
# File lib/openstudio-standards/standards/deer/deer.AirLoopHVAC.rb, line 20 def air_loop_hvac_demand_control_ventilation_limits(air_loop_hvac) min_oa_without_economizer_cfm = 0.01 min_oa_with_economizer_cfm = 0.01 return [min_oa_without_economizer_cfm, min_oa_with_economizer_cfm] end
Determine the limits for the type of economizer present on the AirLoopHVAC, if any. Enthalpy limit is from MASControl3. @param air_loop_hvac [OpenStudio::Model::AirLoopHVAC] air loop @param climate_zone [String] ASHRAE climate zone, e.g. ‘ASHRAE 169-2013-4A’ @return [Array<Double>] [drybulb_limit_f, enthalpy_limit_btu_per_lb, dewpoint_limit_f]
# File lib/openstudio-standards/standards/deer/deer.AirLoopHVAC.rb, line 154 def air_loop_hvac_economizer_limits(air_loop_hvac, climate_zone) drybulb_limit_f = nil enthalpy_limit_btu_per_lb = nil dewpoint_limit_f = nil # Get the OA system and OA controller oa_sys = air_loop_hvac.airLoopHVACOutdoorAirSystem return [nil, nil, nil] unless oa_sys.is_initialized oa_sys = oa_sys.get oa_control = oa_sys.getControllerOutdoorAir economizer_type = oa_control.getEconomizerControlType case economizer_type when 'NoEconomizer' return [nil, nil, nil] when 'FixedDryBulb' enthalpy_limit_btu_per_lb = 28 search_criteria = { 'template' => template, 'climate_zone' => climate_zone } econ_limits = model_find_object(standards_data['economizers'], search_criteria) drybulb_limit_f = econ_limits['fixed_dry_bulb_high_limit_shutoff_temp'] end return [drybulb_limit_f, enthalpy_limit_btu_per_lb, dewpoint_limit_f] end
Determine whether or not this system is required to have an economizer. Logic inferred from MASControl3 INP files and parameters database.
@param air_loop_hvac [OpenStudio::Model::AirLoopHVAC] air loop @param climate_zone [String] ASHRAE climate zone, e.g. ‘ASHRAE 169-2013-4A’ @return [Boolean] returns true if an economizer is required, false if not
# File lib/openstudio-standards/standards/deer/deer.AirLoopHVAC.rb, line 42 def air_loop_hvac_economizer_required?(air_loop_hvac, climate_zone) economizer_required = false # skip systems without outdoor air return economizer_required unless air_loop_hvac.airLoopHVACOutdoorAirSystem.is_initialized # Determine if the airloop serves any computer rooms # / data centers, which changes the economizer. is_dc = false if air_loop_hvac_data_center_area_served(air_loop_hvac) > 0 is_dc = true end # Retrieve economizer limits from JSON search_criteria = { 'template' => template, 'climate_zone' => climate_zone, 'data_center' => is_dc } econ_limits = model_find_object(standards_data['economizers'], search_criteria) if econ_limits.nil? OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "Cannot find economizer limits for template '#{template}' and climate zone '#{climate_zone}', assuming no economizer required.") return economizer_required end # Determine the minimum capacity and whether or not it is a data center minimum_capacity_btu_per_hr = econ_limits['capacity_limit'] # A big number of btu per hr as the minimum requirement if nil in spreadsheet infinity_btu_per_hr = 999_999_999_999 minimum_capacity_btu_per_hr = infinity_btu_per_hr if minimum_capacity_btu_per_hr.nil? # Check whether the system requires an economizer by comparing # the system capacity to the minimum capacity. total_cooling_capacity_w = air_loop_hvac_total_cooling_capacity(air_loop_hvac) total_cooling_capacity_btu_per_hr = OpenStudio.convert(total_cooling_capacity_w, 'W', 'Btu/hr').get # Check whether the system has chilled water cooling has_chilled_water_cooling = false air_loop_hvac.supplyComponents.each do |equip| if equip.to_CoilCoolingWater.is_initialized has_chilled_water_cooling = true end end # Applicability logic from MASControl3 if has_chilled_water_cooling # All systems with chilled water cooling get an economizer regardless of capacity OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "#{air_loop_hvac.name} requires an economizer because it has chilled water cooling.") economizer_required = true else # DX and other systems may have a capacity limit if total_cooling_capacity_btu_per_hr >= minimum_capacity_btu_per_hr if is_dc OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "#{air_loop_hvac.name} requires an economizer because the total cooling capacity of #{total_cooling_capacity_btu_per_hr.round} Btu/hr exceeds the minimum capacity of #{minimum_capacity_btu_per_hr.round} Btu/hr for data centers.") else OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "#{air_loop_hvac.name} requires an economizer because the total cooling capacity of #{total_cooling_capacity_btu_per_hr.round} Btu/hr exceeds the minimum capacity of #{minimum_capacity_btu_per_hr.round} Btu/hr.") end economizer_required = true else if is_dc OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "#{air_loop_hvac.name} does not require an economizer because the total cooling capacity of #{total_cooling_capacity_btu_per_hr.round} Btu/hr is less than the minimum capacity of #{minimum_capacity_btu_per_hr.round} Btu/hr for data centers.") else OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "#{air_loop_hvac.name} does not require an economizer because the total cooling capacity of #{total_cooling_capacity_btu_per_hr.round} Btu/hr is less than the minimum capacity of #{minimum_capacity_btu_per_hr.round} Btu/hr.") end end end return economizer_required end
Check the economizer type currently specified in the ControllerOutdoorAir object on this air loop is acceptable per the standard. Based on the MASControl rules, it appears that only NoEconomizer and FixedDryBulb are allowed.
@param air_loop_hvac [OpenStudio::Model::AirLoopHVAC] air loop @param climate_zone [String] ASHRAE climate zone, e.g. ‘ASHRAE 169-2013-4A’ @return [Boolean] Returns true if allowable, if the system has no economizer or no OA system.
Returns false if the economizer type is not allowable.
# File lib/openstudio-standards/standards/deer/deer.AirLoopHVAC.rb, line 121 def air_loop_hvac_economizer_type_allowable?(air_loop_hvac, climate_zone) # EnergyPlus economizer types # 'NoEconomizer' # 'FixedDryBulb' # 'FixedEnthalpy' # 'DifferentialDryBulb' # 'DifferentialEnthalpy' # 'FixedDewPointAndDryBulb' # 'ElectronicEnthalpy' # 'DifferentialDryBulbAndEnthalpy' # Get the OA system and OA controller oa_sys = air_loop_hvac.airLoopHVACOutdoorAirSystem return true unless oa_sys.is_initialized # No OA system oa_sys = oa_sys.get oa_control = oa_sys.getControllerOutdoorAir economizer_type = oa_control.getEconomizerControlType # Return true if one of the valid choices is used, false otherwise case economizer_type when 'NoEconomizer', 'FixedDryBulb' return true else return false end end
For LA100 calibration, default to systems being left on Overwritten to be required for DEER2020
and beyond
@param air_loop_hvac [OpenStudio::Model::AirLoopHVAC] air loop @return [Boolean] returns true if required, false if not
# File lib/openstudio-standards/standards/deer/deer.AirLoopHVAC.rb, line 9 def air_loop_hvac_unoccupied_fan_shutoff_required?(air_loop_hvac) shutoff_required = false return shutoff_required end
Loads the openstudio standards dataset for this standard.
@param data_directories [Array<String>] array of file paths that contain standards data @return [Hash] a hash of standards data
Standard#load_standards_database
# File lib/openstudio-standards/standards/deer/deer.rb, line 15 def load_standards_database(data_directories = []) super([__dir__] + data_directories) end
Determine the prototypical economizer type for the model. Based on the MASControl rules, it appears that only FixedDryBulb economizers are used.
@param model [OpenStudio::Model::Model] OpenStudio model object @param climate_zone [String] DEER
climate zone @return [String] the economizer type. Possible values are: ‘NoEconomizer’ ‘FixedDryBulb’ ‘FixedEnthalpy’ ‘DifferentialDryBulb’ ‘DifferentialEnthalpy’ ‘FixedDewPointAndDryBulb’ ‘ElectronicEnthalpy’ ‘DifferentialDryBulbAndEnthalpy’
# File lib/openstudio-standards/prototypes/deer/deer.Model.rb, line 19 def model_economizer_type(model, climate_zone) economizer_type = 'FixedDryBulb' return economizer_type end
Determine which climate zone to use. Uses the most specific climate zone set.
# File lib/openstudio-standards/standards/deer/deer.Model.rb, line 6 def model_get_climate_zone_set_from_list(model, possible_climate_zone_sets) # OpenStudio.logFree(OpenStudio::Warn, 'openstudio.model.Model', "SpaceType #{space.spaceType.get.name} does not have a standardsSpaceType assigned.") climate_zone_set = possible_climate_zone_sets.max return climate_zone_set end
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] OpenStudio model object @return [String] the ventilation method, either Sum or Maximum
# File lib/openstudio-standards/standards/deer/deer.Model.rb, line 18 def model_ventilation_method(model) ventilation_method = 'Maximum' return ventilation_method end
If construction properties can be found based on the template, the standards intended surface type, the standards construction type, the climate zone, and the occupancy type, create a construction that meets those properties and assign it to this surface.
90.1-2007, 90.1-2010, 90.1-2013 @param climate_zone [String] DEER
climate zone @param previous_construction_map [Hash] a hash where the keys are an array of inputs
- template, climate_zone, intended_surface_type, standards_construction_type, occ_type
-
and the values are the constructions. If supplied, constructions will be pulled from this hash if already created to avoid creating duplicate constructions. @return [Hash] returns a hash where the key is an array of inputs
- template, climate_zone, intended_surface_type, standards_construction_type, occ_type
-
and the value is the newly created construction. This can be used to avoid creating duplicate constructions. @todo Align the standard construction enumerations in the spreadsheet with the enumerations in OpenStudio (follow CBECC-Com).
# File lib/openstudio-standards/standards/deer/deer.PlanarSurface.rb, line 24 def planar_surface_apply_standard_construction(planar_surface, climate_zone, previous_construction_map = {}) # Skip surfaces not in a space return previous_construction_map if planar_surface.space.empty? space = planar_surface.space.get # Skip surfaces that don't have a construction return previous_construction_map if planar_surface.construction.empty? construction = planar_surface.construction.get # Determine if residential or nonresidential # based on the space type. occ_type = 'Nonresidential' if OpenstudioStandards::Space.space_residential?(space) occ_type = 'HighriseResidential' end # Get the climate zone set climate_zone_set = model_find_climate_zone_set(planar_surface.model, climate_zone) # Get the intended surface type standards_info = construction.standardsInformation surf_type = standards_info.intendedSurfaceType if surf_type.empty? OpenStudio.logFree(OpenStudio::Warn, 'openstudio.model.PlanarSurface', "Could not determine the intended surface type for #{planar_surface.name} from #{construction.name}. This surface will not have the standard applied.") return previous_construction_map end surf_type = surf_type.get # Get the standards type, which is based on different fields # if is intended for a window, a skylight, or something else. # Mapping is between standards-defined enumerations and the # enumerations available in OpenStudio. stds_type = nil case surf_type when 'ExteriorWindow' # Windows stds_type = standards_info.fenestrationFrameType if stds_type.is_initialized stds_type = stds_type.get case stds_type when 'Metal Framing', 'Metal Framing with Thermal Break' stds_type = 'Metal framing (all other)' when 'Non-Metal Framing' stds_type = 'Nonmetal framing (all)' else OpenStudio.logFree(OpenStudio::Warn, 'openstudio.model.PlanarSurface', "The standards fenestration frame type #{stds_type} cannot be used on #{surf_type} in #{planar_surface.name}. This surface will not have the standard applied.") return previous_construction_map end else OpenStudio.logFree(OpenStudio::Warn, 'openstudio.model.PlanarSurface', "Could not determine the standards fenestration frame type for #{planar_surface.name} from #{construction.name}. This surface will not have the standard applied.") return previous_construction_map end when 'Skylight' # Skylights stds_type = standards_info.fenestrationType if stds_type.is_initialized stds_type = stds_type.get case stds_type when 'Glass Skylight with Curb' stds_type = 'Glass with Curb' when 'Plastic Skylight with Curb' stds_type = 'Plastic with Curb' when 'Plastic Skylight without Curb', 'Glass Skylight without Curb' stds_type = 'Without Curb' else OpenStudio.logFree(OpenStudio::Warn, 'openstudio.model.PlanarSurface', "The standards fenestration type #{stds_type} cannot be used on #{surf_type} in #{planar_surface.name}. This surface will not have the standard applied.") return previous_construction_map end else OpenStudio.logFree(OpenStudio::Warn, 'openstudio.model.PlanarSurface', "Could not determine the standards fenestration type for #{planar_surface.name} from #{construction.name}. This surface will not have the standard applied.") return previous_construction_map end when 'ExteriorDoor' # Exterior Doors stds_type = standards_info.standardsConstructionType if stds_type.is_initialized stds_type = stds_type.get case stds_type when 'RollUp', 'Rollup', 'NonSwinging', 'Nonswinging' stds_type = 'NonSwinging' else stds_type = 'Swinging' end else OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.PlanarSurface', "Could not determine the standards construction type for exterior door #{planar_surface.name}. This door will not have the standard applied.") return previous_construction_map end else # All other surface types stds_type = standards_info.standardsConstructionType if stds_type.is_initialized stds_type = stds_type.get else OpenStudio.logFree(OpenStudio::Warn, 'openstudio.model.PlanarSurface', "Could not determine the standards construction type for #{planar_surface.name}. This surface will not have the standard applied.") return previous_construction_map end end # Check if the construction type was already created. # If yes, use that construction. If no, make a new one. new_construction = nil type = [template, climate_zone, surf_type, stds_type, occ_type] if previous_construction_map[type] new_construction = previous_construction_map[type] else new_construction = model_find_and_add_construction(planar_surface.model, climate_zone_set, surf_type, stds_type, occ_type) if !new_construction == false previous_construction_map[type] = new_construction end end # Assign the new construction to the surface if new_construction planar_surface.setConstruction(new_construction) OpenStudio.logFree(OpenStudio::Debug, 'openstudio.model.PlanarSurface', "Set the construction for #{planar_surface.name} to #{new_construction.name}.") else OpenStudio.logFree(OpenStudio::Warn, 'openstudio.model.PlanarSurface', "Could not generate a standard construction for #{planar_surface.name}.") return previous_construction_map end return previous_construction_map end
Baseline infiltration rate
In the MASControl2 database DEER_Rules_ProtoDB.db, table ‘BDLRules’, ‘SPACE:INF-FLOW/AREA’ is: “if (L(rvVertWallArea) < 1 ) then 0.001 else if(SN(LSI(C-ZONE-TYPE)) = ”CRAWL“) then 0.075 else 0.038 * L(rvVertWallArea) / L(AREA) endif endif” meaning the default DEER
infiltation value is 0.038 cfm/ft2 per exterior wall area at typical building pressures. Using the same PNNL prototype assumptions for natural pressure, this correlates to a baseline infiltration rate of 0.3393 cfm/ft2 of exterior wall area at 75Pa. *Note that this implies a baseline infiltration rate ~5 times lower than the PNNL modeling guideline.
@return [Double] the baseline infiltration rate, in cfm/ft^2 exterior above grade wall area at 75 Pa
# File lib/openstudio-standards/standards/deer/deer.Space.rb, line 16 def space_infiltration_rate_75_pa(space = nil) basic_infil_rate_cfm_per_ft2 = 0.3393 return basic_infil_rate_cfm_per_ft2 end