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

new() click to toggle source
# File lib/openstudio-standards/standards/deer/deer.rb, line 6
def initialize
  load_standards_database
end

Public Instance Methods

air_loop_hvac_dcv_required_when_erv(air_loop_hvac) click to toggle source

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
air_loop_hvac_demand_control_ventilation_limits(air_loop_hvac) click to toggle source

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
air_loop_hvac_economizer_limits(air_loop_hvac, climate_zone) click to toggle source

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
air_loop_hvac_economizer_required?(air_loop_hvac, climate_zone) click to toggle source

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
air_loop_hvac_economizer_type_allowable?(air_loop_hvac, climate_zone) click to toggle source

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
air_loop_hvac_unoccupied_fan_shutoff_required?(air_loop_hvac) click to toggle source

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
load_standards_database(data_directories = []) click to toggle source

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

Calls superclass method Standard#load_standards_database
# File lib/openstudio-standards/standards/deer/deer.rb, line 14
def load_standards_database(data_directories = [])
  super([__dir__] + data_directories)
end
model_economizer_type(model, climate_zone) click to toggle source

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
model_get_climate_zone_set_from_list(model, possible_climate_zone_sets) click to toggle source

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
model_ventilation_method(model) click to toggle source

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
planar_surface_apply_standard_construction(planar_surface, climate_zone, previous_construction_map = {}) click to toggle source

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
  # Windows
  if surf_type == 'ExteriorWindow'
    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
  # Skylights
  elsif surf_type == 'Skylight'
    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
  # Exterior Doors
  elsif surf_type == 'ExteriorDoor'
    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
  # All other surface types
  else
    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
space_infiltration_rate_75_pa(space = nil) click to toggle source

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