Class: URBANopt::RNM::Prosumers

Inherits:
Object
  • Object
show all
Defined in:
lib/urbanopt/rnm/prosumers.rb

Overview

creating a class that creates the consumers input required by the RNM-US model, according to their geographic location, energy consumption and peak demand, and power consumption profiles

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(reopt, only_lv_consumers = false, max_num_lv_nodes, average_building_peak_catalog_path, lv_limit) ⇒ Prosumers

initializing all the attributes to build the inputs files required by the RNM-US model



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/urbanopt/rnm/prosumers.rb', line 16

def initialize(reopt, only_lv_consumers = false, max_num_lv_nodes, average_building_peak_catalog_path, lv_limit)
  @reopt = reopt
  @average_building_peak_catalog_path = average_building_peak_catalog_path
  @only_lv_consumers = only_lv_consumers
  @max_num_lv_nodes = max_num_lv_nodes
  @customers = []
  @customers_ext = []
  @profile_date_time = []
  @profile_customer_p = []
  @profile_customer_q = []
  @profile_date_time_ext = []
  @profile_customer_p_ext = []
  @profile_customer_q_ext = []
  @dg = []
  @dg_profile_p = []
  @dg_profile_q = []
  @profile_dg_p_extended = []
  @profile_dg_q_extended = []
  @power_factor = power_factor
  @lv_limit = lv_limit
end

Instance Attribute Details

#customersObject

Returns the value of attribute customers.



13
14
15
# File 'lib/urbanopt/rnm/prosumers.rb', line 13

def customers
  @customers
end

#customers_extObject

Returns the value of attribute customers_ext.



13
14
15
# File 'lib/urbanopt/rnm/prosumers.rb', line 13

def customers_ext
  @customers_ext
end

#dgObject

Returns the value of attribute dg.



13
14
15
# File 'lib/urbanopt/rnm/prosumers.rb', line 13

def dg
  @dg
end

#dg_profile_pObject

Returns the value of attribute dg_profile_p.



13
14
15
# File 'lib/urbanopt/rnm/prosumers.rb', line 13

def dg_profile_p
  @dg_profile_p
end

#dg_profile_qObject

Returns the value of attribute dg_profile_q.



13
14
15
# File 'lib/urbanopt/rnm/prosumers.rb', line 13

def dg_profile_q
  @dg_profile_q
end

#power_factorObject

Returns the value of attribute power_factor.



13
14
15
# File 'lib/urbanopt/rnm/prosumers.rb', line 13

def power_factor
  @power_factor
end

#profile_customer_pObject

Returns the value of attribute profile_customer_p.



13
14
15
# File 'lib/urbanopt/rnm/prosumers.rb', line 13

def profile_customer_p
  @profile_customer_p
end

#profile_customer_p_extObject

Returns the value of attribute profile_customer_p_ext.



13
14
15
# File 'lib/urbanopt/rnm/prosumers.rb', line 13

def profile_customer_p_ext
  @profile_customer_p_ext
end

#profile_customer_qObject

Returns the value of attribute profile_customer_q.



13
14
15
# File 'lib/urbanopt/rnm/prosumers.rb', line 13

def profile_customer_q
  @profile_customer_q
end

#profile_customer_q_extObject

Returns the value of attribute profile_customer_q_ext.



13
14
15
# File 'lib/urbanopt/rnm/prosumers.rb', line 13

def profile_customer_q_ext
  @profile_customer_q_ext
end

#profile_date_timeObject

Returns the value of attribute profile_date_time.



13
14
15
# File 'lib/urbanopt/rnm/prosumers.rb', line 13

def profile_date_time
  @profile_date_time
end

#profile_date_time_extObject

Returns the value of attribute profile_date_time_ext.



13
14
15
# File 'lib/urbanopt/rnm/prosumers.rb', line 13

def profile_date_time_ext
  @profile_date_time_ext
end

#profile_dg_p_extendedObject

Returns the value of attribute profile_dg_p_extended.



