Class: URBANopt::Scenario::OpenDSSPostProcessor
- Inherits:
-
Object
- Object
- URBANopt::Scenario::OpenDSSPostProcessor
- Defined in:
- lib/urbanopt/scenario/scenario_post_processor_opendss.rb
Instance Method Summary collapse
-
#add_summary_results(feature_report) ⇒ Object
create opendss json report results.
-
#compute_transformer_results ⇒ Object
computer transformer results.
-
#initialize(scenario_report, opendss_results_dir_name = 'opendss') ⇒ OpenDSSPostProcessor
constructor
OpenDSSPostProcessor post-processes OpenDSS results to selected OpenDSS results and integrate them in scenario and feature reports.
-
#load_data ⇒ Object
load feature report data and opendss data.
-
#load_feature_report_data ⇒ Object
load feature report data.
-
#load_opendss_data ⇒ Object
load opendss data.
-
#merge_data(feature_report_data, opendss_data) ⇒ Object
merge data.
-
#run ⇒ Object
run opendss post_processor.
-
#save_csv(feature_report, updated_feature_report_csv, file_name = 'default_feature_report') ⇒ Object
Save csv report method.
-
#save_opendss_scenario ⇒ Object
save opendss scenario fields.
-
#save_transformers_reports ⇒ Object
add feature reports for transformers.
Constructor Details
#initialize(scenario_report, opendss_results_dir_name = 'opendss') ⇒ OpenDSSPostProcessor
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
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
# 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 |
Instance Method Details
#add_summary_results(feature_report) ⇒ Object
create opendss json report results
- parameters:
-
feature_report- _feature report object_ - An onject of the feature report
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 |
# File 'lib/urbanopt/scenario/scenario_post_processor_opendss.rb', line 257 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 |
#compute_transformer_results ⇒ Object
computer transformer results
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
# 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, 'incoming_voltage': nil, 'outgoing_voltage': 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 # Voltage on each side of the transformer begin t[:incoming_voltage] = item['windings']['value'][0]['nominal_voltage']['value'] t[:outgoing_voltage] = item['windings']['value'][1]['nominal_voltage']['value'] 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_data ⇒ Object
load feature report data and opendss data
96 97 98 99 100 101 |
# 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 ⇒ Object
load feature report data
86 87 88 89 90 91 92 93 |
# 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 ⇒ Object
load opendss data
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
# 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(feature_report_data, opendss_data) ⇒ Object
merge data
104 105 106 107 108 109 110 111 112 113 114 115 |
# 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 ⇒ Object
run opendss post_processor
384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 |
# File 'lib/urbanopt/scenario/scenario_post_processor_opendss.rb', line 384 def run # Load all OpenDSS and feature report data once before processing each feature. load_data @scenario_report.feature_reports.each do |feature_report| # get summary results add_summary_results(feature_report) # puts "summary results added for feature id #{feature_report.id}." # 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') # puts "json report saved for feature id #{feature_report.id}." # resave updated csv report save_csv(feature_report, updated_feature_csv, 'default_feature_report_opendss') # puts "csv report saved for feature id #{feature_report.id}." 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(feature_report, updated_feature_report_csv, file_name = 'default_feature_report') ⇒ Object
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
248 249 250 |
# File 'lib/urbanopt/scenario/scenario_post_processor_opendss.rb', line 248 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 ⇒ Object
save opendss scenario fields
326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 |
# File 'lib/urbanopt/scenario/scenario_post_processor_opendss.rb', line 326 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 |
#save_transformers_reports ⇒ Object
add feature reports for transformers
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 |
# File 'lib/urbanopt/scenario/scenario_post_processor_opendss.rb', line 158 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 = Pathname(@scenario_report.directory_name) / k FileUtils.mkdir_p(Pathname(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 tx_incoming_voltage = nil tx_outgoing_voltage = nil begin nominal_capacity = t_res[t_key][:nominal_capacity] r_r_ratio = t_res[t_key][:reactance_resistance_ratio] tx_incoming_voltage = t_res[t_key][:incoming_voltage] tx_outgoing_voltage = t_res[t_key][:outgoing_voltage] 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(Pathname(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 transformer_report.power_distribution.tx_incoming_voltage = tx_incoming_voltage transformer_report.power_distribution.tx_outgoing_voltage = tx_outgoing_voltage ## save transformer JSON file # transformer_hash transformer_hash = transformer_report.to_hash # transformer_hash.delete_if { |k, v| v.nil? } json_name_path = Pathname(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 |