urbanopt-rnm-us 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +28 -0
- data/Jenkinsfile +10 -0
- data/lib/urbanopt/rnm/api_client.rb +54 -36
- data/lib/urbanopt/rnm/consumers.rb +8 -5
- data/lib/urbanopt/rnm/prosumers.rb +94 -85
- data/lib/urbanopt/rnm/scenario_report.rb +7 -7
- data/lib/urbanopt/rnm/version.rb +1 -1
- data/urbanopt-rnm-us-gem.gemspec +5 -4
- metadata +28 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 977f93affe9c5d526cd2cb0af1232f443e07693a78d45da7827563f9f804c30e
|
4
|
+
data.tar.gz: 03e3f2642b6ddc140b36d9385e0a77790e1308a1f5bd1b96647775d03f4e4830
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e86574dc52de1655eace0ba423350343257cb234a2e110a2670fd80163fe07fc2f6b7ab7cf56decb02260bcf307b8620ddde128bb9c157ceeb6a94f59487dd16
|
7
|
+
data.tar.gz: 8e7bba90ab83607932f8eecc5bda1487d86c5da2864a32d92fd88d3ae20d4aa18f8567244042dc66a9e640304aa65a0ec44f9090f6a2472124aa191be27db523
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,33 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## Version 0.2.0
|
4
|
+
|
5
|
+
Date Range 11/09/21 - 11/22/21
|
6
|
+
|
7
|
+
- Updated dependencies for OpenStudio 3.3
|
8
|
+
|
9
|
+
## Version 0.1.3
|
10
|
+
|
11
|
+
Date Range 11/02/21 - 11/08/21
|
12
|
+
|
13
|
+
- Fix [#11](https://github.com/urbanopt/urbanopt-rnm-us-gem/issues/11), results files are not downloading in project directory for large projects
|
14
|
+
|
15
|
+
- Fix [#16](https://github.com/urbanopt/urbanopt-rnm-us-gem/issues/16), fix residential enums to be consistent across files and fix typo in multifamily
|
16
|
+
|
17
|
+
## Version 0.1.2
|
18
|
+
|
19
|
+
Date Range 10/29/21 - 11/01/21
|
20
|
+
|
21
|
+
- Fix [#13](https://github.com/urbanopt/urbanopt-rnm-us-gem/issues/13), update rubyzip dependency to fix conflict
|
22
|
+
|
23
|
+
## Version 0.1.1
|
24
|
+
|
25
|
+
Date Range 07/22/21 - 10/28/21
|
26
|
+
|
27
|
+
- Fixed [#10]( https://github.com/urbanopt/urbanopt-rnm-us-gem/issues/7 ), Fix peak profile generation for prosumer profiles
|
28
|
+
- Fixed [#8]( https://github.com/urbanopt/urbanopt-rnm-us-gem/issues/8 ), Use timestep_per_hour defined in UO feature report for the interval reporting
|
29
|
+
- Fixed [#10]( https://github.com/urbanopt/urbanopt-rnm-us-gem/issues/10 ), Add other residential types to prosumer/consumer calculations
|
30
|
+
|
3
31
|
## Version 0.1.0
|
4
32
|
|
5
33
|
Initial version of the RNM-US gem.
|
data/Jenkinsfile
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
//Jenkins pipelines are stored in shared libaries. Please see: https://github.com/tijcolem/nrel_cbci_jenkins_libs
|
2
|
+
|
3
|
+
@Library('cbci_shared_libs') _
|
4
|
+
|
5
|
+
// Build for PR to develop branch only.
|
6
|
+
if ((env.CHANGE_ID) && (env.CHANGE_TARGET) ) { // check if set
|
7
|
+
|
8
|
+
urbanopt_rnm_us()
|
9
|
+
|
10
|
+
}
|
@@ -177,52 +177,70 @@ module URBANopt
|
|
177
177
|
# prepare results directory
|
178
178
|
prepare_results_dir
|
179
179
|
|
180
|
-
max_tries =
|
180
|
+
max_tries = 20
|
181
181
|
tries = 0
|
182
182
|
puts "attempting to retrieve results for simulation #{@sim_id}"
|
183
183
|
while !done && (max_tries != tries)
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
if data['
|
192
|
-
|
184
|
+
begin
|
185
|
+
resp = conn.get("simulations/#{@sim_id}")
|
186
|
+
if resp.status == 200
|
187
|
+
data = JSON.parse(resp.body)
|
188
|
+
if data['status'] && ['failed', 'completed'].include?(data['status'])
|
189
|
+
# done
|
190
|
+
done = true
|
191
|
+
if data['status'] == 'failed'
|
192
|
+
if data['results'] && data['results']['message']
|
193
|
+
puts "Simulation Error: #{data['results']['message']}"
|
194
|
+
else
|
195
|
+
puts 'Simulation Error!'
|
196
|
+
end
|
193
197
|
else
|
194
|
-
|
198
|
+
# edge case, check for results
|
199
|
+
if data['results'].nil?
|
200
|
+
puts "got a 200 but results are null...trying again"
|
201
|
+
tries += 1
|
202
|
+
sleep(3)
|
203
|
+
else
|
204
|
+
# get results
|
205
|
+
@results = data['results'] || []
|
206
|
+
|
207
|
+
puts "downloading results"
|
208
|
+
# download results
|
209
|
+
download_results
|
210
|
+
return @results
|
211
|
+
end
|
195
212
|
end
|
196
213
|
else
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
# download results
|
201
|
-
download_results
|
202
|
-
|
203
|
-
return @results
|
214
|
+
puts "no status yet...trying again"
|
215
|
+
tries += 1
|
216
|
+
sleep(3)
|
204
217
|
end
|
218
|
+
|
205
219
|
else
|
220
|
+
puts("ERROR retrieving: #{resp.body}")
|
206
221
|
tries += 1
|
207
|
-
sleep(1)
|
208
|
-
end
|
209
222
|
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
# try again
|
221
|
-
puts("TRYING AGAIN...#{tries}")
|
222
|
-
sleep(3)
|
223
|
+
if tries == max_tries
|
224
|
+
# now raise the error
|
225
|
+
msg = "Error retrieving simulation #{@sim_id}. error code: #{resp.status}"
|
226
|
+
@@logger.error(msg)
|
227
|
+
raise msg
|
228
|
+
else
|
229
|
+
# try again
|
230
|
+
puts("TRYING AGAIN...#{tries}")
|
231
|
+
sleep(3)
|
232
|
+
end
|
223
233
|
end
|
234
|
+
rescue => error
|
235
|
+
@@logger.error("Error retrieving simulation #{@sim_id}.")
|
236
|
+
@@logger.error(error.message)
|
237
|
+
raise error.message
|
224
238
|
end
|
225
239
|
end
|
240
|
+
if !done
|
241
|
+
@@logger.error("Error retrieving simulation #{@sim_id}.")
|
242
|
+
raise 'Simulation not retrieved...maximum tries reached'
|
243
|
+
end
|
226
244
|
end
|
227
245
|
|
228
246
|
##
|
@@ -243,14 +261,14 @@ module URBANopt
|
|
243
261
|
streamed << chunk
|
244
262
|
end
|
245
263
|
end
|
246
|
-
# puts("STATUS: #{resp.status}, #{resp.body}")
|
247
264
|
|
248
265
|
if resp.status == 200
|
249
266
|
|
250
267
|
file_path = File.join(@rnm_dir, 'results', 'results.zip')
|
251
268
|
|
252
269
|
File.open(file_path, 'wb') { |f| f.write streamed.join }
|
253
|
-
|
270
|
+
puts "RNM-US results.zip downloaded to #{@rnm_dir}"
|
271
|
+
|
254
272
|
# unzip
|
255
273
|
Zip::File.open(file_path) do |zip_file|
|
256
274
|
zip_file.each do |f|
|
@@ -259,7 +277,7 @@ module URBANopt
|
|
259
277
|
zip_file.extract(f, f_path) unless File.exist?(f_path)
|
260
278
|
end
|
261
279
|
end
|
262
|
-
|
280
|
+
puts "results.zip extracted"
|
263
281
|
# delete zip
|
264
282
|
File.delete(file_path)
|
265
283
|
|
@@ -79,6 +79,7 @@ module URBANopt
|
|
79
79
|
yearly_profile_node_reactive = []
|
80
80
|
nodes_per_bldg, area, medium_voltage = av_peak_cons_per_building_type(folder['building_types'])
|
81
81
|
# the default variables are defined (i.e. type and rurality type)
|
82
|
+
puts "consumers 82"
|
82
83
|
closest_node = building_map[3].split('_')[1].to_i # refers to the node, found in the class above
|
83
84
|
node = closest_node
|
84
85
|
cont = 1
|
@@ -175,7 +176,7 @@ module URBANopt
|
|
175
176
|
conservative_factor = 0.8 # considered as a reasonable assumption, but this value could be changed
|
176
177
|
average_peak_folder = JSON.parse(File.read(@average_building_peak_catalog_path))
|
177
178
|
for i in 0..feature_file.length - 1
|
178
|
-
area = (feature_file[i]['floor_area']).round(2)
|
179
|
+
area = feature_file[i].has_key?('floor_area') ? (feature_file[i]['floor_area']).round(2) : feature_file[i]['floor_area_sqft'].round(2)
|
179
180
|
building_type = feature_file[i]['building_type'] # it specifies the type of building, sometimes it is directly the sub-type
|
180
181
|
counter = 0 # counter to find number of buildings type belonging to same "category"
|
181
182
|
average_peak_folder.each do |building_class|
|
@@ -214,16 +215,18 @@ module URBANopt
|
|
214
215
|
# the method passes as arguments the urbanopt json and csv output file for each feature and the building coordinates previously calculated
|
215
216
|
# and the "extreme" hour used to plan the network
|
216
217
|
def customer_files_load(csv_feature_report, json_feature_report, building_map, building_nodes, hour)
|
218
|
+
n_timestep_per_hour = json_feature_report["timesteps_per_hour"].to_i
|
217
219
|
profiles = Hash.new { |h, k| h[k] = [] }
|
218
220
|
single_values = Hash.new(0)
|
219
|
-
hours =
|
221
|
+
hours = 24 * n_timestep_per_hour -1
|
220
222
|
feature_type = json_feature_report['program']['building_types'][0]['building_type']
|
221
|
-
residential_building_types = 'Single-Family Detached'
|
223
|
+
residential_building_types = ['Single-Family Detached', 'Single-Family Attached', 'Multifamily', 'Single-Family', 'Multifamily Detached (2 to 4 units)', 'Multifamily Detached (5 or more units)']
|
224
|
+
|
222
225
|
# finding the index where to start computing and saving the info, from the value of the "worst-case hour" for the max peak consumption of the district
|
223
226
|
if residential_building_types.include? feature_type
|
224
|
-
profile_start_max = hour.hour_index_max_res - hour.peak_hour_max_res
|
227
|
+
profile_start_max = hour.hour_index_max_res - ((hour.peak_hour_max_res.split(':')[0].to_i + (hour.peak_hour_max_res.split(':')[1].to_i / 60)) * n_timestep_per_hour)
|
225
228
|
else
|
226
|
-
profile_start_max = hour.hour_index_max_comm - hour.peak_hour_max_comm
|
229
|
+
profile_start_max = hour.hour_index_max_comm - ((hour.peak_hour_max_comm.split(':')[0].to_i + (hour.peak_hour_max_comm.split(':')[1].to_i / 60)) * n_timestep_per_hour)
|
227
230
|
end
|
228
231
|
k = 0 # index for each hour of the year represented in the csv file
|
229
232
|
i = 0 # to represent the 24 hours of a day
|
@@ -74,7 +74,7 @@ module URBANopt
|
|
74
74
|
|
75
75
|
# method defined for the case of a single node where both battery, DG and consumers are placed
|
76
76
|
# evaluation of the peak power in each node to define the type of connection (e.g. voltage level and n phases)
|
77
|
-
def construct_prosumer_general(profiles, single_values, building_map, area, height, users, der_capacity)
|
77
|
+
def construct_prosumer_general(profiles, profiles_planning, single_values, building_map, area, height, users, der_capacity)
|
78
78
|
id = building_map[3]
|
79
79
|
id_dg = "#{building_map[3]}_DG"
|
80
80
|
id_batt = "#{building_map[3]}_battery"
|
@@ -84,8 +84,8 @@ module URBANopt
|
|
84
84
|
# in the hour with generation max peak
|
85
85
|
# in the hour with storage max peak
|
86
86
|
|
87
|
-
for i in 0..
|
88
|
-
hourly_app_power = ((
|
87
|
+
for i in 0..profiles_planning[:planning_profile_cust_active].length-1
|
88
|
+
hourly_app_power = ((profiles_planning[:planning_profile_cust_active][i] + profiles_planning[:planning_profile_storage_active][i] - profiles_planning[:planning_profile_dg_active][i])/@power_factor).abs
|
89
89
|
if hourly_app_power > peak_app_power_node
|
90
90
|
peak_app_power_node = hourly_app_power
|
91
91
|
end
|
@@ -99,22 +99,22 @@ module URBANopt
|
|
99
99
|
end
|
100
100
|
@customers.push([building_map, id, voltage_default, single_values[:peak_active_power_cons], single_values[:peak_reactive_power_cons], phases])
|
101
101
|
@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])
|
102
|
-
@profile_customer_q.push([id, 48,
|
103
|
-
@profile_customer_p.push([id, 48,
|
102
|
+
@profile_customer_q.push([id, 48, profiles_planning[:planning_profile_cust_reactive]])
|
103
|
+
@profile_customer_p.push([id, 48, profiles_planning[:planning_profile_cust_active]])
|
104
104
|
@profile_customer_p_ext.push([id, 8760, profiles[:yearly_profile_cust_active]])
|
105
105
|
@profile_customer_q_ext.push([id, 8760, profiles[:yearly_profile_cust_reactive]])
|
106
|
-
|
107
|
-
if
|
108
|
-
@customers.push([building_map, id_batt, voltage_default, single_values[:peak_active_power_storage],
|
106
|
+
|
107
|
+
if der_capacity[:storage] != nil && der_capacity[:storage] > 0
|
108
|
+
@customers.push([building_map, id_batt, voltage_default, single_values[:peak_active_power_storage],single_values[:peak_reactive_power_storage], phases])
|
109
109
|
@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])
|
110
|
-
@profile_customer_q.push([id_batt, 48,
|
111
|
-
@profile_customer_p.push([id_batt, 48,
|
110
|
+
@profile_customer_q.push([id_batt, 48, profiles_planning[:planning_profile_storage_reactive]])
|
111
|
+
@profile_customer_p.push([id_batt, 48, profiles_planning[:planning_profile_storage_active]])
|
112
112
|
@profile_customer_p_ext.push([id_batt, 8760, profiles[:yearly_profile_storage_active]])
|
113
113
|
@profile_customer_q_ext.push([id_batt, 8760, profiles[:yearly_profile_storage_reactive]])
|
114
114
|
end
|
115
115
|
@dg.push([building_map, id_dg, voltage_default, der_capacity[:dg], single_values[:peak_active_power_dg].round(2), single_values[:peak_reactive_power_dg].round(2), phases])
|
116
|
-
@dg_profile_p.push([id_dg, 48,
|
117
|
-
@dg_profile_q.push([id_dg, 48,
|
116
|
+
@dg_profile_p.push([id_dg, 48, profiles_planning[:planning_profile_dg_active]])
|
117
|
+
@dg_profile_q.push([id_dg, 48, profiles_planning[:planning_profile_dg_reactive]])
|
118
118
|
@profile_dg_p_extended.push([id_dg, 8760, profiles[:yearly_profile_dg_active]])
|
119
119
|
@profile_dg_q_extended.push([id_dg, 8760, profiles[:yearly_profile_dg_reactive]])
|
120
120
|
end
|
@@ -127,7 +127,7 @@ module URBANopt
|
|
127
127
|
# this method is called only if the user sets the option of "only LV nodes" to true
|
128
128
|
# defining a certain numb of nodes for each building and distributing the peak power values equally
|
129
129
|
# among the nodes of each building
|
130
|
-
def construct_prosumer_lv(nodes_per_bldg = 0, profiles, single_values, building_map, building_nodes, area, height, users, der_capacity)
|
130
|
+
def construct_prosumer_lv(nodes_per_bldg = 0, profiles, profiles_planning, single_values, building_map, building_nodes, area, height, users, der_capacity)
|
131
131
|
# the default variables are defined (i.e. type and rurality type)
|
132
132
|
planning_profile_node_active = []
|
133
133
|
planning_profile_node_reactive = []
|
@@ -138,6 +138,7 @@ module URBANopt
|
|
138
138
|
cont = 1
|
139
139
|
cont_reverse = 1
|
140
140
|
nodes_consumers = nodes_per_bldg - 1
|
141
|
+
|
141
142
|
for i in 1..nodes_per_bldg
|
142
143
|
coordinates = building_map
|
143
144
|
node = closest_node + cont # to set the new nodes with enough distance among each others
|
@@ -145,10 +146,10 @@ module URBANopt
|
|
145
146
|
if i > 1 && node <= building_nodes.length - 2
|
146
147
|
coordinates = building_nodes[node] # take the closest building node index to the street and pass the nodes after it
|
147
148
|
cont += 1
|
148
|
-
elsif i > 1
|
149
|
+
elsif i > 1
|
149
150
|
coordinates = building_nodes[node_reverse]
|
150
151
|
cont_reverse += 1
|
151
|
-
end
|
152
|
+
end
|
152
153
|
# this condition is used to firstly place the building consumption nodes and then the last node
|
153
154
|
# to be placed is the one referred to DG and battery for the building
|
154
155
|
if i < nodes_per_bldg # considering the consumers nodes
|
@@ -157,12 +158,12 @@ module URBANopt
|
|
157
158
|
peak_active_power_cons = (single_values[:peak_active_power_cons] / nodes_consumers).round(2)
|
158
159
|
peak_reactive_power_cons = (single_values[:peak_reactive_power_cons] / nodes_consumers).round(2)
|
159
160
|
voltage_default, phases = voltage_values(peak_active_power_cons / @power_factor)
|
160
|
-
for k in 0..
|
161
|
-
|
162
|
-
|
161
|
+
for k in 0..profiles_planning[:planning_profile_cust_active].length - 1
|
162
|
+
planning_profile_node_active[k] = (profiles_planning[:planning_profile_cust_active][k] / nodes_consumers).round(2)
|
163
|
+
planning_profile_node_reactive[k] = (profiles_planning[:planning_profile_cust_reactive][k] / nodes_consumers).round(2)
|
163
164
|
end
|
164
165
|
for k in 0..profiles[:yearly_profile_cust_active].length - 1
|
165
|
-
yearly_profile_node_active[k] = (profiles[:yearly_profile_cust_active][k] / nodes_consumers).round(2)
|
166
|
+
yearly_profile_node_active[k] = (profiles[:yearly_profile_cust_active][k] / nodes_consumers).round(2)
|
166
167
|
yearly_profile_node_reactive[k] = (profiles[:yearly_profile_cust_reactive][k] / nodes_consumers).round(2)
|
167
168
|
end
|
168
169
|
@customers.push([coordinates, id, voltage_default, peak_active_power_cons, peak_reactive_power_cons, phases])
|
@@ -173,20 +174,20 @@ module URBANopt
|
|
173
174
|
@profile_customer_q_ext.push([id, 8760, yearly_profile_node_reactive])
|
174
175
|
else
|
175
176
|
# considering the DG and battery
|
176
|
-
voltage_default, phases = voltage_values(der_capacity[:dg]) #
|
177
|
+
voltage_default, phases = voltage_values(der_capacity[:dg]) #assuming that the pv capacity is always higher than battery capacity
|
177
178
|
id_dg = "#{coordinates[3]}_DG"
|
178
179
|
id_batt = "#{coordinates[3]}_battery"
|
179
180
|
coordinates.pop
|
180
181
|
@dg.push([coordinates, id_dg, voltage_default, der_capacity[:dg], single_values[:peak_active_power_dg].round(2), single_values[:peak_reactive_power_dg].round(2), phases])
|
181
|
-
@dg_profile_p.push([id_dg, 48,
|
182
|
-
@dg_profile_q.push([id_dg, 48,
|
182
|
+
@dg_profile_p.push([id_dg, 48, profiles_planning[:planning_profile_dg_active]])
|
183
|
+
@dg_profile_q.push([id_dg, 48, profiles_planning[:planning_profile_dg_reactive]])
|
183
184
|
@profile_dg_p_extended.push([id_dg, 8760, profiles[:yearly_profile_dg_active]])
|
184
185
|
@profile_dg_q_extended.push([id_dg, 8760, profiles[:yearly_profile_dg_reactive]])
|
185
|
-
if
|
186
|
+
if der_capacity[:storage] != nil && der_capacity[:storage] > 0
|
186
187
|
@customers.push([coordinates, id_batt, voltage_default, single_values[:peak_active_power_storage], single_values[:peak_reactive_power_storage], phases])
|
187
188
|
@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])
|
188
|
-
@profile_customer_q.push([id_batt, 48,
|
189
|
-
@profile_customer_p.push([id_batt, 48,
|
189
|
+
@profile_customer_q.push([id_batt, 48, profiles_planning[:planning_profile_storage_reactive]])
|
190
|
+
@profile_customer_p.push([id_batt, 48, profiles_planning[:planning_profile_storage_active]])
|
190
191
|
@profile_customer_p_ext.push([id_batt, 8760, profiles[:yearly_profile_storage_active]])
|
191
192
|
@profile_customer_q_ext.push([id_batt, 8760, profiles[:yearly_profile_storage_reactive]])
|
192
193
|
end
|
@@ -247,16 +248,16 @@ module URBANopt
|
|
247
248
|
conservative_factor = 0.8 # considered as a reasonable assumption, but this value could be changed
|
248
249
|
average_peak_folder = JSON.parse(File.read(@average_building_peak_catalog_path))
|
249
250
|
for i in 0..feature_file.length - 1
|
250
|
-
area = (feature_file[i]['floor_area']).round(2)
|
251
|
-
building_type = feature_file[i]['building_type'] #
|
251
|
+
area = feature_file[i].key?('floor_area') ? (feature_file[i]['floor_area']).round(2) : (feature_file[i]['floor_area_sqft']).round(2)
|
252
|
+
building_type = feature_file[i]['building_type'] #it specifies the type of building, sometimes it is directly the sub-type
|
252
253
|
counter = 0 # counter to find number of buildings type belonging to same "category"
|
253
254
|
average_peak_folder.each do |building_class|
|
254
|
-
if building_type == building_class[
|
255
|
+
if (building_type == building_class["building type"] || building_type == building_class["sub-type"])
|
255
256
|
average_peak = (building_class['average peak demand (kW/ft2)'].to_f * area).to_f.round(4) # finding the average peak considering the floor area of the bilding under consideration
|
256
257
|
average_peak_by_size[counter] = average_peak
|
257
258
|
floor_area[counter] = (building_class['floor_area (ft2)'] - area).abs # minimum difference among area and area from the prototypes defined by DOE
|
258
259
|
counter += 1
|
259
|
-
|
260
|
+
# in this way I don t consider residential and I assume it s average_peak = 0, it is ok because we assume always 1 node per RES consumers, single-detached family houses
|
260
261
|
end
|
261
262
|
end
|
262
263
|
if counter > 1
|
@@ -272,100 +273,108 @@ module URBANopt
|
|
272
273
|
average_peak = mixed_use_av_peak # average peak per mixed use considering the building types which are in this building
|
273
274
|
area = area_mixed_use
|
274
275
|
end
|
275
|
-
nodes_per_bldg = (average_peak / (@lv_limit[:three_phase] * @power_factor * conservative_factor)).to_f.ceil # computing number of nodes per building
|
276
|
-
if nodes_per_bldg > @max_num_lv_nodes #
|
276
|
+
nodes_per_bldg = ((average_peak / (@lv_limit[:three_phase] * @power_factor * conservative_factor)).to_f).ceil # computing number of nodes per building
|
277
|
+
if nodes_per_bldg > @max_num_lv_nodes #that it is equal to how it was before
|
277
278
|
nodes_per_bldg = 1
|
278
279
|
@medium_voltage = true
|
279
280
|
end
|
280
|
-
|
281
|
-
|
282
|
-
|
281
|
+
|
282
|
+
nodes_per_bldg += 1 # tacking into account the extra node for distributed generation and the battery
|
283
283
|
return nodes_per_bldg, area
|
284
284
|
end
|
285
285
|
|
286
|
+
# method to order profiles consistently
|
287
|
+
def profiles_planning_creation(profiles_planning, power, single_values, i, hours, power_factor)
|
288
|
+
profiles_planning[:planning_profile_cust_active][i] = power["REopt:Electricity:Load:Total(kw)"].to_f
|
289
|
+
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
|
+
profiles_planning[:planning_profile_dg_active][i] = power["REopt:ElectricityProduced:Total(kw)"].to_f
|
291
|
+
profiles_planning[:planning_profile_cust_reactive][i] = profiles_planning[:planning_profile_cust_active][i] * Math.tan(Math.acos(power_factor))
|
292
|
+
profiles_planning[:planning_profile_storage_reactive][i] = profiles_planning[:planning_profile_storage_active][i] * Math.tan(Math.acos(power_factor))
|
293
|
+
profiles_planning[:planning_profile_dg_reactive][i] = profiles_planning[:planning_profile_dg_active][i] * Math.tan(Math.acos(power_factor))
|
294
|
+
if profiles_planning[:planning_profile_cust_active][i] > single_values[:peak_active_power_cons]
|
295
|
+
single_values[:peak_active_power_cons] = profiles_planning[:planning_profile_cust_active][i]
|
296
|
+
single_values[:peak_reactive_power_cons] = single_values[:peak_active_power_cons] * Math.tan(Math.acos(power_factor))
|
297
|
+
end
|
298
|
+
if profiles_planning[:planning_profile_storage_active][i] > single_values[:peak_active_power_storage]
|
299
|
+
single_values[:peak_active_power_storage] = profiles_planning[:planning_profile_storage_active][i]
|
300
|
+
single_values[:peak_reactive_power_storage] = single_values[:peak_active_power_storage] * Math.tan(Math.acos(power_factor))
|
301
|
+
end
|
302
|
+
if profiles_planning[:planning_profile_dg_active][i] > single_values[:peak_active_power_dg]
|
303
|
+
single_values[:peak_active_power_dg] = profiles_planning[:planning_profile_dg_active][i]
|
304
|
+
single_values[:peak_reactive_power_dg] = single_values[:peak_active_power_dg] * (Math.tan(Math.acos(power_factor)))
|
305
|
+
end
|
306
|
+
return profiles_planning, single_values
|
307
|
+
end
|
308
|
+
|
286
309
|
# defining a method for the customers and generators files creation:
|
287
310
|
# obtaining all the needed input from each feature_report.csv file (active & apparent power and tot energy consumed and produced)
|
288
311
|
# and from each feature_report.json file (area, height, number of users, DG capacity)
|
289
312
|
# the method passes as arguments the urbanopt json and csv output file for each feature and the building coordinates previously calculated
|
290
313
|
# and the "extreme" hours used to plan the network
|
291
314
|
def prosumer_files_load(csv_feature_report, json_feature_report, building_map, building_nodes, hour)
|
292
|
-
|
315
|
+
# add variable to include how many timestep per hour, so the profiles become 48 * n_timestep_per_hour
|
316
|
+
n_timestep_per_hour = json_feature_report["timesteps_per_hour"].to_i
|
317
|
+
profiles_planning = Hash.new{|h, k| h[k] = Array.new(48*n_timestep_per_hour, 0)} # initializing each profile hash to 0 for the number of intervals considered for the planning of the network
|
318
|
+
profiles = Hash.new{|h, k| h[k] = []}
|
293
319
|
single_values = Hash.new(0)
|
294
320
|
@medium_voltage = false
|
295
|
-
hours =
|
296
|
-
feature_type = json_feature_report['program']['building_types'][0][
|
297
|
-
residential_building_types = 'Single-Family Detached'
|
321
|
+
hours = 24 * n_timestep_per_hour -1 # change name, maybe to intervals
|
322
|
+
feature_type = json_feature_report['program']['building_types'][0]["building_type"]
|
323
|
+
residential_building_types = ['Single-Family Detached', 'Single-Family Attached', 'Multifamily', 'Single-Family', 'Multifamily Detached (2 to 4 units)', 'Multifamily Detached (5 or more units)']
|
324
|
+
|
298
325
|
# finding the index where to start computing and saving the info, from the value of the "worst-case hour" for the max peak consumption of the district
|
326
|
+
# considering num timestep per hours and the fact that each day starts from 1 am
|
299
327
|
if residential_building_types.include? feature_type
|
300
|
-
profile_start_max = hour.hour_index_max_res - hour.peak_hour_max_res
|
301
|
-
profile_start_min = hour.hour_index_min_res - hour.peak_hour_min_res
|
328
|
+
profile_start_max = hour.hour_index_max_res - ((hour.peak_hour_max_res.split(':')[0].to_i + (hour.peak_hour_max_res.split(':')[1].to_i / 60)) * n_timestep_per_hour)
|
329
|
+
profile_start_min = hour.hour_index_min_res - ((hour.peak_hour_min_res.split(':')[0].to_i + (hour.peak_hour_min_res.split(':')[1].to_i / 60)) * n_timestep_per_hour)
|
302
330
|
else
|
303
|
-
profile_start_max = hour.hour_index_max_comm - hour.peak_hour_max_comm
|
304
|
-
profile_start_min = hour.hour_index_min_comm - hour.peak_hour_min_comm
|
331
|
+
profile_start_max = hour.hour_index_max_comm - ((hour.peak_hour_max_comm.split(':')[0].to_i + (hour.peak_hour_max_comm.split(':')[1].to_i / 60)) * n_timestep_per_hour)
|
332
|
+
profile_start_min = hour.hour_index_min_comm - ((hour.peak_hour_min_comm.split(':')[0].to_i + (hour.peak_hour_min_comm.split(':')[1].to_i / 60)) * n_timestep_per_hour)
|
305
333
|
end
|
306
334
|
# finding the index where to start computing and saving the info, from the value of the "most extreme hours" for the max peak consumption of the district
|
307
|
-
#
|
308
|
-
#
|
309
|
-
|
310
|
-
i = 0 # to represent the 24 hours
|
335
|
+
k = 0 # index for each hour of the year represented in the csv file
|
336
|
+
i = hours +1 # to represent the 24 hours in case of max_net_generation day
|
337
|
+
j = 0 # to represent the 24 hours in case of peak_demand_day
|
311
338
|
h_cons_batt = 0
|
312
339
|
h_dg_max = 0 # hour with max DG generation
|
313
340
|
h_stor_max = 0 # hour with max storage absorption
|
314
341
|
max_peak = 0
|
315
|
-
# content = CSV.foreach(csv_feature_report, headers: true) do |power|
|
316
342
|
CSV.foreach(csv_feature_report, headers: true) do |power|
|
317
|
-
@power_factor = power[
|
318
|
-
profiles[:yearly_profile_cust_active].push(power[
|
343
|
+
@power_factor = power["Electricity:Facility Power(kW)"].to_f / power["Electricity:Facility Apparent Power(kVA)"].to_f
|
344
|
+
profiles[:yearly_profile_cust_active].push(power["REopt:Electricity:Load:Total(kw)"].to_f)
|
319
345
|
profiles[:yearly_profile_cust_reactive].push(profiles[:yearly_profile_cust_active][k] * Math.tan(Math.acos(@power_factor)))
|
320
|
-
profiles[:yearly_profile_dg_active].push(power[
|
346
|
+
profiles[:yearly_profile_dg_active].push(power["REopt:ElectricityProduced:Total(kw)"].to_f)
|
321
347
|
profiles[:yearly_profile_dg_reactive].push(profiles[:yearly_profile_dg_active][k] * Math.tan(Math.acos(@power_factor)))
|
322
348
|
profiles[:yearly_profile_storage_active].push(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)
|
323
349
|
profiles[:yearly_profile_storage_reactive].push(profiles[:yearly_profile_storage_active][k] * Math.tan(Math.acos(@power_factor)))
|
324
|
-
single_values[:energy] += power[
|
325
|
-
single_values[:energy_dg] += power[
|
350
|
+
single_values[:energy] += power["REopt:Electricity:Load:Total(kw)"].to_f # calculating the yearly energy consumed by each feature
|
351
|
+
single_values[:energy_dg] += power["REopt:ElectricityProduced:Total(kw)"].to_f
|
326
352
|
single_values[:energy_storage] += 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
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
profiles[:planning_profile_storage_active].push((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))
|
335
|
-
if profiles[:planning_profile_storage_active][i] > single_values[:peak_active_power_storage]
|
336
|
-
single_values[:peak_active_power_storage] = profiles[:planning_profile_storage_active][i]
|
337
|
-
single_values[:peak_reactive_power_storage] = single_values[:peak_active_power_storage] * Math.tan(Math.acos(power_factor))
|
338
|
-
single_values[:h_stor_max] = i
|
339
|
-
end
|
340
|
-
profiles[:planning_profile_dg_active].push(power['REopt:ElectricityProduced:Total(kw)'].to_f)
|
341
|
-
if profiles[:planning_profile_dg_active][i] > single_values[:peak_active_power_dg]
|
342
|
-
single_values[:peak_active_power_dg] = profiles[:planning_profile_dg_active][i]
|
343
|
-
single_values[:peak_reactive_power_dg] = single_values[:peak_active_power_dg] * Math.tan(Math.acos(power_factor))
|
344
|
-
single_values[:h_dg_max] = i
|
345
|
-
end
|
346
|
-
profiles[:planning_profile_cust_reactive].push(profiles[:planning_profile_cust_active][i] * Math.tan(Math.acos(power_factor)))
|
347
|
-
profiles[:planning_profile_storage_reactive].push(profiles[:planning_profile_storage_active][i] * Math.tan(Math.acos(power_factor)))
|
348
|
-
profiles[:planning_profile_dg_reactive].push(profiles[:planning_profile_dg_active][i] * Math.tan(Math.acos(power_factor)))
|
349
|
-
i += 1
|
353
|
+
case k
|
354
|
+
when profile_start_min..profile_start_min + (hours)
|
355
|
+
profiles_planning, single_values = self.profiles_planning_creation(profiles_planning, power, single_values, i, hours, power_factor)
|
356
|
+
i+=1
|
357
|
+
when profile_start_max..profile_start_max + (hours)
|
358
|
+
profiles_planning, single_values = self.profiles_planning_creation(profiles_planning, power, single_values, j, hours, power_factor)
|
359
|
+
j+=1
|
350
360
|
end
|
351
|
-
k
|
361
|
+
k+=1
|
352
362
|
end
|
353
363
|
height = (json_feature_report['program']['maximum_roof_height_ft']).round(2)
|
354
364
|
users = json_feature_report['program']['number_of_residential_units']
|
355
|
-
der_capacity = sum_dg(json_feature_report['distributed_generation'])
|
365
|
+
der_capacity = self.sum_dg(json_feature_report['distributed_generation'])
|
356
366
|
if @only_lv_consumers
|
357
|
-
nodes_per_bldg, area = av_peak_cons_per_building_type(json_feature_report['program']['building_types'])
|
358
|
-
if @
|
359
|
-
construct_prosumer_general(profiles, single_values, building_map, area, height, users, der_capacity)
|
367
|
+
nodes_per_bldg, area = self.av_peak_cons_per_building_type(json_feature_report['program']['building_types'])
|
368
|
+
if @max_num_nodes == 1
|
369
|
+
self.construct_prosumer_general(profiles, profiles_planning, single_values, building_map, area, height, users, der_capacity)
|
360
370
|
else
|
361
|
-
construct_prosumer_lv(nodes_per_bldg, profiles, single_values, building_map, building_nodes, area, height, users, der_capacity)
|
371
|
+
self.construct_prosumer_lv(nodes_per_bldg, profiles, profiles_planning, single_values, building_map, building_nodes, area, height, users, der_capacity)
|
362
372
|
end
|
363
373
|
else
|
364
|
-
|
365
|
-
area = json_feature_report['program'].key?('floor_area') ? (json_feature_report['program']['floor_area']).round(2) : (json_feature_report['program']['floor_area_sqft']).round(2)
|
374
|
+
area = json_feature_report['program'].has_key?('floor_area') ? (json_feature_report['program']['floor_area']).round(2) : (json_feature_report['program']['floor_area_sqft']).round(2)
|
366
375
|
# associating 2 nodes (consumers & DG and battery in the same node) per building considering the consumer, the battery and DG
|
367
|
-
construct_prosumer_general(profiles, single_values, building_map, area, height, users, der_capacity)
|
368
|
-
end
|
376
|
+
self.construct_prosumer_general(profiles, profiles_planning, single_values, building_map, area, height, users, der_capacity)
|
377
|
+
end
|
369
378
|
end
|
370
379
|
end
|
371
380
|
end
|
@@ -73,23 +73,23 @@ module URBANopt
|
|
73
73
|
(0..@commercial_consumption.length - 1).each do |j|
|
74
74
|
if @commercial_consumption[j] > max_net_load_comm
|
75
75
|
max_net_load_comm = @commercial_consumption[j]
|
76
|
-
@peak_hour_max_comm = (@time[j].split(' ')[1])
|
76
|
+
@peak_hour_max_comm = (@time[j].split(' ')[1]) # defined the most-stressing scenario
|
77
77
|
@hour_index_max_comm = j
|
78
78
|
end
|
79
79
|
if @commercial_consumption[j] < min_net_load_comm
|
80
80
|
min_net_load_comm = @commercial_consumption[j]
|
81
|
-
@peak_hour_min_comm = (@time[j].split(' ')[1])
|
81
|
+
@peak_hour_min_comm = (@time[j].split(' ')[1]) # defined the most-stressing scenario
|
82
82
|
@hour_index_min_comm = j
|
83
83
|
end
|
84
84
|
|
85
85
|
if @res_consumption[j] > max_net_load_res
|
86
86
|
max_net_load_res = @res_consumption[j]
|
87
|
-
@peak_hour_max_res = (@time[j].split(' ')[1])
|
87
|
+
@peak_hour_max_res = (@time[j].split(' ')[1]) # defined the most-stressing scenario
|
88
88
|
@hour_index_max_res = j
|
89
89
|
end
|
90
90
|
if @res_consumption[j] < min_net_load_res
|
91
91
|
min_net_load_res = @res_consumption[j]
|
92
|
-
@peak_hour_min_res = (@time[j].split(' ')[1])
|
92
|
+
@peak_hour_min_res = (@time[j].split(' ')[1]) # defined the most-stressing scenario
|
93
93
|
@hour_index_min_res = j
|
94
94
|
end
|
95
95
|
end
|
@@ -98,12 +98,12 @@ module URBANopt
|
|
98
98
|
(0..@commercial_consumption.length - 1).each do |j|
|
99
99
|
if @commercial_consumption[j] > max_net_load_comm
|
100
100
|
max_net_load_comm = @commercial_consumption[j]
|
101
|
-
@peak_hour_max_comm = (@time[j].split(' ')[1])
|
101
|
+
@peak_hour_max_comm = (@time[j].split(' ')[1]) # defined the most-stressing scenario
|
102
102
|
@hour_index_max_comm = j
|
103
103
|
end
|
104
104
|
if @res_consumption[j] > max_net_load_res
|
105
105
|
max_net_load_res = @res_consumption[j]
|
106
|
-
@peak_hour_max_res = (@time[j].split(' ')[1])
|
106
|
+
@peak_hour_max_res = (@time[j].split(' ')[1]) # defined the most-stressing scenario
|
107
107
|
@hour_index_max_res = j
|
108
108
|
end
|
109
109
|
end
|
@@ -112,7 +112,7 @@ module URBANopt
|
|
112
112
|
def aggregate_consumption(file_csv, file_json, n_feature)
|
113
113
|
feature_type = file_json['program']['building_types'][0]['building_type']
|
114
114
|
# residential_building_types = "Single-Family Detached" #add the other types
|
115
|
-
residential_building_types = ['Single-Family Detached', 'Single-Family Attached', '
|
115
|
+
residential_building_types = ['Single-Family Detached', 'Single-Family Attached', 'Multifamily', 'Single-Family', 'Multifamily Detached (2 to 4 units)', 'Multifamily Detached (5 or more units)'] # add the other types
|
116
116
|
puts feature_type
|
117
117
|
j = 0
|
118
118
|
CSV.foreach(file_csv, headers: true) do |power|
|
data/lib/urbanopt/rnm/version.rb
CHANGED
data/urbanopt-rnm-us-gem.gemspec
CHANGED
@@ -23,10 +23,11 @@ Gem::Specification.new do |spec|
|
|
23
23
|
spec.require_paths = ['lib', 'catalogs']
|
24
24
|
spec.required_ruby_version = '~> 2.7.0'
|
25
25
|
|
26
|
-
spec.add_dependency '
|
27
|
-
spec.add_dependency '
|
28
|
-
spec.add_dependency '
|
29
|
-
spec.add_dependency '
|
26
|
+
spec.add_dependency 'certified', '~> 1'
|
27
|
+
spec.add_dependency 'faraday', '~> 1.0.1'
|
28
|
+
spec.add_dependency 'geoutm', '~> 1.0.2'
|
29
|
+
spec.add_dependency 'rubyzip', '~> 2.3.2'
|
30
|
+
spec.add_dependency 'urbanopt-geojson', '~> 0.7.0'
|
30
31
|
|
31
32
|
spec.add_development_dependency 'bundler', '~> 2.1'
|
32
33
|
spec.add_development_dependency 'rake', '~> 13.0'
|
metadata
CHANGED
@@ -1,28 +1,42 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: urbanopt-rnm-us
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Katherine Fleming
|
8
8
|
- Luca de Rosa
|
9
|
-
autorequire:
|
9
|
+
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2021-07
|
12
|
+
date: 2021-12-07 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: certified
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '1'
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "~>"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '1'
|
14
28
|
- !ruby/object:Gem::Dependency
|
15
29
|
name: faraday
|
16
30
|
requirement: !ruby/object:Gem::Requirement
|
17
31
|
requirements:
|
18
|
-
- -
|
32
|
+
- - "~>"
|
19
33
|
- !ruby/object:Gem::Version
|
20
34
|
version: 1.0.1
|
21
35
|
type: :runtime
|
22
36
|
prerelease: false
|
23
37
|
version_requirements: !ruby/object:Gem::Requirement
|
24
38
|
requirements:
|
25
|
-
- -
|
39
|
+
- - "~>"
|
26
40
|
- !ruby/object:Gem::Version
|
27
41
|
version: 1.0.1
|
28
42
|
- !ruby/object:Gem::Dependency
|
@@ -43,30 +57,30 @@ dependencies:
|
|
43
57
|
name: rubyzip
|
44
58
|
requirement: !ruby/object:Gem::Requirement
|
45
59
|
requirements:
|
46
|
-
- -
|
60
|
+
- - "~>"
|
47
61
|
- !ruby/object:Gem::Version
|
48
|
-
version: 2.3.
|
62
|
+
version: 2.3.2
|
49
63
|
type: :runtime
|
50
64
|
prerelease: false
|
51
65
|
version_requirements: !ruby/object:Gem::Requirement
|
52
66
|
requirements:
|
53
|
-
- -
|
67
|
+
- - "~>"
|
54
68
|
- !ruby/object:Gem::Version
|
55
|
-
version: 2.3.
|
69
|
+
version: 2.3.2
|
56
70
|
- !ruby/object:Gem::Dependency
|
57
71
|
name: urbanopt-geojson
|
58
72
|
requirement: !ruby/object:Gem::Requirement
|
59
73
|
requirements:
|
60
74
|
- - "~>"
|
61
75
|
- !ruby/object:Gem::Version
|
62
|
-
version: 0.
|
76
|
+
version: 0.7.0
|
63
77
|
type: :runtime
|
64
78
|
prerelease: false
|
65
79
|
version_requirements: !ruby/object:Gem::Requirement
|
66
80
|
requirements:
|
67
81
|
- - "~>"
|
68
82
|
- !ruby/object:Gem::Version
|
69
|
-
version: 0.
|
83
|
+
version: 0.7.0
|
70
84
|
- !ruby/object:Gem::Dependency
|
71
85
|
name: bundler
|
72
86
|
requirement: !ruby/object:Gem::Requirement
|
@@ -164,6 +178,7 @@ files:
|
|
164
178
|
- CHANGELOG.md
|
165
179
|
- CONTRIBUTING.md
|
166
180
|
- Gemfile
|
181
|
+
- Jenkinsfile
|
167
182
|
- LICENSE.md
|
168
183
|
- README.md
|
169
184
|
- Rakefile
|
@@ -195,7 +210,7 @@ files:
|
|
195
210
|
homepage: https://github.com/urbanopt/urbanopt-RNM-us-gem
|
196
211
|
licenses: []
|
197
212
|
metadata: {}
|
198
|
-
post_install_message:
|
213
|
+
post_install_message:
|
199
214
|
rdoc_options: []
|
200
215
|
require_paths:
|
201
216
|
- lib
|
@@ -212,7 +227,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
212
227
|
version: '0'
|
213
228
|
requirements: []
|
214
229
|
rubygems_version: 3.1.4
|
215
|
-
signing_key:
|
230
|
+
signing_key:
|
216
231
|
specification_version: 4
|
217
232
|
summary: Library to create input files for RNM-US
|
218
233
|
test_files: []
|