urbanopt-rnm-us 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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: []
|