Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
518d950
Updated model with outputs for testing
rathod-b Sep 29, 2025
ea88d75
Update julia version and make migrations
rathod-b Sep 30, 2025
08a9ccf
Remove migration 0093 and add billed energy rate field
rathod-b Oct 2, 2025
f5b352c
Merge remote-tracking branch 'origin/develop' into add-elec-tariff-costs
rathod-b Oct 2, 2025
39c7b2b
Add new electric load and tariff output fields
rathod-b Oct 8, 2025
8495639
Add URDB fields to ElectricTariffOutputs model
rathod-b Oct 16, 2025
ca18389
Merge branch 'develop' into add-elec-tariff-costs
Bill-Becker Oct 22, 2025
aebd41f
Update REopt#elec_util_usage after develop merge
Bill-Becker Oct 22, 2025
a7fdea9
Migration --merge after merging develop
Bill-Becker Oct 22, 2025
19d2bb2
Move urdb_metadata into ElectricTariffInputs and group into single js…
Bill-Becker Oct 22, 2025
b2a3b6b
Rename export types to whole words
Bill-Becker Oct 22, 2025
1a3e4fd
Update ElectricTariffInputs urdb_metadata from Julia in process_results
Bill-Becker Oct 22, 2025
242dede
Avoid error handling breakdown if ElectricTariff is not present, and …
Bill-Becker Oct 23, 2025
8260d01
Update to latest REopt#elec_util_usage
Bill-Becker Oct 23, 2025
60d90fe
Reduce complexity of long-solving sector defaults test
Bill-Becker Oct 23, 2025
6cadf5b
Make output.ElectricLoad.monthly_peak_kw consistent with upcoming new…
Bill-Becker Oct 23, 2025
08f8e1e
Update REopt#elec_util_usage for updated monthly_peaks_kw name
Bill-Becker Oct 24, 2025
34a980a
ElectricTariffOuputs Updates
Bill-Becker Oct 28, 2025
06f761e
Remove separate migrations for ElectricTariff
Bill-Becker Oct 28, 2025
3916cde
Update squashed migration after outputs refactor
Bill-Becker Oct 28, 2025
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
4 changes: 3 additions & 1 deletion julia_src/Manifest.toml
Original file line number Diff line number Diff line change
Expand Up @@ -948,7 +948,9 @@ version = "1.11.0"

[[deps.REopt]]
deps = ["ArchGDAL", "CSV", "CoolProp", "DataFrames", "Dates", "DelimitedFiles", "HTTP", "JLD", "JSON", "JuMP", "LinDistFlow", "LinearAlgebra", "Logging", "MathOptInterface", "Requires", "Roots", "Statistics", "TestEnv"]
git-tree-sha1 = "103761fa0f7447377726347af656cde6ab1160cc"
git-tree-sha1 = "277df4545cd30a5fc227bf982897ef65d5ea3520"
repo-rev = "elec_util_usage"
repo-url = "https://github.com/NREL/REopt.jl.git"
uuid = "d36ad4e8-d74a-4f7a-ace1-eaea049febf6"
version = "0.55.1"

