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.
@@ -0,0 +1,41 @@
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_relative 'urbanopt/rnm'
@@ -0,0 +1,59 @@
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 'urbanopt/rnm/version'
42
+ require 'urbanopt/rnm/logger'
43
+ require 'urbanopt/rnm/consumers'
44
+ require 'urbanopt/rnm/prosumers'
45
+ require 'urbanopt/rnm/oh_ug_rate'
46
+ require 'urbanopt/rnm/scenario_report'
47
+ require 'urbanopt/rnm/geojson_input'
48
+ require 'urbanopt/rnm/conversion_to_opendss'
49
+ require 'urbanopt/rnm/capacitor_opendss'
50
+ require 'urbanopt/rnm/carson_eq'
51
+ require 'urbanopt/rnm/processor_opendss_catalog'
52
+ require 'urbanopt/rnm/transformer_opendss'
53
+ require 'urbanopt/rnm/rnm_us_catalog_conversion'
54
+ require 'urbanopt/rnm/wires_class'
55
+ require 'urbanopt/rnm/wires_opendss'
56
+ require 'urbanopt/rnm/input_files'
57
+ require 'urbanopt/rnm/runner'
58
+ require 'urbanopt/rnm/api_client'
59
+ require 'urbanopt/rnm/post_processor'
@@ -0,0 +1,296 @@
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 'faraday'
42
+ require 'fileutils'
43
+ require 'json'
44
+ require 'urbanopt/rnm/logger'
45
+ require 'zip'
46
+
47
+ module URBANopt
48
+ module RNM
49
+ # Client to interface with the RNM-US API
50
+ class ApiClient
51
+ ##
52
+ # Initialize ApiClient attributes: +name+, +rnm_dir+, +template_inputs+, and +use_localhost+
53
+ ##
54
+ # [parameters:]
55
+ # * +name+ - _String_ - Human readable scenario name.
56
+ # * +rnm_dir+ - _String_ - Full path to the rnm_directory of inputs/results for the scenario
57
+ # * +template_inputs+ - _String_ - Location of template inputs for the RNM-US simulation (unused)
58
+ # * +use_localhost+ - _Bool_ - Flag to use localhost API vs production API
59
+ def initialize(name, rnm_dir, use_localhost = false, reopt = false)
60
+ # TODO: eventually add NREL developer api key support
61
+
62
+ @use_localhost = use_localhost
63
+ if @use_localhost
64
+ @base_api = 'http://0.0.0.0:8080/api/v1/'
65
+ else
66
+ @base_api = 'https://rnm.urbanopt.net/api/v1/'
67
+ end
68
+
69
+ puts "Running RNM-US at #{@base_api}"
70
+
71
+ # params
72
+ @name = name
73
+ @rnm_dir = rnm_dir
74
+ @reopt = reopt
75
+
76
+ # default input files for generic and reopt simulations
77
+ @input_files = ['cust_profile_p.txt', 'cust_profile_p_extendido.txt', 'cust_profile_q.txt', 'cust_profile_q_extendido.txt',
78
+ 'customers.txt', 'customers_ext.txt', 'ficheros_entrada.txt', 'ficheros_entrada_inc.txt',
79
+ 'primary_substations.txt', 'streetmapAS.txt', 'udcons.csv']
80
+ @reopt_files = ['gen_profile_p.txt', 'gen_profile_p_extendido.txt', 'gen_profile_q.txt',
81
+ 'gen_profile_q_extendido.txt', 'generators.txt']
82
+
83
+ if @reopt
84
+ @input_files += @reopt_files
85
+ end
86
+
87
+ # initialize @@logger
88
+ @@logger ||= URBANopt::RNM.logger
89
+
90
+ # simulation data
91
+ @sim_id = ''
92
+ # results are not really used in memory. results.json is saved to rnm_dir when results are downloaded
93
+ @results = {}
94
+ end
95
+
96
+ ##
97
+ # Check and Zip files
98
+ ##
99
+ def zip_input_files
100
+ # puts "INPUT FILES: #{input_files}"
101
+
102
+ # check that all files exist in folder
103
+ missing_files = []
104
+ @input_files.each do |f|
105
+ if !File.exist?(File.join(@rnm_dir, f))
106
+ missing_files << f
107
+ end
108
+ end
109
+
110
+ if !missing_files.empty?
111
+ raise "Input Files missing in directory: #{missing_files.join(',')}"
112
+ end
113
+
114
+ # delete zip only if already exists AND input files also exist
115
+ inputs_zip = File.join(@rnm_dir, 'inputs.zip')
116
+ if File.exist?(inputs_zip)
117
+ if File.exist?(File.join(@rnm_dir, @input_files[0]))
118
+ File.delete(inputs_zip)
119
+ else
120
+ # inputs.zip exists but input files do not. keep existing and do nothing else
121
+ puts 'inputs.zip already exists...keeping existing file'
122
+ return
123
+ end
124
+ end
125
+
126
+ # zip up
127
+ Zip::File.open(inputs_zip, Zip::File::CREATE) do |zipfile|
128
+ @input_files.each do |filename|
129
+ # Two arguments:
130
+ # - The name of the file as it will appear in the archive
131
+ # - The original file, including the path to find it
132
+ zipfile.add(filename, File.join(@rnm_dir, filename))
133
+ end
134
+ end
135
+
136
+ # delete input files now that they are zipped up
137
+ delete_inputs
138
+ end
139
+
140
+ ##
141
+ # Submit simulation to RNM-US API
142
+ # Stores sim_id in the class instance
143
+ ##
144
+ def submit_simulation
145
+ conn = Faraday.new(url: @base_api) do |f|
146
+ f.request :multipart
147
+ end
148
+
149
+ # add post data
150
+ payload = { name: @name }
151
+ files = { 'inputs': 'inputs.zip' }
152
+ files.each do |key, the_file|
153
+ payload[key] = Faraday::FilePart.new(File.join(@rnm_dir, the_file), the_file)
154
+ end
155
+
156
+ resp = conn.post('simulations', payload)
157
+ data = JSON.parse(resp.body)
158
+
159
+ if resp.status != 200
160
+ msg = "Error submitting simulation to RNM-US API: status code #{resp.status} #{data['status']} - #{data['message']}"
161
+ @@logger.error(msg)
162
+ raise msg
163
+ end
164
+
165
+ @sim_id = data['simulation_id']
166
+ end
167
+
168
+ ##
169
+ # Poll for results of RNM-US simulation and download
170
+ # when simulation is completed, results.zip file is downloaded to rnm_dir directory
171
+ ##
172
+ def get_results
173
+ # poll until results are returned
174
+ done = false
175
+ conn = Faraday.new(url: @base_api)
176
+
177
+ # prepare results directory
178
+ prepare_results_dir
179
+
180
+ max_tries = 10
181
+ tries = 0
182
+ puts "attempting to retrieve results for simulation #{@sim_id}"
183
+ while !done && (max_tries != tries)
184
+ resp = conn.get("simulations/#{@sim_id}")
185
+ if resp.status == 200
186
+ data = JSON.parse(resp.body)
187
+ if data['status'] && ['failed', 'completed'].include?(data['status'])
188
+ # done
189
+ done = true
190
+ if data['status'] == 'failed'
191
+ if data['results'] && data['results']['message']
192
+ puts "Simulation Error: #{data['results']['message']}"
193
+ else
194
+ puts 'Simulation Error!'
195
+ end
196
+ else
197
+ # get results
198
+ @results = data['results'] || []
199
+
200
+ # download results
201
+ download_results
202
+
203
+ return @results
204
+ end
205
+ else
206
+ tries += 1
207
+ sleep(1)
208
+ end
209
+
210
+ else
211
+ puts("ERROR retrieving: #{resp.body}")
212
+ tries += 1
213
+
214
+ if tries == max_tries
215
+ # now raise the error
216
+ msg = "Error retrieving simulation #{@sim_id}. error code: #{resp.status}"
217
+ @@logger.error(msg)
218
+ raise msg
219
+ else
220
+ # try again
221
+ puts("TRYING AGAIN...#{tries}")
222
+ sleep(3)
223
+ end
224
+ end
225
+ end
226
+ end
227
+
228
+ ##
229
+ # Download results of a specific simulation from RNM-US API. attributes: +sim_id+
230
+ # Results.zip file is downloaded to the rnm_dir directory
231
+ ##
232
+ # [parameters:]
233
+ # * +sim_id+ - _String_ - Simulation ID to retrieve. If not nil, will override id stored in class instance
234
+ def download_results(sim_id = nil)
235
+ conn = Faraday.new(url: @base_api)
236
+ streamed = []
237
+
238
+ the_sim_id = sim_id.nil? ? @sim_id : sim_id
239
+
240
+ resp = conn.get("download/#{the_sim_id}") do |req|
241
+ req.options.on_data = proc do |chunk, overall_received_bytes|
242
+ # puts "Received #{overall_received_bytes} characters"
243
+ streamed << chunk
244
+ end
245
+ end
246
+ # puts("STATUS: #{resp.status}, #{resp.body}")
247
+
248
+ if resp.status == 200
249
+
250
+ file_path = File.join(@rnm_dir, 'results', 'results.zip')
251
+
252
+ File.open(file_path, 'wb') { |f| f.write streamed.join }
253
+ # puts "RNM-US results.zip downloaded to #{@rnm_dir}"
254
+ # unzip
255
+ Zip::File.open(file_path) do |zip_file|
256
+ zip_file.each do |f|
257
+ f_path = File.join(@rnm_dir, 'results', f.name)
258
+ FileUtils.mkdir_p(File.dirname(f_path))
259
+ zip_file.extract(f, f_path) unless File.exist?(f_path)
260
+ end
261
+ end
262
+
263
+ # delete zip
264
+ File.delete(file_path)
265
+
266
+ else
267
+ msg = "Error retrieving results for #{the_sim_id}. error code: #{resp.status}. #{resp.body}"
268
+ @@logger.error(msg)
269
+ raise msg
270
+ end
271
+ end
272
+
273
+ ##
274
+ # Prepare results directory
275
+ # Delete existing results
276
+ ##
277
+ def prepare_results_dir
278
+ if !Dir.exist?(File.join(@rnm_dir, 'results'))
279
+ Dir.mkdir(File.join(@rnm_dir, 'results'))
280
+ else
281
+ del_path = File.join(@rnm_dir, 'results', '*')
282
+ FileUtils.rm_rf Dir.glob(del_path)
283
+ end
284
+ end
285
+
286
+ ##
287
+ # Delete input files once they are zipped up
288
+ ##
289
+ def delete_inputs
290
+ @input_files.each do |filename|
291
+ File.delete(File.join(@rnm_dir, filename)) if File.exist?(File.join(@rnm_dir, filename))
292
+ end
293
+ end
294
+ end
295
+ end
296
+ end
@@ -0,0 +1,57 @@
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
+ module URBANopt
42
+ module RNM
43
+ # creating the Capacitor class with required parameters by the OpenDSS catalog
44
+ class Capacitor
45
+ def create(capacitor)
46
+ hash = {}
47
+ hash[:nameclass] = capacitor[' Name']
48
+ hash[:kvar] = capacitor[' size (kVA)']
49
+ hash[:resistance] = capacitor['resistance(Ohm)']
50
+ hash[:phases] = capacitor[' number of phases']
51
+ hash[:connection] = capacitor['connection']
52
+ hash[:control_type] = capacitor['control_type']
53
+ return hash
54
+ end
55
+ end
56
+ end
57
+ end