module OpenstudioStandards::SqlFile

The SqlFile module provides methods to get information from the EnergyPlus .sql file after a run

The SqlFile module provides methods to get information from the EnergyPlus .sql file after a run

The SqlFile module provides methods to get information from the EnergyPlus .sql file after a run

The SqlFile module provides methods to get information from the EnergyPlus .sql file after a run

Public Class Methods

construction_calculated_fenestration_u_factor(construction) click to toggle source

Return the calculated fenestration U-Factor based on the glass, frame, and divider performance and area from the EnergyPlus Envelope Summary report.

@param construction [OpenStudio:Model:Construction] OpenStudio Construction object @return [Double] the U-Factor in W/m^2*K

# File lib/openstudio-standards/sql_file/fenestration.rb, line 11
def self.construction_calculated_fenestration_u_factor(construction)
  # check for sql file
  sql = construction.model.sqlFile
  unless sql.is_initialized
    OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.SqlFile', 'Model has no sql file containing results, cannot lookup data.')
    return false
  end
  sql = sql.get

  u_factor_w_per_m2_k = nil
  construction_name = construction.name.get.to_s

  row_query = "SELECT RowName
              FROM tabulardatawithstrings
              WHERE ReportName='EnvelopeSummary'
              AND ReportForString='Entire Facility'
              AND TableName='Exterior Fenestration'
              AND Value='#{construction_name.upcase}'"

  row_id = sql.execAndReturnFirstString(row_query)

  if row_id.is_initialized
    row_id = row_id.get
  else
    OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.SqlFile', "U-Factor row ID not found for construction: #{construction_name}.")
    row_id = 9999
  end

  # Glass U-Factor
  glass_u_factor_query = "SELECT Value
              FROM tabulardatawithstrings
              WHERE ReportName='EnvelopeSummary'
              AND ReportForString='Entire Facility'
              AND TableName='Exterior Fenestration'
              AND ColumnName='Glass U-Factor'
              AND RowName='#{row_id}'"

  glass_u_factor_w_per_m2_k = sql.execAndReturnFirstDouble(glass_u_factor_query)

  glass_u_factor_w_per_m2_k = glass_u_factor_w_per_m2_k.is_initialized ? glass_u_factor_w_per_m2_k.get : 0.0

  # Glass area
  glass_area_query = "SELECT Value
                      FROM tabulardatawithstrings
                      WHERE ReportName='EnvelopeSummary'
                      AND ReportForString='Entire Facility'
                      AND TableName='Exterior Fenestration'
                      AND ColumnName='Glass Area'
                      AND RowName='#{row_id}'"

  glass_area_m2 = sql.execAndReturnFirstDouble(glass_area_query)

  glass_area_m2 = glass_area_m2.is_initialized ? glass_area_m2.get : 0.0

  # Frame conductance
  frame_conductance_query = "SELECT Value
              FROM tabulardatawithstrings
              WHERE ReportName='EnvelopeSummary'
              AND ReportForString='Entire Facility'
              AND TableName='Exterior Fenestration'
              AND ColumnName='Frame Conductance'
              AND RowName='#{row_id}'"

  frame_conductance_w_per_m2_k = sql.execAndReturnFirstDouble(frame_conductance_query)

  frame_conductance_w_per_m2_k = frame_conductance_w_per_m2_k.is_initialized ? frame_conductance_w_per_m2_k.get : 0.0

  # Frame area
  frame_area_query = "SELECT Value
                      FROM tabulardatawithstrings
                      WHERE ReportName='EnvelopeSummary'
                      AND ReportForString='Entire Facility'
                      AND TableName='Exterior Fenestration'
                      AND ColumnName='Frame Area'
                      AND RowName='#{row_id}'"

  frame_area_m2 = sql.execAndReturnFirstDouble(frame_area_query)

  frame_area_m2 = frame_area_m2.is_initialized ? frame_area_m2.get : 0.0

  # Divider conductance
  divider_conductance_query = "SELECT Value
              FROM tabulardatawithstrings
              WHERE ReportName='EnvelopeSummary'
              AND ReportForString='Entire Facility'
              AND TableName='Exterior Fenestration'
              AND ColumnName='Divider Conductance'
              AND RowName='#{row_id}'"

  divider_conductance_w_per_m2_k = sql.execAndReturnFirstDouble(divider_conductance_query)

  divider_conductance_w_per_m2_k = divider_conductance_w_per_m2_k.is_initialized ? divider_conductance_w_per_m2_k.get : 0.0

  # Divider area
  divider_area_query = "SELECT Value
                      FROM tabulardatawithstrings
                      WHERE ReportName='EnvelopeSummary'
                      AND ReportForString='Entire Facility'
                      AND TableName='Exterior Fenestration'
                      AND ColumnName='Divder Area'
                      AND RowName='#{row_id}'"

  divider_area_m2 = sql.execAndReturnFirstDouble(divider_area_query)

  divider_area_m2 = divider_area_m2.is_initialized ? divider_area_m2.get : 0.0

  u_factor_w_per_m2_k = (glass_u_factor_w_per_m2_k * glass_area_m2 + frame_conductance_w_per_m2_k * frame_area_m2 + divider_conductance_w_per_m2_k * divider_area_m2) / (glass_area_m2 + frame_area_m2 + divider_area_m2)

  return u_factor_w_per_m2_k
