urbanopt-rnm-us 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/lib/urbanopt/rnm/consumers.rb +6 -5
- data/lib/urbanopt/rnm/prosumers.rb +93 -85
- data/lib/urbanopt/rnm/scenario_report.rb +6 -6
- data/lib/urbanopt/rnm/version.rb +1 -1
- data/urbanopt-rnm-us-gem.gemspec +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f7601a8b6f72052bb8b84d726e2bf579d955b00a69fe4087400f89934ed68ea2
|
4
|
+
data.tar.gz: cd015470a41029e2c0b9c811467ac0609b6fea56beb1f885ba9c86ddd13c1e1d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b823b847e4774a055c7923388a349aa7552527fb2974d5b061fcf820ffa71a4e77dfb4c5b874f72d1223e3c4da9d5877d3a1b8353b5e08ae5a0aed925bdef55e
|
7
|
+
data.tar.gz: d62a00a1f4869153476975a00299d730d16dde5789f8ab114395846b2e466dc47dabb378821c1f008b2eddccede50af2f3bcd26e1e81afa5f677ba14fb6e5dcc
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## Version 0.1.1
|
4
|
+
|
5
|
+
Date Range 07/22/21 - 10/28/21
|
6
|
+
|
7
|
+
- Fixed [#10]( https://github.com/urbanopt/urbanopt-rnm-us-gem/issues/7 ), Fix peak profile generation for prosumer profiles
|
8
|
+
- 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
|
9
|
+
- Fixed [#10]( https://github.com/urbanopt/urbanopt-rnm-us-gem/issues/10 ), Add other residential types to prosumer/consumer calculations
|
10
|
+
|
3
11
|
## Version 0.1.0
|
4
12
|
|
5
13
|
Initial version of the RNM-US gem.
|
@@ -175,7 +175,7 @@ module URBANopt
|
|
175
175
|
conservative_factor = 0.8 # considered as a reasonable assumption, but this value could be changed
|
176
176
|
average_peak_folder = JSON.parse(File.read(@average_building_peak_catalog_path))
|
177
177
|
for i in 0..feature_file.length - 1
|
178
|
-
area = (feature_file[i]['floor_area']).round(2)
|
178
|
+
area = feature_file[i].has_key?('floor_area') ? (feature_file[i]['floor_area']).round(2) : feature_file[i]['floor_area_sqft'].round(2)
|
179
179
|
building_type = feature_file[i]['building_type'] # it specifies the type of building, sometimes it is directly the sub-type
|
180
180
|
counter = 0 # counter to find number of buildings type belonging to same "category"
|
181
181
|
average_peak_folder.each do |building_class|
|
@@ -214,16 +214,17 @@ module URBANopt
|
|
214
214
|
# the method passes as arguments the urbanopt json and csv output file for each feature and the building coordinates previously calculated
|
215
215
|
# and the "extreme" hour used to plan the network
|
216
216
|
def customer_files_load(csv_feature_report, json_feature_report, building_map, building_nodes, hour)
|
217
|
+
n_timestep_per_hour = json_feature_report["timesteps_per_hour"].to_i
|
217
218
|
profiles = Hash.new { |h, k| h[k] = [] }
|
218
219
|
single_values = Hash.new(0)
|
219
|
-
hours =
|
220
|
+
hours = 24 * n_timestep_per_hour -1
|
220
221
|
feature_type = json_feature_report['program']['building_types'][0]['building_type']
|
221
|
-
residential_building_types =
|
222
|
+
residential_building_types = ["Single-Family Detached", "Single-Family Attached", "Multifamily"]
|
222
223
|
# 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
224
|
if residential_building_types.include? feature_type
|
224
|
-
profile_start_max = hour.hour_index_max_res - hour.peak_hour_max_res
|
225
|
+
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
226
|
else
|
226
|
-
profile_start_max = hour.hour_index_max_comm - hour.peak_hour_max_comm
|
227
|
+
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
228
|
end
|
228
229
|
k = 0 # index for each hour of the year represented in the csv file
|
229
230
|
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,107 @@ 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 =
|
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"]
|
298
324
|
# 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
|
325
|
+
# considering num timestep per hours and the fact that each day starts from 1 am
|
299
326
|
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
|
327
|
+
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)
|
328
|
+
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
329
|
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
|
330
|
+
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)
|
331
|
+
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
332
|
end
|
306
333
|
# 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
|
334
|
+
k = 0 # index for each hour of the year represented in the csv file
|
335
|
+
i = hours +1 # to represent the 24 hours in case of max_net_generation day
|
336
|
+
j = 0 # to represent the 24 hours in case of peak_demand_day
|
311
337
|
h_cons_batt = 0
|
312
338
|
h_dg_max = 0 # hour with max DG generation
|
313
339
|
h_stor_max = 0 # hour with max storage absorption
|
314
340
|
max_peak = 0
|
315
|
-
# content = CSV.foreach(csv_feature_report, headers: true) do |power|
|
316
341
|
CSV.foreach(csv_feature_report, headers: true) do |power|
|
317
|
-
@power_factor = power[
|
318
|
-
profiles[:yearly_profile_cust_active].push(power[
|
342
|
+
@power_factor = power["Electricity:Facility Power(kW)"].to_f / power["Electricity:Facility Apparent Power(kVA)"].to_f
|
343
|
+
profiles[:yearly_profile_cust_active].push(power["REopt:Electricity:Load:Total(kw)"].to_f)
|
319
344
|
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[
|
345
|
+
profiles[:yearly_profile_dg_active].push(power["REopt:ElectricityProduced:Total(kw)"].to_f)
|
321
346
|
profiles[:yearly_profile_dg_reactive].push(profiles[:yearly_profile_dg_active][k] * Math.tan(Math.acos(@power_factor)))
|
322
347
|
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
348
|
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[
|
349
|
+
single_values[:energy] += power["REopt:Electricity:Load:Total(kw)"].to_f # calculating the yearly energy consumed by each feature
|
350
|
+
single_values[:energy_dg] += power["REopt:ElectricityProduced:Total(kw)"].to_f
|
326
351
|
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
|
352
|
+
case k
|
353
|
+
when profile_start_min..profile_start_min + (hours)
|
354
|
+
profiles_planning, single_values = self.profiles_planning_creation(profiles_planning, power, single_values, i, hours, power_factor)
|
355
|
+
i+=1
|
356
|
+
when profile_start_max..profile_start_max + (hours)
|
357
|
+
profiles_planning, single_values = self.profiles_planning_creation(profiles_planning, power, single_values, j, hours, power_factor)
|
358
|
+
j+=1
|
350
359
|
end
|
351
|
-
k
|
360
|
+
k+=1
|
352
361
|
end
|
353
362
|
height = (json_feature_report['program']['maximum_roof_height_ft']).round(2)
|
354
363
|
users = json_feature_report['program']['number_of_residential_units']
|
355
|
-
der_capacity = sum_dg(json_feature_report['distributed_generation'])
|
364
|
+
der_capacity = self.sum_dg(json_feature_report['distributed_generation'])
|
356
365
|
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)
|
366
|
+
nodes_per_bldg, area = self.av_peak_cons_per_building_type(json_feature_report['program']['building_types'])
|
367
|
+
if @max_num_nodes == 1
|
368
|
+
self.construct_prosumer_general(profiles, profiles_planning, single_values, building_map, area, height, users, der_capacity)
|
360
369
|
else
|
361
|
-
construct_prosumer_lv(nodes_per_bldg, profiles, single_values, building_map, building_nodes, area, height, users, der_capacity)
|
370
|
+
self.construct_prosumer_lv(nodes_per_bldg, profiles, profiles_planning, single_values, building_map, building_nodes, area, height, users, der_capacity)
|
362
371
|
end
|
363
372
|
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)
|
373
|
+
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
374
|
# 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
|
375
|
+
self.construct_prosumer_general(profiles, profiles_planning, single_values, building_map, area, height, users, der_capacity)
|
376
|
+
end
|
369
377
|
end
|
370
378
|
end
|
371
379
|
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
|
data/lib/urbanopt/rnm/version.rb
CHANGED
data/urbanopt-rnm-us-gem.gemspec
CHANGED
@@ -26,7 +26,7 @@ Gem::Specification.new do |spec|
|
|
26
26
|
spec.add_dependency 'faraday', '1.0.1'
|
27
27
|
spec.add_dependency 'geoutm', '~>1.0.2'
|
28
28
|
spec.add_dependency 'rubyzip', '2.3.0'
|
29
|
-
spec.add_dependency 'urbanopt-geojson', '~> 0.6.
|
29
|
+
spec.add_dependency 'urbanopt-geojson', '~> 0.6.6'
|
30
30
|
|
31
31
|
spec.add_development_dependency 'bundler', '~> 2.1'
|
32
32
|
spec.add_development_dependency 'rake', '~> 13.0'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: urbanopt-rnm-us
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Katherine Fleming
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2021-
|
12
|
+
date: 2021-10-29 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: faraday
|
@@ -59,14 +59,14 @@ dependencies:
|
|
59
59
|
requirements:
|
60
60
|
- - "~>"
|
61
61
|
- !ruby/object:Gem::Version
|
62
|
-
version: 0.6.
|
62
|
+
version: 0.6.6
|
63
63
|
type: :runtime
|
64
64
|
prerelease: false
|
65
65
|
version_requirements: !ruby/object:Gem::Requirement
|
66
66
|
requirements:
|
67
67
|
- - "~>"
|
68
68
|
- !ruby/object:Gem::Version
|
69
|
-
version: 0.6.
|
69
|
+
version: 0.6.6
|
70
70
|
- !ruby/object:Gem::Dependency
|
71
71
|
name: bundler
|
72
72
|
requirement: !ruby/object:Gem::Requirement
|