class URBANopt::Scenario::OpenDSSPostProcessor
Public Class Methods
OpenDSSPostProcessor
post-processes OpenDSS results to selected OpenDSS results and integrate them in scenario and feature reports.
- parameters:
-
scenario_report
- ScenarioBase - An object of Scenario_report class. -
opendss_results_dir_name
- _directory name of opendss results
# File lib/urbanopt/scenario/scenario_post_processor_opendss.rb, line 22 def initialize(scenario_report, opendss_results_dir_name = 'opendss') if !scenario_report.nil? @scenario_report = scenario_report @opendss_results_dir = File.join(@scenario_report.directory_name, opendss_results_dir_name) else raise 'scenario_report is not valid' end # hash of column_name to array of values, does not get serialized to hash @mutex = Mutex.new # initialize opendss data @opendss_data = {} # initialize feature_reports data @feature_reports_data = {} # initialize opendss json results @opendss_json_results = {} # initialize logger @@logger ||= URBANopt::Reporting::DefaultReports.logger end
Public Instance Methods
create opendss json report results
- parameters:
-
feature_report
- _feature report object_ - An onject of the feature report
# File lib/urbanopt/scenario/scenario_post_processor_opendss.rb, line 244 def add_summary_results(feature_report) under_voltage_hrs = 0 over_voltage_hrs = 0 kw = nil kvar = nil nominal_voltage = nil id = feature_report.id @opendss_data[id].each_with_index do |row, i| if !row[1].include? 'voltage' if row[1].to_f > 1.05 over_voltage_hrs += 1 end if row[1].to_f < 0.95 under_voltage_hrs += 1 end end end # also add additional keys for OpenDSS Loads loads = @opendss_json_results['model'].select { |d| d['class'] == 'Load' } if loads # RNM includes other strings around the Load ID, like: load_1_27, where the id is the 1 in the middle. # Use the ID part of the string as the key. bld_load = loads.select { |d| d['name']['value'] == id || d['name']['value'].split('_')[1] == id } if !bld_load.empty? if bld_load.is_a?(Array) bld_load = bld_load[0] end kw = 0 kvar = 0 # nominal_voltage (V) nominal_voltage = bld_load['nominal_voltage']['value'] if nominal_voltage < 300 nominal_voltage *= Math.sqrt(3) end # max_power_kw # max_reactive_power_kvar pls = bld_load['phase_loads']['value'] pls.each do |pl| kw += pl['p']['value'] kvar += pl['q']['value'] end kw /= 1000 kvar /= 1000 else @@logger.info("No load matching id #{id} found in OpenDSS Model.json results") end else @@logger.info('No loads information found in OpenDSS Model.json results file') end # assign results to feature report feature_report.power_distribution.over_voltage_hours = over_voltage_hrs feature_report.power_distribution.under_voltage_hours = under_voltage_hrs feature_report.power_distribution.nominal_voltage = nominal_voltage feature_report.power_distribution.max_power_kw = kw feature_report.power_distribution.max_reactive_power_kvar = kvar return feature_report end
computer transformer results
# File lib/urbanopt/scenario/scenario_post_processor_opendss.rb, line 118 def compute_transformer_results # using values from opendss Model.json results = {} # retrieve all transformers trsfmrs = @opendss_json_results['model'].select { |d| d['class'] == 'PowerTransformer' } trsfmrs.each do |item| t = { 'nominal_capacity': nil, 'reactance_resistance_ratio': nil } name = item['name']['value'] # nominal capacity in kVA (Model.json stores it in VA) # TODO: assuming that all windings would have the same rated power, so grabbing first one begin t['nominal_capacity'] = item['windings']['value'][0]['rated_power']['value'] / 1000 rescue StandardError end # reactance to resistance ratio: begin # TODO: grabbing the first one for now. Handle when there are multiple reactances and winding resistances reactance = item['reactances']['value'][0]['value'] resistance = item['windings']['value'][0]['resistance']['value'] t['reactance_resistance_ratio'] = reactance / resistance rescue StandardError end results[name] = t end return results end
load feature report data and opendss data
# File lib/urbanopt/scenario/scenario_post_processor_opendss.rb, line 96 def load_data # load selected opendss data load_opendss_data # load selected feature reports data load_feature_report_data end
load feature report data
# File lib/urbanopt/scenario/scenario_post_processor_opendss.rb, line 86 def load_feature_report_data @scenario_report.feature_reports.each do |feature_report| # read feature results feature_csv = CSV.read(File.join(feature_report.timeseries_csv.path)) # add results to data @feature_reports_data[feature_report.id] = feature_csv end end
load opendss data
# File lib/urbanopt/scenario/scenario_post_processor_opendss.rb, line 47 def load_opendss_data # load building features data @scenario_report.feature_reports.each do |feature_report| # read results from opendss opendss_csv = CSV.read(File.join(@opendss_results_dir, 'results', 'Features', "#{feature_report.id}.csv")) # add results to data @opendss_data[feature_report.id] = opendss_csv end # load Model.json results (if exists) opendss_json_filename = File.join(@opendss_results_dir, 'json_files', 'Model.json') if File.exist?(opendss_json_filename) @opendss_json_results = JSON.parse(File.read(opendss_json_filename)) end ## load transformers data # transformers results directory path tf_results_path = File.join(@opendss_results_dir, 'results', 'Transformers') # get transformer ids transformer_ids = [] Dir.entries(tf_results_path.to_s).select do |f| if !File.directory? f fn = File.basename(f, '.csv') transformer_ids << fn end end # add transformer results to @opendss_data transformer_ids.each do |id| # read results from transformers transformer_csv = CSV.read(File.join(tf_results_path, "#{id}.csv")) # add results to data @opendss_data[id] = transformer_csv end end
merge data
# File lib/urbanopt/scenario/scenario_post_processor_opendss.rb, line 104 def merge_data(feature_report_data, opendss_data) output = CSV.generate do |csv| opendss_data.each_with_index do |row, i| if row.include? 'Datetime' row.map { |header| header.prepend('opendss_') } end csv << (feature_report_data[i] + row[1..]) end end return output end
run opendss post_processor
# File lib/urbanopt/scenario/scenario_post_processor_opendss.rb, line 371 def run @scenario_report.feature_reports.each do |feature_report| # load data load_data # puts " @opendss data = #{@opendss_data}" # get summary results add_summary_results(feature_report) # merge csv data id = feature_report.id updated_feature_csv = merge_data(@feature_reports_data[id], @opendss_data[id]) # save feature reports feature_report.save_json_report('default_feature_report_opendss') # resave updated csv report save_csv(feature_report, updated_feature_csv, 'default_feature_report_opendss') end # add transformer reports save_transformers_reports # save additional global opendss fields save_opendss_scenario # save the updated scenario reports # set save_feature_reports to false since only the scenario reports should be saved now @scenario_report.save(file_name = 'scenario_report_opendss', save_feature_reports = false) end
Save csv report method
- parameters:
-
feature_report
- _feature report object_ - An onject of the feature report -
updated_feature_report_csv
- CSV - An updated feature report csv -
file_name
- String - Assigned name to save the file with no extension
# File lib/urbanopt/scenario/scenario_post_processor_opendss.rb, line 235 def save_csv(feature_report, updated_feature_report_csv, file_name = 'default_feature_report') File.write(File.join(feature_report.directory_name, 'feature_reports', "#{file_name}.csv"), updated_feature_report_csv) end
save opendss scenario fields
# File lib/urbanopt/scenario/scenario_post_processor_opendss.rb, line 313 def save_opendss_scenario @scenario_report.scenario_power_distribution = URBANopt::Reporting::DefaultReports::ScenarioPowerDistribution.new ## SUBSTATION subs = [] feeders = @opendss_json_results['model'].select { |d| d['class'] == 'Feeder_metadata' } feeders.each do |item| # nominal_voltage - RMS voltage low side (V) substation = { nominal_voltage: item['nominal_voltage']['value'] } subs.append(substation) end @scenario_report.scenario_power_distribution.substations = subs ## LINES # retrieve all lines dist_lines = [] lines = @opendss_json_results['model'].select { |d| d['class'] == 'Line' } lines.each do |item| line = {} # length (m) line['length'] = item['length']['value'] # max ampacity: iterate through N-1 wires and add up ampacity amps = 0 num_wires = item['wires']['value'].length (0..(num_wires - 1)).each do |i| amps += item['wires']['value'][i]['ampacity']['value'] end line['ampacity'] = amps # commercial line type line['commercial_line_type'] = [] item['wires']['value'].each do |wire| line['commercial_line_type'].append(wire['nameclass']['value']) end dist_lines.append(line) end @scenario_report.scenario_power_distribution.distribution_lines = dist_lines # CAPACITORS caps = [] capacitors = @opendss_json_results['model'].select { |d| d['class'] == 'Capacitors' } capacitors.each do |item| cap = 0 item['phase_capacitors']['value'].each do |pc| if pc['var']['value'] cap += pc['var']['value'] end end caps.append({ nominal_capacity: cap }) end @scenario_report.scenario_power_distribution.capacitors = caps end
add feature reports for transformers
# File lib/urbanopt/scenario/scenario_post_processor_opendss.rb, line 151 def save_transformers_reports t_res = compute_transformer_results @opendss_data.each_key do |k| if k.include? 'Transformer' t_key = k.sub('Transformer.', '') # create transformer directory transformer_dir = File.join(@scenario_report.directory_name, k) FileUtils.mkdir_p(File.join(transformer_dir, 'feature_reports')) # write data to csv # store under voltages and over voltages under_voltage_hrs = 0 over_voltage_hrs = 0 nominal_capacity = nil r_r_ratio = nil begin nominal_capacity = t_res[t_key]['nominal_capacity'] r_r_ratio = t_res[t_key]['reactance_resistance_ratio'] rescue StandardError end transformer_csv = CSV.generate do |csv| @opendss_data[k].each_with_index do |row, i| csv << row if !row[1].include? 'loading' if row[1].to_f > 1.05 over_voltage_hrs += 1 end if row[1].to_f < 0.95 under_voltage_hrs += 1 end end end end # save transformer CSV report File.write(File.join(transformer_dir, 'feature_reports', 'default_feature_report_opendss.csv'), transformer_csv) # create transformer report transformer_report = URBANopt::Reporting::DefaultReports::FeatureReport.new(id: k, name: k, directory_name: transformer_dir, feature_type: 'Transformer', timesteps_per_hour: @scenario_report.timesteps_per_hour, simulation_status: 'complete') # assign results to transformer report transformer_report.power_distribution.over_voltage_hours = over_voltage_hrs transformer_report.power_distribution.under_voltage_hours = under_voltage_hrs transformer_report.power_distribution.nominal_capacity = nominal_capacity transformer_report.power_distribution.reactance_resistance_ratio = r_r_ratio ## save transformer JSON file # transformer_hash transformer_hash = transformer_report.to_hash # transformer_hash.delete_if { |k, v| v.nil? } json_name_path = File.join(transformer_dir, 'feature_reports', 'default_feature_report_opendss.json') # save the json file File.open(json_name_path, 'w') do |f| f.puts JSON.pretty_generate(transformer_hash) # make sure data is written to the disk one way or the other begin f.fsync rescue StandardError f.flush end end # add transformers reports to scenario_report @scenario_report.feature_reports << transformer_report end end end