end
model_get_annual_energy_by_fuel_and_enduse(model, fuel_type, end_use) click to toggle source

Gets the model annual energy consumption by fuel and enduse in GJ from the sql file

@param model [OpenStudio::Model::Model] OpenStudio model object @param fuel_type [String] the fuel type, e.g. ‘Electricity’ @param end_use [String] the end use, e.g. ‘InteriorEquipment’ @return [Double] the model energy fuel type end use in Gigajoules

# File lib/openstudio-standards/sql_file/energy_use.rb, line 12
def self.model_get_annual_energy_by_fuel_and_enduse(model, fuel_type, end_use)
  # get model sql file
  sql_file = OpenstudioStandards::SqlFile.model_get_sql_file(model)

  # setup the queries
  query = "SELECT Value
          FROM TabularDataWithStrings
          WHERE ReportName='AnnualBuildingUtilityPerformanceSummary'
          AND ReportForString='Entire Facility'
          AND TableName='End Uses'
          AND RowName = '#{end_use}'
          AND ColumnName='#{fuel_type}'"

  # get the info
  energy_gj = sql_file.execAndReturnFirstDouble(query)

  # make sure all the data are available
  if energy_gj.empty?
    OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.SqlFile', "Could not get energy for #{fuel_type} #{end_use}.")
    return 0.0
  end

  return energy_gj.get
end
model_get_annual_eui_kbtu_per_ft2(model) click to toggle source

Gets the model total annual energy use intensity in kBtu/ft^2 from the sql file

@param model [OpenStudio::Model::Model] OpenStudio model object @return [Double] the model total annual site energy use intensity in kBtu/ft^2, inclusive of all spaces

# File lib/openstudio-standards/sql_file/energy_use.rb, line 152
def self.model_get_annual_eui_kbtu_per_ft2(model)
  # get model sql file
  sql_file = OpenstudioStandards::SqlFile.model_get_sql_file(model)

  # make sure all required data are available
  if sql_file.totalSiteEnergy.empty?
    OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.SqlFile', 'Site energy data unavailable.')
    return false
  end

  total_site_energy_kbtu = OpenStudio.convert(sql_file.totalSiteEnergy.get, 'GJ', 'kBtu').get
  floor_area_ft2 = OpenStudio.convert(model.getBuilding.floorArea, 'm^2', 'ft^2').get
  site_eui_kbtu_per_ft2 = total_site_energy_kbtu / floor_area_ft2

  return site_eui_kbtu_per_ft2
end
model_get_annual_eui_kbtu_per_ft2_by_fuel_and_enduse(model, fuel_type, end_use) click to toggle source

Gets annual energy use intensity by fuel and end use in kBtu/ft^2 from the sql file

@param model [OpenStudio::Model::Model] OpenStudio model object @return [Double] a hash of annual energy use intensity by each fuel and end use in kBtu/ft^2, inclusive of all spaces

# File lib/openstudio-standards/sql_file/energy_use.rb, line 139
def self.model_get_annual_eui_kbtu_per_ft2_by_fuel_and_enduse(model, fuel_type, end_use)
  energy_gj = OpenstudioStandards::SqlFile.model_get_annual_energy_by_fuel_and_enduse(model, fuel_type, end_use)
  energy_kbtu = OpenStudio.convert(energy_gj, 'GJ', 'kBtu').get
  floor_area_ft2 = OpenStudio.convert(model.getBuilding.floorArea, 'm^2', 'ft^2').get
  eui_kbtu_per_ft2 = energy_kbtu / floor_area_ft2

  return eui_kbtu_per_ft2
end
model_get_annual_occupied_unmet_cooling_hours(model, tolerance: nil) click to toggle source

Gets the annual occupied unmet cooling hours from the sql file

@param model [OpenStudio::Model::Model] OpenStudio model object @param tolerance [Double] tolerance in degrees Rankine to log an unmet hour

If this is unspecified, the tolerance will be the tolerance specified in OutputControl:ReportingTolerances.
If there isn't an OutputControl:ReportingTolerances object, the EnergyPlus default is 0.2 degrees Kelvin.
If a tolerance is defined and does not match the tolerance defined in OutputControl:ReportingTolerances,
this method will compare the zone temperature and setpoint temperature timeseries for each zone.
Generally, it is much faster to define tolerances with the OutputControl:ReportingTolerances object.

@return [Double] occupied cooling unmet hours