13
14
15
# File 'lib/urbanopt/rnm/prosumers.rb', line 13

def profile_dg_p_extended
  @profile_dg_p_extended
end

#profile_dg_q_extendedObject

Returns the value of attribute profile_dg_q_extended.



13
14
15
# File 'lib/urbanopt/rnm/prosumers.rb', line 13

def profile_dg_q_extended
  @profile_dg_q_extended
end

Instance Method Details

#av_peak_cons_per_building_type(feature_file) ⇒ Object

creating a method to define the number of nodes for each building in case the user set the option “only LV” to true. this method calculates the number of nodes for each building in the project and in case the numb of nodes is higher than 4 than the building is considered as a single node connected in MV the numb of nodes is calculated based on the average_peak_catalog which is obtained from DOE open_source data per location and per building type



219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
# File 'lib/urbanopt/rnm/prosumers.rb', line 219

def av_peak_cons_per_building_type(feature_file)
  average_peak_by_size = []
  floor_area = []
  average_peak = 5 # defining a random value first, since now the residential buildings are not considered in the catalog
  mixed_use_av_peak = 0
  area_mixed_use = 0
  # defining a conservative factor which creates some margin with the number of nodes found using the av_peak catalog, with the
  # actual nodes that could be found with the current buildings peak consumptions in the project
  conservative_factor = 0.8 # considered as a reasonable assumption, but this value could be changed
  average_peak_folder = JSON.parse(File.read(@average_building_peak_catalog_path))
  for i in 0..feature_file.length - 1
    area = feature_file[i].key?('floor_area') ? (feature_file[i]['floor_area']).round(2) : (feature_file[i]['floor_area_sqft']).round(2)
    building_type = feature_file[i]['building_type'] # it specifies the type of building, sometimes it is directly the sub-type
    counter = 0 # counter to find number of buildings type belonging to same "category"
    average_peak_folder.each do |building_class|
      if building_type == building_class['building type'] || building_type == building_class['sub-type']
        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
        average_peak_by_size[counter] = average_peak
        floor_area[counter] = (building_class['floor_area (ft2)'] - area).abs # minimum difference among area and area from the prototypes defined by DOE
        counter += 1
        # 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
      end
    end
    if counter > 1
      index = floor_area.index(floor_area.min)
      average_peak = average_peak_by_size[index]
    end
    if feature_file.length > 1 # defined for Mixed_use buildings, which include more building types
      mixed_use_av_peak += average_peak
      area_mixed_use += area
    end
  end
  if feature_file.length > 1
    average_peak = mixed_use_av_peak # average peak per mixed use considering the building types which are in this building
    area = area_mixed_use
  end
  nodes_per_bldg = (average_peak / (@lv_limit[:three_phase] * @power_factor * conservative_factor)).to_f.ceil # computing number of nodes per building
  if nodes_per_bldg > @max_num_lv_nodes # that it is equal to how it was before
    nodes_per_bldg = 1
    @medium_voltage = true
  end

  nodes_per_bldg += 1 # tacking into account the extra node for distributed generation and the battery
  return nodes_per_bldg, area
end

#construct_prosumer_general(profiles, profiles_planning, single_values, building_map, area, height, users, der_capacity) ⇒ Object

method defined for the case of a single node where both battery, DG and consumers are placed evaluation of the peak power in each node to define the type of connection (e.g. voltage level and n phases)



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/urbanopt/rnm/prosumers.rb', line 44

