urbanopt-rnm-us 0.4.0 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 48d2e5b31538bcf737ce6e1d6d6c581dcfa8cddf779c6c2174590d9a9784a878
4
- data.tar.gz: fc4973c31698c5461451c675e47d4ae837aef99e314684e37d35caea0da18e2f
3
+ metadata.gz: 78e5fa9802676b79db0cfaf9c6184581dafc1d5499c51dcf6c32070974baa221
4
+ data.tar.gz: f77669a0f5fa76cff7cf76b58d0fc67bc7671d8cbf964b6b9faec90ba2343a65
5
5
  SHA512:
6
- metadata.gz: 800c18d91f1bb12ed9e46ca6a14a810d570fad5b4eb08e66e7dcca79507676545b53cb49db0f1046ec8b2dbdfeccc2d2c3eb3863b3f12699d79fd9bd1aa256a8
7
- data.tar.gz: 865a614718260125d16e49fa459d90826b8b86519ec8e75dc1ebdee61b12de8e386a6763ae89599e3fa050f5666b0b3a8ac4636f2f224ffd0b868b2b9e9007fb
6
+ metadata.gz: 16e51e2d9b1c0aa9fbcb15f9e946a0feb65ab66133d1c43ab0c91ae6cab4afd0ff0746ef142a5bec279aec48be8812c37e845322800dc846e09a294916823ec7
7
+ data.tar.gz: 282ddcc5091620663891c2bd1637dd77fdb19097af21049b0d1f8992ac8a45bd9d68058bd1f61c201596dbfad0ccbb6872d0b2e90a85df26938aa540e71502a8
@@ -0,0 +1,46 @@
1
+
2
+ name: ci
3
+
4
+ on:
5
+ # push:
6
+ schedule:
7
+ # https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#schedule
8
+ # 5:23 am UTC (11:23pm MDT the day before) every weekday night in MDT
9
+ - cron: '23 5 * * 2-6'
10
+
11
+ env:
12
+ # This env var should enforce develop branch of all dependencies
13
+ FAVOR_LOCAL_GEMS: true
14
+ GEM_DEVELOPER_KEY: ${{ secrets.GEM_DEVELOPER_KEY }}
15
+
16
+ jobs:
17
+ weeknight-tests:
18
+ runs-on: ubuntu-latest
19
+ container:
20
+ image: docker://nrel/openstudio:3.5.1
21
+ steps:
22
+ - uses: actions/checkout@v3
23
+ - name: Update gems
24
+ run: |
25
+ ruby --version
26
+ bundle update
27
+ bundle exec certified-update
28
+ - name: Run Rspec
29
+ continue-on-error: true
30
+ # Continue to upload step even if a test fails, so we can troubleshoot
31
+ run: bundle exec rspec
32
+ - name: Upload artifacts
33
+ uses: actions/upload-artifact@v3
34
+ if: failure() # Only upload if rspec fails
35
+ with:
36
+ name: rspec_results
37
+ path: |
38
+ spec/test*/**
39
+ # coverage/
40
+ retention-days: 7 # save for 1 week before deleting
41
+ # coveralls action docs: https://github.com/marketplace/actions/coveralls-github-action
42
+ - name: Coveralls
43
+ uses: coverallsapp/github-action@master
44
+ with:
45
+ github-token: ${{ secrets.GITHUB_TOKEN }}
46
+ path-to-lcov: "./coverage/lcov/urbanopt-rnm-us-gem.lcov"
data/.gitignore CHANGED
@@ -29,4 +29,6 @@ runner.conf
29
29
  # ignore template files for now
30
30
  /template_inputs/*
31
31
  test_opendss_catalog.json
32
- *.sql
32
+ *.sql
33
+
34
+ *__pycache__
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## Version 0.5.1
4
+ Date Range 12/9/22 - 6/7/23
5
+
6
+ - Fix to handle buildings with courtyard without creating additional erroneous buildings
7
+
8
+ ## Version 0.5.0
9
+ Date Range 9/30/22 - 12/8/22
10
+
11
+ - Update dependencies for Extension Gem 0.6.0 and OpenStudio 3.5.0
12
+
3
13
  ## Version 0.4.0
4
14
  Date Range 05/10/22 - 9/30/22
5
15
 
data/Gemfile CHANGED
@@ -9,8 +9,9 @@ allow_local = ENV['FAVOR_LOCAL_GEMS']
9
9
  # Below is an example of how to configure the gemfile for developing with local gems
10
10
  # modify as appropriate
11
11
 
12
- if allow_local && File.exists?('../urbanopt-geojson-gem')
13
- gem 'urbanopt-geojson', path: '../urbanopt-geojson-gem'
14
- elsif allow_local
15
- gem 'urbanopt-geojson', github: 'URBANopt/urbanopt-geojson-gem', branch: 'develop'
16
- end
12
+ # if allow_local && File.exists?('../urbanopt-geojson-gem')
13
+ # gem 'urbanopt-geojson', path: '../urbanopt-geojson-gem'
14
+ # elsif allow_local
15
+ # gem 'urbanopt-geojson', github: 'URBANopt/urbanopt-geojson-gem', branch: 'develop'
16
+ # end
17
+
data/LICENSE.md CHANGED
@@ -1,4 +1,4 @@
1
- URBANopt (tm), Copyright (c) 2019-2022, Alliance for Sustainable Energy, LLC, and other
1
+ URBANopt (tm), Copyright (c) 2019-2023, Alliance for Sustainable Energy, LLC, and other
2
2
  contributors. All rights reserved.
3
3
 
4
4
  Redistribution and use in source and binary forms, with or without modification,
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
+ [![Coverage Status](https://coveralls.io/repos/github/urbanopt/urbanopt-rnm-us-gem/badge.svg?branch=develop)](https://coveralls.io/github/urbanopt/urbanopt-rnm-us-gem?branch=develop)
2
+
1
3
  # URBANopt RNM-US Gem
2
4
 
3
- The URBANopt<sup>&trade;</sup> RNM-US Gem includes functionalities to interface the URBANopt SDK to the RNM-US Gem for the development of a synthetic optimum distribution network in the considered district, given data related to the buildings energy consumption/DER energy generation and location, modeled by the other URBANopt modules.
5
+ The URBANopt<sup>&trade;</sup> RNM-US Gem includes functionalities to interface the URBANopt SDK to the RNM-US Gem for the development of a synthetic optimum distribution network in the considered district, given data related to the buildings energy consumption/DER energy generation and location, modeled by the other URBANopt modules.
4
6
  The RNM-US Gem is used to collect required data for the execution of RNM-US, which has been modeled in the other URBANopt modules, translating information provided
5
7
  in .json and .csv format into .txt files
6
8
 
@@ -15,7 +17,7 @@ The OpenDSS format presents results for power system analysis and simulations.
15
17
  The current functionalities of the RNM-US Gem include the creation of a streetmap text file, the substation txt file and multiple txt files related to the consumers peak loads and profiles and DERs peak generation and profiles.
16
18
  The streetmap text file is developed from coordinates information provided by geoJSON feature file input. The customers and generators text files, which define all the network consumers and DG included in the project, are created from their peak electricity demand/generation, and building location, provided by csv and json feature_report files modeled by the URBANopt Scenario Gem.
17
19
  The profiles txt files are divided among the consumers hourly profiles of active and reactive power and the DG hourly profiles of active and reactive power for the 2 most "extreme" days of maximum net demand and maximum net generation for the district .
18
- Finally, the extended profiles txt files provide the active and reactive profiles for each consumer/DG for the whole year.
20
+ Finally, the extended profiles txt files provide the active and reactive profiles for each consumer/DG for the whole year.
19
21
 
20
22
 
21
23
  ## Generate input files
@@ -47,10 +49,24 @@ bundle exec rake create_opendss_catalog[/desired/path/to/opendss_catalog.json]
47
49
  | API Version | RNM-US Gem Version | RNM-US exe Version |
48
50
  | ----------- | ----------- | ---------------- |
49
51
  | v1 | 0.3.0 and earlier | RNM-US_20220819 |
50
- | v2 | 0.4.0 | RNM-US_20220927 |
52
+ | v2 | 0.4.0 | RNM-US_20221018 |
53
+
54
+
55
+ ## Validation Functionality
56
+
57
+ The validation and results visualization functionality is written in python. Follow these steps if you would like to use it.
58
+
59
+ 1. Install python (3.10) if you do not already have it installed
60
+ 1. Clone the repo to your computer
61
+ 1. cd into the repo directory
62
+ 1. run `bundle install` to install the required ruby dependencies
63
+ 1. run `pip install -r requirements.txt` to install the required python dependencies for the validation module
64
+ 1. create input files and run the simulation as usual
65
+ 1. run `bundle exec rake run_validation[/path/to/scenario/csv]` to run the validation
66
+
51
67
 
52
68
  ## Testing
53
69
 
54
70
  ```bash
55
71
  bundle exec rspec
56
- ```
72
+ ```
data/Rakefile CHANGED
@@ -148,6 +148,29 @@ task :run_simulation, [:scenario_csv_path, :reopt, :use_localhost] do |t, args|
148
148
  puts '...done!'
149
149
  end
150
150
 
151
+ # Full Runner workflow (mimics UO CLI functionality)
152
+ # pass in the path to the scenario csv, geojson path, whether this is a reopt analysis (true/false), and whether to use localhost RNM API (true/false)
153
+ desc 'Full Runner workflow'
154
+ task :full_runner_workflow, [:scenario_csv_path, :geojson_path, :reopt, :use_localhost] do |t, args|
155
+ # todo: could allow passing in extended catalog, average peak catalog, and opendss_catalog flags too
156
+ # if no path passed in, use default:
157
+ scenario_csv = args[:scenario_csv_path] || 'spec/test/example_project/run/baseline_scenario'
158
+ geojson_path = args[:geojson_path] || 'spec/test/example_project/example_project_with_network_and_streets'
159
+ root_dir, scenario_file_name = File.split(File.expand_path(scenario_csv))
160
+ scenario_name = File.basename(scenario_file_name, File.extname(scenario_file_name))
161
+ run_dir = File.join(root_dir, 'run', scenario_name.downcase)
162
+ reopt = args[:reopt] || false
163
+ reopt = reopt == 'true'
164
+ use_local = args[:use_localhost] || false
165
+
166
+ runner = URBANopt::RNM::Runner.new(scenario_name, run_dir, scenario_csv, geojson_path, reopt: reopt)
167
+ runner.create_simulation_files
168
+ runner.run(use_local)
169
+ runner.post_process
170
+
171
+ puts '...done!'
172
+ end
173
+
151
174
  # Create opendss catalog from extended catalog
152
175
  # pass in the path and filename where the OpenDSS catalog should be saved
153
176
  desc 'Create OpenDSS catalog'
@@ -169,20 +192,22 @@ end
169
192
  # run validation
170
193
  # pass in the path to the scenario csv
171
194
  desc 'Run Validation'
172
- task :run_validation, [:scenario_csv_path, :reopt, :use_localhost] do |t, args|
195
+ task :run_validation, [:scenario_csv_path, :use_numeric_ids] do |t, args|
173
196
  #Exammple to run validation
174
- #bundle exec rake run_validation[D:/.../urbanopt-rnm-us-gem/spec/files/example_project/baseline_scenario.csv]
197
+ #bundle exec rake run_validation[D:/.../urbanopt-rnm-us-gem/spec/files/example_project/baseline_scenario.csv,true]
198
+
175
199
  puts 'Running OpenDSS validation'
200
+
176
201
  # if no path passed in, use default:
177
202
  scenario_csv = args[:scenario_csv_path] || 'spec/test/example_project/run/baseline_scenario'
178
203
  root_dir, scenario_file_name = File.split(File.expand_path(scenario_csv))
179
204
  scenario_name = File.basename(scenario_file_name, File.extname(scenario_file_name))
180
205
  run_dir = File.join(root_dir, 'run', scenario_name.downcase)
181
-
182
206
  rnm_dir = File.join(run_dir, 'rnm-us')
183
207
 
184
-
185
-
208
+ #Use numeric ids (for the hierarchical plot of the network)
209
+ use_numeric_ids = args[:use_numeric_ids] || false
210
+ use_numeric_ids = use_numeric_ids == 'true'
186
211
 
187
212
  if !File.exist?(rnm_dir)
188
213
  puts rnm_dir
@@ -190,7 +215,7 @@ task :run_validation, [:scenario_csv_path, :reopt, :use_localhost] do |t, args|
190
215
  end
191
216
 
192
217
  puts "run dir path: #{run_dir}"
193
- validation = URBANopt::RNM::Validation.new(rnm_dir)
218
+ validation = URBANopt::RNM::Validation.new(rnm_dir,use_numeric_ids)
194
219
  validation.run_validation()
195
220
 
196
221
  puts '...done!'
@@ -108,6 +108,7 @@ module URBANopt
108
108
  end
109
109
 
110
110
  if !missing_files.empty?
111
+ puts "RNM DIR: #{@rnm_dir}"
111
112
  raise "Input Files missing in directory: #{missing_files.join(',')}"
112
113
  end
113
114
 
@@ -281,6 +282,13 @@ module URBANopt
281
282
  # delete zip
282
283
  File.delete(file_path)
283
284
 
285
+ # check if zip is empty
286
+ if Dir.empty? File.join(@rnm_dir, 'results')
287
+ msg = "Error in simulation: Results.zip empty"
288
+ @@logger.error(msg)
289
+ raise msg
290
+ end
291
+
284
292
  else
285
293
  msg = "Error retrieving results for #{the_sim_id}. error code: #{resp.status}. #{resp.body}"
286
294
  @@logger.error(msg)
@@ -45,7 +45,7 @@ require 'csv'
45
45
  module URBANopt
46
46
  module RNM
47
47
  class Consumers
48
- attr_accessor :customers, :customers_ext, :profile_customer_p, :profile_customer_q, :profile_customer_p_ext, :profile_customer_q_ext, :power_factor
48
+ attr_accessor :customers, :customers_ext, :profile_customer_p, :profile_customer_q, :profile_customer_p_ext, :profile_customer_q_ext, :power_factor, :profile_date_time, :profile_date_time_ext
49
49
 
50
50
  # initializing all the attributes to build the inputs files required by the RNM-US model
51
51
  def initialize(reopt, only_lv_consumers = false, max_num_lv_nodes, average_building_peak_catalog_path, lv_limit)
@@ -55,8 +55,10 @@ module URBANopt
55
55
  @max_num_lv_nodes = max_num_lv_nodes
56
56
  @customers = []
57
57
  @customers_ext = []
58
+ @profile_date_time = []
58
59
  @profile_customer_p = []
59
60
  @profile_customer_q = []
61
+ @profile_date_time_ext = []
60
62
  @profile_customer_p_ext = []
61
63
  @profile_customer_q_ext = []
62
64
  @power_factor = power_factor
@@ -73,8 +75,10 @@ module URBANopt
73
75
  # while the 2nd option is run in case "only LV" set to false and the consumption for each building will be placed in a single node
74
76
  def construct_consumer(profiles, single_values, building_map, building_nodes, height, users, folder)
75
77
  if @only_lv_consumers
78
+ planning_date_time = []
76
79
  planning_profile_node_active = []
77
80
  planning_profile_node_reactive = []
81
+ yearly_date_time = []
78
82
  yearly_profile_node_active = []
79
83
  yearly_profile_node_reactive = []
80
84
  nodes_per_bldg, area, medium_voltage = av_peak_cons_per_building_type(folder['building_types'])
@@ -106,19 +110,23 @@ module URBANopt
106
110
  else
107
111
  voltage_default, phases = voltage_values(peak_active_power_cons / @power_factor)
108
112
  end
109
-
113
+
110
114
  for k in 0..profiles[:planning_profile_cust_active].length - 1
115
+ planning_date_time[k]=profiles[:planning_date_time][k]
111
116
  planning_profile_node_active[k] = ((profiles[:planning_profile_cust_active][k]) / nodes_per_bldg).round(2)
112
117
  planning_profile_node_reactive[k] = ((profiles[:planning_profile_cust_reactive][k]) / nodes_per_bldg).round(2)
113
118
  end
114
119
  for k in 0..profiles[:yearly_profile_cust_active].length - 1
120
+ yearly_date_time[k]=profiles[:yearly_date_time][k]
115
121
  yearly_profile_node_active[k] = ((profiles[:yearly_profile_cust_active][k]) / nodes_per_bldg).round(2)
116
122
  yearly_profile_node_reactive[k] = ((profiles[:yearly_profile_cust_reactive][k]) / nodes_per_bldg).round(2)
117
123
  end
118
124
  @customers.push([coordinates, voltage_default, peak_active_power_cons, peak_reactive_power_cons, phases])
119
125
  @customers_ext.push([coordinates, voltage_default, peak_active_power_cons, peak_reactive_power_cons, phases, area, height, (single_values[:energy] / nodes_per_bldg).round(2), peak_active_power_cons, peak_reactive_power_cons, users])
126
+ @profile_date_time.push([planning_date_time])
120
127
  @profile_customer_q.push([id, 24, planning_profile_node_reactive])
121
128
  @profile_customer_p.push([id, 24, planning_profile_node_active])
129
+ @profile_date_time_ext.push([yearly_date_time])
122
130
  @profile_customer_p_ext.push([id, 8760, yearly_profile_node_active])
123
131
  @profile_customer_q_ext.push([id, 8760, yearly_profile_node_reactive])
124
132
 
@@ -131,8 +139,10 @@ module URBANopt
131
139
  voltage_default, phases = voltage_values(single_values[:peak_active_power_cons] / @power_factor * 0.9) # applying safety factor
132
140
  @customers.push([building_map, voltage_default, single_values[:peak_active_power_cons], single_values[:peak_reactive_power_cons], phases])
133
141
  @customers_ext.push([building_map, voltage_default, single_values[:peak_active_power_cons], single_values[:peak_reactive_power_cons], phases, area, height, (single_values[:energy]).round(2), single_values[:peak_active_power_cons], single_values[:peak_reactive_power_cons], users])
142
+ @profile_date_time.push([profiles[:planning_date_time]])
134
143
  @profile_customer_q.push([id, 24, profiles[:planning_profile_cust_reactive]])
135
144
  @profile_customer_p.push([id, 24, profiles[:planning_profile_cust_active]])
145
+ @profile_date_time_ext.push([profiles[:yearly_date_time]])
136
146
  @profile_customer_p_ext.push([id, 8760, profiles[:yearly_profile_cust_active]])
137
147
  @profile_customer_q_ext.push([id, 8760, profiles[:yearly_profile_cust_reactive]])
138
148
  end
@@ -234,10 +244,12 @@ module URBANopt
234
244
  # content = CSV.foreach(csv_feature_report, headers: true) do |power|
235
245
  CSV.foreach(csv_feature_report, headers: true) do |power|
236
246
  @power_factor = power['Electricity:Facility Power(kW)'].to_f / power['Electricity:Facility Apparent Power(kVA)'].to_f
247
+ profiles[:yearly_date_time].push(power['Datetime'])
237
248
  profiles[:yearly_profile_cust_active].push(power['Electricity:Facility Power(kW)'].to_f)
238
249
  profiles[:yearly_profile_cust_reactive].push(profiles[:yearly_profile_cust_active][k] * Math.tan(Math.acos(@power_factor)))
239
250
  single_values[:energy] += power['REopt:Electricity:Load:Total(kw)'].to_f # calculating the yearly energy consumed by each feature
240
251
  if k >= profile_start_max && k <= profile_start_max + hours
252
+ profiles[:planning_date_time].push(power['Datetime'])
241
253
  profiles[:planning_profile_cust_active].push(power['Electricity:Facility Power(kW)'].to_f)
242
254
  if power['Electricity:Facility Power(kW)'].to_f > single_values[:peak_active_power_cons]
243
255
  single_values[:peak_active_power_cons] = power['Electricity:Facility Power(kW)'].to_f
@@ -210,7 +210,11 @@ module URBANopt
210
210
  street_coordinates[street_number] = each_street
211
211
  street_number += 1
212
212
  elsif street['geometry']['type'] == 'Polygon' && street['properties']['type'] == 'Building' && scenario_features.include?(street['properties']['id'])
213
- for k in 0..street['geometry']['coordinates'].length - 1
213
+ puts "------ processing Building #{ street['properties']['name']} --------"
214
+ # this loop goes through each polygon and assign it a building and is not correct for buildings with
215
+ # inner courtyards (it would create 2 buildings)
216
+ # This assumes the outward footprint is the first polygon -- we only process the first one here
217
+ for k in 0..0
214
218
  h = 0 # index representing number of nodes for each single building
215
219
  building = [] # array containing every building node coordinates and id of 1 building
216
220
  for j in 0..street['geometry']['coordinates'][k].length - 1
@@ -258,6 +258,10 @@ module URBANopt
258
258
  File.open(File.join(@run_dir, @rnm_dirname, 'customers_ext.txt'), 'w+') do |g|
259
259
  g.puts(prosumers.customers_ext.map { |w| w.join(';') })
260
260
  end
261
+ File.open(File.join(@run_dir, @rnm_dirname, 'timestamps.csv'), 'w+') do |g|
262
+ g.puts("Datetime\n")
263
+ g.puts(prosumers.profile_date_time[0].map { |w| w.join("\n") })
264
+ end
261
265
  File.open(File.join(@run_dir, @rnm_dirname, 'cust_profile_p.txt'), 'w+') do |g|
262
266
  g.puts(prosumers.profile_customer_p.map { |w| w.join(';') })
263
267
  end
@@ -267,6 +271,10 @@ module URBANopt
267
271
  # CSV.open(File.join(@run_dir, @rnm_dirname, "cust_profile_q_extendido.csv"), "w") do |csv|
268
272
  # csv << [prosumers.profile_customer_q_ext]
269
273
  # end
274
+ File.open(File.join(@run_dir, @rnm_dirname, 'timestamps_extendido.csv'), 'w+') do |g|
275
+ g.puts("Datetime\n")
276
+ g.puts(prosumers.profile_date_time_ext[0].map { |w| w.join("\n") })
277
+ end
270
278
  File.open(File.join(@run_dir, @rnm_dirname, 'cust_profile_q_extendido.txt'), 'w+') do |g|
271
279
  g.puts(prosumers.profile_customer_q_ext.map { |w| w.join(';') })
272
280
  end
@@ -289,6 +297,7 @@ module URBANopt
289
297
  File.open(File.join(@run_dir, @rnm_dirname, 'gen_profile_p_extendido.txt'), 'w+') do |g|
290
298
  g.puts(prosumers.profile_dg_p_extended.map { |w| w.join(';') })
291
299
  end
300
+ ficheros_entrada_inc.push('Timestamps;timestamps.csv;timestamps_extendido.csv')
292
301
  ficheros_entrada_inc.push('CClienteGreenfield;customers_ext.txt;cust_profile_p.txt;cust_profile_q.txt;cust_profile_p_extendido.txt;cust_profile_q_extendido.txt')
293
302
  ficheros_entrada_inc.push('CGeneradorGreenfield;generators.txt;gen_profile_p.txt;gen_profile_q.txt;gen_profile_p_extendido.txt;gen_profile_q_extendido.txt')
294
303
  ficheros_entrada_inc.push('END')
@@ -302,18 +311,27 @@ module URBANopt
302
311
  File.open(File.join(@run_dir, @rnm_dirname, 'customers_ext.txt'), 'w+') do |g|
303
312
  g.puts(consumers.customers_ext.map { |w| w.join(';') })
304
313
  end
314
+ File.open(File.join(@run_dir, @rnm_dirname, 'timestamps.csv'), 'w+') do |g|
315
+ g.puts("Datetime\n")
316
+ g.puts(consumers.profile_date_time[0].map { |w| w.join("\n") })
317
+ end
305
318
  File.open(File.join(@run_dir, @rnm_dirname, 'cust_profile_p.txt'), 'w+') do |g|
306
319
  g.puts(consumers.profile_customer_p.map { |w| w.join(';') })
307
320
  end
308
321
  File.open(File.join(@run_dir, @rnm_dirname, 'cust_profile_q.txt'), 'w+') do |g|
309
322
  g.puts(consumers.profile_customer_q.map { |w| w.join(';') })
310
323
  end
324
+ File.open(File.join(@run_dir, @rnm_dirname, 'timestamps_extendido.csv'), 'w+') do |g|
325
+ g.puts("Datetime\n")
326
+ g.puts(consumers.profile_date_time_ext[0].map { |w| w.join("\n") })
327
+ end
311
328
  File.open(File.join(@run_dir, @rnm_dirname, 'cust_profile_q_extendido.txt'), 'w+') do |g|
312
329
  g.puts(consumers.profile_customer_q_ext.map { |w| w.join(';') })
313
330
  end
314
331
  File.open(File.join(@run_dir, @rnm_dirname, 'cust_profile_p_extendido.txt'), 'w+') do |g|
315
332
  g.puts(consumers.profile_customer_p_ext.map { |w| w.join(';') })
316
333
  end
334
+ ficheros_entrada_inc.push('Timestamps;timestamps.csv;timestamps_extendido.csv')
317
335
  ficheros_entrada_inc.push('CClienteGreenfield;customers_ext.txt;cust_profile_p.txt;cust_profile_q.txt;cust_profile_p_extendido.txt;cust_profile_q_extendido.txt')
318
336
  ficheros_entrada_inc.push('END')
319
337
  File.open(File.join(@run_dir, @rnm_dirname, 'ficheros_entrada_inc.txt'), 'w+') do |g|
@@ -45,7 +45,7 @@ module URBANopt
45
45
  # creating a class that creates the consumers input required by the RNM-US model,
46
46
  # according to their geographic location, energy consumption and peak demand, and power consumption profiles
47
47
  class Prosumers
48
- attr_accessor :customers, :customers_ext, :profile_customer_p, :profile_customer_q, :profile_customer_p_ext, :profile_customer_q_ext, :dg, :dg_profile_p, :dg_profile_q, :profile_dg_p_extended, :profile_dg_q_extended, :power_factor
48
+ attr_accessor :customers, :customers_ext, :profile_customer_p, :profile_customer_q, :profile_customer_p_ext, :profile_customer_q_ext, :dg, :dg_profile_p, :dg_profile_q, :profile_dg_p_extended, :profile_dg_q_extended, :power_factor, :profile_date_time, :profile_date_time_ext
49
49
 
50
50
  # initializing all the attributes to build the inputs files required by the RNM-US model
51
51
  def initialize(reopt, only_lv_consumers = false, max_num_lv_nodes, average_building_peak_catalog_path, lv_limit)
@@ -55,8 +55,10 @@ module URBANopt
55
55
  @max_num_lv_nodes = max_num_lv_nodes
56
56
  @customers = []
57
57
  @customers_ext = []
58
+ @profile_date_time = []
58
59
  @profile_customer_p = []
59
60
  @profile_customer_q = []
61
+ @profile_date_time_ext = []
60
62
  @profile_customer_p_ext = []
61
63
  @profile_customer_q_ext = []
62
64
  @dg = []
@@ -99,16 +101,20 @@ module URBANopt
99
101
  end
100
102
  @customers.push([building_map, id, voltage_default, single_values[:peak_active_power_cons], single_values[:peak_reactive_power_cons], phases])
101
103
  @customers_ext.push([building_map, id, voltage_default, single_values[:peak_active_power_cons], single_values[:peak_reactive_power_cons], phases, area, height, (single_values[:energy]).round(2), single_values[:peak_active_power_cons], single_values[:peak_reactive_power_cons], users])
104
+ @profile_date_time.push([profiles_planning[:planning_date_time]])
102
105
  @profile_customer_q.push([id, 48, profiles_planning[:planning_profile_cust_reactive]])
103
106
  @profile_customer_p.push([id, 48, profiles_planning[:planning_profile_cust_active]])
107
+ @profile_date_time_ext.push([profiles[:yearly_date_time]])
104
108
  @profile_customer_p_ext.push([id, 8760, profiles[:yearly_profile_cust_active]])
105
109
  @profile_customer_q_ext.push([id, 8760, profiles[:yearly_profile_cust_reactive]])
106
110
 
107
111
  if !der_capacity[:storage].nil? && der_capacity[:storage] > 0
108
112
  @customers.push([building_map, id_batt, voltage_default, single_values[:peak_active_power_storage], single_values[:peak_reactive_power_storage], phases])
109
113
  @customers_ext.push([building_map, id_batt, voltage_default, single_values[:peak_active_power_storage], single_values[:peak_reactive_power_storage], phases, area, height, (single_values[:energy]).round(2), single_values[:peak_active_power_storage], single_values[:peak_reactive_power_storage], users])
114
+ @profile_date_time.push([profiles_planning[:planning_date_time]])
110
115
  @profile_customer_q.push([id_batt, 48, profiles_planning[:planning_profile_storage_reactive]])
111
116
  @profile_customer_p.push([id_batt, 48, profiles_planning[:planning_profile_storage_active]])
117
+ @profile_date_time_ext.push([profiles[:yearly_date_time]])
112
118
  @profile_customer_p_ext.push([id_batt, 8760, profiles[:yearly_profile_storage_active]])
113
119
  @profile_customer_q_ext.push([id_batt, 8760, profiles[:yearly_profile_storage_reactive]])
114
120
  end
@@ -129,8 +135,10 @@ module URBANopt
129
135
  # among the nodes of each building
130
136
  def construct_prosumer_lv(nodes_per_bldg = 0, profiles, profiles_planning, single_values, building_map, building_nodes, area, height, users, der_capacity)
131
137
  # the default variables are defined (i.e. type and rurality type)
138
+ planning_date_time = []
132
139
  planning_profile_node_active = []
133
140
  planning_profile_node_reactive = []
141
+ yearly_date_time = []
134
142
  yearly_profile_node_active = []
135
143
  yearly_profile_node_reactive = []
136
144
  closest_node = building_map[3].split('_')[1].to_i # refers to the closest node of the building in consideration to the street
@@ -159,17 +167,21 @@ module URBANopt
159
167
  peak_reactive_power_cons = (single_values[:peak_reactive_power_cons] / nodes_consumers).round(2)
160
168
  voltage_default, phases = voltage_values(peak_active_power_cons / @power_factor)
161
169
  for k in 0..profiles_planning[:planning_profile_cust_active].length - 1
170
+ planning_date_time[k]=profiles_planning[:planning_date_time][k]
162
171
  planning_profile_node_active[k] = (profiles_planning[:planning_profile_cust_active][k] / nodes_consumers).round(2)
163
172
  planning_profile_node_reactive[k] = (profiles_planning[:planning_profile_cust_reactive][k] / nodes_consumers).round(2)
164
173
  end
165
174
  for k in 0..profiles[:yearly_profile_cust_active].length - 1
175
+ yearly_date_time[k]=profiles[:yearly_date_time][k]
166
176
  yearly_profile_node_active[k] = (profiles[:yearly_profile_cust_active][k] / nodes_consumers).round(2)
167
177
  yearly_profile_node_reactive[k] = (profiles[:yearly_profile_cust_reactive][k] / nodes_consumers).round(2)
168
178
  end
169
179
  @customers.push([coordinates, id, voltage_default, peak_active_power_cons, peak_reactive_power_cons, phases])
170
180
  @customers_ext.push([coordinates, id, voltage_default, peak_active_power_cons, peak_reactive_power_cons, phases, area, height, (single_values[:energy] / nodes_consumers).round(2), peak_active_power_cons, peak_reactive_power_cons, users])
181
+ @profile_date_time.push([planning_date_time])
171
182
  @profile_customer_q.push([id, 48, planning_profile_node_reactive])
172
183
  @profile_customer_p.push([id, 48, planning_profile_node_active])
184
+ @profile_date_time_ext.push([yearly_date_time])
173
185
  @profile_customer_p_ext.push([id, 8760, yearly_profile_node_active])
174
186
  @profile_customer_q_ext.push([id, 8760, yearly_profile_node_reactive])
175
187
  else
@@ -186,8 +198,10 @@ module URBANopt
186
198
  if !der_capacity[:storage].nil? && der_capacity[:storage] > 0
187
199
  @customers.push([coordinates, id_batt, voltage_default, single_values[:peak_active_power_storage], single_values[:peak_reactive_power_storage], phases])
188
200
  @customers_ext.push([coordinates, id_batt, voltage_default, single_values[:peak_active_power_storage], single_values[:peak_reactive_power_storage], phases, area, height, (single_values[:energy]).round(2), single_values[:peak_active_power_storage], single_values[:peak_reactive_power_storage], users])
201
+ @profile_date_time.push([profiles[:planning_date_time]])
189
202
  @profile_customer_q.push([id_batt, 48, profiles_planning[:planning_profile_storage_reactive]])
190
203
  @profile_customer_p.push([id_batt, 48, profiles_planning[:planning_profile_storage_active]])
204
+ @profile_date_time_ext.push([profiles[:yearly_date_time]])
191
205
  @profile_customer_p_ext.push([id_batt, 8760, profiles[:yearly_profile_storage_active]])
192
206
  @profile_customer_q_ext.push([id_batt, 8760, profiles[:yearly_profile_storage_reactive]])
193
207
  end
@@ -285,6 +299,7 @@ module URBANopt
285
299
 
286
300
  # method to order profiles consistently
287
301
  def profiles_planning_creation(profiles_planning, power, single_values, i, hours, power_factor)
302
+ profiles_planning[:planning_date_time][i]=power['Datetime']
288
303
  profiles_planning[:planning_profile_cust_active][i] = power['REopt:Electricity:Load:Total(kw)'].to_f
289
304
  profiles_planning[:planning_profile_storage_active][i] = power['REopt:Electricity:Grid:ToBattery(kw)'].to_f + power['REopt:ElectricityProduced:Generator:ToBattery(kw)'].to_f + power['REopt:ElectricityProduced:PV:ToBattery(kw)'].to_f + power['REopt:ElectricityProduced:Wind:ToBattery(kw)'].to_f - power['REopt:Electricity:Storage:ToLoad(kw)'].to_f - power['REopt:Electricity:Storage:ToGrid(kw)'].to_f
290
305
  profiles_planning[:planning_profile_dg_active][i] = power['REopt:ElectricityProduced:Total(kw)'].to_f
@@ -341,6 +356,7 @@ module URBANopt
341
356
  max_peak = 0
342
357
  CSV.foreach(csv_feature_report, headers: true) do |power|
343
358
  @power_factor = power['Electricity:Facility Power(kW)'].to_f / power['Electricity:Facility Apparent Power(kVA)'].to_f
359
+ profiles[:yearly_date_time].push(power['Datetime'])
344
360
  profiles[:yearly_profile_cust_active].push(power['REopt:Electricity:Load:Total(kw)'].to_f)
345
361
  profiles[:yearly_profile_cust_reactive].push(profiles[:yearly_profile_cust_active][k] * Math.tan(Math.acos(@power_factor)))
346
362
  profiles[:yearly_profile_dg_active].push(power['REopt:ElectricityProduced:Total(kw)'].to_f)