# File lib/openstudio-standards/sql_file/unmet_hours.rb, line 271
def self.model_get_annual_occupied_unmet_cooling_hours(model, tolerance: nil)
  reporting_tolerances = model.getOutputControlReportingTolerances
  model_tolerance = reporting_tolerances.toleranceforTimeHeatingSetpointNotMet
  model_tolerance_r = OpenStudio.convert(model_tolerance, 'K', 'R')

  # get model sql file
  sql_file = OpenstudioStandards::SqlFile.model_get_sql_file(model)

  use_detailed = false
  unless tolerance.nil?
    # check to see if input argument tolerance matches model tolerance
    tolerance_k = OpenStudio.convert(tolerance, 'R', 'K').get
    unless (model_tolerance - tolerance_k).abs < 1e-3
      # input argument tolerance does not match model tolerance; need to recalculate unmet hours
      use_detailed = true
    end
  end

  if use_detailed
    # calculate unmet hours for each zone using zone time series
    zones_unmet_hours = OpenstudioStandards::SqlFile.model_get_annual_occupied_unmet_cooling_hours_detailed(model, tolerance: tolerance)
    cooling_unmet_hours = zones_unmet_hours['sum_bldg_occupied_unmet_hours']
  else
    # use default EnergyPlus unmet hours reporting
    OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.SqlFile', "Calculating cooling unmet hours with #{model_tolerance_r} R tolerance")

    # setup the queries
    cooling_setpoint_unmet_query = "SELECT Value
                                  FROM TabularDataWithStrings
                                  WHERE ReportName='SystemSummary'
                                  AND ReportForString='Entire Facility'
                                  AND TableName='Time Setpoint Not Met'
                                  AND RowName = 'Facility'
                                  AND ColumnName='During Occupied Cooling'"
    # get the info
    cooling_setpoint_unmet = sql_file.execAndReturnFirstDouble(cooling_setpoint_unmet_query)

    # make sure all the data are available
    if cooling_setpoint_unmet.empty?
      OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.SqlFile', 'Could not get unmet cooling hours information.')
      return false
    end

    cooling_unmet_hours = cooling_setpoint_unmet.get
  end

  return cooling_unmet_hours
end
model_get_annual_occupied_unmet_cooling_hours_detailed(model, tolerance: 1.0, occupied_percentage_threshold: 0.05) click to toggle source

Gets the annual occupied unmet cooling hours from zone temperature time series in the sql file

@param model [OpenStudio::Model::Model] OpenStudio model object @param occupied_percentage_threshold [Double] the minimum fraction (0 to 1) that counts as occupied @param tolerance [Double] tolerance in degrees Rankine to log an unmet hour @return [Hash] Hash with ‘sum’ of cooling unmet hours and ‘zone_temperature_differences’ of all zone unmet hours data @todo account for operative temperature thermostats

