urbanopt-rnm-us 0.1.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 +7 -0
- data/.gitignore +32 -0
- data/.rubocop.yml +9 -0
- data/CHANGELOG.md +5 -0
- data/CONTRIBUTING.md +58 -0
- data/Gemfile +10 -0
- data/LICENSE.md +38 -0
- data/README.md +49 -0
- data/Rakefile +166 -0
- data/catalogs/average_peak_per_building_type.json +94 -0
- data/catalogs/extended_catalog.json +15109 -0
- data/lib/urbanopt-rnm.rb +41 -0
- data/lib/urbanopt/rnm.rb +59 -0
- data/lib/urbanopt/rnm/api_client.rb +296 -0
- data/lib/urbanopt/rnm/capacitor_opendss.rb +57 -0
- data/lib/urbanopt/rnm/carson_eq.rb +389 -0
- data/lib/urbanopt/rnm/consumers.rb +255 -0
- data/lib/urbanopt/rnm/conversion_to_opendss.rb +152 -0
- data/lib/urbanopt/rnm/geojson_input.rb +261 -0
- data/lib/urbanopt/rnm/input_files.rb +335 -0
- data/lib/urbanopt/rnm/logger.rb +52 -0
- data/lib/urbanopt/rnm/oh_ug_rate.rb +129 -0
- data/lib/urbanopt/rnm/post_processor.rb +220 -0
- data/lib/urbanopt/rnm/processor_opendss_catalog.rb +73 -0
- data/lib/urbanopt/rnm/prosumers.rb +372 -0
- data/lib/urbanopt/rnm/rnm_us_catalog_conversion.rb +140 -0
- data/lib/urbanopt/rnm/runner.rb +177 -0
- data/lib/urbanopt/rnm/scenario_report.rb +145 -0
- data/lib/urbanopt/rnm/substation_location.rb +63 -0
- data/lib/urbanopt/rnm/transformer_opendss.rb +67 -0
- data/lib/urbanopt/rnm/version.rb +45 -0
- data/lib/urbanopt/rnm/wires_class.rb +70 -0
- data/lib/urbanopt/rnm/wires_opendss.rb +93 -0
- data/urbanopt-rnm-us-gem.gemspec +37 -0
- metadata +218 -0
@@ -0,0 +1,152 @@
|
|
1
|
+
# *********************************************************************************
|
2
|
+
# URBANopt (tm), Copyright (c) 2019-2021, Alliance for Sustainable Energy, LLC, and other
|
3
|
+
# contributors. All rights reserved.
|
4
|
+
|
5
|
+
# Redistribution and use in source and binary forms, with or without modification,
|
6
|
+
# are permitted provided that the following conditions are met:
|
7
|
+
|
8
|
+
# Redistributions of source code must retain the above copyright notice, this list
|
9
|
+
# of conditions and the following disclaimer.
|
10
|
+
|
11
|
+
# Redistributions in binary form must reproduce the above copyright notice, this
|
12
|
+
# list of conditions and the following disclaimer in the documentation and/or other
|
13
|
+
# materials provided with the distribution.
|
14
|
+
|
15
|
+
# Neither the name of the copyright holder nor the names of its contributors may be
|
16
|
+
# used to endorse or promote products derived from this software without specific
|
17
|
+
# prior written permission.
|
18
|
+
|
19
|
+
# Redistribution of this software, without modification, must refer to the software
|
20
|
+
# by the same designation. Redistribution of a modified version of this software
|
21
|
+
# (i) may not refer to the modified version by the same designation, or by any
|
22
|
+
# confusingly similar designation, and (ii) must refer to the underlying software
|
23
|
+
# originally provided by Alliance as "URBANopt". Except to comply with the foregoing,
|
24
|
+
# the term "URBANopt", or any confusingly similar designation may not be used to
|
25
|
+
# refer to any modified version of this software or any modified version of the
|
26
|
+
# underlying software originally provided by Alliance without the prior written
|
27
|
+
# consent of Alliance.
|
28
|
+
|
29
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
30
|
+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
31
|
+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
32
|
+
# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
33
|
+
# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
34
|
+
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
35
|
+
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
36
|
+
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
37
|
+
# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
38
|
+
# OF THE POSSIBILITY OF SUCH DAMAGE.
|
39
|
+
# *********************************************************************************
|
40
|
+
|
41
|
+
require 'json'
|
42
|
+
module URBANopt
|
43
|
+
module RNM
|
44
|
+
# class created to convert the extended catalog into the OpenDSS catalog to be used by the OpenDSS Gem
|
45
|
+
class ConversionToOpendssCatalog
|
46
|
+
attr_accessor :hash
|
47
|
+
|
48
|
+
def initialize(extended_catalog_path)
|
49
|
+
@extended_catalog_path = extended_catalog_path
|
50
|
+
@hash = hash
|
51
|
+
end
|
52
|
+
|
53
|
+
# method to convert initial SI units in the ext catalog into Imperial units used by the Carson equation
|
54
|
+
def convert_to_imperial_units(hash)
|
55
|
+
hash_new = {}
|
56
|
+
hash.each do |k, v|
|
57
|
+
if k.include? '(mm)'
|
58
|
+
mm_ft = 0.00328
|
59
|
+
hash_new[k] = (v * mm_ft).round(5)
|
60
|
+
elsif k.include? '(ohm/km)'
|
61
|
+
km_miles = 0.6214
|
62
|
+
hash_new[k] = (v / km_miles).round(2)
|
63
|
+
elsif k.include? '(m)'
|
64
|
+
m_ft = 3.281
|
65
|
+
hash_new[k] = (v * m_ft).round(2)
|
66
|
+
else
|
67
|
+
hash_new[k] = hash[k]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
return hash_new
|
71
|
+
end
|
72
|
+
|
73
|
+
def create_catalog(save_path)
|
74
|
+
@hash = {}
|
75
|
+
component = []
|
76
|
+
catalog = JSON.parse(File.read(@extended_catalog_path))
|
77
|
+
i = 0
|
78
|
+
z = 0
|
79
|
+
|
80
|
+
catalog.each do |key, value|
|
81
|
+
case key
|
82
|
+
when 'SUBSTATIONS AND DISTRIBUTION TRANSFORMERS'
|
83
|
+
transformers = URBANopt::RNM::ProcessorOpendss.new
|
84
|
+
component = []
|
85
|
+
for ii in 0..catalog[key].length - 1 # assessing each type of transformer (Urban, Inter)
|
86
|
+
catalog[key][ii].each do |k, v|
|
87
|
+
# calling a method to verify that the same transformers are not repeated in the OpenDSS catalog
|
88
|
+
transformers.process_data(catalog[key][ii][k])
|
89
|
+
end
|
90
|
+
end
|
91
|
+
for i in 0..transformers.list.length - 1
|
92
|
+
trafo = URBANopt::RNM::Transformers.new
|
93
|
+
component.push(trafo.create(transformers.list[i]))
|
94
|
+
end
|
95
|
+
@hash['transformers_properties'] = component
|
96
|
+
when 'CAPACITORS'
|
97
|
+
capacitors = URBANopt::RNM::ProcessorOpendss.new
|
98
|
+
component = []
|
99
|
+
catalog[key].each do |k, v|
|
100
|
+
# calling a method to verify that the same capacitors are not repeated in the OpenDSS catalog
|
101
|
+
capacitors.process_data(catalog[key][k])
|
102
|
+
for i in 0..capacitors.list.length - 1
|
103
|
+
capacitor = URBANopt::RNM::Capacitor.new
|
104
|
+
component.push(capacitor.create(capacitors.list[i]))
|
105
|
+
end
|
106
|
+
end
|
107
|
+
@hash['capacitor_properties'] = component
|
108
|
+
when 'LINES'
|
109
|
+
@conductors = URBANopt::RNM::ProcessorOpendss.new
|
110
|
+
component = []
|
111
|
+
for ii in 1..catalog[key].length - 1
|
112
|
+
catalog[key][ii].each do |k, v| # assessing if interurban section, urban section, etc.
|
113
|
+
for jj in 0..catalog[key][ii][k].length - 1 # assessing each power line
|
114
|
+
catalog[key][ii][k][jj].each do |attribute, values|
|
115
|
+
if attribute == 'Line geometry'
|
116
|
+
# calling a method to verify that the same lines are not repeated in the OpenDSS catalog
|
117
|
+
@conductors.process_data(catalog[key][ii][k][jj][attribute])
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
key = 'WIRES'
|
127
|
+
component = []
|
128
|
+
updated_value = 0
|
129
|
+
i = 0 # counter for all the lines in the OpenDSS catalog
|
130
|
+
catalog[key].each do |k, v|
|
131
|
+
for jj in 0..@conductors.cont - 1
|
132
|
+
for ii in 0..catalog[key][k].length - 1
|
133
|
+
if @conductors.list[jj]['wire'] == catalog[key][k][ii]['nameclass']
|
134
|
+
@conductors.list[jj] = convert_to_imperial_units(@conductors.list[jj])
|
135
|
+
updated_value = convert_to_imperial_units(catalog[key][k][ii])
|
136
|
+
wire = URBANopt::RNM::WiresOpendss.new
|
137
|
+
component.push(wire.create(@conductors.list[jj], updated_value))
|
138
|
+
i += 1
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
@hash['wires'] = component
|
144
|
+
|
145
|
+
# save to save_path
|
146
|
+
File.open(save_path, 'w') do |f|
|
147
|
+
f.write(JSON.pretty_generate(@hash))
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
@@ -0,0 +1,261 @@
|
|
1
|
+
# *********************************************************************************
|
2
|
+
# URBANopt (tm), Copyright (c) 2019-2021, Alliance for Sustainable Energy, LLC, and other
|
3
|
+
# contributors. All rights reserved.
|
4
|
+
|
5
|
+
# Redistribution and use in source and binary forms, with or without modification,
|
6
|
+
# are permitted provided that the following conditions are met:
|
7
|
+
|
8
|
+
# Redistributions of source code must retain the above copyright notice, this list
|
9
|
+
# of conditions and the following disclaimer.
|
10
|
+
|
11
|
+
# Redistributions in binary form must reproduce the above copyright notice, this
|
12
|
+
# list of conditions and the following disclaimer in the documentation and/or other
|
13
|
+
# materials provided with the distribution.
|
14
|
+
|
15
|
+
# Neither the name of the copyright holder nor the names of its contributors may be
|
16
|
+
# used to endorse or promote products derived from this software without specific
|
17
|
+
# prior written permission.
|
18
|
+
|
19
|
+
# Redistribution of this software, without modification, must refer to the software
|
20
|
+
# by the same designation. Redistribution of a modified version of this software
|
21
|
+
# (i) may not refer to the modified version by the same designation, or by any
|
22
|
+
# confusingly similar designation, and (ii) must refer to the underlying software
|
23
|
+
# originally provided by Alliance as "URBANopt". Except to comply with the foregoing,
|
24
|
+
# the term "URBANopt", or any confusingly similar designation may not be used to
|
25
|
+
# refer to any modified version of this software or any modified version of the
|
26
|
+
# underlying software originally provided by Alliance without the prior written
|
27
|
+
# consent of Alliance.
|
28
|
+
|
29
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
30
|
+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
31
|
+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
32
|
+
# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
33
|
+
# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
34
|
+
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
35
|
+
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
36
|
+
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
37
|
+
# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
38
|
+
# OF THE POSSIBILITY OF SUCH DAMAGE.
|
39
|
+
# *********************************************************************************
|
40
|
+
|
41
|
+
require 'geoutm'
|
42
|
+
require 'json'
|
43
|
+
|
44
|
+
module URBANopt
|
45
|
+
module RNM
|
46
|
+
# creating a class to process parse and process the geographic information for buildings, streets and substations
|
47
|
+
class GeojsonInput
|
48
|
+
# Set constants
|
49
|
+
UG_RATIO_DEFAULT = 0.9
|
50
|
+
ONLY_LV_CONSUMERS_DEFAULT = true
|
51
|
+
MAX_LV_NODES_DEFAULT = 1
|
52
|
+
|
53
|
+
# defining a method to set each street nodes to a uniform distance among eachothers, valid for both streets and buildings
|
54
|
+
# the method is passing as arguments the hash with each feature info from the geojson file, the latitude and longitude to be converted to UTM,
|
55
|
+
# the array containing the already processed nodes, the index defining the position of the lat and lon passed in this method
|
56
|
+
# and the index defining the reached position in the array with the processed nodes
|
57
|
+
# this method returns the array with the processed nodes and its index
|
58
|
+
def coordinates(hash, lat, lon, coordinates, k, i)
|
59
|
+
lat_lon = GeoUtm::LatLon.new(lat, lon)
|
60
|
+
z = 0 # default value for surface elevation
|
61
|
+
uniform_distance = 15 # values set as uniform distance among nodes
|
62
|
+
utm = lat_lon.to_utm # converting latitude and longitude to UTM
|
63
|
+
x_utm = utm.e.round(2) # UTM x-distance from the origin
|
64
|
+
y_utm = utm.n.round(2) # UTM y-distance from the origin
|
65
|
+
identifier = hash['properties']['id']
|
66
|
+
# creating streetmap nodes every 10 m for each road, considering the angle of each road
|
67
|
+
if k != 0
|
68
|
+
distance_y = y_utm - coordinates[i - 1][1]
|
69
|
+
distance_x = x_utm - coordinates[i - 1][0]
|
70
|
+
distance = (distance_x**2 + distance_y**2)**0.5
|
71
|
+
intervals = (distance / uniform_distance).to_i
|
72
|
+
# creating variables for x, y for each node, with the right street inclination
|
73
|
+
x_uniform = uniform_distance * (distance_x / distance)
|
74
|
+
y_uniform = uniform_distance * (distance_y / distance)
|
75
|
+
n = 1 # counter to keep track when the number of intervals for each "distnce" is reached
|
76
|
+
# creating nodes in the coordinates array with the right street inclination and uniform distance among each others
|
77
|
+
while n <= intervals
|
78
|
+
id = identifier.to_s + "_#{i}"
|
79
|
+
coordinates[i] = (coordinates[i - 1][0] + x_uniform).round(2), (coordinates[i - 1][1] + y_uniform).round(2), z, id
|
80
|
+
i += 1
|
81
|
+
n += 1
|
82
|
+
end
|
83
|
+
# when the last interval of each road is reached, the last node values are given as the streets coordinates
|
84
|
+
# imported from the street.json file
|
85
|
+
id = identifier.to_s + "_#{i}"
|
86
|
+
coordinates[i] = x_utm.round(2), y_utm.round(2), z, id
|
87
|
+
i += 1
|
88
|
+
else
|
89
|
+
# in the 1st node of each road, the coordinates are tacken directly from the streetmap.json file
|
90
|
+
id = identifier.to_s + "_#{i}"
|
91
|
+
coordinates[i] = x_utm, y_utm, z, id
|
92
|
+
i += 1
|
93
|
+
end
|
94
|
+
return coordinates, i
|
95
|
+
end
|
96
|
+
|
97
|
+
# defining a method to find the coordinates of the closest node of each building building to the closest street, to be used for the customers_ext.txt file
|
98
|
+
# the method receives as arguments every nodes of 1 building and the array containing all the street nodes computed before
|
99
|
+
# and it returns the coordinates and id of the closest node of the building to the street
|
100
|
+
## The new algorithm developed calculates an approximate distance: (x+y)/2, of each building-node with each street-node and compares it with the "minimum_distance"
|
101
|
+
# this approximate distance has been defined in order to be able to disregard all the distances which are greater than the "minimum distance" computed until that moment, without being required to compute the Pithagorean Theorem, which requires a long computational time.
|
102
|
+
# Therefore (x+y)/2 has been computed knowing that: if the minimum length of the hypothenuse of a right triangle is when the triangle is isosceles so when the hyphothenuse (d) is equal to d = sqrt(2)*x (where x is the distance among the nodes on the x-axis),
|
103
|
+
# so we can assume that x = (x+y)/2, than if d = sqrt(2)*((x+y)/2) > (x+y)/2 > minimum_distance
|
104
|
+
# than it confirmes that x and y can be disregarded and there is no need to compute the real distance for that node since the approximate distance value (which represents the minimum possible distance for the sum of those catheti)
|
105
|
+
# is greater than the minimum_distance that it is been compared with.
|
106
|
+
# This process it is iterated for all the distances among the building-nodes with the street-nodes until an approximate distance (x+y)/2 is lower than the minimum distance computed until that moment
|
107
|
+
# and in that case the real distance with the Pythagorean Theorem is computed and compared with the minimum distance.
|
108
|
+
def consumer_coordinates(building, street)
|
109
|
+
dist_min = 5000 # assuming a first fitticious minimum distance that will be replaced later on by the real minimum distance
|
110
|
+
# iterating the distance among each node of each street and each node of each building until the minimum distance is found
|
111
|
+
for j in 0..building.length - 1 # assessing each building node of the considered building
|
112
|
+
for i in 0..street.length - 1 # assessing each street node
|
113
|
+
y = building[j][1] - street[i][1] # calculating the distance on the y-axis
|
114
|
+
x = building[j][0] - street[i][0] # calculating the distance on the x-axis
|
115
|
+
distance_approx = (x + y) / 2 # finding the "approximate" distance of each building node with each street node, in order to reduce computational time (considering that if the sum of the 2 cathets divided by 2 is lower than the minimum distance, than the real distance of this building node to the closest street-node will be further processed to see if it can be a "candidate" for the minimum distance)
|
116
|
+
if distance_approx < dist_min # if the the new distance found is lower than the minimum one than the real distance considering this building-node and this street-node will be computed
|
117
|
+
distance = (x**2 + y**2)**0.5 # the real distance between the building node and the street node is computed
|
118
|
+
if distance < dist_min # if the new distance is lower than the minimum distance found until that moment, than this new "distance" value will be set as the minimum distance between the building node and the street node
|
119
|
+
dist_min = distance
|
120
|
+
chosen_coord = building[j] # assigning the node coordinates values and id of the building with the minimium distance to the street to chose_coord variable
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
return chosen_coord
|
126
|
+
end
|
127
|
+
|
128
|
+
# defining a method for parsing the coordinates of the substations to be passed in the RNM-US model
|
129
|
+
# choose the closes coord to the street or the one in the midle of the polygon since the sub is far away from district and streets
|
130
|
+
def sub_coordinates(points_coord, id)
|
131
|
+
x_utm = []
|
132
|
+
y_utm = []
|
133
|
+
for i in 0..points_coord.length - 1
|
134
|
+
lat_lon = GeoUtm::LatLon.new(points_coord[i][1], points_coord[i][0])
|
135
|
+
utm = lat_lon.to_utm # converting latitude and longitude to UTM
|
136
|
+
x_utm[i] = utm.e.round(2) # UTM x-distance from the origin
|
137
|
+
y_utm[i] = utm.n.round(2) # UTM y-distance from the origin
|
138
|
+
end
|
139
|
+
coord_sub = [(x_utm[0] + x_utm[2]) / 2, (y_utm[0] + y_utm[2]) / 2, 0, "sub_#{id}"]
|
140
|
+
return coord_sub
|
141
|
+
end
|
142
|
+
|
143
|
+
# creating a method passing the GeoJSON file from URBANopt as the argument to define options that can be modified by the user
|
144
|
+
# streets and building and primary substation coordinates
|
145
|
+
# and returning the street coordinates array, the building coordinates array and the tot number of buildings in the project
|
146
|
+
def coordinates_feature_hash(geojson_hash,scenario_features=[])
|
147
|
+
i = 0 # index representing the number of street_nodes
|
148
|
+
building_number = 0 # variable created to keep track the number of buildings in the project
|
149
|
+
street_number = 0 # variable created to keep track the number of streets in the project
|
150
|
+
substation_number = 0 # variable created to keep track the number of substations in the project
|
151
|
+
customers_coordinates = [] # array containing the coordinates and id of the closest node of each building to the street
|
152
|
+
street_coordinates = [] # array containing every street node coordinates and id
|
153
|
+
coordinates_buildings = [] # array containing every building node coordinates and id
|
154
|
+
building_ids = [] # array containing building_ids to retrieve urbanopt results later
|
155
|
+
building_floors = [] # array containing numb of floors for each building
|
156
|
+
substation_location = []
|
157
|
+
utm_zone = 0
|
158
|
+
streets = geojson_hash
|
159
|
+
puts "SCENARIO FEATURES: #{scenario_features}"
|
160
|
+
# parsing the options defined by the user to run the RNM-US with a certain % of UG cables and designing the network with only LV nodes
|
161
|
+
# to be consistent in case several case-studies are run to have an homogenous parameter on how to compare the same buildings with different energy consumption
|
162
|
+
# Use defaults and warn user if these fields are unset
|
163
|
+
if streets.key?('project') && streets['project'].key?('underground_cables_ratio')
|
164
|
+
ug_ratio = streets['project']['underground_cables_ratio'].to_f
|
165
|
+
puts "RNM-US gem INFO: using underground_cables_ratio of #{ug_ratio}"
|
166
|
+
else
|
167
|
+
ug_ratio = UG_RATIO_DEFAULT
|
168
|
+
puts "RNM-US gem WARNING: field ['project']['underground_cables_ratio'] not specified in Feature File...using default value of #{UG_RATIO_DEFAULT}"
|
169
|
+
end
|
170
|
+
if streets.key?('project') && streets['project'].key?('only_lv_consumers')
|
171
|
+
only_lv_consumers = streets['project']['only_lv_consumers']
|
172
|
+
puts "RNM-US gem INFO: using only_lv_consumers ratio of #{only_lv_consumers}"
|
173
|
+
else
|
174
|
+
only_lv_consumers = ONLY_LV_CONSUMERS_DEFAULT
|
175
|
+
puts "RNM-US gem WARNING: field ['project']['only_lv_consumers'] not specified in Feature File...using default value of #{ONLY_LV_CONSUMERS_DEFAULT}"
|
176
|
+
end
|
177
|
+
if streets.key?('project') && streets['project'].key?('max_number_of_lv_nodes_per_building')
|
178
|
+
max_num_lv_nodes = streets['project']['max_number_of_lv_nodes_per_building']
|
179
|
+
puts "RNM-US gem INFO: using at max #{max_num_lv_nodes} lv nodes per building"
|
180
|
+
else
|
181
|
+
max_num_lv_nodes = MAX_LV_NODES_DEFAULT
|
182
|
+
puts "RNM-US gem WARNING: field ['project']['max_number_of_lv_nodes_per_building'] not specified in Feature File...using default value of #{MAX_LV_NODES_DEFAULT}"
|
183
|
+
end
|
184
|
+
# each features (linestring, multilinestring and polygon) are processed in an external method, to create intermediate nodes
|
185
|
+
# for a better graphical representation of the district
|
186
|
+
# "Point" geometry is ignored (site origin feature)
|
187
|
+
# put error if there is not this info, and use default values
|
188
|
+
streets['features'].each do |street|
|
189
|
+
# the geojson file is read and according to the "type" of feature (linestring, multilinestring, polygon)
|
190
|
+
# a different loop is executed to fill every node coordinates in a specific array
|
191
|
+
if street['properties']['type'] == 'District System' && street['properties']['district_system_type'] == 'Electrical Substation'
|
192
|
+
substation_location[substation_number] = sub_coordinates(street['geometry']['coordinates'][0], street['properties']['id'])
|
193
|
+
substation_number += 1
|
194
|
+
elsif street['geometry']['type'] == 'LineString' && street['properties']['type'] == 'Road'
|
195
|
+
each_street = [] # defining an array for each street, that includes all the nodes coordinates for each street
|
196
|
+
i = 0 # index representing the number of street_nodes
|
197
|
+
for k in 0..street['geometry']['coordinates'].length - 1
|
198
|
+
each_street, i = coordinates(street, street['geometry']['coordinates'][k][1], street['geometry']['coordinates'][k][0], each_street, k, i)
|
199
|
+
end
|
200
|
+
street_coordinates[street_number] = each_street
|
201
|
+
street_number += 1
|
202
|
+
elsif street['geometry']['type'] == 'MultiLineString' && street['properties']['type'] == 'Road'
|
203
|
+
each_street = []
|
204
|
+
i = 0 # index representing the number of street_nodes
|
205
|
+
for k in 0..street['geometry']['coordinates'].length - 1
|
206
|
+
for j in 0..street['geometry']['coordinates'][k].length - 1
|
207
|
+
each_street, i = coordinates(street, street['geometry']['coordinates'][k][j][1], street['geometry']['coordinates'][k][j][0], each_street, j, i)
|
208
|
+
end
|
209
|
+
end
|
210
|
+
street_coordinates[street_number] = each_street
|
211
|
+
street_number += 1
|
212
|
+
elsif street['geometry']['type'] == 'Polygon' && street['properties']['type'] == 'Building' and scenario_features.include? street['properties']['id']
|
213
|
+
for k in 0..street['geometry']['coordinates'].length - 1
|
214
|
+
h = 0 # index representing number of nodes for each single building
|
215
|
+
building = [] # array containing every building node coordinates and id of 1 building
|
216
|
+
for j in 0..street['geometry']['coordinates'][k].length - 1
|
217
|
+
building, h = URBANopt::RNM::GeojsonInput.new.coordinates(street, street['geometry']['coordinates'][k][j][1], street['geometry']['coordinates'][k][j][0], building, j, h)
|
218
|
+
end
|
219
|
+
coordinates_buildings[building_number] = building # inserting in each index the nodes coordinates and id of each building
|
220
|
+
building_ids[building_number] = street['properties']['id']
|
221
|
+
building_floors[building_number] = street['properties']['number_of_stories']
|
222
|
+
if building_number == 0
|
223
|
+
utm = GeoUtm::LatLon.new(street['geometry']['coordinates'][k][j][1], street['geometry']['coordinates'][k][j][0]).to_utm
|
224
|
+
utm_zone = utm.zone
|
225
|
+
end
|
226
|
+
building_number += 1
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
# raise error if no streets were found
|
232
|
+
if street_number == 0
|
233
|
+
raise 'ERROR: No roads were found in the Feature File. Road locations must be in the Feature File for RNM analysis.'
|
234
|
+
end
|
235
|
+
|
236
|
+
street_type = []
|
237
|
+
for i in 0..street_number - 1
|
238
|
+
# creating a class to define when the lines in each street have to be considered OH or UG
|
239
|
+
street_type[i] = URBANopt::RNM::OhUgRate.new
|
240
|
+
# obtaining the average height for each street and numb of buildings in each street
|
241
|
+
street_type[i].height_building(coordinates_buildings, street_coordinates[i], building_floors)
|
242
|
+
end
|
243
|
+
street = []
|
244
|
+
jj = 0
|
245
|
+
# defining each street as "OH" or "UG" according to the threshold value obtained from the user's input
|
246
|
+
for i in 0..street_number - 1
|
247
|
+
street_type[i].classify_street_type(street_type, ug_ratio)
|
248
|
+
for j in 0..street_coordinates[i].length - 1
|
249
|
+
street[jj] = street_coordinates[i][j][0], street_coordinates[i][j][1], street_coordinates[i][j][2], street_coordinates[i][j][3], street_type[i].type
|
250
|
+
jj += 1
|
251
|
+
end
|
252
|
+
end
|
253
|
+
# an external method is called to find the coordinates of the closest node of each building to the street
|
254
|
+
for i in 0..building_number - 1
|
255
|
+
customers_coordinates[i] = consumer_coordinates(coordinates_buildings[i], street)
|
256
|
+
end
|
257
|
+
return street, customers_coordinates, coordinates_buildings, building_number, building_ids, substation_location, only_lv_consumers, max_num_lv_nodes, utm_zone # considering creating an hash as attribute
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|
@@ -0,0 +1,335 @@
|
|
1
|
+
# *********************************************************************************
|
2
|
+
# URBANopt (tm), Copyright (c) 2019-2021, Alliance for Sustainable Energy, LLC, and other
|
3
|
+
# contributors. All rights reserved.
|
4
|
+
|
5
|
+
# Redistribution and use in source and binary forms, with or without modification,
|
6
|
+
# are permitted provided that the following conditions are met:
|
7
|
+
|
8
|
+
# Redistributions of source code must retain the above copyright notice, this list
|
9
|
+
# of conditions and the following disclaimer.
|
10
|
+
|
11
|
+
# Redistributions in binary form must reproduce the above copyright notice, this
|
12
|
+
# list of conditions and the following disclaimer in the documentation and/or other
|
13
|
+
# materials provided with the distribution.
|
14
|
+
|
15
|
+
# Neither the name of the copyright holder nor the names of its contributors may be
|
16
|
+
# used to endorse or promote products derived from this software without specific
|
17
|
+
# prior written permission.
|
18
|
+
|
19
|
+
# Redistribution of this software, without modification, must refer to the software
|
20
|
+
# by the same designation. Redistribution of a modified version of this software
|
21
|
+
# (i) may not refer to the modified version by the same designation, or by any
|
22
|
+
# confusingly similar designation, and (ii) must refer to the underlying software
|
23
|
+
# originally provided by Alliance as "URBANopt". Except to comply with the foregoing,
|
24
|
+
# the term "URBANopt", or any confusingly similar designation may not be used to
|
25
|
+
# refer to any modified version of this software or any modified version of the
|
26
|
+
# underlying software originally provided by Alliance without the prior written
|
27
|
+
# consent of Alliance.
|
28
|
+
|
29
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
30
|
+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
31
|
+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
32
|
+
# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
33
|
+
# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
34
|
+
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
35
|
+
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
36
|
+
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
37
|
+
# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
38
|
+
# OF THE POSSIBILITY OF SUCH DAMAGE.
|
39
|
+
# *********************************************************************************
|
40
|
+
|
41
|
+
require 'csv'
|
42
|
+
require 'json'
|
43
|
+
require 'urbanopt/rnm/logger'
|
44
|
+
require 'urbanopt/geojson'
|
45
|
+
|
46
|
+
module URBANopt
|
47
|
+
module RNM
|
48
|
+
class InputFiles
|
49
|
+
##
|
50
|
+
# Initialize InputFiles attributes: +run_dir+, +feature_file_path+, +reopt+, +extended_catalog_path+, +average_building_peak_catalog_path+, +rnm_dirname+, +opendss_catalog+
|
51
|
+
##
|
52
|
+
# [parameters:]
|
53
|
+
# * +run_dir+ - _String_ - Full path to directory for simulation of this scenario
|
54
|
+
# * +feature_file_path+ - _String_ - Full path to GeoJSON feature file containing features and streets for simulation.
|
55
|
+
# * +extended_catalog_path+ - _String_ - Full path to the extended_catalog which include all the info about electric equipment and RNM-US parameters
|
56
|
+
# * +average_building_peak_catalog_path+ - _String_ - Full path to the catalog providing average peak building consumption per floor area and average floor area per building type
|
57
|
+
# * +reopt+ - _Boolean_ - Input command from the user to either include or not DG capabilities in planning the network, if REopt was ran before
|
58
|
+
# * +opendss_catalog+ - _Boolean_ - Input command from the user to either run or not the opendss_conversion_script to convert the extended_catalog in OpenDSS catalog
|
59
|
+
# * +rnm_dirname+ - _String_ - name of RNM-US directory that will contain the input files (within the scenario directory)
|
60
|
+
##
|
61
|
+
def initialize(run_dir, scenario_features, feature_file, extended_catalog_path, average_building_peak_catalog_path, reopt: false, opendss_catalog: true, rnm_dirname: 'rnm-us')
|
62
|
+
@run_dir = run_dir
|
63
|
+
@feature_file = feature_file
|
64
|
+
@scenario_features = scenario_features
|
65
|
+
@rnm_dirname = rnm_dirname
|
66
|
+
@extended_catalog_path = extended_catalog_path
|
67
|
+
@average_building_peak_catalog_path = average_building_peak_catalog_path
|
68
|
+
@reopt = reopt
|
69
|
+
@opendss_catalog = opendss_catalog
|
70
|
+
# initialize @@logger
|
71
|
+
@@logger ||= URBANopt::RNM.logger
|
72
|
+
|
73
|
+
# initialize RNM directory
|
74
|
+
if !Dir.exist?(File.join(@run_dir, @rnm_dirname))
|
75
|
+
FileUtils.mkdir_p(File.join(@run_dir, @rnm_dirname))
|
76
|
+
@@logger.info("Created directory: #{File.join(@run_dir, @rnm_dirname)}")
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# finding the limits on LV defined by the equipments in the catalog
|
81
|
+
def catalog_limits
|
82
|
+
catalog = JSON.parse(File.read(@extended_catalog_path))
|
83
|
+
limit = Hash.new(0)
|
84
|
+
limit_lines = Hash.new(0)
|
85
|
+
limit_trafo = Hash.new(0)
|
86
|
+
# evaluating first all the LV power lines included in the extended catalog and finding the LV 3-phase and single-phase
|
87
|
+
# lines with the highest capacity
|
88
|
+
catalog['LINES'][1].each do |key, v|
|
89
|
+
(0..catalog['LINES'][1][key].length - 1).each do |ii|
|
90
|
+
if catalog['LINES'][1][key][ii]['Voltage(kV)'] == '0.416'
|
91
|
+
if catalog['LINES'][1][key][ii]['Line geometry'][0]['phase'] != 'N'
|
92
|
+
wire = catalog['LINES'][1][key][ii]['Line geometry'][0]['wire']
|
93
|
+
else
|
94
|
+
wire = catalog['LINES'][1][key][ii]['Line geometry'][1]['wire']
|
95
|
+
end
|
96
|
+
jj = 0
|
97
|
+
jj += 1 while catalog['WIRES']['WIRES CATALOG'][jj]['nameclass'] != wire
|
98
|
+
current = catalog['WIRES']['WIRES CATALOG'][jj]['ampacity (A)'].to_i
|
99
|
+
if catalog['LINES'][1][key][ii]['Nphases'].to_i == 3
|
100
|
+
if ((current * (catalog['LINES'][1][key][ii]['Voltage(kV)']).to_f) * (3**0.5)) > limit_lines[:three_phase]
|
101
|
+
limit_lines[:three_phase] = ((current * (catalog['LINES'][1][key][ii]['Voltage(kV)']).to_f) * (3**0.5)).round(2)
|
102
|
+
end
|
103
|
+
else
|
104
|
+
if (current * (catalog['LINES'][1][key][ii]['Voltage(kV)']).to_f) > limit_lines[:single_phase]
|
105
|
+
limit_lines[:single_phase] = (current * (catalog['LINES'][1][key][ii]['Voltage(kV)']).to_f).round(2)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
# evaluating all the distribution transformers included in the extended catalog and finding 3-phase and single-phase
|
112
|
+
# distr. transformers with the highest capacity
|
113
|
+
(0..catalog['SUBSTATIONS AND DISTRIBUTION TRANSFORMERS'].length - 1).each do |k, v|
|
114
|
+
catalog['SUBSTATIONS AND DISTRIBUTION TRANSFORMERS'][k].each do |key, value|
|
115
|
+
if catalog['SUBSTATIONS AND DISTRIBUTION TRANSFORMERS'][k][key][0]['Voltage level'] == 'MV-LV'
|
116
|
+
(0..catalog['SUBSTATIONS AND DISTRIBUTION TRANSFORMERS'][k][key].length - 1).each do |i|
|
117
|
+
if catalog['SUBSTATIONS AND DISTRIBUTION TRANSFORMERS'][k][key][i]['Nphases'] == '3'
|
118
|
+
if (catalog['SUBSTATIONS AND DISTRIBUTION TRANSFORMERS'][k][key][i]['Guaranteed Power(kVA)'].to_i) > limit_trafo[:three_phase]
|
119
|
+
limit_trafo[:three_phase] = catalog['SUBSTATIONS AND DISTRIBUTION TRANSFORMERS'][k][key][i]['Guaranteed Power(kVA)'].to_i
|
120
|
+
end
|
121
|
+
else
|
122
|
+
if (catalog['SUBSTATIONS AND DISTRIBUTION TRANSFORMERS'][k][key][i]['Guaranteed Power(kVA)'].to_i) > limit_trafo[:single_phase]
|
123
|
+
limit_trafo[:single_phase] = catalog['SUBSTATIONS AND DISTRIBUTION TRANSFORMERS'][k][key][i]['Guaranteed Power(kVA)'].to_i
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
trafo_margin = catalog['OTHERS']['Margin of design of new facilities LV (100.0 = designs with double so that 50% is left over)'].to_i
|
131
|
+
limit_trafo[:single_phase] = limit_trafo[:single_phase] / (1 + (trafo_margin / 100))
|
132
|
+
limit_trafo[:three_phase] = limit_trafo[:three_phase] / (1 + (trafo_margin / 100))
|
133
|
+
# setting as the limit for single-phase and 3-phase the component with the lowest capacity
|
134
|
+
if limit_trafo[:three_phase] < limit_lines[:three_phase]
|
135
|
+
limit[:three_phase] = limit_trafo[:three_phase]
|
136
|
+
else
|
137
|
+
limit[:three_phase] = limit_lines[:three_phase]
|
138
|
+
end
|
139
|
+
if limit_trafo[:single_phase] < limit_lines[:single_phase]
|
140
|
+
limit[:single_phase] = limit_trafo[:single_phase]
|
141
|
+
else
|
142
|
+
limit[:single_phase] = limit_lines[:single_phase]
|
143
|
+
end
|
144
|
+
return limit
|
145
|
+
end
|
146
|
+
|
147
|
+
##
|
148
|
+
# Create the files that are required as input in RNM-US.
|
149
|
+
# (e.g. streetmapAS.txt, customers.txt, customers_ext.txt, customers_profiles_p.txt, customers_profiles_q.txt,
|
150
|
+
# customers_profiles_p_ext.txt, customers_profiles_q_ext.txt,substation_location.txt, generators.txt,
|
151
|
+
# generator_profiles_p.txt, generator_profiles_q.txt, generator_profiles_p_ext.txt, generator_profiles_q_ext.txt,
|
152
|
+
# ficheros_entrada.txt, ficheros_entrada_inc.txt, udcons.csv)
|
153
|
+
##
|
154
|
+
def create
|
155
|
+
# the GEOjson file is loaded and a method is called to extract the required information regarding the street, building and substation location
|
156
|
+
street_coordinates, customers_coordinates, coordinates_buildings, tot_buildings, building_ids, substation_location, only_lv_consumers, max_num_lv_nodes, utm_zone = URBANopt::RNM::GeojsonInput.new.coordinates_feature_hash(@feature_file, @scenario_features)
|
157
|
+
# puts("BUILDING IDS: #{building_ids}")
|
158
|
+
# define the LV/MV limit imposed by the components of the catalog: distr.transformers and power lines and exporting the utm_zone to the catalog
|
159
|
+
lv_limit = catalog_limits
|
160
|
+
# verifying if running RNM-US with REopt option
|
161
|
+
|
162
|
+
if @reopt
|
163
|
+
if !File.join(@run_dir, 'feature_optimization').nil?
|
164
|
+
scenario_report_path = File.join(@run_dir, 'feature_optimization')
|
165
|
+
# creating a class prosumers with all the info for all the DER and consumption for each building
|
166
|
+
prosumers = URBANopt::RNM::Prosumers.new(@reopt, only_lv_consumers, max_num_lv_nodes, @average_building_peak_catalog_path, lv_limit) # passing these 2 conditions to see what option did the user
|
167
|
+
else
|
168
|
+
raise 'scenario report is not found'
|
169
|
+
end
|
170
|
+
|
171
|
+
else
|
172
|
+
if !File.join(@run_dir, 'scenario_report').nil?
|
173
|
+
scenario_report_path = File.join(@run_dir, 'default_scenario_report')
|
174
|
+
# creating a class consumers with all the info about the consumption for each building
|
175
|
+
consumers = URBANopt::RNM::Consumers.new(@reopt, only_lv_consumers, max_num_lv_nodes, @average_building_peak_catalog_path, lv_limit) # passing these 2 conditions to see what option did the user applied
|
176
|
+
else
|
177
|
+
raise 'scenario_report is not found'
|
178
|
+
end
|
179
|
+
end
|
180
|
+
file_csv = []
|
181
|
+
file_json = []
|
182
|
+
# finding the 2 most extreme hours of the year (maximum net demand and maximum net generation) the distribution network is planned
|
183
|
+
hours = URBANopt::RNM::ReportScenario.new(@reopt)
|
184
|
+
# hours_commercial = URBANopt::RNM::ReportScenario.new(@reopt)
|
185
|
+
(0..tot_buildings - 1).each do |j|
|
186
|
+
if @reopt
|
187
|
+
file_csv[j] = File.join(@run_dir, (building_ids[j]).to_s, 'feature_reports', 'feature_optimization.csv')
|
188
|
+
|
189
|
+
# check that reopt json file exists (feature optimization only)
|
190
|
+
if !File.exist?(File.join(@run_dir, (building_ids[j]).to_s, 'feature_reports', 'feature_optimization.json'))
|
191
|
+
msg = 'REopt feature_optimization.json file not found. To use REopt results in the RNM analysis,' \
|
192
|
+
'first post-process the project with the --reopt-feature flag.'
|
193
|
+
raise msg
|
194
|
+
end
|
195
|
+
|
196
|
+
file_json[j] = JSON.parse(File.read(File.join(@run_dir, (building_ids[j]).to_s, 'feature_reports', 'feature_optimization.json')))
|
197
|
+
hours.aggregate_consumption(file_csv[j], file_json[j], j)
|
198
|
+
else
|
199
|
+
|
200
|
+
file_csv[j] = File.join(@run_dir, (building_ids[j]).to_s, 'feature_reports', 'default_feature_report.csv')
|
201
|
+
file_json[j] = JSON.parse(File.read(File.join(@run_dir, (building_ids[j]).to_s, 'feature_reports', 'default_feature_report.json')))
|
202
|
+
|
203
|
+
hours.aggregate_consumption(file_csv[j], file_json[j], j)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
hours.scenario_report_results
|
207
|
+
|
208
|
+
# iterating over each building to define each consumer/prosumer
|
209
|
+
(0..tot_buildings - 1).each do |j| # (0..20).each do |j|
|
210
|
+
# use building_ids lookup to get name of results directory
|
211
|
+
# reports will be in 'feature_reports' directory
|
212
|
+
if @reopt
|
213
|
+
# file_path = File.join(@run_dir, "#{building_ids[j]}", 'feature_reports', 'feature_optimization')
|
214
|
+
# prosumers.prosumer_files_load(file_path[j] + ".csv", File.read(file_path + ".json"), customers_coordinates[j], coordinates_buildings[j], hours)
|
215
|
+
prosumers.prosumer_files_load(file_csv[j], file_json[j], customers_coordinates[j], coordinates_buildings[j], hours)
|
216
|
+
else
|
217
|
+
# file_path = File.join(@run_dir, "#{building_ids[j]}", '014_default_feature_reports', 'default_feature_reports')
|
218
|
+
consumers.customer_files_load(file_csv[j], file_json[j], customers_coordinates[j], coordinates_buildings[j], hours)
|
219
|
+
end
|
220
|
+
end
|
221
|
+
rnm_us_catalog = URBANopt::RNM::RnmUsCatalogConversion.new(@extended_catalog_path, @run_dir, @rnm_dirname)
|
222
|
+
rnm_us_catalog.processing_data(utm_zone)
|
223
|
+
# call and create the opendss_catalog class if the user wants to convert the extended catalog into OpenDSS catalog
|
224
|
+
if @opendss_catalog
|
225
|
+
@opendss_catalog = URBANopt::RNM::ConversionToOpendssCatalog.new(@extended_catalog_path)
|
226
|
+
# create catalog and save to specified path
|
227
|
+
@opendss_catalog.create_catalog(File.join(@run_dir, 'opendss_catalog.json'))
|
228
|
+
end
|
229
|
+
# creating all the inputs files required by the RNM-US model in the folder Inputs in the RNM folder
|
230
|
+
File.open(File.join(@run_dir, @rnm_dirname, 'streetmapAS.txt'), 'w+') do |f|
|
231
|
+
f.puts(street_coordinates.map { |x| x.join(';') })
|
232
|
+
end
|
233
|
+
ficheros_entrada = []
|
234
|
+
if substation_location != 'nil'
|
235
|
+
File.open(File.join(@run_dir, @rnm_dirname, 'primary_substations.txt'), 'w+') do |f|
|
236
|
+
f.puts(substation_location.map { |x| x.join(';') })
|
237
|
+
end
|
238
|
+
ficheros_entrada = []
|
239
|
+
ficheros_entrada.push('CSubestacionDistribucionGreenfield;primary_substations.txt')
|
240
|
+
ficheros_entrada.push('CPuntoCallejero;streetmapAS.txt')
|
241
|
+
ficheros_entrada.push('END')
|
242
|
+
File.open(File.join(@run_dir, @rnm_dirname, 'ficheros_entrada.txt'), 'w+') do |f|
|
243
|
+
f.puts(ficheros_entrada)
|
244
|
+
end
|
245
|
+
else
|
246
|
+
puts('substation location automatically chosen by RNM-US model')
|
247
|
+
ficheros_entrada.push('CPuntoCallejero;streetmapAS.txt')
|
248
|
+
ficheros_entrada.push('END')
|
249
|
+
File.open(File.join(@run_dir, @rnm_dirname, 'ficheros_entrada.txt'), 'w+') do |f|
|
250
|
+
f.puts(ficheros_entrada)
|
251
|
+
end
|
252
|
+
end
|
253
|
+
ficheros_entrada_inc = []
|
254
|
+
if @reopt
|
255
|
+
File.open(File.join(@run_dir, @rnm_dirname, 'customers.txt'), 'w+') do |f|
|
256
|
+
f.puts(prosumers.customers.map { |x| x.join(';') })
|
257
|
+
end
|
258
|
+
File.open(File.join(@run_dir, @rnm_dirname, 'customers_ext.txt'), 'w+') do |g|
|
259
|
+
g.puts(prosumers.customers_ext.map { |w| w.join(';') })
|
260
|
+
end
|
261
|
+
File.open(File.join(@run_dir, @rnm_dirname, 'cust_profile_p.txt'), 'w+') do |g|
|
262
|
+
g.puts(prosumers.profile_customer_p.map { |w| w.join(';') })
|
263
|
+
end
|
264
|
+
File.open(File.join(@run_dir, @rnm_dirname, 'cust_profile_q.txt'), 'w+') do |g|
|
265
|
+
g.puts(prosumers.profile_customer_q.map { |w| w.join(';') })
|
266
|
+
end
|
267
|
+
# CSV.open(File.join(@run_dir, @rnm_dirname, "cust_profile_q_extendido.csv"), "w") do |csv|
|
268
|
+
# csv << [prosumers.profile_customer_q_ext]
|
269
|
+
# end
|
270
|
+
File.open(File.join(@run_dir, @rnm_dirname, 'cust_profile_q_extendido.txt'), 'w+') do |g|
|
271
|
+
g.puts(prosumers.profile_customer_q_ext.map { |w| w.join(';') })
|
272
|
+
end
|
273
|
+
File.open(File.join(@run_dir, @rnm_dirname, 'cust_profile_p_extendido.txt'), 'w+') do |g|
|
274
|
+
g.puts(prosumers.profile_customer_p_ext.map { |w| w.join(';') })
|
275
|
+
end
|
276
|
+
File.open(File.join(@run_dir, @rnm_dirname, 'generators.txt'), 'w+') do |g|
|
277
|
+
g.puts(prosumers.dg.map { |w| w.join(';') })
|
278
|
+
end
|
279
|
+
# creating profiles txt files
|
280
|
+
File.open(File.join(@run_dir, @rnm_dirname, 'gen_profile_p.txt'), 'w+') do |g|
|
281
|
+
g.puts(prosumers.dg_profile_p.map { |w| w.join(';') })
|
282
|
+
end
|
283
|
+
File.open(File.join(@run_dir, @rnm_dirname, 'gen_profile_q.txt'), 'w+') do |g|
|
284
|
+
g.puts(prosumers.dg_profile_q.map { |w| w.join(';') })
|
285
|
+
end
|
286
|
+
File.open(File.join(@run_dir, @rnm_dirname, 'gen_profile_q_extendido.txt'), 'w+') do |g|
|
287
|
+
g.puts(prosumers.profile_dg_q_extended.map { |w| w.join(';') })
|
288
|
+
end
|
289
|
+
File.open(File.join(@run_dir, @rnm_dirname, 'gen_profile_p_extendido.txt'), 'w+') do |g|
|
290
|
+
g.puts(prosumers.profile_dg_p_extended.map { |w| w.join(';') })
|
291
|
+
end
|
292
|
+
ficheros_entrada_inc.push('CClienteGreenfield;customers_ext.txt;cust_profile_p.txt;cust_profile_q.txt;cust_profile_p_extendido.txt;cust_profile_q_extendido.txt')
|
293
|
+
ficheros_entrada_inc.push('CGeneradorGreenfield;generators.txt;gen_profile_p.txt;gen_profile_q.txt;gen_profile_p_extendido.txt;gen_profile_q_extendido.txt')
|
294
|
+
ficheros_entrada_inc.push('END')
|
295
|
+
File.open(File.join(@run_dir, @rnm_dirname, 'ficheros_entrada_inc.txt'), 'w+') do |g|
|
296
|
+
g.puts(ficheros_entrada_inc)
|
297
|
+
end
|
298
|
+
else
|
299
|
+
File.open(File.join(@run_dir, @rnm_dirname, 'customers.txt'), 'w+') do |f|
|
300
|
+
f.puts(consumers.customers.map { |x| x.join(';') })
|
301
|
+
end
|
302
|
+
File.open(File.join(@run_dir, @rnm_dirname, 'customers_ext.txt'), 'w+') do |g|
|
303
|
+
g.puts(consumers.customers_ext.map { |w| w.join(';') })
|
304
|
+
end
|
305
|
+
File.open(File.join(@run_dir, @rnm_dirname, 'cust_profile_p.txt'), 'w+') do |g|
|
306
|
+
g.puts(consumers.profile_customer_p.map { |w| w.join(';') })
|
307
|
+
end
|
308
|
+
File.open(File.join(@run_dir, @rnm_dirname, 'cust_profile_q.txt'), 'w+') do |g|
|
309
|
+
g.puts(consumers.profile_customer_q.map { |w| w.join(';') })
|
310
|
+
end
|
311
|
+
File.open(File.join(@run_dir, @rnm_dirname, 'cust_profile_q_extendido.txt'), 'w+') do |g|
|
312
|
+
g.puts(consumers.profile_customer_q_ext.map { |w| w.join(';') })
|
313
|
+
end
|
314
|
+
File.open(File.join(@run_dir, @rnm_dirname, 'cust_profile_p_extendido.txt'), 'w+') do |g|
|
315
|
+
g.puts(consumers.profile_customer_p_ext.map { |w| w.join(';') })
|
316
|
+
end
|
317
|
+
ficheros_entrada_inc.push('CClienteGreenfield;customers_ext.txt;cust_profile_p.txt;cust_profile_q.txt;cust_profile_p_extendido.txt;cust_profile_q_extendido.txt')
|
318
|
+
ficheros_entrada_inc.push('END')
|
319
|
+
File.open(File.join(@run_dir, @rnm_dirname, 'ficheros_entrada_inc.txt'), 'w+') do |g|
|
320
|
+
g.puts(ficheros_entrada_inc)
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
##
|
326
|
+
# Delete the RNM-US input files directory
|
327
|
+
##
|
328
|
+
def delete
|
329
|
+
if Dir.exist?(File.join(@run_dir, @rnm_dirname))
|
330
|
+
FileUtils.rm_rf(File.join(@run_dir, @rnm_dirname))
|
331
|
+
end
|
332
|
+
end
|
333
|
+
end
|
334
|
+
end
|
335
|
+
end
|