Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updated Grid RE and Emissions #628

Open
wants to merge 25 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,28 @@ Classify the change according to the following categories:
##### Removed
### Patches

## gridRE
### Major Updates
### Added
- Added the following inputs to account for the clean or renewable energy fraction of grid-purchased electricity:
- ElectricUtility **cambium_cef_metric** to utilize clean energy data from NREL's Cambium database
- ElectricUtility **renewable_energy_fraction_series** to supply a custom grid clean or renewable energy scalar or series
- Site **include_grid_renewable_fraction_in_RE_constraints** - to allow user to choose whether to include grid RE in min max constraints
- Added the following outputs:
- ElectricUtility **annual_renewable_electricity_supplied_kwh**
- Site **onsite_and_grid_renewable_electricity_fraction_of_elec_load**
- Site **onsite_and_grid_renewable_energy_fraction_of_total_load**
- Added input option optimize_soc_init_fraction (defaults to false) to ElectricStorage, which makes the optimization choose the inital SOC (equal to final SOC) instead of using soc_init_fraction. The initial SOC is also constrained to equal the final SOC, which eliminates the "free energy" issue. We currently do not fix SOC when soc_init_fraction is used because this has caused infeasibility.
### Changed
- Changed name of the following inputs:
- ElectricUtility input **cambium_metric_col** changed to **cambium_co2_metric**, to distinguish between the CO2 and clean energy fraction metrics
- Changed name of the following outputs:
- ElectricUtility **cambium_emissions_region** changed to **cambium_region**
- Site **annual_renewable_electricity_kwh** changed to **annual_onsite_renewable_electricity_kwh**
- Site **renewable_electricity_fraction** changed to **onsite_renewable_electricity_fraction_of_elec_load**
- Site **total_renewable_energy_fraction** changed to **onsite_renewable_energy_fraction_of_total_load**
- Changed v3 endpoint "cambium_emissions_profile" to "cambium_profile"
- In REopt.jl: Updated Cambium API call to Cambium 2023 dataset, Updated AVERT emissions data to v4.3, which uses Regional Data Files for year 2023 for CONUS. For Alaska and Hawaii (regions AKGD, HIMS, HIOA), updated eGRID data to eGRID2022 datafile, adjusted to CO2e values.
## v3.11.0
### Minor Updates
##### Changed
Expand Down
6 changes: 4 additions & 2 deletions julia_src/Manifest.toml
Original file line number Diff line number Diff line change
Expand Up @@ -929,9 +929,11 @@ uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb"

[[deps.REopt]]
deps = ["ArchGDAL", "CSV", "CoolProp", "DataFrames", "Dates", "DelimitedFiles", "HTTP", "JLD", "JSON", "JuMP", "LinDistFlow", "LinearAlgebra", "Logging", "MathOptInterface", "Requires", "Roots", "Statistics", "TestEnv"]
git-tree-sha1 = "324394f21cb7e2db3d9e7ebde19c4e83c5a64e0f"
git-tree-sha1 = "b58ddeb73296aadfb077c2bb41229fec4091834f"
repo-rev = "gridRE-dev"
repo-url = "https://github.com/NREL/REopt.jl.git"
uuid = "d36ad4e8-d74a-4f7a-ace1-eaea049febf6"
version = "0.50.0"
version = "0.49.1"