# File lib/openstudio-standards/sql_file/unmet_hours.rb, line 111
def self.model_get_annual_occupied_unmet_cooling_hours_detailed(model, tolerance: 1.0, occupied_percentage_threshold: 0.05)
  OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.SqlFile', "Calculating zone cooling occupied unmet hours with #{tolerance} R tolerance. This may take some time.")
  # get model sql file
  sql_file = OpenstudioStandards::SqlFile.model_get_sql_file(model)

  # std to access thermal zone methods. Replace when thermal zone methods are moved to modules
  std = Standard.build('90.1-2013')

  # convert tolerance to Kelvin
  tolerance_k = OpenStudio.convert(tolerance, 'R', 'K').get

  ann_env_pd = OpenstudioStandards::SqlFile.model_get_weather_run_period(model)
  unless ann_env_pd
    OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.SqlFile', 'Could not get annual run period.')
    return false
  end

  # for each zone calculate unmet hours and store in array
  bldg_unmet_hours = []
  bldg_occ_unmet_hours = []
  zone_data = []
  model.getThermalZones.each do |zone|
    # skip zones that aren't cooled
    next unless OpenstudioStandards::ThermalZone.thermal_zone_cooled?(zone)

    # get zone air temperatures
    zone_temp_timeseries = sql_file.timeSeries(ann_env_pd, 'Hourly', 'Zone Air Temperature', zone.name.get)
    if zone_temp_timeseries.empty?
      # try mean air temperature instead
      zone_temp_timeseries = sql_file.timeSeries(ann_env_pd, 'Hourly', 'Zone Mean Air Temperature', zone.name.get)
      if zone_temp_timeseries.empty?
        # no air temperature found
        OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.SqlFile', "Could not find zone air temperature timeseries for zone '#{zone.name.get}'")
        return false
      end
    end
    zone_temp_timeseries = zone_temp_timeseries.get.values

    # get zone thermostat heating setpoint temperatures
    zone_setpoint_temp_timeseries = sql_file.timeSeries(ann_env_pd, 'Hourly', 'Zone Thermostat Cooling Setpoint Temperature', zone.name.get)
    if zone_setpoint_temp_timeseries.empty?
      # no setpoint temperature found
      OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.SqlFile', "Could not find cooling setpoint temperature timeseries for zone '#{zone.name.get}'")
      return false
    end
    zone_setpoint_temp_timeseries = zone_setpoint_temp_timeseries.get.values

    # calculate zone occupancy by making a new ruleset schedule
    occ_schedule_ruleset = OpenstudioStandards::ThermalZone.thermal_zone_get_occupancy_schedule(zone)
    occ_values = OpenstudioStandards::Schedules.schedule_ruleset_get_hourly_values(occ_schedule_ruleset)

    # calculate difference accounting for unmet hours tolerance
    zone_temperature_diff = zone_setpoint_temp_timeseries.map.with_index { |t, x| (t - zone_temp_timeseries[x]) }
    zone_unmet_hours = zone_temperature_diff.map { |x| (x - tolerance_k) > 0 ? 1 : 0 }
    zone_occ_unmet_hours = []
    for i in (0..zone_unmet_hours.size - 1)
      bldg_unmet_hours[i] = 0 if bldg_unmet_hours[i].nil?
      bldg_occ_unmet_hours[i] = 0 if bldg_occ_unmet_hours[i].nil?
      bldg_unmet_hours[i] += zone_unmet_hours[i]
      if occ_values[i] >= occupied_percentage_threshold
        zone_occ_unmet_hours[i] = zone_unmet_hours[i]
        bldg_occ_unmet_hours[i] += zone_unmet_hours[i]
      else
        zone_occ_unmet_hours[i] = 0
      end
    end

    # log information for zone
    # could reduce the number of returned variables if this poses a storage or data transfer problem
    zone_data << {
      'zone_name' => zone.name,
      'zone_area' => zone.floorArea,
      'zone_air_temperatures' => zone_temp_timeseries.map { |t| t.round(3) },
      'zone_air_setpoint_temperatures' => zone_setpoint_temp_timeseries.map { |t| t.round(3) },
      'zone_air_temperature_differences' => zone_temperature_diff.map { |d| d.round(3) },
      'zone_occupancy' => occ_values.map { |x| x.round(3) },
      'zone_unmet_hours' => zone_unmet_hours,
      'zone_occupied_unmet_hours' => zone_occ_unmet_hours,
      'sum_zone_unmet_hours' => zone_unmet_hours.count { |x| x > 0 },
      'sum_zone_occupied_unmet_hours' => zone_occ_unmet_hours.count { |x| x > 0 }
    }
  end

  occupied_unmet_cooling_hours_detailed = { 'sum_bldg_unmet_hours' => bldg_unmet_hours.count { |x| x > 0 },
                                            'sum_bldg_occupied_unmet_hours' => bldg_occ_unmet_hours.count { |x| x > 0 },
                                            'bldg_unmet_hours' => bldg_unmet_hours,
                                            'bldg_occupied_unmet_hours' => bldg_occ_unmet_hours,
                                            'zone_data' => zone_data }
  return occupied_unmet_cooling_hours_detailed
end
model_get_annual_occupied_unmet_heating_hours(model, tolerance: nil) click to toggle source

Gets the annual occupied unmet heating hours from the sql file

@param model [OpenStudio::Model::Model] OpenStudio model object @param tolerance [Double] tolerance in degrees Rankine to log an unmet hour

If this is unspecified, the tolerance will be the tolerance specified in OutputControl:ReportingTolerances.
If there isn't an OutputControl:ReportingTolerances object, the EnergyPlus default is 0.2 degrees Kelvin.
If a tolerance is defined and does not match the tolerance defined in OutputControl:ReportingTolerances,
this method will compare the zone temperature and setpoint temperature timeseries for each zone.
Generally, it is much faster to define tolerances with the OutputControl:ReportingTolerances object.

@return [Double] occupied heating unmet hours

