class URBANopt::Reporting::DefaultReports::FeatureReport

FeatureReport generates two types of reports in a simulation_dir. The default_feature_reports measure writes a 'default_feature_reports.json' file containing information on all features in the simulation. It also writes a 'default_feature_reports.csv' containing timeseries data for all features in the simulation. The DefaultPostProcessor reads these feature reports and aggregates them to create a ScenarioReport.

Public Class Methods

from_simulation_dir(simulation_dir) click to toggle source

Return an Array of FeatureReports for the simulation_dir as multiple Features can be simulated together in a single simulation directory.

  • Ensure that simulation_dir include only one feature.

  • Read in the reports written by measure if they exist.

parameters:

simulation_dir - SimulationDirOSW - A simulation directory from an OSW simulation, must include 'default_feature_reports' measure.

# File lib/urbanopt/reporting/default_reports/feature_report.rb, line 106
def self.from_simulation_dir(simulation_dir)
  result = []

  # simulation dir can include only one feature
  features = simulation_dir.features
  if features.size != 1
    raise 'FeatureReport cannot support multiple features per OSW'
  end

  # read in the reports written by measure
  default_feature_reports_json = nil
  default_feature_reports_csv = nil

  simulation_status = simulation_dir.simulation_status
  if simulation_status == 'Complete' || simulation_status == 'Failed'

    # read in the scenario reports JSON and CSV
    Dir.glob(File.join(simulation_dir.run_dir, '*_default_feature_reports/')).each do |dir|
      scenario_reports_json_path = File.join(dir, 'default_feature_reports.json')
      if File.exist?(scenario_reports_json_path)
        File.open(scenario_reports_json_path, 'r') do |file|
          default_feature_reports_json = JSON.parse(file.read, symbolize_names: true)
        end
      end
      scenario_reports_csv_path = File.join(dir, 'default_feature_reports.csv')
      if File.exist?(scenario_reports_csv_path)
        default_feature_reports_csv = scenario_reports_csv_path
      end
    end

  end

  # if we loaded the json
  if default_feature_reports_json # && default_feature_reports_json[:feature_reports]
    # default_feature_reports_json.each do |feature_report|
    # result << FeatureReport.new(feature_report)
    # end
    result << FeatureReport.new(default_feature_reports_json) # should we keep it as an array !? or each each report can only include 1 feature

  else
    # we did not find a report
    features.each do |feature|
      hash = {}
      hash[:id] = feature.id
      hash[:name] = feature.name
      hash[:directory_name] = simulation_dir.run_dir
      hash[:simulation_status] = simulation_status
      result << FeatureReport.new(hash)
    end
  end

  # validate feature_report json against schema
  if @@validator.validate(@@schema[:definitions][:FeatureReport][:properties], default_feature_reports_json).any?
    raise "default_feature_report_json properties does not match schema: #{@@validator.validate(@@schema[:definitions][:FeatureReport][:properties], default_feature_reports_json)}"
  end

  return result
end
new(hash = {}) click to toggle source

Each FeatureReport object corresponds to a single Feature.

parameters:

hash - Hash - A hash which may contain a deserialized feature_report.

# File lib/urbanopt/reporting/default_reports/feature_report.rb, line 40
def initialize(hash = {})
  hash.delete_if { |k, v| v.nil? }
  hash = defaults.merge(hash)

  @id = hash[:id]
  @name = hash[:name]
  @directory_name = hash[:directory_name]
  @feature_type = hash[:feature_type]
  @timesteps_per_hour = hash[:timesteps_per_hour]
  @simulation_status = hash[:simulation_status]
  @qaqc_flags = QAQC.new(hash[:qaqc_flags])
  @timeseries_csv = TimeseriesCSV.new(hash[:timeseries_csv])
  @timeseries_csv.run_dir_name(@directory_name)
  @location = Location.new(hash[:location])
  @program = Program.new(hash[:program])
  # design_parameters to add later
  @construction_costs = []
  hash[:construction_costs].each do |cc|
    @constructiion_costs << ConstructionCost.new(cc)
  end

  @reporting_periods = []
  hash[:reporting_periods].each do |rp|
    @reporting_periods << ReportingPeriod.new(rp)
  end

  @distributed_generation = DistributedGeneration.new(hash[:distributed_generation])

  @power_distribution = PowerDistribution.new(hash[:power_distribution])

  @thermal_storage = ThermalStorage.new(hash[:thermal_storage])

  # initialize class variables @@validator and @@schema
  @@validator ||= Validator.new
  @@schema ||= @@validator.schema

  # initialize feature report file name to be saved.
  @file_name = 'default_feature_report'
end

Public Instance Methods

defaults() click to toggle source

Assign default values if values does not exist.

# File lib/urbanopt/reporting/default_reports/feature_report.rb, line 83
def defaults
  hash = {}
  hash[:timeseries_csv] = {}
  hash[:location] = {}
  hash[:program] = {}
  hash[:construction_costs] = []
  hash[:reporting_periods] = []
  hash[:distributed_generation] = {}
  hash[:power_distribution] = {}
  hash[:thermal_storage] = {}
  hash[:qaqc_flags] = {}
  return hash
end
save(file_name = 'default_feature_report') click to toggle source

Saves the 'default_feature_report.json' and 'default_feature_report.csv' files

[parameters]: file_name - String - Assign a name to the saved feature results file without an extension