def construct_prosumer_general(profiles, profiles_planning, single_values, building_map, area, height, users, der_capacity)
  id = building_map[3]
  id_dg = "#{building_map[3]}_DG"
  id_batt = "#{building_map[3]}_battery"
  building_map.pop # deleting the last_element of the list which represents the id
  peak_app_power_node = 0
  # defining the max peak in the hour with consumption max peak
  # in the hour with generation max peak
  # in the hour with storage max peak

  for i in 0..profiles_planning[:planning_profile_cust_active].length - 1
    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
    if hourly_app_power > peak_app_power_node
      peak_app_power_node = hourly_app_power
    end
  end
  # creating the customer text files (treating also the battery as a consumer) & the DG text file
  if @medium_voltage
    voltage_default = 12.47
    phases = 3
  else
    voltage_default, phases = voltage_values(peak_app_power_node / 0.9) # margin to consider 0.9 for safety reasons
  end
  @customers.push([building_map, id, voltage_default, single_values[:peak_active_power_cons], single_values[:peak_reactive_power_cons], phases])
  @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])
  @profile_date_time.push([profiles_planning[:planning_date_time]])
  @profile_customer_q.push([id, 48, profiles_planning[:planning_profile_cust_reactive]])
  @profile_customer_p.push([id, 48, profiles_planning[:planning_profile_cust_active]])
  @profile_date_time_ext.push([profiles[:yearly_date_time]])
  @profile_customer_p_ext.push([id, 8760, profiles[:yearly_profile_cust_active]])
  @profile_customer_q_ext.push([id, 8760, profiles[:yearly_profile_cust_reactive]])

  if !der_capacity[:storage].nil? && der_capacity[:storage] > 0
    @customers.push([building_map, id_batt, voltage_default, single_values[:peak_active_power_storage], single_values[:peak_reactive_power_storage], phases])
    @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])
    @profile_date_time.push([profiles_planning[:planning_date_time]])
    @profile_customer_q.push([id_batt, 48, profiles_planning[:planning_profile_storage_reactive]])
    @profile_customer_p.push([id_batt, 48, profiles_planning[:planning_profile_storage_active]])
    @profile_date_time_ext.push([profiles[:yearly_date_time]])
    @profile_customer_p_ext.push([id_batt, 8760, profiles[:yearly_profile_storage_active]])
    @profile_customer_q_ext.push([id_batt, 8760, profiles[:yearly_profile_storage_reactive]])
  end
  @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])
  @dg_profile_p.push([id_dg, 48, profiles_planning[:planning_profile_dg_active]])
  @dg_profile_q.push([id_dg, 48, profiles_planning[:planning_profile_dg_reactive]])
  @profile_dg_p_extended.push([id_dg, 8760, profiles[:yearly_profile_dg_active]])
  @profile_dg_q_extended.push([id_dg, 8760, profiles[:yearly_profile_dg_reactive]])
end

#construct_prosumer_lv(nodes_per_bldg = 0, profiles, profiles_planning, single_values, building_map, building_nodes, area, height, users, der_capacity) ⇒ Object

this method is called only if the user sets the option of “only LV nodes” to true defining a certain numb of nodes for each building and distributing the peak power values equally among the nodes of each building



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/urbanopt/rnm/prosumers.rb', line 101