# File lib/openstudio-standards/sql_file/unmet_hours.rb, line 212
def self.model_get_annual_occupied_unmet_heating_hours(model, tolerance: nil)
  reporting_tolerances = model.getOutputControlReportingTolerances
  model_tolerance = reporting_tolerances.toleranceforTimeHeatingSetpointNotMet
  model_tolerance_r = OpenStudio.convert(model_tolerance, 'K', 'R')

  # get model sql file
  sql_file = OpenstudioStandards::SqlFile.model_get_sql_file(model)

  use_detailed = false
  unless tolerance.nil?
    # check to see if input argument tolerance matches model tolerance
    tolerance_k = OpenStudio.convert(tolerance, 'R', 'K').get
    unless (model_tolerance - tolerance_k).abs < 1e-3
      # input argument tolerance does not match model tolerance; need to recalculate unmet hours
      use_detailed = true
    end
  end

  if use_detailed
    # calculate unmet hours for each zone using zone time series
    zones_unmet_hours = OpenstudioStandards::SqlFile.model_get_annual_occupied_unmet_heating_hours_detailed(model, tolerance: tolerance)
    heating_unmet_hours = zones_unmet_hours['sum_bldg_occupied_unmet_hours']
  else
    # use default EnergyPlus unmet hours reporting
    OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.SqlFile', "Calculating heating unmet hours with #{model_tolerance_r} R tolerance")

    # setup the queries
    heating_setpoint_unmet_query = "SELECT Value
                                  FROM TabularDataWithStrings
                                  WHERE ReportName='SystemSummary'
                                  AND ReportForString='Entire Facility'
                                  AND TableName='Time Setpoint Not Met'
                                  AND RowName = 'Facility'
                                  AND ColumnName='During Occupied Heating'"
    # get the info
    heating_setpoint_unmet = sql_file.execAndReturnFirstDouble(heating_setpoint_unmet_query)

    # make sure all the data are available
    if heating_setpoint_unmet.empty?
      OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.SqlFile', 'Could not get unmet heating hours information.')
      return false
    end

    heating_unmet_hours = heating_setpoint_unmet.get
  end

  return heating_unmet_hours
end
model_get_annual_occupied_unmet_heating_hours_detailed(model, tolerance: 1.0, occupied_percentage_threshold: 0.05) click to toggle source

Gets the annual occupied unmet heating hours from zone temperature time series in the sql file

@param model [OpenStudio::Model::Model] OpenStudio model object @param tolerance [Double] tolerance in degrees Rankine to log an unmet hour @param occupied_percentage_threshold [Double] the minimum fraction (0 to 1) that counts as occupied @return [Hash] Hash with ‘sum’ of heating unmet hours and ‘zone_temperature_differences’ of all zone unmet hours data @todo account for operative temperature thermostats

# File lib/openstudio-standards/sql_file/unmet_hours.rb, line 13
def self.model_get_annual_occupied_unmet_heating_hours_detailed(model, tolerance: 1.0, occupied_percentage_threshold: 0.05)
  OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.SqlFile', "Calculating zone heating occupied unmet hours with #{tolerance} R tolerance.  This may take some time.")
  # get model sql file
  sql_file = OpenstudioStandards::SqlFile.model_get_sql_file(model)

  # std to access thermal zone methods. Replace when thermal zone methods are moved to modules
  std = Standard.build('90.1-2013')

  # convert tolerance to Kelvin
  tolerance_k = OpenStudio.convert(tolerance, 'R', 'K').get

  ann_env_pd = OpenstudioStandards::SqlFile.model_get_weather_run_period(model)
  unless ann_env_pd
    OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.SqlFile', 'Could not get annual run period.')
    return false
  end

  # for each zone calculate unmet hours and store in array
  bldg_unmet_hours = []
  bldg_occ_unmet_hours = []
  zone_data = []
  model.getThermalZones.each do |zone|
    # skip zones that aren't heated
    next unless OpenstudioStandards::ThermalZone.thermal_zone_heated?(zone)

    # get zone air temperatures
    zone_temp_timeseries = sql_file.timeSeries(ann_env_pd, 'Hourly', 'Zone Air Temperature', zone.name.get)
    if zone_temp_timeseries.empty?
      # try mean air temperature instead
      zone_temp_timeseries = sql_file.timeSeries(ann_env_pd, 'Hourly', 'Zone Mean Air Temperature', zone.name.get)
      if zone_temp_timeseries.empty?
        # no air temperature found
        OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.SqlFile', "Could not find zone air temperature timeseries for zone '#{zone.name.get}'")
        return false
      end
    end
    zone_temp_timeseries = zone_temp_timeseries.get.values

    # get zone thermostat heating setpoint temperatures
    zone_setpoint_temp_timeseries = sql_file.timeSeries(ann_env_pd, 'Hourly', 'Zone Thermostat Heating Setpoint Temperature', zone.name.get)
    if zone_setpoint_temp_timeseries.empty?
      # no setpoint temperature found
      OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.SqlFile', "Could not find heating setpoint temperature timeseries for zone '#{zone.name.get}'")
      return false
    end
    zone_setpoint_temp_timeseries = zone_setpoint_temp_timeseries.get.values

    # calculate zone occupancy by making a new ruleset schedule
    occ_schedule_ruleset = OpenstudioStandards::ThermalZone.thermal_zone_get_occupancy_schedule(zone)
    occ_values = OpenstudioStandards::Schedules.schedule_ruleset_get_hourly_values(occ_schedule_ruleset)

    # calculate difference accounting for unmet hours tolerance
    zone_temperature_diff = zone_setpoint_temp_timeseries.map.with_index { |t, x| (zone_temp_timeseries[x] - t) }
    zone_unmet_hours = zone_temperature_diff.map { |x| (x + tolerance_k) < 0 ? 1 : 0 }
    zone_occ_unmet_hours = []
    for i in (0..zone_unmet_hours.size - 1)
      bldg_unmet_hours[i] = 0 if bldg_unmet_hours[i].nil?
      bldg_occ_unmet_hours[i] = 0 if bldg_occ_unmet_hours[i].nil?
      bldg_unmet_hours[i] += zone_unmet_hours[i]
      if occ_values[i] >= occupied_percentage_threshold
        zone_occ_unmet_hours[i] = zone_unmet_hours[i]
        bldg_occ_unmet_hours[i] += zone_unmet_hours[i]
      else
        zone_occ_unmet_hours[i] = 0
      end
    end

    # log information for zone
    # could reduce the number of returned variables if this poses a storage or data transfer problem
    zone_data << {
      'zone_name' => zone.name,
      'zone_area' => zone.floorArea,
      'zone_air_temperatures' => zone_temp_timeseries.map { |t| t.round(3) },
      'zone_air_setpoint_temperatures' => zone_setpoint_temp_timeseries.map { |t| t.round(3) },
      'zone_air_temperature_differences' => zone_temperature_diff.map { |d| d.round(3) },
      'zone_occupancy' => occ_values.map { |x| x.round(3) },
      'zone_unmet_hours' => zone_unmet_hours,
      'zone_occupied_unmet_hours' => zone_occ_unmet_hours,
      'sum_zone_unmet_hours' => zone_unmet_hours.count { |x| x > 0 },
      'sum_zone_occupied_unmet_hours' => zone_occ_unmet_hours.count { |x| x > 0 }
    }
  end

  occupied_unmet_heating_hours_detailed = { 'sum_bldg_unmet_hours' => bldg_unmet_hours.count { |x| x > 0 },
                                            'sum_bldg_occupied_unmet_hours' => bldg_occ_unmet_hours.count { |x| x > 0 },
                                            'bldg_unmet_hours' => bldg_unmet_hours,
                                            'bldg_occupied_unmet_hours' => bldg_occ_unmet_hours,
                                            'zone_data' => zone_data }
  return occupied_unmet_heating_hours_detailed
