Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ Classify the change according to the following categories:
##### Removed
### Patches

## peak-scaling
### Minor Updates
##### Added
- **ElectricLoad** input **monthly_peaks_kw**. Can be used to scale loads_kw or doe_reference loads to monthly peaks while maintaining monthly energy.


## v3.16.2
### Patches
- Added `CST` and `HighTempThermalStorage` to all/superset inputs test.
Expand Down
6 changes: 4 additions & 2 deletions julia_src/Manifest.toml
Original file line number Diff line number Diff line change
Expand Up @@ -948,9 +948,11 @@ 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 = "016a24bc463c0b3dd261877efc2d7850de70fcc9"
repo-rev = "peak-scaling"
repo-url = "https://github.com/NREL/REopt.jl.git"
uuid = "d36ad4e8-d74a-4f7a-ace1-eaea049febf6"
version = "0.55.1"
version = "0.55.0"

[[deps.Random]]
deps = ["SHA"]
Expand Down
8 changes: 4 additions & 4 deletions julia_src/http.jl
Original file line number Diff line number Diff line change
Expand Up @@ -544,18 +544,18 @@ function simulated_load(req::HTTP.Request)
end

# Convert vectors which come in as Vector{Any} to Vector{Float} (within Vector{<:Real})
vector_types = ["percent_share", "cooling_pct_share", "monthly_totals_kwh", "monthly_mmbtu",
vector_types = ["percent_share", "cooling_pct_share", "monthly_totals_kwh", "monthly_peaks_kw", "monthly_mmbtu",
"monthly_tonhour", "monthly_fraction", "addressable_load_fraction", "load_profile"]
for key in vector_types
if key in keys(d) && typeof(d[key]) <: Vector{}
d[key] = convert(Vector{Real}, d[key])
d[key] = convert(Vector{Float64}, d[key])
elseif key in keys(d) && key == "addressable_load_fraction"
# Scalar version of input, convert Any to Real
d[key] = convert(Real, d[key])
d[key] = convert(Float64, d[key])
end
end

@info "Getting CRB Loads..."
@info "Getting Loads..."
data = Dict()
error_response = Dict()
try
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Generated by Django 4.2.24 on 2025-10-23 21:21

import django.contrib.postgres.fields
import django.core.validators
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='electricloadinputs',
name='monthly_peaks_kw',
field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(100000000.0)]), blank=True, default=list, help_text="Monthly peak power consumption (an array 12 entries long), in kW, used to scale either loads_kw series (with normalize_and_scale_load_profile_input) or the simulated default building load profile for the site's climate zone.Monthly energy is maintained while scaling to the monthly peaks.", size=None),
),
migrations.AlterField(
model_name='electricloadinputs',
name='monthly_totals_kwh',
field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(100000000.0)]), blank=True, default=list, help_text="Monthly site energy consumption (an array 12 entries long), in kWh, used to scale either loads_kw series (with normalize_and_scale_load_profile_input) or the simulated default building load profile for the site's climate zone", size=None),
),
migrations.AlterField(
model_name='electricloadinputs',
name='normalize_and_scale_load_profile_input',
field=models.BooleanField(blank=True, default=False, help_text='Takes the input loads_kw and normalizes and scales it to the inputs annual_kwh, monthly_totals_kwh, and/or monthly_peaks_kw.'),
),
]
20 changes: 17 additions & 3 deletions reoptjl/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1387,8 +1387,22 @@ class ElectricLoadInputs(BaseModel, models.Model):
blank=True
),
default=list, blank=True,
help_text=("Monthly site energy consumption from electricity series (an array 12 entries long), in kWh, used "
"to scale simulated default building load profile for the site's climate zone")
help_text=("Monthly site energy consumption (an array 12 entries long), in kWh, used to scale either loads_kw series "
"(with normalize_and_scale_load_profile_input) or the simulated default building load profile for the site's climate zone")
)
monthly_peaks_kw = ArrayField(
models.FloatField(
validators=[
MinValueValidator(0),
MaxValueValidator(1.0e8)
],
blank=True
),
default=list, blank=True,
help_text=("Monthly peak power consumption (an array 12 entries long), in kW, used to scale either loads_kw series "
"(with normalize_and_scale_load_profile_input) or the simulated default building load profile for the site's climate zone."
"Monthly energy is maintained while scaling to the monthly peaks."
)
)
loads_kw = ArrayField(
models.FloatField(blank=True),
Expand All @@ -1401,7 +1415,7 @@ class ElectricLoadInputs(BaseModel, models.Model):
normalize_and_scale_load_profile_input = models.BooleanField(
blank=True,
default=False,
help_text=("Takes the input loads_kw and normalizes and scales it to annual or monthly energy inputs.")
help_text=("Takes the input loads_kw and normalizes and scales it to the inputs annual_kwh, monthly_totals_kwh, and/or monthly_peaks_kw.")
)
critical_loads_kw = ArrayField(
models.FloatField(blank=True),
Expand Down
1 change: 1 addition & 0 deletions reoptjl/test/posts/all_inputs_test.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"doe_reference_name": "MidriseApartment",
"year": 2017,
"monthly_totals_kwh": [],
"monthly_peaks_kw": [],
"loads_kw": [],
"critical_loads_kw": [],
"loads_kw_is_net": true,
Expand Down
14 changes: 14 additions & 0 deletions reoptjl/test/posts/validator_post.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,20 @@
1200,
1200
],
"monthly_peaks_kw": [
1.5,
1.5,
1.5,
1.5,
1.5,
1.5,
1.5,
1.5,
1.5,
1.5,
1.5,
1.5
],
"outage_start_time_step": 11,
"outage_end_time_step": 501,
"critical_load_fraction": 0.5,
Expand Down
9 changes: 5 additions & 4 deletions reoptjl/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -582,10 +582,10 @@ def simulated_load(request):
# Required for GET - will throw a Missing Error if not included
if request.method == "GET":
valid_keys = ["doe_reference_name","industrial_reference_name","latitude","longitude","load_type","percent_share","annual_kwh",
"monthly_totals_kwh","annual_mmbtu","annual_fraction","annual_tonhour","monthly_tonhour",
"monthly_totals_kwh","monthly_peaks_kw","annual_mmbtu","annual_fraction","annual_tonhour","monthly_tonhour",
"monthly_mmbtu","monthly_fraction","max_thermal_factor_on_peak_load","chiller_cop",
"addressable_load_fraction", "cooling_doe_ref_name", "cooling_pct_share", "boiler_efficiency",
"normalize_and_scale_load_profile_input", "year"]
"normalize_and_scale_load_profile_input", "year", "time_steps_per_hour"]
for key in request.GET.keys():
k = key
if "[" in key:
Expand Down Expand Up @@ -658,7 +658,7 @@ def simulated_load(request):
# TODO make year optional?
inputs[field] = data[field]
if inputs["load_type"] == "electric":
for energy in ["annual_kwh", "monthly_totals_kwh"]:
for energy in ["annual_kwh", "monthly_totals_kwh", "monthly_peaks_kw"]:
if data.get(energy) is not None:
inputs[energy] = data.get(energy)
elif inputs["load_type"] in ["space_heating", "domestic_hot_water", "process_heat"]:
Expand All @@ -669,7 +669,8 @@ def simulated_load(request):
for energy in ["annual_tonhour", "monthly_tonhour"]:
if data.get(energy) is not None:
inputs[energy] = data.get(energy)
# TODO cooling, not in REopt.jl yet
if len(inputs["load_profile"]) != 8760:
inputs["time_steps_per_hour"] = data["time_steps_per_hour"]

# TODO consider changing all requests to POST so that we don't have to do the weird array processing like percent_share[0], [1], etc?
# json.dump(inputs, open("sim_load_post.json", "w"))
Expand Down