def construct_prosumer_lv(nodes_per_bldg = 0, profiles, profiles_planning, single_values, building_map, building_nodes, area, height, users, der_capacity)
  # the default variables are defined (i.e. type and rurality type)
  planning_date_time = []
  planning_profile_node_active = []
  planning_profile_node_reactive = []
  yearly_date_time = []
  yearly_profile_node_active = []
  yearly_profile_node_reactive = []
  closest_node = building_map[3].split('_')[1].to_i # refers to the closest node of the building in consideration to the street
  node = closest_node
  cont = 1
  cont_reverse = 1
  nodes_consumers = nodes_per_bldg - 1

  for i in 1..nodes_per_bldg
    coordinates = building_map
    node = closest_node + cont # to set the new nodes with enough distance among each others
    node_reverse = closest_node - cont_reverse
    if i > 1 && node <= building_nodes.length - 2
      coordinates = building_nodes[node] # take the closest building node index to the street and pass the nodes after it
      cont += 1
    elsif i > 1
      coordinates = building_nodes[node_reverse]
      cont_reverse += 1
    end
    # this condition is used to firstly place the building consumption nodes and then the last node
    # to be placed is the one referred to DG and battery for the building
    if i < nodes_per_bldg # considering the consumers nodes
      id = coordinates[3]
      coordinates.pop
      peak_active_power_cons = (single_values[:peak_active_power_cons] / nodes_consumers).round(2)
      peak_reactive_power_cons = (single_values[:peak_reactive_power_cons] / nodes_consumers).round(2)
      voltage_default, phases = voltage_values(peak_active_power_cons / @power_factor)
      for k in 0..profiles_planning[:planning_profile_cust_active].length - 1
        planning_date_time[k]=profiles_planning[:planning_date_time][k]
        planning_profile_node_active[k] = (profiles_planning[:planning_profile_cust_active][k] / nodes_consumers).round(2)
        planning_profile_node_reactive[k] = (profiles_planning[:planning_profile_cust_reactive][k] / nodes_consumers).round(2)
      end
      for k in 0..profiles[:yearly_profile_cust_active].length - 1
        yearly_date_time[k]=profiles[:yearly_date_time][k]
        yearly_profile_node_active[k] = (profiles[:yearly_profile_cust_active][k] / nodes_consumers).round(2)
        yearly_profile_node_reactive[k] = (profiles[:yearly_profile_cust_reactive][k] / nodes_consumers).round(2)
      end
      @customers.push([coordinates, id, voltage_default, peak_active_power_cons, peak_reactive_power_cons, phases])
      @customers_ext.push([coordinates, id, voltage_default, peak_active_power_cons, peak_reactive_power_cons, phases, area, height, (single_values[:energy] / nodes_consumers).round(2), peak_active_power_cons, peak_reactive_power_cons, users])
      @profile_date_time.push([planning_date_time])
      @profile_customer_q.push([id, 48, planning_profile_node_reactive])
      @profile_customer_p.push([id, 48, planning_profile_node_active])
      @profile_date_time_ext.push([yearly_date_time])
      @profile_customer_p_ext.push([id, 8760, yearly_profile_node_active])
      @profile_customer_q_ext.push([id, 8760, yearly_profile_node_reactive])
    else
      # considering the DG and battery
      voltage_default, phases = voltage_values(der_capacity[:dg]) # assuming that the pv capacity is always higher than battery capacity
      id_dg = "#{coordinates[3]}_DG"
      id_batt = "#{coordinates[3]}_battery"
      coordinates.pop
      @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])
      @dg_profile_p.push([id_dg, 48, profiles_planning[:planning_profile_dg_active]])
      @dg_profile_q.push([id_dg, 48, profiles_planning[:planning_profile_dg_reactive]])
      @profile_dg_p_extended.push([id_dg, 8760, profiles[:yearly_profile_dg_active]])
      @profile_dg_q_extended.push([id_dg, 8760, profiles[:yearly_profile_dg_reactive]])
      if !der_capacity[:storage].nil? && der_capacity[:storage] > 0
        @customers.push([coordinates, id_batt, voltage_default, single_values[:peak_active_power_storage], single_values[:peak_reactive_power_storage], phases])
        @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])
        @profile_date_time.push([profiles[:planning_date_time]])
        @profile_customer_q.push([id_batt, 48, profiles_planning[:planning_profile_storage_reactive]])
        @profile_customer_p.push([id_batt, 48, profiles_planning[:planning_profile_storage_active]])
        @profile_date_time_ext.push([profiles[:yearly_date_time]])
        @profile_customer_p_ext.push([id_batt, 8760, profiles[:yearly_profile_storage_active]])
        @profile_customer_q_ext.push([id_batt, 8760, profiles[:yearly_profile_storage_reactive]])
      end
    end
  end
end

#profiles_planning_creation(profiles_planning, power, single_values, i, hours, power_factor) ⇒ Object

method to order profiles consistently



266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
# File 'lib/urbanopt/rnm/prosumers.rb', line 266