end
model_get_annual_occupied_unmet_hours(model) click to toggle source

Gets the annual occupied unmet hours from the sql file

@param model [OpenStudio::Model::Model] OpenStudio model object @return [Double] the total number of unmet heating or cooling hours

# File lib/openstudio-standards/sql_file/unmet_hours.rb, line 324
def self.model_get_annual_occupied_unmet_hours(model)
  heating_setpoint_unmet = OpenstudioStandards::SqlFile.model_get_annual_occupied_unmet_heating_hours(model)
  cooling_setpoint_unmet = OpenstudioStandards::SqlFile.model_get_annual_occupied_unmet_cooling_hours(model)

  # aggregate heating and cooling hrs
  heating_or_cooling_setpoint_unmet = heating_setpoint_unmet + cooling_setpoint_unmet

  return heating_or_cooling_setpoint_unmet
end
model_get_annual_results_by_end_use_and_fuel_type(model) click to toggle source

Gets all annual energy consumption by enduse and fuel type from the sql file

@param model [OpenStudio::Model::Model] OpenStudio model object @return [Hash] a hash of results for each fuel, where the keys are in the form ‘End Use|Fuel Type’, e.g. Heating|Electricity, Exterior Equipment|Water. All end use/fuel type combos are present, with values of 0.0 if none of this end use/fuel type combo was used by the simulation. @todo update for fuel type changes

# File lib/openstudio-standards/sql_file/energy_use.rb, line 84
def self.model_get_annual_results_by_end_use_and_fuel_type(model)
  energy_values = {}

  # List of all fuel types
  fuel_types = ['Electricity', 'Natural Gas', 'Additional Fuel', 'District Cooling', 'District Heating', 'Water']

  # List of all end uses
  end_uses = ['Heating', 'Cooling', 'Interior Lighting', 'Exterior Lighting', 'Interior Equipment', 'Exterior Equipment', 'Fans', 'Pumps', 'Heat Rejection', 'Humidification', 'Heat Recovery', 'Water Systems', 'Refrigeration', 'Generators']

  # Get the value for each end use/ fuel type combination
  end_uses.each do |end_use|
    fuel_types.each do |fuel_type|
      energy_values["#{end_use}|#{fuel_type}"] = OpenstudioStandards::SqlFile.model_get_annual_energy_by_fuel_and_enduse(model, fuel_type, end_use)
    end
  end

  return energy_values
end
model_get_dd_energy_by_fuel_and_enduse(model, fuel_type, end_use) click to toggle source

Gets the model design day energy consumption by fuel and enduse in J from the sql file Uses the meter data dictionary instead of annual building utility performance summary

@param model [OpenStudio::Model::Model] OpenStudio model object @param fuel_type [String] the fuel type, e.g. ‘Electricity’ @param end_use [String] the end use, e.g. ‘InteriorEquipment’ @return [Double] the model energy fuel type end use in Joules