[[deps.Random]]
deps = ["SHA"]
Expand Down
11 changes: 6 additions & 5 deletions julia_src/http.jl
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ function reopt(req::HTTP.Request)
]
inputs_with_defaults_from_avert_or_cambium = [
:emissions_factor_series_lb_CO2_per_kwh, :emissions_factor_series_lb_NOx_per_kwh,
:emissions_factor_series_lb_SO2_per_kwh, :emissions_factor_series_lb_PM25_per_kwh
:emissions_factor_series_lb_SO2_per_kwh, :emissions_factor_series_lb_PM25_per_kwh,
:renewable_energy_fraction_series
]
if haskey(d, "CHP")
inputs_with_defaults_from_chp = [
Expand Down Expand Up @@ -348,9 +349,9 @@ function avert_emissions_profile(req::HTTP.Request)
return HTTP.Response(200, JSON.json(data))
end

function cambium_emissions_profile(req::HTTP.Request)
function cambium_profile(req::HTTP.Request)
d = JSON.parse(String(req.body))
@info "Getting Cambium CO2 emissions profile..."
@info "Getting emissions or clean energy data from Cambium..."
data = Dict()
error_response = Dict()
try
Expand All @@ -360,7 +361,7 @@ function cambium_emissions_profile(req::HTTP.Request)
lifetime = typeof(d["lifetime"]) == String ? parse(Int, d["lifetime"]) : d["lifetime"]
load_year = typeof(d["load_year"]) == String ? parse(Int, d["load_year"]) : d["load_year"]

data = reoptjl.cambium_emissions_profile(;scenario= d["scenario"],
data = reoptjl.cambium_profile(;scenario= d["scenario"],
location_type = d["location_type"],
latitude=latitude,
longitude=longitude,
Expand Down Expand Up @@ -597,7 +598,7 @@ HTTP.register!(ROUTER, "POST", "/erp", erp)
HTTP.register!(ROUTER, "POST", "/ghpghx", ghpghx)
HTTP.register!(ROUTER, "GET", "/chp_defaults", chp_defaults)
HTTP.register!(ROUTER, "GET", "/avert_emissions_profile", avert_emissions_profile)
HTTP.register!(ROUTER, "GET", "/cambium_emissions_profile", cambium_emissions_profile)
hdunham marked this conversation as resolved.
Show resolved Hide resolved
HTTP.register!(ROUTER, "GET", "/cambium_profile", cambium_profile)
HTTP.register!(ROUTER, "GET", "/easiur_costs", easiur_costs)
HTTP.register!(ROUTER, "GET", "/simulated_load", simulated_load)
HTTP.register!(ROUTER, "GET", "/absorption_chiller_defaults", absorption_chiller_defaults)
Expand Down
4 changes: 2 additions & 2 deletions reoptjl/custom_table_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -453,8 +453,8 @@
{
"label" : "Annual % Renewable Electricity (%)",
"key" : "annual_renewable_electricity",
"bau_value" : lambda df: safe_get(df, "outputs.Site.renewable_electricity_fraction_bau"),
"scenario_value": lambda df: safe_get(df, "outputs.Site.renewable_electricity_fraction")
"bau_value" : lambda df: safe_get(df, "outputs.Site.onsite_renewable_electricity_fraction_of_elec_load_bau"),
"scenario_value": lambda df: safe_get(df, "outputs.Site.onsite_renewable_electricity_fraction_of_elec_load")
},
{
"label" : "Annual CO2 Emissions (tonnes)",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# Generated by Django 4.0.7 on 2025-01-24 22:02

import django.contrib.postgres.fields
import django.core.validators
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('reoptjl', '0076_ashpspaceheaterinputs_force_dispatch_and_more'),
]

operations = [
migrations.RenameField(
model_name='electricutilityinputs',
old_name='cambium_metric_col',
new_name='cambium_co2_metric',
),
migrations.RenameField(
model_name='electricutilityoutputs',
old_name='cambium_emissions_region',
new_name='cambium_region',
),
migrations.RenameField(
model_name='siteoutputs',
old_name='annual_renewable_electricity_kwh',
new_name='annual_onsite_renewable_electricity_kwh',
),
migrations.RenameField(
model_name='siteoutputs',
old_name='annual_renewable_electricity_kwh_bau',
new_name='annual_onsite_renewable_electricity_kwh_bau',
),
migrations.RenameField(
model_name='siteoutputs',
old_name='renewable_electricity_fraction',
new_name='onsite_renewable_electricity_fraction_of_elec_load',
),
migrations.RenameField(
model_name='siteoutputs',
old_name='renewable_electricity_fraction_bau',
new_name='onsite_renewable_electricity_fraction_of_elec_load_bau',
),
migrations.RemoveField(
model_name='siteoutputs',
name='total_renewable_energy_fraction',
),
migrations.RemoveField(
model_name='siteoutputs',
name='total_renewable_energy_fraction_bau',
),
migrations.AddField(
model_name='electricstorageinputs',
name='optimize_soc_init_fraction',
field=models.BooleanField(blank=True, default=False, help_text='If true, soc_init_fraction will not apply. Model will optimize initial SOC and constrain initial SOC = final SOC.'),
),
migrations.AddField(
model_name='electricutilityinputs',
name='cambium_cef_metric',
field=models.TextField(blank=True, default='cef_load', help_text="Options = ['cef_load', 'cef_gen']. cef_load is the fraction of generation that is clean, for the generation that is allocated to a region’s end-use load; cef_gen is the fraction of generation that is clean within a region."),
),
migrations.AddField(
model_name='electricutilityinputs',
name='renewable_energy_fraction_series',
field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True), blank=True, default=list, help_text='Fraction of energy supplied by the grid that is renewable. Can be scalar or timeseries (aligned with time_steps_per_hour).', size=None),
),
migrations.AddField(
model_name='electricutilityoutputs',
name='annual_renewable_electricity_supplied_kwh',
field=models.FloatField(blank=True, help_text='Total renewable electricity supplied from the grid in an average year.', null=True),
),
migrations.AddField(
model_name='electricutilityoutputs',
name='annual_renewable_electricity_supplied_kwh_bau',
field=models.FloatField(blank=True, help_text='Total renewable electricity supplied from the grid in an average year.', null=True),
),
migrations.AddField(
model_name='siteinputs',
name='include_grid_renewable_fraction_in_RE_constraints',
field=models.BooleanField(blank=True, default=True, help_text='If True, then the renewable energy content of energy from the grid is included in any min or max renewable energy requirements.'),
),
migrations.AddField(
model_name='siteoutputs',
name='onsite_and_grid_renewable_electricity_fraction_of_elec_load',
field=models.FloatField(blank=True, help_text='Calculation is the same as onsite_renewable_electricity_fraction_of_elec_load, but additionally includes the renewable energycontent of grid-purchased electricity, accounting for any battery efficiency losses.', null=True),
),
migrations.AddField(
model_name='siteoutputs',
name='onsite_and_grid_renewable_electricity_fraction_of_elec_load_bau',
field=models.FloatField(blank=True, help_text='Calculation is the same as onsite_renewable_electricity_fraction_of_elec_load, but additionally includes the renewable energycontent of grid-purchased electricity, accounting for any battery efficiency losses.', null=True),
),
migrations.AddField(
model_name='siteoutputs',
name='onsite_and_grid_renewable_energy_fraction_of_total_load',
field=models.FloatField(blank=True, help_text='Calculation is the same as onsite_renewable_energy_fraction_of_total_load, but additionally includes the renewable energycontent of grid-purchased electricity, accounting for any battery efficiency losses.', null=True),
),
migrations.AddField(
model_name='siteoutputs',
name='onsite_and_grid_renewable_energy_fraction_of_total_load_bau',
field=models.FloatField(blank=True, help_text='Calculation is the same as onsite_renewable_energy_fraction_of_total_load, but additionally includes the renewable energycontent of grid-purchased electricity, accounting for any battery efficiency losses.', null=True),
),
migrations.AddField(
model_name='siteoutputs',
name='onsite_renewable_energy_fraction_of_total_load',
field=models.FloatField(blank=True, help_text='Portion of annual total energy consumption that is derived from on-site renewable resource generation.The numerator is calculated as total annual RE electricity consumption (calculation described for annual_onsite_renewable_electricity_kwh output),plus total annual thermal energy content of steam/hot water generated from renewable fuels (non-electrified heat loads).The thermal energy content is calculated as total energy content of steam/hot water generation from renewable fuels,minus waste heat generated by renewable fuels, minus any applicable hot water thermal energy storage efficiency losses.The denominator is calculated as total annual electricity consumption plus total annual thermal steam/hot water load.', null=True),
),
migrations.AddField(
model_name='siteoutputs',
name='onsite_renewable_energy_fraction_of_total_load_bau',
field=models.FloatField(blank=True, help_text='Portion of annual total energy consumption that is derived from on-site renewable resource generation in the BAU case.The numerator is calculated as total annual RE electricity consumption (calculation described for annual_onsite_renewable_electricity_kwh_bau output),plus total annual thermal energy content of steam/hot water generated from renewable fuels (non-electrified heat loads).The thermal energy content is calculated as total energy content of steam/hot water generation from renewable fuels,minus waste heat generated by renewable fuels, minus any applicable hot water thermal energy storage efficiency losses.The denominator is calculated as total annual electricity consumption plus total annual thermal steam/hot water load.', null=True),
),
migrations.AlterField(
model_name='electricutilityinputs',
name='cambium_levelization_years',
field=models.IntegerField(blank=True, help_text='Expected lifetime or analysis period of the intervention being studied. Emissions and clean energy fraction will be averaged over this period. Default: analysis_years (from Financial struct)', null=True, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(100)]),
),
migrations.AlterField(
model_name='electricutilityinputs',
name='cambium_location_type',
field=models.TextField(blank=True, default='GEA Regions 2023', help_text="Geographic boundary at which emissions and clean energy fraction are calculated. Options: ['Nations', 'GEA Regions 2023']."),
),
migrations.AlterField(
model_name='electricutilityinputs',
name='cambium_scenario',
field=models.TextField(blank=True, default='Mid-case', help_text="Cambium Scenario for evolution of electricity sector (see Cambium documentation for descriptions).Options: ['Mid-case', 'Low renewable energy cost', 'High renewable energy cost', 'High demand growth', 'Low natural gas prices', 'High natural gas prices', 'Mid-case with 95% decarbonization by 2050', 'Mid-case with 100% decarbonization by 2035']"),
),
migrations.AlterField(
model_name='electricutilityinputs',
name='cambium_start_year',
field=models.IntegerField(blank=True, default=2025, help_text='First year of operation of system. Emissions will be levelized starting in this year for the duration of cambium_levelization_years.', validators=[django.core.validators.MinValueValidator(2025), django.core.validators.MaxValueValidator(2050)]),
),
]
14 changes: 14 additions & 0 deletions reoptjl/migrations/0078_merge_20250128_1541.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Generated by Django 4.0.7 on 2025-01-28 15:41

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('reoptjl', '0077_electricstorageinputs_max_duration_hours_and_more'),
('reoptjl', '0077_rename_cambium_metric_col_electricutilityinputs_cambium_co2_metric_and_more'),
]

operations = [
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Generated by Django 4.0.7 on 2025-02-05 18:26

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('reoptjl', '0078_merge_20250128_1541'),
]

operations = [
migrations.AlterField(
model_name='siteinputs',
name='include_grid_renewable_fraction_in_RE_constraints',
field=models.BooleanField(blank=True, default=False, help_text='If True, then the renewable energy content of energy from the grid is included in any min or max renewable energy requirements.'),
),
migrations.AlterField(
model_name='siteoutputs',
name='onsite_and_grid_renewable_electricity_fraction_of_elec_load_bau',
field=models.FloatField(blank=True, help_text='Calculation is the same as onsite_renewable_electricity_fraction_of_elec_load_bau, but additionally includes the renewable energycontent of grid-purchased electricity, accounting for any battery efficiency losses.', null=True),
),
migrations.AlterField(
model_name='siteoutputs',
name='onsite_and_grid_renewable_energy_fraction_of_total_load_bau',
field=models.FloatField(blank=True, help_text='Calculation is the same as onsite_renewable_energy_fraction_of_total_load_bau, but additionally includes the renewable energycontent of grid-purchased electricity, accounting for any battery efficiency losses.', null=True),
),
]
Loading