# File lib/urbanopt/reporting/default_reports/feature_report.rb, line 212
def save(file_name = 'default_feature_report')
  # reassign the initialize local variable @file_name to the file name input.
  @file_name = file_name

  # save the feature reports csv and json data
  old_timeseries_path = nil
  if !@timeseries_csv.path.nil?
    old_timeseries_path = @timeseries_csv.path
  end

  # define the results_dir_path
  results_dir_path = File.join(@directory_name, 'feature_reports')
  # create feature reports directory
  Dir.mkdir(results_dir_path) unless Dir.exist?(File.join(@directory_name, 'feature_reports'))

  @timeseries_csv.path = File.join(@directory_name, 'feature_reports', "#{file_name}.csv")
  FileUtils.mkdir_p File.dirname(@timeseries_csv.path)
  @timeseries_csv.save_data

  ## save json report
  # feature_hash
  feature_hash = to_hash

  json_name_path = File.join(results_dir_path, "#{file_name}.json")

  File.open(json_name_path, 'w') do |f|
    f.puts JSON.pretty_generate(feature_hash)
    # make sure data is written to the disk one way or the other
    begin
      f.fsync
    rescue StandardError
      f.flush
    end
  end

  if !old_timeseries_path.nil?
    @timeseries_csv.path = old_timeseries_path
  else
    @timeseries_csv.path = File.join(@directory_name, "#{file_name}.csv")
  end

  return true
end
save_csv_report(file_name = 'default_feature_report') click to toggle source

Saves the 'default_feature_report.csv' file to the results directory This method only copies the CSV feature reports from the folder generated by the reporting measure (<meausure number>_default_feature_reports/) to the new feature_reports/ folder

[parameters]: file_name - String - Assign a name to the saved feature report file without an extension

# File lib/urbanopt/reporting/default_reports/feature_report.rb, line 305
def save_csv_report(file_name = 'default_feature_report')
  # reassign the initialize local variable @file_name to the file name input.
  @file_name = file_name

  # define the results_dir_path
  results_dir_path = File.join(@directory_name, 'feature_reports')
  # create feature reports directory
  Dir.mkdir(results_dir_path) unless Dir.exist?(File.join(@directory_name, 'feature_reports'))

  ## copy CSV report to the new feature_reports folder
  # get all folder names in the feature diectory
  directory_folders = Dir.glob "#{@directory_name}/*/"
  # copy the CSV report to the new feature_reports folder
  directory_folders.each do |f|
    if f.include? '_default_feature_reports'
      FileUtils.cp(File.join(f, 'default_feature_reports.csv'), File.join(results_dir_path, "#{@file_name}.csv"))
    end
  end
end
save_feature_report(file_name = 'default_feature_report') click to toggle source

Calls the individual functions to save 'default_feature_report.json' and 'default_feature_report.csv' For backward compatibility and ease of use

[parameters]: file_name - String - Assign a name to the saved feature report file without an extension

# File lib/urbanopt/reporting/default_reports/feature_report.rb, line 262
def save_feature_report(file_name = 'default_feature_report')
  save_json_report(file_name)
  save_csv_report(file_name)
end
save_json_report(file_name = 'default_feature_report') click to toggle source

Saves the 'default_feature_report.json' file to the results directory

[parameters]: file_name - String - Assign a name to the saved feature report file without an extension

# File lib/urbanopt/reporting/default_reports/feature_report.rb, line 272
def save_json_report(file_name = 'default_feature_report')
  # reassign the initialize local variable @file_name to the file name input.
  @file_name = file_name

  # define the results_dir_path
  results_dir_path = File.join(@directory_name, 'feature_reports')
  # create feature reports directory
  Dir.mkdir(results_dir_path) unless Dir.exist?(File.join(@directory_name, 'feature_reports'))

  ## save json rport
  # feature_hash
  feature_hash = to_hash

  json_name_path = File.join(results_dir_path, "#{file_name}.json")

  File.open(json_name_path, 'w') do |f|
    f.puts JSON.pretty_generate(feature_hash)
    # make sure data is written to the disk one way or the other
    begin
      f.fsync
    rescue StandardError
      f.flush
    end
  end
end
to_hash() click to toggle source

Convert to a Hash equivalent for JSON serialization

  • Exclude attributes with nil values.

  • Validate feature_report hash properties against schema.

# File lib/urbanopt/reporting/default_reports/feature_report.rb, line 171
def to_hash
  result = {}
  result[:id] = @id if @id
  result[:name] = @name if @name
  result[:directory_name] = @directory_name if @directory_name
  result[:feature_type] = @feature_type if @feature_type
  result[:timesteps_per_hour] = @timesteps_per_hour if @timesteps_per_hour
  result[:simulation_status] = @simulation_status if @simulation_status
  result[:timeseries_csv] = @timeseries_csv.to_hash

  result[:location] = @location.to_hash if @location

  result[:program] = @program.to_hash

  result[:construction_costs] = []
  @construction_costs.each { |cc| result[:construction_costs] << cc.to_hash }

  result[:reporting_periods] = []
  @reporting_periods.each { |rp| result[:reporting_periods] << rp.to_hash }

  result[:distributed_generation] = @distributed_generation.to_hash if @distributed_generation

  result[:power_distribution] = @power_distribution.to_hash if @power_distribution

  result[:thermal_storage] = @thermal_storage.to_hash if @thermal_storage

  result[:qaqc_flags] = @qaqc_flags.to_hash if @qaqc_flags

  # validate feature_report properties against schema
  if @@validator.validate(@@schema[:definitions][:FeatureReport][:properties], result).any?
    raise "feature_report properties does not match schema: #{@@validator.validate(@@schema[:definitions][:FeatureReport][:properties], result)}"
  end

  return result
end