# File lib/openstudio-standards/sql_file/energy_use.rb, line 44
def self.model_get_dd_energy_by_fuel_and_enduse(model, fuel_type, end_use)
  # get model sql file
  sql_file = OpenstudioStandards::SqlFile.model_get_sql_file(model)

  # setup the end use index query
  get_rpt_mtr_data_dic_idx = "SELECT ReportMeterDataDictionaryIndex
                              FROM ReportMeterDataDictionary
                              WHERE VariableName='#{end_use}:#{fuel_type}'"

  # get the end use index
  idx = sql_file.execAndReturnFirstDouble(get_rpt_mtr_data_dic_idx)

  # if no index it means that the end use isn't used in the model
  if idx.empty?
    return 0.0
  end

  # setup the energy use retrieval queries for the design days
  get_energy_j = "SELECT SUM (VariableValue)
                  FROM ReportMeterData
                  WHERE ReportMeterDataDictionaryIndex='#{idx}'"

  # get the end use energy value
  energy_j = sql_file.execAndReturnFirstDouble(get_energy_j)

  # no energy value, means that something isn't right, set it to 0 as a safeguard
  if energy_j.empty?
    return 0.0
  end

  return energy_j.get
end
model_get_dd_results_by_end_use_and_fuel_type(model) click to toggle source

Gets all design day energy consumption by enduse and fuel type from the sql file

@param model [OpenStudio::Model::Model] OpenStudio model object @return [Hash] a hash of results for each fuel, where the keys are in the form ‘EndUse|FuelType’,

# e.g. Heating|Electricity, ExteriorEquipment|Water.  All end use/fuel type combos are present, with
# values of 0.0 if none of this end use/fuel type combo was used by the simulation.
# File lib/openstudio-standards/sql_file/energy_use.rb, line 109
def self.model_get_dd_results_by_end_use_and_fuel_type(model)
  energy_values = {}

  # List of all fuel types, based on Table 5.1 of EnergyPlus' Input Output Reference manual
  if model.version < OpenStudio::VersionString.new('3.7.0')
    fuel_types = ['Electricity', 'Gas', 'Gasoline', 'Diesel', 'Coal', 'FuelOilNo1', 'FuelOilNo2', 'Propane', 'OtherFuel1', 'OtherFuel2', 'Water', 'Steam', 'DistrictCooling',
                  'DistrictHeating', 'ElectricityPurchased', 'ElectricitySurplusSold', 'ElectricityNet']
  else
    fuel_types = ['Electricity', 'Gas', 'Gasoline', 'Diesel', 'Coal', 'FuelOilNo1', 'FuelOilNo2', 'Propane', 'OtherFuel1', 'OtherFuel2', 'Water', 'DistrictCooling',
                  'DistrictHeatingWater', 'DistrictHeatingSteam', 'ElectricityPurchased', 'ElectricitySurplusSold', 'ElectricityNet']
  end

  # List of all end uses, based on Table 5.3 of EnergyPlus' Input Output Reference manual
  end_uses = ['InteriorLights', 'ExteriorLights', 'InteriorEquipment', 'ExteriorEquipment', 'Fans', 'Pumps', 'Heating', 'Cooling', 'HeatRejection', 'Humidifier',
              'HeatRecovery', 'DHW', 'Cogeneration', 'Refrigeration', 'WaterSystems']

  # Get the value for each end use/ fuel type combination
  end_uses.each do |end_use|
    fuel_types.each do |fuel_type|
      energy_values["#{end_use}|#{fuel_type}"] = OpenstudioStandards::SqlFile.model_get_dd_energy_by_fuel_and_enduse(model, fuel_type, end_use)
    end
  end

  return energy_values
end
model_get_sql_file(model) click to toggle source

Gets the sql file for the model, erroring if not found

@param model [OpenStudio::Model::Model] OpenStudio model object @return [OpenStudio::SqlFile] OpenStudio sqlFile associated with the model, boolean false if not found

# File lib/openstudio-standards/sql_file/sql_file.rb, line 82
def self.model_get_sql_file(model)
  # Ensure that the model has a sql file associated with it
  if model.sqlFile.empty?
    OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.SqlFile', 'Failed to retrieve data because the sql file containing results is missing.')
    return false
  end

  return model.sqlFile.get
end
model_get_weather_run_period(model) click to toggle source

Get the weather run period for the model

@param model [OpenStudio::Model::Model] OpenStudio model object @return [<OpenStudio::EnvironmentType>] the weather run period environment type

# File lib/openstudio-standards/sql_file/sql_file.rb, line 54
def self.model_get_weather_run_period(model)
  # get model sql file
  sql_file = OpenstudioStandards::SqlFile.model_get_sql_file(model)

  # get the weather file run period
  ann_env_pd = nil
  sql_file.availableEnvPeriods.each do |env_pd|
    env_type = sql_file.environmentType(env_pd)
    next unless env_type.is_initialized

    if env_type.get == OpenStudio::EnvironmentType.new('WeatherRunPeriod')
      ann_env_pd = env_pd
    end
  end

  # make sure the annual run exists
  unless ann_env_pd
    OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.SqlFile', 'Cannot find the annual simulation run period.')
    return false
  end

  return ann_env_pd
