urbanopt-reopt 0.12.0 → 1.0.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 +4 -7
- data/.gitignore +1 -1
- data/CHANGELOG.md +18 -0
- data/Gemfile +16 -11
- data/LICENSE.md +1 -1
- data/doc_templates/LICENSE.md +1 -1
- data/doc_templates/copyright_erb.txt +1 -1
- data/docs/schemas/reopt-output-schema.md +1 -1
- data/lib/urbanopt/reopt/feature_report_adapter.rb +6 -7
- data/lib/urbanopt/reopt/reopt_ghp_adapter.rb +337 -0
- data/lib/urbanopt/reopt/reopt_ghp_api.rb +156 -0
- data/lib/urbanopt/reopt/reopt_ghp_files/reopt_ghp_assumption.json +27 -0
- data/lib/urbanopt/reopt/reopt_ghp_post_processor.rb +149 -0
- data/lib/urbanopt/reopt/reopt_lite_api.rb +8 -9
- data/lib/urbanopt/reopt/reopt_logger.rb +1 -1
- data/lib/urbanopt/reopt/reopt_post_processor.rb +2 -3
- data/lib/urbanopt/reopt/reopt_schema/REopt-GHP-input.json +148 -0
- data/lib/urbanopt/reopt/reopt_schema/reopt_input_schema.json +2 -2
- data/lib/urbanopt/reopt/reopt_schema/reopt_output_schema.json +40 -0
- data/lib/urbanopt/reopt/scenario_report_adapter.rb +6 -10
- data/lib/urbanopt/reopt/utilities.rb +2 -2
- data/lib/urbanopt/reopt/version.rb +1 -1
- data/lib/urbanopt/reopt.rb +3 -0
- data/urbanopt-reopt.gemspec +9 -10
- metadata +27 -64
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d7585e601e08ef8ae4b677124c3f6790129b44dbfe7da4c701168698437df94c
|
4
|
+
data.tar.gz: fb375bcd95645f6821ee8f06fce5d27eb682f1253db82d71ada98b9f043c921f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a8d65764ca0eb6ec58a63f188882c2e70ba5457299f7ddbac75c7a98cc5d2762f62a82f1ecb7cb5d61c330d9ad29d6e7a38d1b24ee429c5d30ef910199f1919c
|
7
|
+
data.tar.gz: c91ec78859161c11e381771b0a5f231e7ee168dc758f49f9e80035e15483ca8c1b95c42a32334dca830f60c67d167c5016cc6ea776be2cff39180bde42091852
|
@@ -2,13 +2,11 @@ name: REopt-gem CI
|
|
2
2
|
|
3
3
|
on:
|
4
4
|
workflow_dispatch:
|
5
|
-
|
5
|
+
push:
|
6
6
|
schedule:
|
7
7
|
# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#schedule
|
8
8
|
# 5:23 am UTC (11:23pm MDT the day before) every weekday night in MDT
|
9
9
|
- cron: '23 5 * * 2-6'
|
10
|
-
pull_request:
|
11
|
-
types: [review_requested]
|
12
10
|
|
13
11
|
env:
|
14
12
|
# This env var should enforce develop branch of all dependencies
|
@@ -21,17 +19,16 @@ jobs:
|
|
21
19
|
# https://github.com/rbenv/ruby-build/discussions/1940
|
22
20
|
runs-on: ubuntu-latest
|
23
21
|
container:
|
24
|
-
image: docker://nrel/openstudio:3.
|
22
|
+
image: docker://nrel/openstudio:3.9.0
|
25
23
|
steps:
|
26
24
|
- uses: actions/checkout@v4
|
27
25
|
- name: set git config options
|
28
26
|
shell: bash
|
29
|
-
run:
|
30
|
-
git config --global --add safe.directory '*'
|
27
|
+
run: git config --global --add safe.directory '*'
|
31
28
|
- name: Update gems
|
32
29
|
run: |
|
30
|
+
bundle install
|
33
31
|
bundle update
|
34
|
-
bundle exec certified-update
|
35
32
|
- name: Run Rspec
|
36
33
|
run: bundle exec rspec
|
37
34
|
- name: Coveralls
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,12 +1,29 @@
|
|
1
1
|
# URBANopt REopt Gem
|
2
2
|
|
3
|
+
## Version 1.0.0
|
4
|
+
|
5
|
+
## What's Changed
|
6
|
+
|
7
|
+
* Upgrade to OpenStudio 3.9 and Ruby 3.2.2 by @vtnate in https://github.com/urbanopt/urbanopt-reopt-gem/pull/155
|
8
|
+
* Updates for REopt v3.11 by @vtnate in https://github.com/urbanopt/urbanopt-reopt-gem/pull/157
|
9
|
+
|
10
|
+
### Other Changes
|
11
|
+
|
12
|
+
* expose error messages for certificate verify failed by @kflemin in https://github.com/urbanopt/urbanopt-reopt-gem/pull/151
|
13
|
+
* REopt GHP LCCA Analysis by @tanushree04 in https://github.com/urbanopt/urbanopt-reopt-gem/pull/153
|
14
|
+
|
15
|
+
**Full Changelog**: https://github.com/urbanopt/urbanopt-reopt-gem/compare/v0.12.0...v1.0.0
|
16
|
+
|
3
17
|
## Version 0.12.0
|
18
|
+
|
4
19
|
Date Range: 01/11/24 - 05/06/24
|
20
|
+
|
5
21
|
* Use Reopt v3 by @vtnate in https://github.com/urbanopt/urbanopt-reopt-gem/pull/149
|
6
22
|
|
7
23
|
**Full Changelog**: https://github.com/urbanopt/urbanopt-reopt-gem/compare/v0.11.0...v0.12.0
|
8
24
|
|
9
25
|
## Version 0.11.0
|
26
|
+
|
10
27
|
Date Range: 07/06/23 - 01/11/24
|
11
28
|
|
12
29
|
* Use different error messages in different places by @vtnate in https://github.com/urbanopt/urbanopt-reopt-gem/pull/145
|
@@ -17,6 +34,7 @@ Date Range: 07/06/23 - 01/11/24
|
|
17
34
|
**Full Changelog**: https://github.com/urbanopt/urbanopt-reopt-gem/compare/v0.10.0...v0.11.0
|
18
35
|
|
19
36
|
## Version 0.10.0
|
37
|
+
|
20
38
|
Date Range: 12/13/22 - 7/6/23
|
21
39
|
|
22
40
|
- Update dependencies for OpenStudio 3.6.1
|
data/Gemfile
CHANGED
@@ -17,7 +17,9 @@ allow_local = ENV['FAVOR_LOCAL_GEMS']
|
|
17
17
|
# if allow_local && File.exist?('../OpenStudio-extension-gem')
|
18
18
|
# gem 'openstudio-extension', path: '../OpenStudio-extension-gem'
|
19
19
|
# elsif allow_local
|
20
|
-
#
|
20
|
+
# gem 'openstudio-extension', github: 'NREL/OpenStudio-extension-gem', branch: 'develop'
|
21
|
+
# else
|
22
|
+
# gem 'openstudio-extension', '~> 0.8.1'
|
21
23
|
# end
|
22
24
|
#
|
23
25
|
# if allow_local && File.exist?('../openstudio-common-measures-gem')
|
@@ -41,14 +43,17 @@ allow_local = ENV['FAVOR_LOCAL_GEMS']
|
|
41
43
|
# gem 'openstudio-model-articulation', '0.1.0'
|
42
44
|
# end
|
43
45
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
46
|
+
if allow_local && File.exist?('../urbanopt-scenario-gem')
|
47
|
+
gem 'urbanopt-scenario', path: '../urbanopt-scenario-gem'
|
48
|
+
elsif allow_local
|
49
|
+
gem 'urbanopt-scenario', github: 'URBANopt/urbanopt-scenario-gem', branch: 'develop'
|
50
|
+
end
|
49
51
|
|
50
|
-
#
|
51
|
-
#
|
52
|
-
|
53
|
-
|
54
|
-
|
52
|
+
# Temporary! Remove this once reporting-gem is merged/released
|
53
|
+
# gem 'urbanopt-reporting', github: 'URBANopt/urbanopt-reporting-gem', branch: 'develop'
|
54
|
+
|
55
|
+
if allow_local && File.exist?('../urbanopt-geojson-gem')
|
56
|
+
gem 'urbanopt-geojson', path: '../urbanopt-geojson-gem'
|
57
|
+
elsif allow_local
|
58
|
+
gem 'urbanopt-geojson', github: 'URBANopt/urbanopt-geojson-gem', branch: 'develop'
|
59
|
+
end
|
data/LICENSE.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
URBANopt (tm), Copyright (c) 2019-
|
1
|
+
URBANopt (tm), Copyright (c) 2019-2025, Alliance for Sustainable Energy, LLC, and other
|
2
2
|
contributors. All rights reserved.
|
3
3
|
|
4
4
|
Redistribution and use in source and binary forms, with or without modification,
|
data/doc_templates/LICENSE.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
URBANopt (tm), Copyright (c) 2019-
|
1
|
+
URBANopt (tm), Copyright (c) 2019-2025, Alliance for Sustainable Energy, LLC, and other
|
2
2
|
contributors. All rights reserved.
|
3
3
|
|
4
4
|
Redistribution and use in source and binary forms, with or without modification,
|
@@ -1,6 +1,6 @@
|
|
1
1
|
<%
|
2
2
|
# *********************************************************************************
|
3
|
-
# URBANopt (tm), Copyright (c) 2019-
|
3
|
+
# URBANopt (tm), Copyright (c) 2019-2025, Alliance for Sustainable Energy, LLC, and other
|
4
4
|
# contributors. All rights reserved.
|
5
5
|
|
6
6
|
# Redistribution and use in source and binary forms, with or without modification,
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# REopt Lite Outputs Schema
|
2
2
|
|
3
|
-
When the gem calls the REopt Lite APUI it
|
3
|
+
When the gem calls the REopt Lite APUI it receives the following complete set of results described in the data dictionary below. Only those needed to update a Feature or Scenario Report's distributed_generation attribute set and timeseries CSV are pulled from the response and transferred to the Feature or Scenario Report. You may choose to modify the code to include more or less of the full REopt Lite response.
|
4
4
|
|
5
5
|
## Data Dictionary
|
6
6
|
<ReoptOutputSchema />
|
@@ -41,9 +41,9 @@ module URBANopt # :nodoc:
|
|
41
41
|
else
|
42
42
|
@@logger.info('Using default REopt assumptions')
|
43
43
|
reopt_inputs = {
|
44
|
-
Settings:{},
|
44
|
+
Settings: {},
|
45
45
|
Site: {},
|
46
|
-
Financial:{},
|
46
|
+
Financial: {},
|
47
47
|
ElectricTariff: {
|
48
48
|
monthly_demand_rates: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
49
49
|
monthly_energy_rates: [0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13]
|
@@ -151,14 +151,13 @@ module URBANopt # :nodoc:
|
|
151
151
|
#
|
152
152
|
# [*parameters:*]
|
153
153
|
#
|
154
|
-
# * +feature_report+ - _URBANopt::Reporting::DefaultReports::FeatureReport_ - FeatureReport to update from a \REopt
|
155
|
-
# * +reopt_output+ - _Hash_ - A
|
154
|
+
# * +feature_report+ - _URBANopt::Reporting::DefaultReports::FeatureReport_ - FeatureReport to update from a \REopt response hash.
|
155
|
+
# * +reopt_output+ - _Hash_ - A response hash from the \REopt API to use in overwriting FeatureReport technology sizes, costs and dispatch strategies.
|
156
156
|
# * +timeseries_csv_path+ - _String_ - Optional. The path to a file at which a new timeseries CSV will be written. If not provided a file is created based on the run_uuid of the \REopt optimization task.
|
157
157
|
#
|
158
158
|
# [*return:*] _URBANopt::Reporting::DefaultReports::FeatureReport_ - Returns an updated FeatureReport.
|
159
159
|
##
|
160
160
|
def update_feature_report(feature_report, reopt_output, timeseries_csv_path = nil, resilience_stats = nil)
|
161
|
-
|
162
161
|
# Check if the \REopt response is valid
|
163
162
|
if reopt_output['status'] != 'optimal'
|
164
163
|
@@logger.error("ERROR cannot update Feature Report #{feature_report.name} #{feature_report.id} - REopt optimization was non-optimal")
|
@@ -439,7 +438,7 @@ module URBANopt # :nodoc:
|
|
439
438
|
x[$storage_to_grid_col] = $storage_to_grid[i] || 0 if defined?(storage)
|
440
439
|
x[$storage_soc_col] = $storage_soc[i] || 0 if defined?(storage)
|
441
440
|
x[$generator_total_col] = $generator_total[i] || 0 if defined?(generator)
|
442
|
-
x[$generator_to_battery_col] = $generator_to_battery[i] || 0 if
|
441
|
+
x[$generator_to_battery_col] = $generator_to_battery[i] || 0 if defined?(generator) && defined?(storage)
|
443
442
|
x[$generator_to_load_col] = $generator_to_load[i] || 0 if defined?(generator)
|
444
443
|
x[$generator_to_grid_col] = $generator_to_grid[i] || 0 if defined?(generator)
|
445
444
|
x[$pv_total_col] = $pv_total[i] || 0
|
@@ -447,7 +446,7 @@ module URBANopt # :nodoc:
|
|
447
446
|
x[$pv_to_load_col] = $pv_to_load[i] || 0
|
448
447
|
x[$pv_to_grid_col] = $pv_to_grid[i] || 0
|
449
448
|
x[$wind_total_col] = $wind_total[i] || 0 if defined?(wind)
|
450
|
-
x[$wind_to_battery_col] = $wind_to_battery[i] || 0 if
|
449
|
+
x[$wind_to_battery_col] = $wind_to_battery[i] || 0 if defined?(wind) && defined?(storage)
|
451
450
|
x[$wind_to_load_col] = $wind_to_load[i] || 0 if defined?(wind)
|
452
451
|
x[$wind_to_grid_col] = $wind_to_grid[i] || 0 if defined?(wind)
|
453
452
|
return x
|
@@ -0,0 +1,337 @@
|
|
1
|
+
# *********************************************************************************
|
2
|
+
# URBANopt (tm), Copyright (c) Alliance for Sustainable Energy, LLC.
|
3
|
+
# See also https://github.com/urbanopt/urbanopt-reopt-gem/blob/develop/LICENSE.md
|
4
|
+
# *********************************************************************************
|
5
|
+
|
6
|
+
module URBANopt # :nodoc:
|
7
|
+
module REopt # :nodoc:
|
8
|
+
class REoptGHPAdapter
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
# initialize @@logger
|
12
|
+
@@logger ||= URBANopt::REopt.reopt_logger
|
13
|
+
# Define class variable
|
14
|
+
@@hours_in_year = 8760
|
15
|
+
end
|
16
|
+
|
17
|
+
def create_reopt_input_building(run_dir, system_parameter_hash, reopt_ghp_assumptions_hash, building_id, modelica_result)
|
18
|
+
|
19
|
+
# Define variables
|
20
|
+
reopt_inputs_building = {}
|
21
|
+
if !reopt_ghp_assumptions_hash.nil?
|
22
|
+
reopt_inputs_building = reopt_ghp_assumptions_hash
|
23
|
+
else
|
24
|
+
@@logger.info('Using default REopt assumptions')
|
25
|
+
# create a dictionary for REopt Inputs
|
26
|
+
reopt_inputs_building = {
|
27
|
+
Site: {},
|
28
|
+
SpaceHeatingLoad: {},
|
29
|
+
DomesticHotWaterLoad: {},
|
30
|
+
ElectricLoad: {},
|
31
|
+
ElectricTariff: {
|
32
|
+
urdb_label: ""
|
33
|
+
},
|
34
|
+
GHP: {},
|
35
|
+
ExistingBoiler: {}
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
# The URDB label is required to be specified in the input assumption file
|
40
|
+
if reopt_inputs_building[:ElectricTariff][:urdb_label].nil? || reopt_inputs_building[:ElectricTariff][:urdb_label].empty?
|
41
|
+
raise "Missing value for urdb_label - this is a required input"
|
42
|
+
end
|
43
|
+
|
44
|
+
scenario_json_path = File.join(run_dir, "default_scenario_report.json")
|
45
|
+
if File.exist?(scenario_json_path)
|
46
|
+
File.open(scenario_json_path, 'r') do |file|
|
47
|
+
scenario_json_data = JSON.parse(file.read, symbolize_names: true)
|
48
|
+
# update site location
|
49
|
+
@latitude = scenario_json_data[:scenario_report][:location][:latitude_deg]
|
50
|
+
@longitude = scenario_json_data[:scenario_report][:location][:longitude_deg]
|
51
|
+
reopt_inputs_building[:Site][:latitude] = @latitude
|
52
|
+
reopt_inputs_building[:Site][:longitude] = @longitude
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
reopt_inputs_building[:SpaceHeatingLoad][:fuel_loads_mmbtu_per_hour] = []
|
58
|
+
# Read the default csv report
|
59
|
+
default_feature_report_path = File.join(run_dir, building_id.to_s, "feature_reports", "default_feature_report.csv")
|
60
|
+
if File.exist?(default_feature_report_path)
|
61
|
+
timeseries_data = CSV.read(default_feature_report_path, headers: true)
|
62
|
+
|
63
|
+
# Initialize the total kBtu sum
|
64
|
+
total_kbtu = 0.0
|
65
|
+
|
66
|
+
# Convert each value in "Heating:NaturalGas(kBtu)" to MMBtu and store in the array
|
67
|
+
timeseries_data.each do |row|
|
68
|
+
if row['Heating:NaturalGas(kBtu)'] # Ensure the value exists
|
69
|
+
kBtu_value = row['Heating:NaturalGas(kBtu)'].to_f # Convert to float
|
70
|
+
total_kbtu += kBtu_value # Sum kBtu values
|
71
|
+
end
|
72
|
+
end
|
73
|
+
# Check if the total kBtu is zero
|
74
|
+
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] = [0.000001] * @@hours_in_year
|
77
|
+
else
|
78
|
+
# If not zero, convert and append to the array
|
79
|
+
timeseries_data.each do |row|
|
80
|
+
if row['Heating:NaturalGas(kBtu)'] # Ensure the value exists
|
81
|
+
kBtu_value = row['Heating:NaturalGas(kBtu)'].to_f # Convert to float
|
82
|
+
mMBtu_value = kBtu_value / 1000 # Convert kBtu to MMBtu
|
83
|
+
reopt_inputs_building[:SpaceHeatingLoad][:fuel_loads_mmbtu_per_hour] << mMBtu_value # Append to the array
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
else
|
89
|
+
# Calculate space heating load
|
90
|
+
reopt_inputs_building[:SpaceHeatingLoad][:fuel_loads_mmbtu_per_hour] = [0.000001] * @@hours_in_year
|
91
|
+
puts "Existing heating fuel cost was not taken into consideration in result calculations."
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
# read_modelica_result
|
96
|
+
modelica_project = File.expand_path(modelica_result)
|
97
|
+
project_name = File.basename(modelica_project)
|
98
|
+
@modelica_csv = File.join(
|
99
|
+
modelica_project,
|
100
|
+
"#{project_name}.Districts.DistrictEnergySystem_results",
|
101
|
+
"#{project_name}.Districts.DistrictEnergySystem_result.csv"
|
102
|
+
)
|
103
|
+
|
104
|
+
if File.exist?(@modelica_csv)
|
105
|
+
modelica_data = CSV.read(@modelica_csv, headers: true)
|
106
|
+
heating_power = "heating_electric_power_#{building_id}"
|
107
|
+
cooling_power = "cooling_electric_power_#{building_id}"
|
108
|
+
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
|
+
|
113
|
+
heating_power_values = cooling_power_values = pump_power_values = ets_pump_power_values = []
|
114
|
+
total_electric_load_building = []
|
115
|
+
# Ensure the column exists
|
116
|
+
if modelica_data.headers.include?(heating_power)
|
117
|
+
heating_power_values = modelica_data[heating_power]
|
118
|
+
end
|
119
|
+
if modelica_data.headers.include?(cooling_power)
|
120
|
+
cooling_power_values = modelica_data[cooling_power]
|
121
|
+
end
|
122
|
+
if modelica_data.headers.include?(pump_power)
|
123
|
+
pump_power_values = modelica_data[pump_power]
|
124
|
+
end
|
125
|
+
if modelica_data.headers.include?(ets_pump_power)
|
126
|
+
ets_pump_power_values = modelica_data[ets_pump_power]
|
127
|
+
end
|
128
|
+
|
129
|
+
total_electric_load_building = heating_power_values.zip(cooling_power_values, pump_power_values, ets_pump_power_values).map do |elements|
|
130
|
+
# Convert watts to kilowatts
|
131
|
+
elements.map { |e| e.to_f / 1000 }.sum
|
132
|
+
end
|
133
|
+
|
134
|
+
peak_combined_heatpump_thermal_ton = 0
|
135
|
+
|
136
|
+
if modelica_data.headers.include?(heating_system_capacity)
|
137
|
+
heating_system_capacity_value = modelica_data[heating_system_capacity][0]
|
138
|
+
end
|
139
|
+
if modelica_data.headers.include?(cooling_system_capacity)
|
140
|
+
cooling_system_capacity_value = modelica_data[cooling_system_capacity][0]
|
141
|
+
end
|
142
|
+
|
143
|
+
watts_per_ton_cooling_capacity = 3517
|
144
|
+
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
|
+
|
146
|
+
# Store the result in reopt_inputs_building ElectricLoad
|
147
|
+
reopt_inputs_building[:ElectricLoad][:loads_kw] = total_electric_load_building
|
148
|
+
|
149
|
+
|
150
|
+
domestic_hot_water = total_electric_load_building.map do |load|
|
151
|
+
load * 0
|
152
|
+
end
|
153
|
+
|
154
|
+
# This is not used in REopt calculation but required for formatting.
|
155
|
+
reopt_inputs_building[:DomesticHotWaterLoad][:fuel_loads_mmbtu_per_hour] = domestic_hot_water
|
156
|
+
|
157
|
+
# Add GHP Fields
|
158
|
+
reopt_inputs_building[:GHP] = {}
|
159
|
+
# REopt default
|
160
|
+
reopt_inputs_building[:GHP][:require_ghp_purchase] = 1
|
161
|
+
reopt_inputs_building[:GHP][:om_cost_per_sqft_year] = 0
|
162
|
+
reopt_inputs_building[:GHP][:heatpump_capacity_sizing_factor_on_peak_load] = 1.0
|
163
|
+
# Add the floor area
|
164
|
+
building_json_path = File.join(run_dir, building_id.to_s, "feature_reports", "default_feature_report.json")
|
165
|
+
if File.exist?(building_json_path)
|
166
|
+
File.open(building_json_path, 'r') do |file|
|
167
|
+
building_json_data = JSON.parse(file.read, symbolize_names: true)
|
168
|
+
reopt_inputs_building[:GHP][:building_sqft] = building_json_data[:program][:floor_area_sqft]
|
169
|
+
end
|
170
|
+
else
|
171
|
+
puts "File not found: #{building_json_path}"
|
172
|
+
end
|
173
|
+
|
174
|
+
# Add existing boiler fuel cost
|
175
|
+
# TODO : Add this as optional user input
|
176
|
+
nat_gas_dollars_per_mmbtu = 13.5
|
177
|
+
reopt_inputs_building[:ExistingBoiler][:fuel_cost_per_mmbtu] = nat_gas_dollars_per_mmbtu
|
178
|
+
|
179
|
+
# Add ghpghx_responses
|
180
|
+
ghpghx_output = {}
|
181
|
+
ghpghx_output[:outputs] = {}
|
182
|
+
ghpghx_output[:inputs] = {}
|
183
|
+
ghpghx_output[:outputs][:heat_pump_configuration] = "WSHP"
|
184
|
+
# This is not used in REopt calculation but required for formatting.
|
185
|
+
ghpghx_output[:outputs][:yearly_ghx_pump_electric_consumption_series_kw] = [0] * @@hours_in_year
|
186
|
+
ghpghx_output[:outputs][:number_of_boreholes] = 0
|
187
|
+
ghpghx_output[:outputs][:length_boreholes_ft] = 0
|
188
|
+
|
189
|
+
ghpghx_output[:outputs][:peak_combined_heatpump_thermal_ton] = peak_combined_heatpump_thermal_ton
|
190
|
+
ghpghx_output[:outputs][:yearly_total_electric_consumption_kwh] = total_electric_load_building.sum
|
191
|
+
ghpghx_output[:outputs][:yearly_total_electric_consumption_series_kw] = total_electric_load_building
|
192
|
+
ghpghx_output[:outputs][:yearly_heating_heatpump_electric_consumption_series_kw] = total_electric_load_building
|
193
|
+
ghpghx_output[:outputs][:yearly_cooling_heatpump_electric_consumption_series_kw] = [0] * @@hours_in_year
|
194
|
+
# This is not used in REopt calculation but required for formatting.
|
195
|
+
ghpghx_output[:inputs][:heating_thermal_load_mmbtu_per_hr] = [0.0001] * @@hours_in_year
|
196
|
+
# This is not used in REopt calculation but required for formatting.
|
197
|
+
ghpghx_output[:inputs][:cooling_thermal_load_ton] = [0] * @@hours_in_year
|
198
|
+
|
199
|
+
ghpghx_output_all = [ghpghx_output]
|
200
|
+
reopt_inputs_building[:GHP][:ghpghx_responses] = {}
|
201
|
+
reopt_inputs_building[:GHP][:ghpghx_responses] = ghpghx_output_all
|
202
|
+
|
203
|
+
end
|
204
|
+
|
205
|
+
#save output report in reopt_ghp directory
|
206
|
+
reopt_ghp_dir = File.join(run_dir, "reopt_ghp", "reopt_ghp_inputs")
|
207
|
+
json_file_path = File.join(reopt_ghp_dir, "GHP_building_#{building_id}.json")
|
208
|
+
pretty_json = JSON.pretty_generate(reopt_inputs_building)
|
209
|
+
File.write(json_file_path, pretty_json)
|
210
|
+
|
211
|
+
end
|
212
|
+
|
213
|
+
def create_reopt_input_district(run_dir, system_parameter_hash, reopt_ghp_assumptions_hash, ghp_id, modelica_result)
|
214
|
+
|
215
|
+
reopt_inputs_district = {}
|
216
|
+
|
217
|
+
if !reopt_ghp_assumptions_hash.nil?
|
218
|
+
reopt_inputs_district = reopt_ghp_assumptions_hash
|
219
|
+
else
|
220
|
+
@@logger.info('Using default REopt assumptions')
|
221
|
+
# create a dictionary for REopt Inputs
|
222
|
+
reopt_inputs_district = {
|
223
|
+
Site: {},
|
224
|
+
SpaceHeatingLoad: {},
|
225
|
+
DomesticHotWaterLoad: {},
|
226
|
+
ElectricLoad: {},
|
227
|
+
ElectricTariff: {
|
228
|
+
"urdb_label": ""
|
229
|
+
},
|
230
|
+
GHP: {},
|
231
|
+
ExistingBoiler: {}
|
232
|
+
}
|
233
|
+
end
|
234
|
+
|
235
|
+
reopt_inputs_district[:Site] = {}
|
236
|
+
reopt_inputs_district[:Site][:latitude] = @latitude
|
237
|
+
reopt_inputs_district[:Site][:longitude] = @longitude
|
238
|
+
# The URDB label is required to be specified in the input assumption file
|
239
|
+
if reopt_inputs_district[:ElectricTariff][:urdb_label].nil? || reopt_inputs_district[:ElectricTariff][:urdb_label].empty?
|
240
|
+
|
241
|
+
raise "Missing value for urdb_label - this is a required input"
|
242
|
+
|
243
|
+
end
|
244
|
+
|
245
|
+
# This is not used in REopt calculation but required for formatting.
|
246
|
+
reopt_inputs_district[:SpaceHeatingLoad][:fuel_loads_mmbtu_per_hour] = [0.000001]*@@hours_in_year
|
247
|
+
# This is not used in REopt calculation but required for formatting.
|
248
|
+
reopt_inputs_district[:DomesticHotWaterLoad][:fuel_loads_mmbtu_per_hour] = [0.0000001]*@@hours_in_year
|
249
|
+
|
250
|
+
# Adding year for ElectricLoad so district electric load can be calculated with REopt API v3.11
|
251
|
+
reopt_inputs_district[:ElectricLoad] = {:year => 2017}
|
252
|
+
#required for reopt formatting
|
253
|
+
reopt_inputs_district[:ElectricLoad][:loads_kw] = [0.00001]*@@hours_in_year
|
254
|
+
|
255
|
+
reopt_inputs_district[:ExistingBoiler] = {}
|
256
|
+
reopt_inputs_district[:ExistingBoiler][:fuel_cost_per_mmbtu] = 13.5
|
257
|
+
|
258
|
+
# GHP inputs
|
259
|
+
reopt_inputs_district[:GHP] = {}
|
260
|
+
reopt_inputs_district[:GHP][:require_ghp_purchase] = 1
|
261
|
+
reopt_inputs_district[:GHP][:building_sqft] = 0.00001
|
262
|
+
reopt_inputs_district[:GHP][:om_cost_per_sqft_year] = 0
|
263
|
+
reopt_inputs_district[:GHP][:heatpump_capacity_sizing_factor_on_peak_load] = 1.0
|
264
|
+
|
265
|
+
# Add ghpghx outputs
|
266
|
+
ghpghx_output = {}
|
267
|
+
ghpghx_output[:outputs] = {}
|
268
|
+
ghpghx_output[:inputs] = {}
|
269
|
+
|
270
|
+
ghpghx_output[:inputs][:heating_thermal_load_mmbtu_per_hr] = [0]*@@hours_in_year
|
271
|
+
ghpghx_output[:inputs][:cooling_thermal_load_ton] = [0] * @@hours_in_year
|
272
|
+
|
273
|
+
|
274
|
+
# Read GHX sizes from system parameter hash
|
275
|
+
ghe_specific_params = system_parameter_hash[:district_system][:fifth_generation][:ghe_parameters][:ghe_specific_params]
|
276
|
+
ghe_specific_params.each do |ghe_specific_param|
|
277
|
+
if ghe_specific_param[:ghe_id] = ghp_id
|
278
|
+
number_of_boreholes = ghe_specific_param[:borehole][:number_of_boreholes]
|
279
|
+
length_of_boreholes = ghe_specific_param[:borehole][:length_of_boreholes]
|
280
|
+
ghpghx_output[:outputs][:number_of_boreholes] = number_of_boreholes
|
281
|
+
# convert meters to feet
|
282
|
+
ghpghx_output[:outputs][:length_boreholes_ft] = (length_of_boreholes)*3.28084
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
if File.exist?(@modelica_csv)
|
287
|
+
|
288
|
+
modelica_data = CSV.read(@modelica_csv, headers: true)
|
289
|
+
|
290
|
+
electrical_power_consumed = modelica_data["electrical_power_consumed"]
|
291
|
+
# Convert watts to kilowatts
|
292
|
+
electrical_power_consumed_kw = electrical_power_consumed.map { |e| e.to_f / 1000 }
|
293
|
+
# if ghp_id.include?('-')
|
294
|
+
# # Note: For some reason when reading columns, '-' from the column headers are removed, whereas ghp_id has -
|
295
|
+
# ghp_id_formatted = ghp_id.delete('-')
|
296
|
+
# ghp_column = "electrical_power_consumed_#{ghp_id_formatted}".to_sym
|
297
|
+
|
298
|
+
# else
|
299
|
+
# # Note: For some reason when reading columns, '-' from the column headers are removed, whereas ghp_id has -
|
300
|
+
# ghp_column = "electrical_power_consumed_#{ghp_id}".to_sym
|
301
|
+
# end
|
302
|
+
|
303
|
+
# # Ensure the column exists
|
304
|
+
# unless modelica_data.headers.include?(ghp_column)
|
305
|
+
# puts "Column #{ghp_column} does not exist in the CSV file."
|
306
|
+
# end
|
307
|
+
|
308
|
+
# # Access values from the column
|
309
|
+
# column_values = modelica_data.by_col[ghp_column]
|
310
|
+
|
311
|
+
ghpghx_output[:outputs][:yearly_ghx_pump_electric_consumption_series_kw] = electrical_power_consumed_kw
|
312
|
+
else
|
313
|
+
ghpghx_output[:outputs][:yearly_ghx_pump_electric_consumption_series_kw] = [0.000000001]*@@hours_in_year
|
314
|
+
end
|
315
|
+
|
316
|
+
# This is not used in REopt calculation but required for formatting.
|
317
|
+
ghpghx_output[:outputs][:peak_combined_heatpump_thermal_ton] = 0.000000001
|
318
|
+
|
319
|
+
ghpghx_output[:outputs][:heat_pump_configuration] = "WSHP"
|
320
|
+
# Required for REpot formatting
|
321
|
+
ghpghx_output[:outputs][:yearly_total_electric_consumption_series_kw] = [0.00001] * @@hours_in_year
|
322
|
+
ghpghx_output[:outputs][:yearly_heating_heatpump_electric_consumption_series_kw] = [0] * @@hours_in_year
|
323
|
+
ghpghx_output[:outputs][:yearly_cooling_heatpump_electric_consumption_series_kw] = [0] * @@hours_in_year
|
324
|
+
|
325
|
+
ghpghx_output_all = [ghpghx_output, ghpghx_output]
|
326
|
+
reopt_inputs_district[:GHP][:ghpghx_responses] = ghpghx_output_all
|
327
|
+
|
328
|
+
#save output report in reopt_ghp directory
|
329
|
+
reopt_ghp_dir = File.join(run_dir, "reopt_ghp", "reopt_ghp_inputs")
|
330
|
+
json_file_path = File.join(reopt_ghp_dir, "GHX_#{ghp_id}.json")
|
331
|
+
pretty_json = JSON.pretty_generate(reopt_inputs_district)
|
332
|
+
File.write(json_file_path, pretty_json)
|
333
|
+
end
|
334
|
+
|
335
|
+
end
|
336
|
+
end
|
337
|
+
end
|