def profiles_planning_creation(profiles_planning, power, single_values, i, hours, power_factor)
  profiles_planning[:planning_date_time][i]=power['Datetime']
  profiles_planning[:planning_profile_cust_active][i] = power['REopt:Electricity:Load:Total(kw)'].to_f
  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
  profiles_planning[:planning_profile_dg_active][i] = power['REopt:ElectricityProduced:Total(kw)'].to_f
  profiles_planning[:planning_profile_cust_reactive][i] = profiles_planning[:planning_profile_cust_active][i] * Math.tan(Math.acos(power_factor))
  profiles_planning[:planning_profile_storage_reactive][i] = profiles_planning[:planning_profile_storage_active][i] * Math.tan(Math.acos(power_factor))
  profiles_planning[:planning_profile_dg_reactive][i] = profiles_planning[:planning_profile_dg_active][i] * Math.tan(Math.acos(power_factor))
  if profiles_planning[:planning_profile_cust_active][i] > single_values[:peak_active_power_cons]
    single_values[:peak_active_power_cons] = profiles_planning[:planning_profile_cust_active][i]
    single_values[:peak_reactive_power_cons] = single_values[:peak_active_power_cons] * Math.tan(Math.acos(power_factor))
  end
  if profiles_planning[:planning_profile_storage_active][i] > single_values[:peak_active_power_storage]
    single_values[:peak_active_power_storage] = profiles_planning[:planning_profile_storage_active][i]
    single_values[:peak_reactive_power_storage] = single_values[:peak_active_power_storage] * Math.tan(Math.acos(power_factor))
  end
  if profiles_planning[:planning_profile_dg_active][i] > single_values[:peak_active_power_dg]
    single_values[:peak_active_power_dg] = profiles_planning[:planning_profile_dg_active][i]
    single_values[:peak_reactive_power_dg] = single_values[:peak_active_power_dg] * Math.tan(Math.acos(power_factor))
  end
  return profiles_planning, single_values
end

#prosumer_files_load(csv_feature_report, json_feature_report, building_map, building_nodes, hour) ⇒ Object

defining a method for the customers and generators files creation: obtaining all the needed input from each feature_report.csv file (active & apparent power and tot energy consumed and produced) and from each feature_report.json file (area, height, number of users, DG capacity) the method passes as arguments the urbanopt json and csv output file for each feature and the building coordinates previously calculated and the “extreme” hours used to plan the network



294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
# File 'lib/urbanopt/rnm/prosumers.rb', line 294

