urbanopt-reopt 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/nightly_ci_build.yml +1 -1
- data/CHANGELOG.md +11 -0
- data/CONTRIBUTING.md +3 -3
- data/Gemfile +24 -19
- data/LICENSE.md +1 -1
- data/RDOC_MAIN.md +2 -2
- data/README.md +2 -2
- data/Rakefile +1 -1
- data/developer_nrel_key.rb +1 -1
- data/doc_templates/LICENSE.md +1 -1
- data/doc_templates/copyright_erb.txt +1 -1
- data/doc_templates/copyright_js.txt +1 -1
- data/doc_templates/copyright_ruby.txt +1 -1
- data/docs/README.md +2 -2
- data/docs/package.json +1 -1
- data/index.md +2 -2
- data/lib/urbanopt/reopt/extension.rb +1 -1
- data/lib/urbanopt/reopt/feature_report_adapter.rb +10 -11
- data/lib/urbanopt/reopt/{reopt_ghp_adapter.rb → reopt_ghp_adapter_ghp.rb} +238 -48
- data/lib/urbanopt/reopt/reopt_ghp_api.rb +3 -3
- data/lib/urbanopt/reopt/reopt_ghp_files/reopt_ghp_assumption.json +3 -3
- data/lib/urbanopt/reopt/reopt_ghp_post_processor.rb +13 -4
- data/lib/urbanopt/reopt/reopt_ghp_result.rb +145 -0
- data/lib/urbanopt/reopt/reopt_lite_api.rb +54 -13
- data/lib/urbanopt/reopt/reopt_logger.rb +1 -1
- data/lib/urbanopt/reopt/reopt_post_processor.rb +54 -18
- data/lib/urbanopt/reopt/reopt_schema/REopt-BAU-input.json +174 -0
- data/lib/urbanopt/reopt/reopt_schema/REopt-GHP-input.json +263 -95
- data/lib/urbanopt/reopt/scenario/reopt_scenario_csv.rb +1 -1
- data/lib/urbanopt/reopt/scenario_report_adapter.rb +8 -9
- data/lib/urbanopt/reopt/utilities.rb +1 -1
- data/lib/urbanopt/reopt/version.rb +2 -2
- data/lib/urbanopt/reopt.rb +3 -2
- data/lib/urbanopt/reopt_scenario.rb +1 -1
- data/lib/urbanopt-reopt.rb +1 -1
- data/urbanopt-reopt.gemspec +6 -3
- metadata +53 -9
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# *********************************************************************************
|
|
2
|
-
# URBANopt (tm), Copyright (c) Alliance for
|
|
2
|
+
# URBANopt (tm), Copyright (c) Alliance for Energy Innovation, LLC.
|
|
3
3
|
# See also https://github.com/urbanopt/urbanopt-reopt-gem/blob/develop/LICENSE.md
|
|
4
4
|
# *********************************************************************************
|
|
5
5
|
|
|
@@ -10,11 +10,16 @@ module URBANopt # :nodoc:
|
|
|
10
10
|
def initialize
|
|
11
11
|
# initialize @@logger
|
|
12
12
|
@@logger ||= URBANopt::REopt.reopt_logger
|
|
13
|
-
# Define class
|
|
13
|
+
# Define class variables
|
|
14
14
|
@@hours_in_year = 8760
|
|
15
|
+
@@nat_gas_dollars_per_mmbtu = 13.5
|
|
16
|
+
@@year_of_simulation = 2023
|
|
17
|
+
@@year_of_simulation = 2023
|
|
18
|
+
@@small_multiplier = [0.00001]
|
|
19
|
+
|
|
15
20
|
end
|
|
16
21
|
|
|
17
|
-
def
|
|
22
|
+
def create_reopt_input_building_ghp(run_dir, system_parameter_hash, reopt_ghp_assumptions_hash, building_id, modelica_result)
|
|
18
23
|
|
|
19
24
|
# Define variables
|
|
20
25
|
reopt_inputs_building = {}
|
|
@@ -28,9 +33,6 @@ module URBANopt # :nodoc:
|
|
|
28
33
|
SpaceHeatingLoad: {},
|
|
29
34
|
DomesticHotWaterLoad: {},
|
|
30
35
|
ElectricLoad: {},
|
|
31
|
-
ElectricTariff: {
|
|
32
|
-
urdb_label: ""
|
|
33
|
-
},
|
|
34
36
|
GHP: {},
|
|
35
37
|
ExistingBoiler: {}
|
|
36
38
|
}
|
|
@@ -38,7 +40,7 @@ module URBANopt # :nodoc:
|
|
|
38
40
|
|
|
39
41
|
# The URDB label is required to be specified in the input assumption file
|
|
40
42
|
if reopt_inputs_building[:ElectricTariff][:urdb_label].nil? || reopt_inputs_building[:ElectricTariff][:urdb_label].empty?
|
|
41
|
-
|
|
43
|
+
raise "Missing value for urdb_label - this is a required input"
|
|
42
44
|
end
|
|
43
45
|
|
|
44
46
|
scenario_json_path = File.join(run_dir, "default_scenario_report.json")
|
|
@@ -50,7 +52,6 @@ module URBANopt # :nodoc:
|
|
|
50
52
|
@longitude = scenario_json_data[:scenario_report][:location][:longitude_deg]
|
|
51
53
|
reopt_inputs_building[:Site][:latitude] = @latitude
|
|
52
54
|
reopt_inputs_building[:Site][:longitude] = @longitude
|
|
53
|
-
|
|
54
55
|
end
|
|
55
56
|
end
|
|
56
57
|
|
|
@@ -72,8 +73,8 @@ module URBANopt # :nodoc:
|
|
|
72
73
|
end
|
|
73
74
|
# Check if the total kBtu is zero
|
|
74
75
|
if total_kbtu.zero?
|
|
75
|
-
# If zero, populate with hourly values meet reopts formatting requirements
|
|
76
|
-
reopt_inputs_building[:SpaceHeatingLoad][:fuel_loads_mmbtu_per_hour] =
|
|
76
|
+
# If zero, populate with near zero hourly values to meet reopts formatting requirements
|
|
77
|
+
reopt_inputs_building[:SpaceHeatingLoad][:fuel_loads_mmbtu_per_hour] = @@small_multiplier * @@hours_in_year
|
|
77
78
|
else
|
|
78
79
|
# If not zero, convert and append to the array
|
|
79
80
|
timeseries_data.each do |row|
|
|
@@ -86,8 +87,8 @@ module URBANopt # :nodoc:
|
|
|
86
87
|
end
|
|
87
88
|
|
|
88
89
|
else
|
|
89
|
-
#
|
|
90
|
-
reopt_inputs_building[:SpaceHeatingLoad][:fuel_loads_mmbtu_per_hour] =
|
|
90
|
+
# populate with near zero hourly values to meet reopts formatting requirements
|
|
91
|
+
reopt_inputs_building[:SpaceHeatingLoad][:fuel_loads_mmbtu_per_hour] = @@small_multiplier * @@hours_in_year
|
|
91
92
|
puts "Existing heating fuel cost was not taken into consideration in result calculations."
|
|
92
93
|
end
|
|
93
94
|
|
|
@@ -103,12 +104,21 @@ module URBANopt # :nodoc:
|
|
|
103
104
|
|
|
104
105
|
if File.exist?(@modelica_csv)
|
|
105
106
|
modelica_data = CSV.read(@modelica_csv, headers: true)
|
|
106
|
-
|
|
107
|
-
|
|
107
|
+
heating_power_header = modelica_data.headers.select{ |h| h.to_s.start_with?("heating_electric_power") }
|
|
108
|
+
prefix = ""
|
|
109
|
+
|
|
110
|
+
if heating_power_header[0].split("_")[-1].include? ("B")
|
|
111
|
+
prefix = "B"
|
|
112
|
+
else
|
|
113
|
+
prefix = ""
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
heating_power = "heating_electric_power_#{prefix}#{building_id}"
|
|
117
|
+
cooling_power = "cooling_electric_power_#{prefix}#{building_id}"
|
|
118
|
+
ets_pump_power = "ets_pump_power_#{prefix}#{building_id}"
|
|
119
|
+
heating_system_capacity = "heating_system_capacity_#{prefix}#{building_id}"
|
|
120
|
+
cooling_system_capacity = "cooling_system_capacity_#{prefix}#{building_id}"
|
|
108
121
|
pump_power = "pump_power_#{building_id}"
|
|
109
|
-
ets_pump_power = "ets_pump_power_#{building_id}"
|
|
110
|
-
heating_system_capacity = "heating_system_capacity_#{building_id}"
|
|
111
|
-
cooling_system_capacity = "cooling_system_capacity_#{building_id}"
|
|
112
122
|
|
|
113
123
|
heating_power_values = cooling_power_values = pump_power_values = ets_pump_power_values = []
|
|
114
124
|
total_electric_load_building = []
|
|
@@ -130,7 +140,6 @@ module URBANopt # :nodoc:
|
|
|
130
140
|
# Convert watts to kilowatts
|
|
131
141
|
elements.map { |e| e.to_f / 1000 }.sum
|
|
132
142
|
end
|
|
133
|
-
|
|
134
143
|
peak_combined_heatpump_thermal_ton = 0
|
|
135
144
|
|
|
136
145
|
if modelica_data.headers.include?(heating_system_capacity)
|
|
@@ -139,13 +148,13 @@ module URBANopt # :nodoc:
|
|
|
139
148
|
if modelica_data.headers.include?(cooling_system_capacity)
|
|
140
149
|
cooling_system_capacity_value = modelica_data[cooling_system_capacity][0]
|
|
141
150
|
end
|
|
142
|
-
|
|
151
|
+
|
|
143
152
|
watts_per_ton_cooling_capacity = 3517
|
|
144
153
|
peak_combined_heatpump_thermal_ton = ([heating_system_capacity_value.to_f.abs, cooling_system_capacity_value.to_f.abs].max) / watts_per_ton_cooling_capacity
|
|
145
154
|
|
|
146
155
|
# Store the result in reopt_inputs_building ElectricLoad
|
|
147
156
|
reopt_inputs_building[:ElectricLoad][:loads_kw] = total_electric_load_building
|
|
148
|
-
|
|
157
|
+
reopt_inputs_building[:ElectricLoad][:year] = @@year_of_simulation
|
|
149
158
|
|
|
150
159
|
domestic_hot_water = total_electric_load_building.map do |load|
|
|
151
160
|
load * 0
|
|
@@ -156,16 +165,29 @@ module URBANopt # :nodoc:
|
|
|
156
165
|
|
|
157
166
|
# Add GHP Fields
|
|
158
167
|
reopt_inputs_building[:GHP] = {}
|
|
168
|
+
|
|
169
|
+
# Add avoided capital cost of all buildings
|
|
170
|
+
if reopt_inputs_building[:features] && !reopt_inputs_building[:features].empty?
|
|
171
|
+
# Find the feature matching this building_id
|
|
172
|
+
matching_feature = reopt_inputs_building[:features].find { |feature| feature[:feature_id] == building_id.to_i}
|
|
173
|
+
|
|
174
|
+
if matching_feature
|
|
175
|
+
# Set avoided capex value into GHP block
|
|
176
|
+
reopt_inputs_building[:GHP][:avoided_capex_by_ghp_present_value] = matching_feature[:avoided_capex_by_ghp_present_value]
|
|
177
|
+
end
|
|
178
|
+
end
|
|
159
179
|
# REopt default
|
|
160
180
|
reopt_inputs_building[:GHP][:require_ghp_purchase] = 1
|
|
161
181
|
reopt_inputs_building[:GHP][:om_cost_per_sqft_year] = 0
|
|
162
182
|
reopt_inputs_building[:GHP][:heatpump_capacity_sizing_factor_on_peak_load] = 1.0
|
|
163
183
|
# Add the floor area
|
|
164
184
|
building_json_path = File.join(run_dir, building_id.to_s, "feature_reports", "default_feature_report.json")
|
|
185
|
+
|
|
165
186
|
if File.exist?(building_json_path)
|
|
187
|
+
puts building_json_path
|
|
166
188
|
File.open(building_json_path, 'r') do |file|
|
|
167
189
|
building_json_data = JSON.parse(file.read, symbolize_names: true)
|
|
168
|
-
reopt_inputs_building[:GHP][:building_sqft] = building_json_data[:program][:
|
|
190
|
+
reopt_inputs_building[:GHP][:building_sqft] = building_json_data[:program][:footprint_area_sqft].to_f
|
|
169
191
|
end
|
|
170
192
|
else
|
|
171
193
|
puts "File not found: #{building_json_path}"
|
|
@@ -173,8 +195,8 @@ module URBANopt # :nodoc:
|
|
|
173
195
|
|
|
174
196
|
# Add existing boiler fuel cost
|
|
175
197
|
# TODO : Add this as optional user input
|
|
176
|
-
|
|
177
|
-
reopt_inputs_building[:ExistingBoiler][:fuel_cost_per_mmbtu] = nat_gas_dollars_per_mmbtu
|
|
198
|
+
# Cost of Natural Gas in $/mmbtu as per REopt Defaults
|
|
199
|
+
reopt_inputs_building[:ExistingBoiler][:fuel_cost_per_mmbtu] = @@nat_gas_dollars_per_mmbtu
|
|
178
200
|
|
|
179
201
|
# Add ghpghx_responses
|
|
180
202
|
ghpghx_output = {}
|
|
@@ -192,7 +214,8 @@ module URBANopt # :nodoc:
|
|
|
192
214
|
ghpghx_output[:outputs][:yearly_heating_heatpump_electric_consumption_series_kw] = total_electric_load_building
|
|
193
215
|
ghpghx_output[:outputs][:yearly_cooling_heatpump_electric_consumption_series_kw] = [0] * @@hours_in_year
|
|
194
216
|
# This is not used in REopt calculation but required for formatting.
|
|
195
|
-
|
|
217
|
+
# populate with near zero hourly values to meet reopts formatting requirements
|
|
218
|
+
ghpghx_output[:inputs][:heating_thermal_load_mmbtu_per_hr] = @@small_multiplier * @@hours_in_year
|
|
196
219
|
# This is not used in REopt calculation but required for formatting.
|
|
197
220
|
ghpghx_output[:inputs][:cooling_thermal_load_ton] = [0] * @@hours_in_year
|
|
198
221
|
|
|
@@ -210,7 +233,7 @@ module URBANopt # :nodoc:
|
|
|
210
233
|
|
|
211
234
|
end
|
|
212
235
|
|
|
213
|
-
def
|
|
236
|
+
def create_reopt_input_district_ghp(run_dir, system_parameter_hash, reopt_ghp_assumptions_hash, ghp_id, modelica_result)
|
|
214
237
|
|
|
215
238
|
reopt_inputs_district = {}
|
|
216
239
|
|
|
@@ -238,27 +261,29 @@ module URBANopt # :nodoc:
|
|
|
238
261
|
# The URDB label is required to be specified in the input assumption file
|
|
239
262
|
if reopt_inputs_district[:ElectricTariff][:urdb_label].nil? || reopt_inputs_district[:ElectricTariff][:urdb_label].empty?
|
|
240
263
|
|
|
241
|
-
|
|
264
|
+
raise "Missing value for urdb_label - this is a required input"
|
|
242
265
|
|
|
243
266
|
end
|
|
244
|
-
|
|
267
|
+
# populate with near zero hourly values to meet reopts formatting requirements
|
|
245
268
|
# This is not used in REopt calculation but required for formatting.
|
|
246
|
-
reopt_inputs_district[:SpaceHeatingLoad][:fuel_loads_mmbtu_per_hour] =
|
|
269
|
+
reopt_inputs_district[:SpaceHeatingLoad][:fuel_loads_mmbtu_per_hour] = @@small_multiplier*@@hours_in_year
|
|
270
|
+
# populate with near zero hourly values to meet reopts formatting requirements
|
|
247
271
|
# This is not used in REopt calculation but required for formatting.
|
|
248
|
-
reopt_inputs_district[:DomesticHotWaterLoad][:fuel_loads_mmbtu_per_hour] =
|
|
272
|
+
reopt_inputs_district[:DomesticHotWaterLoad][:fuel_loads_mmbtu_per_hour] = @@small_multiplier*@@hours_in_year
|
|
249
273
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
#required for
|
|
253
|
-
reopt_inputs_district[:ElectricLoad][:loads_kw] =
|
|
274
|
+
reopt_inputs_district[:ElectricLoad] = {}
|
|
275
|
+
# populate with near zero hourly values to meet reopts formatting requirements
|
|
276
|
+
# This is not used in REopt calculation but required for formatting.
|
|
277
|
+
reopt_inputs_district[:ElectricLoad][:loads_kw] = @@small_multiplier*@@hours_in_year
|
|
278
|
+
reopt_inputs_district[:ElectricLoad][:year] = @@year_of_simulation
|
|
254
279
|
|
|
255
280
|
reopt_inputs_district[:ExistingBoiler] = {}
|
|
256
|
-
reopt_inputs_district[:ExistingBoiler][:fuel_cost_per_mmbtu] =
|
|
281
|
+
reopt_inputs_district[:ExistingBoiler][:fuel_cost_per_mmbtu] = @@nat_gas_dollars_per_mmbtu
|
|
257
282
|
|
|
258
283
|
# GHP inputs
|
|
259
284
|
reopt_inputs_district[:GHP] = {}
|
|
260
285
|
reopt_inputs_district[:GHP][:require_ghp_purchase] = 1
|
|
261
|
-
reopt_inputs_district[:GHP][:building_sqft] = 0.
|
|
286
|
+
reopt_inputs_district[:GHP][:building_sqft] = format('%.8f', @@small_multiplier[0]).to_f
|
|
262
287
|
reopt_inputs_district[:GHP][:om_cost_per_sqft_year] = 0
|
|
263
288
|
reopt_inputs_district[:GHP][:heatpump_capacity_sizing_factor_on_peak_load] = 1.0
|
|
264
289
|
|
|
@@ -272,15 +297,39 @@ module URBANopt # :nodoc:
|
|
|
272
297
|
|
|
273
298
|
|
|
274
299
|
# Read GHX sizes from system parameter hash
|
|
275
|
-
ghe_specific_params = system_parameter_hash[:district_system][:fifth_generation][:ghe_parameters][:
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
300
|
+
ghe_specific_params = system_parameter_hash[:district_system][:fifth_generation][:ghe_parameters][:borefields]
|
|
301
|
+
|
|
302
|
+
ghe_specific_params.each do |ghe|
|
|
303
|
+
if ghe[:ghe_id] == ghp_id
|
|
304
|
+
unless ghe[:pre_designed_borefield]
|
|
305
|
+
if ghe[:autosized_rectangle_borefield]
|
|
306
|
+
borefield = ghe[:autosized_rectangle_borefield]
|
|
307
|
+
|
|
308
|
+
elsif ghe[:autosized_rectangle_constrained_borefield]
|
|
309
|
+
borefield = ghe[:autosized_rectangle_constrained_borefield]
|
|
310
|
+
|
|
311
|
+
elsif ghe[:autosized_birectangle_borefield]
|
|
312
|
+
borefield = ghe[:autosized_birectangle_borefield]
|
|
313
|
+
|
|
314
|
+
elsif ghe[:autosized_birectangle_constrained_borefield]
|
|
315
|
+
borefield = ghe[:autosized_birectangle_constrained_borefield]
|
|
316
|
+
|
|
317
|
+
elsif ghe[:autosized_bizoned_rectangle_borefield]
|
|
318
|
+
borefield = ghe[:autosized_bizoned_rectangle_borefield]
|
|
319
|
+
|
|
320
|
+
elsif ghe[:autosized_near_square_borefield]
|
|
321
|
+
borefield = ghe[:autosized_near_square_borefield]
|
|
322
|
+
|
|
323
|
+
elsif ghe[:autosized_rowwise_borefield]
|
|
324
|
+
borefield = ghe[:autosized_rowwise_borefield]
|
|
325
|
+
end
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
ghpghx_output[:outputs][:number_of_boreholes] = borefield[:number_of_boreholes]
|
|
329
|
+
# convert meters to feet by multiplying with 3.28084
|
|
330
|
+
ghpghx_output[:outputs][:length_boreholes_ft] = (borefield[:borehole_length])*3.28084
|
|
331
|
+
|
|
332
|
+
end
|
|
284
333
|
end
|
|
285
334
|
|
|
286
335
|
if File.exist?(@modelica_csv)
|
|
@@ -309,16 +358,19 @@ module URBANopt # :nodoc:
|
|
|
309
358
|
# column_values = modelica_data.by_col[ghp_column]
|
|
310
359
|
|
|
311
360
|
ghpghx_output[:outputs][:yearly_ghx_pump_electric_consumption_series_kw] = electrical_power_consumed_kw
|
|
312
|
-
else
|
|
313
|
-
|
|
361
|
+
else
|
|
362
|
+
# populate with near zero hourly values to meet reopts formatting requirements
|
|
363
|
+
# This is not used in REopt calculation but required for formatting.
|
|
364
|
+
ghpghx_output[:outputs][:yearly_ghx_pump_electric_consumption_series_kw] = @@small_multiplier*@@hours_in_year
|
|
314
365
|
end
|
|
315
366
|
|
|
316
367
|
# This is not used in REopt calculation but required for formatting.
|
|
317
368
|
ghpghx_output[:outputs][:peak_combined_heatpump_thermal_ton] = 0.000000001
|
|
318
369
|
|
|
319
370
|
ghpghx_output[:outputs][:heat_pump_configuration] = "WSHP"
|
|
320
|
-
#
|
|
321
|
-
|
|
371
|
+
# populate with near zero hourly values to meet reopts formatting requirements
|
|
372
|
+
# This is not used in REopt calculation but required for formatting.
|
|
373
|
+
ghpghx_output[:outputs][:yearly_total_electric_consumption_series_kw] = @@small_multiplier * @@hours_in_year
|
|
322
374
|
ghpghx_output[:outputs][:yearly_heating_heatpump_electric_consumption_series_kw] = [0] * @@hours_in_year
|
|
323
375
|
ghpghx_output[:outputs][:yearly_cooling_heatpump_electric_consumption_series_kw] = [0] * @@hours_in_year
|
|
324
376
|
|
|
@@ -332,6 +384,144 @@ module URBANopt # :nodoc:
|
|
|
332
384
|
File.write(json_file_path, pretty_json)
|
|
333
385
|
end
|
|
334
386
|
|
|
387
|
+
def create_reopt_input_building_bau(run_dir, system_parameter_hash, reopt_ghp_assumptions_hash, building_id, modelica_result)
|
|
388
|
+
reopt_inputs_building_bau = {}
|
|
389
|
+
if !reopt_ghp_assumptions_hash.nil?
|
|
390
|
+
reopt_inputs_building_bau = reopt_ghp_assumptions_hash
|
|
391
|
+
else
|
|
392
|
+
@@logger.info('Using default REopt assumptions')
|
|
393
|
+
# create a dictionary for REopt Inputs
|
|
394
|
+
reopt_inputs_building_bau = {
|
|
395
|
+
Site: {},
|
|
396
|
+
SpaceHeatingLoad: {},
|
|
397
|
+
DomesticHotWaterLoad: {},
|
|
398
|
+
ElectricLoad: {},
|
|
399
|
+
ElectricTariff: {
|
|
400
|
+
urdb_label: ""
|
|
401
|
+
},
|
|
402
|
+
ExistingBoiler: {}
|
|
403
|
+
}
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
# The URDB label is required to be specified in the input assumption file
|
|
407
|
+
if reopt_inputs_building_bau.nil? || reopt_inputs_building_bau[:ElectricTariff].nil? || reopt_inputs_building_bau[:ElectricTariff][:urdb_label].nil? || reopt_inputs_building_bau[:ElectricTariff][:urdb_label].empty?
|
|
408
|
+
raise "Missing value for urdb_label - this is a required input"
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
scenario_json_path = File.join(run_dir, "default_scenario_report.json")
|
|
412
|
+
if File.exist?(scenario_json_path)
|
|
413
|
+
File.open(scenario_json_path, 'r') do |file|
|
|
414
|
+
scenario_json_data = JSON.parse(file.read, symbolize_names: true)
|
|
415
|
+
# update site location
|
|
416
|
+
@latitude = scenario_json_data[:scenario_report][:location][:latitude_deg]
|
|
417
|
+
@longitude = scenario_json_data[:scenario_report][:location][:longitude_deg]
|
|
418
|
+
reopt_inputs_building_bau[:Site][:latitude] = @latitude
|
|
419
|
+
reopt_inputs_building_bau[:Site][:longitude] = @longitude
|
|
420
|
+
|
|
421
|
+
end
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
reopt_inputs_building_bau[:SpaceHeatingLoad][:fuel_loads_mmbtu_per_hour] = []
|
|
425
|
+
# Read the default csv report
|
|
426
|
+
default_feature_report_path = File.join(run_dir, building_id.to_s, "feature_reports", "default_feature_report.csv")
|
|
427
|
+
if File.exist?(default_feature_report_path)
|
|
428
|
+
timeseries_data = CSV.read(default_feature_report_path, headers: true)
|
|
429
|
+
|
|
430
|
+
# Initialize the total kBtu sum
|
|
431
|
+
heating_kbtu = 0.0
|
|
432
|
+
cooling_kbtu = 0.0
|
|
433
|
+
total_kwh_heating = 0.0
|
|
434
|
+
total_kwh_cooling = 0.0
|
|
435
|
+
|
|
436
|
+
# TODO : add other Heating Fuels if present (Heating:Propane(kBtu) etc.)
|
|
437
|
+
# Convert each value in "Heating:NaturalGas(kBtu)" to MMBtu and store in the array
|
|
438
|
+
timeseries_data.each do |row|
|
|
439
|
+
if row['Heating:NaturalGas(kBtu)'] # Ensure the value exists
|
|
440
|
+
heating_kBtu_value = row['Heating:NaturalGas(kBtu)'].to_f # Convert to float
|
|
441
|
+
heating_kbtu += heating_kBtu_value # Sum kBtu values
|
|
442
|
+
end
|
|
443
|
+
if row.headers.include?('Cooling:NaturalGas(kBtu)') && row['Cooling:NaturalGas(kBtu)']
|
|
444
|
+
cooling_kBtu_value = row['Cooling:NaturalGas(kBtu)'].to_f # Convert to float
|
|
445
|
+
cooling_kbtu += cooling_kBtu_value
|
|
446
|
+
end
|
|
447
|
+
if row['Heating:Electricity(kWh)']
|
|
448
|
+
heating_value = row['Heating:Electricity(kWh)'].to_f # Convert to float
|
|
449
|
+
total_kwh_heating += heating_value # Sum heating values
|
|
450
|
+
end
|
|
451
|
+
if row['Cooling:Electricity(kWh)']
|
|
452
|
+
cooling_value = row['Cooling:Electricity(kWh)'].to_f # Convert to float
|
|
453
|
+
total_kwh_cooling += cooling_value # Sum cooling values
|
|
454
|
+
end
|
|
455
|
+
end
|
|
456
|
+
# Check if the heating kBtu is zero
|
|
457
|
+
if heating_kbtu.zero?
|
|
458
|
+
# If zero, populate with near zero hourly values to meet reopts formatting requirements
|
|
459
|
+
reopt_inputs_building_bau[:SpaceHeatingLoad][:fuel_loads_mmbtu_per_hour] = @@small_multiplier * @@hours_in_year
|
|
460
|
+
else
|
|
461
|
+
# If not zero, convert and append to the array
|
|
462
|
+
timeseries_data.each do |row|
|
|
463
|
+
if row['Heating:NaturalGas(kBtu)'] # Ensure the value exists
|
|
464
|
+
kBtu_value = row['Heating:NaturalGas(kBtu)'].to_f # Convert to float
|
|
465
|
+
mMBtu_value = kBtu_value / 1000 # Convert kBtu to MMBtu
|
|
466
|
+
reopt_inputs_building_bau[:SpaceHeatingLoad][:fuel_loads_mmbtu_per_hour] << mMBtu_value # Append to the array
|
|
467
|
+
end
|
|
468
|
+
end
|
|
469
|
+
# Add fuel cost for existing boiler
|
|
470
|
+
reopt_inputs_building_bau[:ExistingBoiler][:fuel_cost_per_mmbtu] = @@nat_gas_dollars_per_mmbtu
|
|
471
|
+
end
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
# if cooling_kbtu.zero?
|
|
475
|
+
# # If zero, populate with near zero hourly values to meet reopts formatting requirements
|
|
476
|
+
# reopt_inputs_building_bau[:CoolingLoad][:fuel_loads_mmbtu_per_hour] = @@small_multiplier * @@hours_in_year
|
|
477
|
+
# else
|
|
478
|
+
|
|
479
|
+
# if there is cooling through natural gas it needs to be added to coolingload. If there is electricity based cooling, it is included in electric load
|
|
480
|
+
if !cooling_kbtu.zero?
|
|
481
|
+
# Add fuel load values for cooling
|
|
482
|
+
reopt_inputs_building_bau[:CoolingLoad] = {}
|
|
483
|
+
# If not zero, convert and append to the array
|
|
484
|
+
timeseries_data.each do |row|
|
|
485
|
+
if row['Cooling:NaturalGas(kBtu)'] # Ensure the value exists
|
|
486
|
+
kBtu_value = row['Cooling:NaturalGas(kBtu)'].to_f # Convert to float
|
|
487
|
+
ton_value = kBtu_value / 12 # Convert kBtu to ton
|
|
488
|
+
reopt_inputs_building_bau[:CoolingLoad][:thermal_loads_ton] << ton_value # Append to the array
|
|
489
|
+
end
|
|
490
|
+
end
|
|
491
|
+
# Add fuel cost for existing chiller
|
|
492
|
+
reopt_inputs_building_bau[:ExistingChiller][:fuel_cost_per_mmbtu] = @@nat_gas_dollars_per_mmbtu
|
|
493
|
+
end
|
|
494
|
+
|
|
495
|
+
total_kwh_load = total_kwh_heating + total_kwh_cooling
|
|
496
|
+
reopt_inputs_building_bau[:ElectricLoad][:year] = @@year_of_simulation
|
|
497
|
+
|
|
498
|
+
# Check if the total kwh is zero
|
|
499
|
+
if total_kwh_load.zero?
|
|
500
|
+
# This is the total of heating and cooling electricity loads
|
|
501
|
+
reopt_inputs_building_bau[:ElectricLoad][:loads_kw] = @@small_multiplier * @@hours_in_year
|
|
502
|
+
else
|
|
503
|
+
total_value_kwh = [] # Initialize the array
|
|
504
|
+
|
|
505
|
+
timeseries_data.each do |row|
|
|
506
|
+
heating_value_kwh = row['Heating:Electricity(kWh)'] ? row['Heating:Electricity(kWh)'].to_f : 0.0
|
|
507
|
+
cooling_value_kwh = row['Cooling:Electricity(kWh)'] ? row['Cooling:Electricity(kWh)'].to_f : 0.0
|
|
508
|
+
|
|
509
|
+
total = heating_value_kwh + cooling_value_kwh # Sum the values
|
|
510
|
+
total_value_kwh << total # Append to the array
|
|
511
|
+
reopt_inputs_building_bau[:ElectricLoad][:loads_kw] = total_value_kwh
|
|
512
|
+
end
|
|
513
|
+
end
|
|
514
|
+
|
|
515
|
+
end
|
|
516
|
+
|
|
517
|
+
#save output report in reopt_ghp directory
|
|
518
|
+
reopt_ghp_dir = File.join(run_dir, "reopt_ghp", "reopt_ghp_inputs")
|
|
519
|
+
json_file_path = File.join(reopt_ghp_dir, "BAU_building_#{building_id}.json")
|
|
520
|
+
pretty_json = JSON.pretty_generate(reopt_inputs_building_bau)
|
|
521
|
+
File.write(json_file_path, pretty_json)
|
|
522
|
+
|
|
523
|
+
end
|
|
524
|
+
|
|
335
525
|
end
|
|
336
526
|
end
|
|
337
527
|
end
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# *********************************************************************************
|
|
2
|
-
# URBANopt (tm), Copyright (c) Alliance for
|
|
2
|
+
# URBANopt (tm), Copyright (c) Alliance for Energy Innovation, LLC.
|
|
3
3
|
# See also https://github.com/urbanopt/urbanopt-reopt-gem/blob/develop/LICENSE.md
|
|
4
4
|
# *********************************************************************************
|
|
5
5
|
|
|
@@ -22,7 +22,7 @@ module URBANopt # :nodoc:
|
|
|
22
22
|
if [nil, '', '<insert your key here>'].include? DEVELOPER_NREL_KEY
|
|
23
23
|
raise 'A developer.nrel.gov API key is required. Please see https://developer.nrel.gov/signup/ then update the file urbanopt-reopt-gem/developer_nrel_key.rb'
|
|
24
24
|
else
|
|
25
|
-
#Store the
|
|
25
|
+
#Store the NLR developer key
|
|
26
26
|
nrel_developer_key = DEVELOPER_NREL_KEY
|
|
27
27
|
end
|
|
28
28
|
end
|
|
@@ -111,7 +111,7 @@ module URBANopt # :nodoc:
|
|
|
111
111
|
run_id
|
|
112
112
|
end
|
|
113
113
|
|
|
114
|
-
def reopt_request(results_url, poll_interval = 5, max_timeout =
|
|
114
|
+
def reopt_request(results_url, poll_interval = 5, max_timeout = 600)
|
|
115
115
|
|
|
116
116
|
key_error_count = 0
|
|
117
117
|
key_error_threshold = 3
|
|
@@ -14,9 +14,9 @@
|
|
|
14
14
|
"urdb_label": "594976725457a37b1175d089"
|
|
15
15
|
},
|
|
16
16
|
"GHP":{
|
|
17
|
-
"installed_cost_heatpump_per_ton":
|
|
18
|
-
"installed_cost_ghx_per_ft":
|
|
19
|
-
"installed_cost_building_hydronic_loop_per_sqft":
|
|
17
|
+
"installed_cost_heatpump_per_ton": 1904,
|
|
18
|
+
"installed_cost_ghx_per_ft": 17,
|
|
19
|
+
"installed_cost_building_hydronic_loop_per_sqft": 0,
|
|
20
20
|
"om_cost_per_sqft_year": 0,
|
|
21
21
|
"macrs_bonus_fraction": 0.6,
|
|
22
22
|
"macrs_itc_reduction": 0.5,
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
# *********************************************************************************
|
|
2
|
-
# URBANopt (tm), Copyright (c) Alliance for
|
|
2
|
+
# URBANopt (tm), Copyright (c) Alliance for Energy Innovation, LLC.
|
|
3
3
|
# See also https://github.com/urbanopt/urbanopt-reopt-gem/blob/develop/LICENSE.md
|
|
4
4
|
# *********************************************************************************
|
|
5
5
|
|
|
6
6
|
require 'bundler/setup'
|
|
7
7
|
require 'urbanopt/reopt/reopt_logger'
|
|
8
8
|
require 'urbanopt/reopt/reopt_ghp_api'
|
|
9
|
+
require 'urbanopt/reopt/reopt_ghp_result'
|
|
9
10
|
require 'csv'
|
|
10
11
|
require 'json'
|
|
11
12
|
require 'fileutils'
|
|
@@ -115,12 +116,14 @@ module URBANopt # :nodoc:
|
|
|
115
116
|
end
|
|
116
117
|
|
|
117
118
|
building_ids.each do |building_id|
|
|
118
|
-
# create REopt building input file for all buildings in loop order list
|
|
119
|
-
reopt_input_building = adapter.
|
|
119
|
+
# create REopt building input file for all buildings in loop order list in GHP scenario
|
|
120
|
+
reopt_input_building = adapter.create_reopt_input_building_ghp(@run_dir, @system_parameter_input_hash, @reopt_ghp_assumptions_input_hash, building_id, @modelica_result_input)
|
|
121
|
+
#create REopt building input file for all buildings in loop order list in BAU scenario
|
|
122
|
+
reopt_input_building_bau = adapter.create_reopt_input_building_bau(@run_dir, @system_parameter_input_hash, @reopt_ghp_assumptions_input_hash, building_id, @modelica_result_input)
|
|
120
123
|
end
|
|
121
124
|
ghp_ids.each do |ghp_id|
|
|
122
125
|
# create REopt district input file
|
|
123
|
-
reopt_input_district = adapter.
|
|
126
|
+
reopt_input_district = adapter.create_reopt_input_district_ghp(@run_dir, @system_parameter_input_hash, @reopt_ghp_assumptions_input_hash, ghp_id, @modelica_result_input)
|
|
124
127
|
end
|
|
125
128
|
|
|
126
129
|
Dir.foreach(reopt_ghp_input) do |input_file|
|
|
@@ -142,8 +145,14 @@ module URBANopt # :nodoc:
|
|
|
142
145
|
# call the REopt API
|
|
143
146
|
api = URBANopt::REopt::REoptLiteGHPAPI.new(reopt_input_data, DEVELOPER_NREL_KEY, reopt_output_file, @localhost)
|
|
144
147
|
api.get_api_results
|
|
148
|
+
|
|
145
149
|
end
|
|
150
|
+
|
|
151
|
+
## POST PROCESS RESULTS
|
|
152
|
+
ghp_results = URBANopt::REopt::REoptGHPResult.new
|
|
153
|
+
results = ghp_results.result_calculate(reopt_ghp_dir)
|
|
146
154
|
end
|
|
155
|
+
|
|
147
156
|
end # REoptGHPPostProcessor
|
|
148
157
|
end # REopt
|
|
149
158
|
end # URBANopt
|