Expand Down
18 changes: 17 additions & 1 deletion julia_src/http.jl
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ function reopt(req::HTTP.Request)
# Catch handled/unhandled exceptions in data pre-processing, JuMP setup
try
model_inputs = reoptjl.REoptInputs(d)
@info "Successfully processed REopt inputs."
catch e
@error "Something went wrong during REopt inputs processing!" exception=(e, catch_backtrace())
error_response["error"] = sprint(showerror, e)
Expand All @@ -102,6 +103,7 @@ function reopt(req::HTTP.Request)
# Catch handled/unhandled exceptions in optimization
try
results = reoptjl.run_reopt(ms, model_inputs)
@info "Successfully ran REopt optimization."
inputs_with_defaults_from_julia_financial = [
:NOx_grid_cost_per_tonne, :SO2_grid_cost_per_tonne, :PM25_grid_cost_per_tonne,
:NOx_onsite_fuelburn_cost_per_tonne, :SO2_onsite_fuelburn_cost_per_tonne, :PM25_onsite_fuelburn_cost_per_tonne,
Expand Down Expand Up @@ -235,6 +237,14 @@ function reopt(req::HTTP.Request)
high_temp_storage_dict = Dict(key=>getfield(model_inputs.s.storage.attr["HighTempThermalStorage"], key) for key in inputs_with_defaults_from_julia_high_temp_storage)
else
high_temp_storage_dict = Dict()
end
if haskey(d, "ElectricTariff") && !isempty(model_inputs.s.electric_tariff.urdb_metadata)
inputs_from_julia_electric_tariff = [
:urdb_metadata
]
electric_tariff_dict = Dict(key=>getfield(model_inputs.s.electric_tariff, key) for key in inputs_from_julia_electric_tariff)
else
electric_tariff_dict = Dict()
end
inputs_with_defaults_set_in_julia = Dict(
"Financial" => Dict(key=>getfield(model_inputs.s.financial, key) for key in inputs_with_defaults_from_julia_financial),
Expand All @@ -251,7 +261,8 @@ function reopt(req::HTTP.Request)
"ElectricStorage" => electric_storage_dict,
"ColdThermalStorage" => cold_storage_dict,
"HotThermalStorage" => hot_storage_dict,
"HighTempThermalStorage" => high_temp_storage_dict
"HighTempThermalStorage" => high_temp_storage_dict,
"ElectricTariff" => electric_tariff_dict
)
catch e
@error "Something went wrong in REopt optimization!" exception=(e, catch_backtrace())
Expand All @@ -272,6 +283,11 @@ function reopt(req::HTTP.Request)

if isempty(error_response)
@info "REopt model solved with status $(results["status"])."
# These are matrices that need to be vector.
if haskey(results, "ElectricTariff")
results["ElectricTariff"]["year_one_electric_to_load_energy_cost_series_before_tax"] = results["ElectricTariff"]["year_one_electric_to_load_energy_cost_series_before_tax"][:,1]
results["ElectricTariff"]["monthly_facility_demand_cost_series_before_tax"] = results["ElectricTariff"]["monthly_facility_demand_cost_series_before_tax"][:,1]
end
response = Dict(
"results" => results,
"reopt_version" => string(pkgversion(reoptjl))
Expand Down
139 changes: 139 additions & 0 deletions reoptjl/migrations/0110_electricloadoutputs_annual_peak_kw_and_more.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# Generated by Django 4.2.25 on 2025-10-28 18:05

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


class Migration(migrations.Migration):

dependencies = [
('reoptjl', '0109_remove_ghpoutputs_iterations_auto_guess_and_more'),
]

operations = [
migrations.AddField(
model_name='electricloadoutputs',
name='annual_peak_kw',
field=models.FloatField(blank=True, help_text='Annual peak energy demand determined from load_series_kw. Does not include electric load for any new heating or cooling techs.', null=True),
),
migrations.AddField(
model_name='electricloadoutputs',
name='monthly_calculated_kwh',
field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), default=list, help_text='Monthly energy consumption calculated by summing up load_series_kw. Does not include electric load for any new heating or cooling techs.', size=None),
),
migrations.AddField(
model_name='electricloadoutputs',
name='monthly_peaks_kw',
field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), default=list, help_text='Monthly peak energy demand determined from load_series_kw. Does not include electric load for any new heating or cooling techs.', size=None),
),
migrations.AddField(
model_name='electrictariffinputs',
name='urdb_metadata',
field=models.JSONField(blank=True, help_text='Utility rate meta data from Utility Rate Database API', null=True),
),
migrations.AddField(
model_name='electrictariffoutputs',
name='demand_rate_average_series',
field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), default=list, help_text='Series of average (across tiers) demand rates for each timestep in year one.', size=None),
),
migrations.AddField(
model_name='electrictariffoutputs',
name='energy_cost_series_before_tax',
field=models.JSONField(blank=True, help_text='Series of cost of power purchased from grid to serve load in each timestep, by Tier_i.', null=True),
),
migrations.AddField(
model_name='electrictariffoutputs',
name='energy_cost_series_before_tax_bau',
field=models.JSONField(blank=True, help_text='Business as usual series of cost of power purchased from grid to serve load in each timestep, by Tier_i.', null=True),
),
migrations.AddField(
model_name='electrictariffoutputs',
name='energy_rate_average_series',
field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), default=list, help_text='Series of average (across tiers) energy rates for each timestep in year one.', size=None),
),
migrations.AddField(
model_name='electrictariffoutputs',
name='energy_rate_series',
field=models.JSONField(blank=True, help_text='Series of billed energy rates for each timestep in year one.', null=True),
),
migrations.AddField(
model_name='electrictariffoutputs',
name='energy_rate_tier_limits',
field=models.JSONField(blank=True, help_text='Energy rate tier limits', null=True),
),
migrations.AddField(
model_name='electrictariffoutputs',
name='facility_demand_monthly_rate_series',
field=models.JSONField(blank=True, help_text='Facility (not dependent on TOU) demand charge rates by Tier_i', null=True),
),
migrations.AddField(
model_name='electrictariffoutputs',
name='facility_demand_monthly_rate_tier_limits',
field=models.JSONField(blank=True, help_text='Facility (not dependent on TOU) demand charge tier limits', null=True),
),
migrations.AddField(
model_name='electrictariffoutputs',
name='monthly_demand_cost_series_before_tax',
field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), default=list, help_text='Series of total (facility and TOU for all tiers) monthly demand charges for each month.', size=None),
),
migrations.AddField(
model_name='electrictariffoutputs',
name='monthly_demand_cost_series_before_tax_bau',
field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), default=list, help_text='Business as usual series of total (facility and TOU for all tiers) monthly demand charges for each month.', size=None),
),
migrations.AddField(
model_name='electrictariffoutputs',
name='monthly_energy_cost_series_before_tax',
field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), default=list, help_text='Series of monthly cost of power purchased from grid to serve loads.', size=None),
),
migrations.AddField(
model_name='electrictariffoutputs',
name='monthly_energy_cost_series_before_tax_bau',
field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), default=list, help_text='Business as usual series of monthly cost of power purchased from grid to serve loads.', size=None),
),
migrations.AddField(
model_name='electrictariffoutputs',
name='monthly_facility_demand_cost_series_before_tax',
field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), default=list, help_text='Series of total (all tiers) monthly facility demand charges by month.', size=None),
),
migrations.AddField(
model_name='electrictariffoutputs',
name='monthly_facility_demand_cost_series_before_tax_bau',
field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), default=list, help_text='Business as usual series of total (all tiers) monthly facility demand charges by month.', size=None),
),
migrations.AddField(
model_name='electrictariffoutputs',
name='monthly_fixed_cost',
field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), default=list, help_text='Year one fixed utility costs for each month.', size=None),
),
migrations.AddField(
model_name='electrictariffoutputs',
name='monthly_fixed_cost_bau',
field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), default=list, help_text='Business as usual year one fixed utility costs for each month.', size=None),
),
migrations.AddField(
model_name='electrictariffoutputs',
name='monthly_tou_demand_cost_series_before_tax',
field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), default=list, help_text='Series of total time-of-use demand charges for each month.', size=None),
),
migrations.AddField(
model_name='electrictariffoutputs',
name='monthly_tou_demand_cost_series_before_tax_bau',
field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), default=list, help_text='Business as usual series of total time-of-use demand charges for each month.', size=None),
),
migrations.AddField(
model_name='electrictariffoutputs',
name='tou_demand_metrics',
field=models.JSONField(blank=True, help_text='Dictionary of TOU demand metrics, including month, tier, demand_rate, measured_tou_peak_demand, and demand_charge_before_tax', null=True),
),
migrations.AddField(
model_name='electrictariffoutputs',
name='tou_demand_rate_series',
field=models.JSONField(blank=True, help_text='Series of demand rates by Tier_i for each timestep.', null=True),
),
migrations.AddField(
model_name='electrictariffoutputs',
name='tou_demand_rate_tier_limits',
field=models.JSONField(blank=True, help_text='TOU demand rate tier limits', null=True),
),
]
143 changes: 143 additions & 0 deletions reoptjl/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1546,6 +1546,24 @@ class ElectricLoadOutputs(BaseModel, models.Model):
null=True, blank=True,
help_text="Annual energy consumption calculated by summing up load_series_kw. Does not include electric load for any new heating or cooling techs."
)
monthly_calculated_kwh = ArrayField(
models.FloatField(
null=True, blank=True
),
default=list,
help_text="Monthly energy consumption calculated by summing up load_series_kw. Does not include electric load for any new heating or cooling techs."
)
monthly_peaks_kw = ArrayField(
models.FloatField(
null=True, blank=True
),
default=list,
help_text="Monthly peak energy demand determined from load_series_kw. Does not include electric load for any new heating or cooling techs."
)
annual_peak_kw = models.FloatField(
null=True, blank=True,
help_text="Annual peak energy demand determined from load_series_kw. Does not include electric load for any new heating or cooling techs."
)
annual_electric_load_with_thermal_conversions_kwh = models.FloatField(
null=True, blank=True,
help_text="Total end-use electrical load, including electrified heating and cooling end-use load."
Expand Down Expand Up @@ -1720,6 +1738,10 @@ class ElectricTariffInputs(BaseModel, models.Model):
help_text=("Optional coincident peak demand charge that is applied to the max load during the time_steps "
"specified in coincident_peak_load_active_time_steps")
)
urdb_metadata = models.JSONField(
null=True, blank=True,
help_text=("Utility rate meta data from Utility Rate Database API")
)