end
model_tabular_data_query(model, report_name, table_name, row_name, column_name, units = '*') click to toggle source

Write out a SQL query to retrieve simulation outputs from the TabularDataWithStrings table in the SQL database produced by OpenStudio/EnergyPlus after running a simulation.

@param model [OpenStudio::Model::Model] OpenStudio model object @param report_name [String] Name of the report as defined in the HTM simulation output file @param table_name [String] Name of the table as defined in the HTM simulation output file @param row_name [String] Name of the row as defined in the HTM simulation output file @param column_name [String] Name of the column as defined in the HTM simulation output file @param units [String] Unit of the value to be retrieved @return [String, Double] Result of the query

# File lib/openstudio-standards/sql_file/sql_file.rb, line 31
def self.model_tabular_data_query(model, report_name, table_name, row_name, column_name, units = '*')
  # get model sql file
  sql_file = OpenstudioStandards::SqlFile.model_get_sql_file(model)

  # Define the query
  query = "Select Value FROM TabularDataWithStrings WHERE
  ReportName = '#{report_name}' AND
  TableName = '#{table_name}' AND
  RowName = '#{row_name}' AND
  ColumnName = '#{column_name}' AND
  Units = '#{units}'"

  # Run the query if the expected output is a string
  return sql_file.execAndReturnFirstString(query).get if units.empty?

  # Run the query if the expected output is a double
  return sql_file.execAndReturnFirstDouble(query).get
end
sql_file_safe_load(sql_file_path) click to toggle source

Load and return an sql file, or error if not found

@param sql_file_path [String] path to the SQL file @return [OpenStudio::SqlFile] An OpenStudio SqlFile object, boolean false if not found

# File lib/openstudio-standards/sql_file/sql_file.rb, line 8
def self.sql_file_safe_load(sql_file_path)
  sql_path = OpenStudio::Path.new(sql_file_path)
  if OpenStudio.exists(sql_path)
    sql = OpenStudio::SqlFile.new(sql_path)
  else
    OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.SqlFile', "Invalid file path #{sql_path}.")
    return false
  end
  return sql
end
thermal_zone_get_annual_occupied_unmet_cooling_hours(thermal_zone) click to toggle source

Determine the number of unmet occupied cooling load hours for a thermal zone

@param thermal_zone [OpenStudio::Model::ThermalZone] OpenStudio ThermalZone object @return [Double] occupied cooling unmet hours

# File lib/openstudio-standards/sql_file/unmet_hours.rb, line 364
def self.thermal_zone_get_annual_occupied_unmet_cooling_hours(thermal_zone)
  # get the model sql file
  sql_file = OpenstudioStandards::SqlFile.model_get_sql_file(thermal_zone.model)

  # run unmet load hours query for the specific thermal zone
  query = "SELECT Value
          FROM tabulardatawithstrings
          WHERE ReportName='SystemSummary'
          AND ReportForString='Entire Facility'
          AND TableName='Time Setpoint Not Met'
          AND ColumnName='During Occupied Cooling'
          AND RowName='#{thermal_zone.name.to_s.upcase}'
          AND Units='hr'"
  umlh = sql_file.execAndReturnFirstDouble(query)
  if umlh.empty?
    OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.SqlFile', "Could not get unmet occupied cooling hours for thermal zone #{thermal_zone.name}.")
    return false
  end

  return umlh.get
end
thermal_zone_get_annual_occupied_unmet_heating_hours(thermal_zone) click to toggle source

Determine the number of unmet occupied heating load hours for a thermal zone

@param thermal_zone [OpenStudio::Model::ThermalZone] OpenStudio ThermalZone object @return [Double] occupied heating unmet hours

# File lib/openstudio-standards/sql_file/unmet_hours.rb, line 338
def self.thermal_zone_get_annual_occupied_unmet_heating_hours(thermal_zone)
  # get the model sql file
  sql_file = OpenstudioStandards::SqlFile.model_get_sql_file(thermal_zone.model)

  # run unmet load hours query for the specific thermal zone
  query = "SELECT Value
          FROM tabulardatawithstrings
          WHERE ReportName='SystemSummary'
          AND ReportForString='Entire Facility'
          AND TableName='Time Setpoint Not Met'
          AND ColumnName='During Occupied Heating'
          AND RowName='#{thermal_zone.name.to_s.upcase}'
          AND Units='hr'"
  umlh = sql_file.execAndReturnFirstDouble(query)
  if umlh.empty?
    OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.SqlFile', "Could not get unmet occupied heating hours for thermal zone #{thermal_zone.name}.")
    return false
  end

  return umlh.get
end