def prosumer_files_load(csv_feature_report, json_feature_report, building_map, building_nodes, hour)
  # add variable to include how many timestep per hour, so the profiles become 48 * n_timestep_per_hour
  n_timestep_per_hour = json_feature_report['timesteps_per_hour'].to_i
  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
  profiles = Hash.new { |h, k| h[k] = [] }
  single_values = Hash.new(0)
  @medium_voltage = false
  hours = 24 * n_timestep_per_hour - 1 # change name, maybe to intervals
  feature_type = json_feature_report['program']['building_types'][0]['building_type']
  residential_building_types = ['Single-Family Detached', 'Single-Family Attached', 'Multifamily', 'Single-Family', 'Multifamily Detached (2 to 4 units)', 'Multifamily Detached (5 or more units)']

  # 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
  # considering num timestep per hours and the fact that each day starts from 1 am
  if residential_building_types.include? feature_type
    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)
    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)
  else
    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)
    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)
  end
  # 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
  k = 0 # index for each hour of the year represented in the csv file
  i = hours + 1 # to represent the 24 hours in case of max_net_generation day
  j = 0 # to represent the 24 hours in case of peak_demand_day
  h_cons_batt = 0
  h_dg_max = 0 # hour with max DG generation
  h_stor_max = 0 # hour with max storage absorption
  max_peak = 0
  CSV.foreach(csv_feature_report, headers: true) do |power|
    @power_factor = power['Electricity:Facility Power(kW)'].to_f / power['Electricity:Facility Apparent Power(kVA)'].to_f
    profiles[:yearly_date_time].push(power['Datetime'])
    profiles[:yearly_profile_cust_active].push(power['REopt:Electricity:Load:Total(kw)'].to_f)
    profiles[:yearly_profile_cust_reactive].push(profiles[:yearly_profile_cust_active][k] * Math.tan(Math.acos(@power_factor)))
    profiles[:yearly_profile_dg_active].push(power['REopt:ElectricityProduced:Total(kw)'].to_f)
    profiles[:yearly_profile_dg_reactive].push(profiles[:yearly_profile_dg_active][k] * Math.tan(Math.acos(@power_factor)))
    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)
    profiles[:yearly_profile_storage_reactive].push(profiles[:yearly_profile_storage_active][k] * Math.tan(Math.acos(@power_factor)))
    single_values[:energy] += power['REopt:Electricity:Load:Total(kw)'].to_f # calculating the yearly energy consumed by each feature
    single_values[:energy_dg] += power['REopt:ElectricityProduced:Total(kw)'].to_f
    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
    case k
    when profile_start_min..profile_start_min + hours
      profiles_planning, single_values = profiles_planning_creation(profiles_planning, power, single_values, i, hours, power_factor)
      i += 1
    when profile_start_max..profile_start_max + hours
      profiles_planning, single_values = profiles_planning_creation(profiles_planning, power, single_values, j, hours, power_factor)
      j += 1
    end
    k += 1
  end
  height = (json_feature_report['program']['maximum_roof_height_ft']).round(2)
  users = json_feature_report['program']['number_of_residential_units']
  der_capacity = sum_dg(json_feature_report['distributed_generation'])
  if @only_lv_consumers
    nodes_per_bldg, area = av_peak_cons_per_building_type(json_feature_report['program']['building_types'])
    if @max_num_nodes == 1
      construct_prosumer_general(profiles, profiles_planning, single_values, building_map, area, height, users, der_capacity)
    else
      construct_prosumer_lv(nodes_per_bldg, profiles, profiles_planning, single_values, building_map, building_nodes, area, height, users, der_capacity)
    end
  else
    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)
    # associating 2 nodes (consumers & DG and battery in the same node) per building considering the consumer, the battery and DG
    construct_prosumer_general(profiles, profiles_planning, single_values, building_map, area, height, users, der_capacity)
  end
end

#sum_dg(dg) ⇒ Object

defining a method to calculate the total sum of DG and battery capacity for each building in the district



200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/urbanopt/rnm/prosumers.rb', line 200

def sum_dg(dg)
  capacity = Hash.new(0)
  for i in 0..dg['solar_pv'].length - 1
    capacity[:dg] += dg['solar_pv'][i]['size_kw'].to_f.round(2)
  end
  for i in 0..dg['wind'].length - 1
    capacity[:dg] += dg['wind'][i]['size_kw'].to_f.round(2)
  end
  for i in 0..dg['generator'].length - 1
    capacity[:dg] += dg['generator'][i]['size_kw'].to_f.round(2)
  end
  capacity[:storage] = dg['total_storage_kw']
  return capacity
end

#voltage_values(peak_apparent_power) ⇒ Object

creating a function that for each node defines the connection (e.g LV, MV, single-phase, 3-phase) according to the catalog limits previously calculated



179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# File 'lib/urbanopt/rnm/prosumers.rb', line 179

def voltage_values(peak_apparent_power)
  case peak_apparent_power
    when -10000..@lv_limit[:single_phase] # set by the catalog limits
      phases = 1
      voltage_default = 0.12
    when @lv_limit[:single_phase]..@lv_limit[:three_phase] # defined from the catalog (from the wires)
      phases = 3
      voltage_default = 0.48
    # MV and 3 phases untill 16 MVA defined by SMART-DS project
    when @lv_limit[:three_phase]..16000
      phases = 3
      voltage_default = 12.47
    else
      # HV and 3 phases for over 16 MVA
      phases = 3
      voltage_default = 69
  end
  return voltage_default, phases
end