def clean(self):
error_messages = {}
Expand Down Expand Up @@ -2603,6 +2625,127 @@ class ElectricTariffOutputs(BaseModel, models.Model):
related_name="ElectricTariffOutputs",
primary_key=True
)

monthly_fixed_cost = ArrayField(
models.FloatField(
null=True, blank=True
),
default=list,
help_text="Year one fixed utility costs for each month."
)
monthly_fixed_cost_bau = ArrayField(
models.FloatField(
null=True, blank=True
),
default=list,
help_text="Business as usual year one fixed utility costs for each month."
)
energy_cost_series_before_tax = models.JSONField(
null=True, blank=True,
help_text="Series of cost of power purchased from grid to serve load in each timestep, by Tier_i."
)
energy_cost_series_before_tax_bau = models.JSONField(
null=True, blank=True,
help_text="Business as usual series of cost of power purchased from grid to serve load in each timestep, by Tier_i."
)
monthly_energy_cost_series_before_tax = ArrayField(
models.FloatField(
null=True, blank=True
),
default=list,
help_text="Series of monthly cost of power purchased from grid to serve loads."
)
monthly_energy_cost_series_before_tax_bau = ArrayField(
models.FloatField(
null=True, blank=True
),
default=list,
help_text="Business as usual series of monthly cost of power purchased from grid to serve loads."
)
monthly_facility_demand_cost_series_before_tax = ArrayField(
models.FloatField(
null=True, blank=True
),
default=list,
help_text="Series of total (all tiers) monthly facility demand charges by month."
)
monthly_facility_demand_cost_series_before_tax_bau = ArrayField(
models.FloatField(
null=True, blank=True
),
default=list,
help_text="Business as usual series of total (all tiers) monthly facility demand charges by month."
)
energy_rate_series = models.JSONField(
null=True, blank=True,
help_text="Series of billed energy rates for each timestep in year one."
)
energy_rate_average_series = ArrayField(
models.FloatField(
null=True, blank=True
),
default=list,
help_text="Series of average (across tiers) energy rates for each timestep in year one."
)
monthly_tou_demand_cost_series_before_tax = ArrayField(
models.FloatField(
null=True, blank=True
),
default=list,
help_text="Series of total time-of-use demand charges for each month."
)
monthly_tou_demand_cost_series_before_tax_bau = ArrayField(
models.FloatField(
null=True, blank=True
),
default=list,
help_text="Business as usual series of total time-of-use demand charges for each month."
)
monthly_demand_cost_series_before_tax = ArrayField(
models.FloatField(
null=True, blank=True
),
default=list,
help_text="Series of total (facility and TOU for all tiers) monthly demand charges for each month."
)
monthly_demand_cost_series_before_tax_bau = ArrayField(
models.FloatField(
null=True, blank=True
),
default=list,
help_text="Business as usual series of total (facility and TOU for all tiers) monthly demand charges for each month."
)
tou_demand_metrics = models.JSONField(
null=True, blank=True,
help_text="Dictionary of TOU demand metrics, including month, tier, demand_rate, measured_tou_peak_demand, and demand_charge_before_tax"
)
facility_demand_monthly_rate_tier_limits = models.JSONField(
null=True, blank=True,
help_text="Facility (not dependent on TOU) demand charge tier limits"
)
facility_demand_monthly_rate_series = models.JSONField(
null=True, blank=True,
help_text="Facility (not dependent on TOU) demand charge rates by Tier_i"
)
tou_demand_rate_tier_limits = models.JSONField(
null=True, blank=True,
help_text="TOU demand rate tier limits"
)
energy_rate_tier_limits = models.JSONField(
null=True, blank=True,
help_text="Energy rate tier limits"
)
tou_demand_rate_series = models.JSONField(
null=True, blank=True,
help_text="Series of demand rates by Tier_i for each timestep."
)
demand_rate_average_series = ArrayField(
models.FloatField(
null=True, blank=True
),
default=list,
help_text="Series of average (across tiers) demand rates for each timestep in year one."
)
year_one_energy_cost_before_tax = models.FloatField(
null=True, blank=True,
help_text="Optimal year one utility energy cost"
Expand Down
Loading
Loading