Skip to content

Commit

Permalink
Merge pull request #622 from NREL/norm-scale-load
Browse files Browse the repository at this point in the history
Normalize and Scale Load Profiles
  • Loading branch information
Bill-Becker authored Dec 26, 2024
2 parents e52e720 + bf5c08b commit bd79024
Show file tree
Hide file tree
Showing 10 changed files with 537 additions and 283 deletions.
390 changes: 198 additions & 192 deletions julia_src/Manifest.toml

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions julia_src/http.jl
Original file line number Diff line number Diff line change
Expand Up @@ -411,15 +411,15 @@ function simulated_load(req::HTTP.Request)
d = JSON.parse(String(req.body))

# Arrays in d are being parsed as type Vector{Any} instead of fixed type Vector{String or <:Real} without conversion
for key in ["doe_reference_name", "cooling_doe_ref_name"]
for key in ["doe_reference_name", "cooling_doe_ref_name", "industrial_reference_name"]
if key in keys(d) && typeof(d[key]) <: Vector{}
d[key] = convert(Vector{String}, d[key])
end
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",
"monthly_tonhour", "addressable_load_fraction"]
"monthly_tonhour", "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])
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Generated by Django 4.0.7 on 2024-10-27 04:32

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


class Migration(migrations.Migration):

dependencies = [
('reoptjl', '0070_merge_20240925_0600'),
]

operations = [
migrations.RenameField(
model_name='processheatloadinputs',
old_name='industry_reference_name',
new_name='industrial_reference_name',
),
migrations.RemoveField(
model_name='processheatloadinputs',
name='blended_industry_reference_names',
),
migrations.RemoveField(
model_name='processheatloadinputs',
name='blended_industry_reference_percents',
),
migrations.AddField(
model_name='processheatloadinputs',
name='blended_industrial_reference_names',
field=django.contrib.postgres.fields.ArrayField(base_field=models.TextField(blank=True, choices=[('Chemical', 'Chemical'), ('Warehouse', 'Warehouse'), ('FlatLoad', 'Flatload'), ('FlatLoad_24_5', 'Flatload 24 5'), ('FlatLoad_16_7', 'Flatload 16 7'), ('FlatLoad_16_5', 'Flatload 16 5'), ('FlatLoad_8_7', 'Flatload 8 7'), ('FlatLoad_8_5', 'Flatload 8 5')], null=True), blank=True, default=list, help_text='Used in concert with blended_industrial_reference_percents to create a blended load profile from multiple Industrial reference facility/sector types.', size=None),
),
migrations.AddField(
model_name='processheatloadinputs',
name='blended_industrial_reference_percents',
field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1.0)]), blank=True, default=list, help_text='Used in concert with blended_industrial_reference_names to create a blended load profile from multiple Industrial reference facility/sector types. Must sum to 1.0.', size=None),
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Generated by Django 4.0.7 on 2024-10-27 04:45

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('reoptjl', '0071_rename_industry_reference_name_processheatloadinputs_industrial_reference_name_and_more'),
]

operations = [
migrations.AddField(
model_name='domestichotwaterloadinputs',
name='normalize_and_scale_load_profile_input',
field=models.BooleanField(blank=True, default=True, help_text='Takes the input fuel_loads_mmbtu_per_hour and normalizes and scales it to annual or monthly energy inputs.'),
),
migrations.AddField(
model_name='electricloadinputs',
name='normalize_and_scale_load_profile_input',
field=models.BooleanField(blank=True, default=True, help_text='Takes the input loads_kw and normalizes and scales it to annual or monthly energy inputs.'),
),
migrations.AddField(
model_name='processheatloadinputs',
name='normalize_and_scale_load_profile_input',
field=models.BooleanField(blank=True, default=True, help_text='Takes the input fuel_loads_mmbtu_per_hour and normalizes and scales it to annual or monthly energy inputs.'),
),
migrations.AddField(
model_name='spaceheatingloadinputs',
name='normalize_and_scale_load_profile_input',
field=models.BooleanField(blank=True, default=True, help_text='Takes the input fuel_loads_mmbtu_per_hour and normalizes and scales it to annual or monthly energy inputs.'),
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Generated by Django 4.0.7 on 2024-10-28 20:15

import django.core.validators
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('reoptjl', '0072_domestichotwaterloadinputs_normalize_and_scale_load_profile_input_and_more'),
]

operations = [
migrations.AddField(
model_name='domestichotwaterloadinputs',
name='year',
field=models.IntegerField(blank=True, default=2022, help_text="Year of Custom Load Profile. If a custom load profile is uploaded via the fuel_loads_mmbtu_per_hour parameter, it is important that this year correlates with the electric load profile so that weekdays/weekends are determined correctly for the utility rate tariff. If a DOE Reference Building profile (aka 'simulated' profile) is used, the year is set to 2017 since the DOE profiles start on a Sunday.", null=True, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(9999)]),
),
migrations.AddField(
model_name='processheatloadinputs',
name='year',
field=models.IntegerField(blank=True, default=2022, help_text="Year of Custom Load Profile. If a custom load profile is uploaded via the fuel_loads_mmbtu_per_hour parameter, it is important that this year correlates with the electric load profile so that weekdays/weekends are determined correctly for the utility rate tariff. If a DOE Reference Building profile (aka 'simulated' profile) is used, the year is set to 2017 since the DOE profiles start on a Sunday.", null=True, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(9999)]),
),
migrations.AddField(
model_name='spaceheatingloadinputs',
name='year',
field=models.IntegerField(blank=True, default=2022, help_text="Year of Custom Load Profile. If a custom load profile is uploaded via the fuel_loads_mmbtu_per_hour parameter, it is important that this year correlates with the electric load profile so that weekdays/weekends are determined correctly for the utility rate tariff. If a DOE Reference Building profile (aka 'simulated' profile) is used, the year is set to 2017 since the DOE profiles start on a Sunday.", null=True, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(9999)]),
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Generated by Django 4.0.7 on 2024-10-29 04:04

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('reoptjl', '0073_domestichotwaterloadinputs_year_and_more'),
]

operations = [
migrations.AlterField(
model_name='domestichotwaterloadinputs',
name='normalize_and_scale_load_profile_input',
field=models.BooleanField(blank=True, default=False, help_text='Takes the input fuel_loads_mmbtu_per_hour and normalizes and scales it to annual or monthly energy inputs.'),
),
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 annual or monthly energy inputs.'),
),
migrations.AlterField(
model_name='processheatloadinputs',
name='normalize_and_scale_load_profile_input',
field=models.BooleanField(blank=True, default=False, help_text='Takes the input fuel_loads_mmbtu_per_hour and normalizes and scales it to annual or monthly energy inputs.'),
),
migrations.AlterField(
model_name='spaceheatingloadinputs',
name='normalize_and_scale_load_profile_input',
field=models.BooleanField(blank=True, default=False, help_text='Takes the input fuel_loads_mmbtu_per_hour and normalizes and scales it to annual or monthly energy inputs.'),
),
]
114 changes: 94 additions & 20 deletions reoptjl/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1244,6 +1244,11 @@ class ElectricLoadInputs(BaseModel, models.Model):
"equal to zero. "
)
)
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.")
)
critical_loads_kw = ArrayField(
models.FloatField(blank=True),
default=list, blank=True,
Expand Down Expand Up @@ -6896,6 +6901,25 @@ class SpaceHeatingLoadInputs(BaseModel, models.Model):
)
)

normalize_and_scale_load_profile_input = models.BooleanField(
blank=True,
default=False,
help_text=("Takes the input fuel_loads_mmbtu_per_hour and normalizes and scales it to annual or monthly energy inputs.")
)

year = models.IntegerField(
default=2022,
validators=[
MinValueValidator(1),
MaxValueValidator(9999)
],
null=True, blank=True,
help_text=("Year of Custom Load Profile. If a custom load profile is uploaded via the fuel_loads_mmbtu_per_hour parameter, it "
"is important that this year correlates with the electric load profile so that weekdays/weekends are "
"determined correctly for the utility rate tariff. If a DOE Reference Building profile (aka "
"'simulated' profile) is used, the year is set to 2017 since the DOE profiles start on a Sunday.")
)

blended_doe_reference_names = ArrayField(
models.TextField(
choices=DOE_REFERENCE_NAME.choices,
Expand Down Expand Up @@ -7055,9 +7079,27 @@ class DomesticHotWaterLoadInputs(BaseModel, models.Model):
"520 samples), or 15 minute (35,040 samples). All non-net load values must be greater than or "
"equal to zero. "
)
)

normalize_and_scale_load_profile_input = models.BooleanField(
blank=True,
default=False,
help_text=("Takes the input fuel_loads_mmbtu_per_hour and normalizes and scales it to annual or monthly energy inputs.")
)

year = models.IntegerField(
default=2022,
validators=[
MinValueValidator(1),
MaxValueValidator(9999)
],
null=True, blank=True,
help_text=("Year of Custom Load Profile. If a custom load profile is uploaded via the fuel_loads_mmbtu_per_hour parameter, it "
"is important that this year correlates with the electric load profile so that weekdays/weekends are "
"determined correctly for the utility rate tariff. If a DOE Reference Building profile (aka "
"'simulated' profile) is used, the year is set to 2017 since the DOE profiles start on a Sunday.")
)

blended_doe_reference_names = ArrayField(
models.TextField(
choices=DOE_REFERENCE_NAME.choices,
Expand Down Expand Up @@ -7138,14 +7180,14 @@ class ProcessHeatLoadInputs(BaseModel, models.Model):

possible_sets = [
["fuel_loads_mmbtu_per_hour"],
["industry_reference_name", "monthly_mmbtu"],
["annual_mmbtu", "industry_reference_name"],
["industry_reference_name"],
["blended_industry_reference_names", "blended_industry_reference_percents"],
["industrial_reference_name", "monthly_mmbtu"],
["annual_mmbtu", "industrial_reference_name"],
["industrial_reference_name"],
["blended_industrial_reference_names", "blended_industrial_reference_percents"],
[]
]

INDUSTRY_REFERENCE_NAME = models.TextChoices('INDUSTRY_REFERENCE_NAME', (
INDUSTRIAL_REFERENCE_NAME = models.TextChoices('INDUSTRIAL_REFERENCE_NAME', (
'Chemical '
'Warehouse '
'FlatLoad '
Expand All @@ -7167,10 +7209,10 @@ class ProcessHeatLoadInputs(BaseModel, models.Model):
"to scale simulated default industry load profile [MMBtu]")
)

industry_reference_name = models.TextField(
industrial_reference_name = models.TextField(
null=True,
blank=True,
choices=INDUSTRY_REFERENCE_NAME.choices,
choices=INDUSTRIAL_REFERENCE_NAME.choices,
help_text=("Industrial process heat load reference facility/sector type")
)

Expand Down Expand Up @@ -7199,19 +7241,51 @@ class ProcessHeatLoadInputs(BaseModel, models.Model):
)
)

blended_industry_reference_names = ArrayField(
year = models.IntegerField(
default=2022,
validators=[
MinValueValidator(1),
MaxValueValidator(9999)
],
null=True, blank=True,
help_text=("Year of Custom Load Profile. If a custom load profile is uploaded via the fuel_loads_mmbtu_per_hour parameter, it "
"is important that this year correlates with the electric load profile so that weekdays/weekends are "
"determined correctly for the utility rate tariff. If a DOE Reference Building profile (aka "
"'simulated' profile) is used, the year is set to 2017 since the DOE profiles start on a Sunday.")
)

normalize_and_scale_load_profile_input = models.BooleanField(
blank=True,
default=False,
help_text=("Takes the input fuel_loads_mmbtu_per_hour and normalizes and scales it to annual or monthly energy inputs.")
)

year = models.IntegerField(
default=2022,
validators=[
MinValueValidator(1),
MaxValueValidator(9999)
],
null=True, blank=True,
help_text=("Year of Custom Load Profile. If a custom load profile is uploaded via the fuel_loads_mmbtu_per_hour parameter, it "
"is important that this year correlates with the electric load profile so that weekdays/weekends are "
"determined correctly for the utility rate tariff. If a DOE Reference Building profile (aka "
"'simulated' profile) is used, the year is set to 2017 since the DOE profiles start on a Sunday.")
)

blended_industrial_reference_names = ArrayField(
models.TextField(
choices=INDUSTRY_REFERENCE_NAME.choices,
choices=INDUSTRIAL_REFERENCE_NAME.choices,
blank=True,
null=True
),
default=list,
blank=True,
help_text=("Used in concert with blended_industry_reference_percents to create a blended load profile from multiple "
help_text=("Used in concert with blended_industrial_reference_percents to create a blended load profile from multiple "
"Industrial reference facility/sector types.")
)

blended_industry_reference_percents = ArrayField(
blended_industrial_reference_percents = ArrayField(
models.FloatField(
null=True, blank=True,
validators=[
Expand All @@ -7221,7 +7295,7 @@ class ProcessHeatLoadInputs(BaseModel, models.Model):
),
default=list,
blank=True,
help_text=("Used in concert with blended_industry_reference_names to create a blended load profile from multiple "
help_text=("Used in concert with blended_industrial_reference_names to create a blended load profile from multiple "
"Industrial reference facility/sector types. Must sum to 1.0.")
)

Expand All @@ -7247,15 +7321,15 @@ def clean(self):
error_messages["required inputs"] = \
"Must provide at least one set of valid inputs from {}.".format(self.possible_sets)

if len(self.blended_industry_reference_names) > 0 and self.industry_reference_name == "":
if len(self.blended_industry_reference_names) != len(self.blended_industry_reference_percents):
error_messages["blended_industry_reference_names"] = \
"The number of blended_industry_reference_names must equal the number of blended_industry_reference_percents."
if not math.isclose(sum(self.blended_industry_reference_percents), 1.0):
error_messages["blended_industry_reference_percents"] = "Sum must = 1.0."
if len(self.blended_industrial_reference_names) > 0 and self.industrial_reference_name == "":
if len(self.blended_industrial_reference_names) != len(self.blended_industrial_reference_percents):
error_messages["blended_industrial_reference_names"] = \
"The number of blended_industrial_reference_names must equal the number of blended_industrial_reference_percents."
if not math.isclose(sum(self.blended_industrial_reference_percents), 1.0):
error_messages["blended_industrial_reference_percents"] = "Sum must = 1.0."

if self.industry_reference_name != "" or \
len(self.blended_industry_reference_names) > 0:
if self.industrial_reference_name != "" or \
len(self.blended_industrial_reference_names) > 0:
self.year = 2017 # the validator provides an "info" message regarding this)

if self.addressable_load_fraction == None:
Expand Down
20 changes: 15 additions & 5 deletions reoptjl/test/posts/all_inputs_test.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@
"operating_reserve_required_fraction": 0.0,
"min_load_met_annual_fraction": 1.0,
"blended_doe_reference_names": [],
"blended_doe_reference_percents": []
"blended_doe_reference_percents": [],
"normalize_and_scale_load_profile_input": false
},
"Site": {
"latitude": 38.96345294964369,
Expand Down Expand Up @@ -265,24 +266,33 @@
},
"SpaceHeatingLoad": {
"annual_mmbtu": null,
"year": 2017,
"doe_reference_name": "MidriseApartment",
"monthly_mmbtu": [],
"fuel_loads_mmbtu_per_hour": [],
"blended_doe_reference_names": [],
"blended_doe_reference_percents": []
"blended_doe_reference_percents": [],
"normalize_and_scale_load_profile_input": false,
"year": 2017
},
"DomesticHotWaterLoad": {
"annual_mmbtu": 8760.0,
"doe_reference_name": "MidriseApartment",
"monthly_mmbtu": [],
"fuel_loads_mmbtu_per_hour": [],
"blended_doe_reference_names": [],
"blended_doe_reference_percents": []
"blended_doe_reference_percents": [],
"normalize_and_scale_load_profile_input": false,
"year": 2017
},
"ProcessHeatLoad": {
"annual_mmbtu": 8760.0,
"industry_reference_name": "Warehouse"
"industrial_reference_name": "Chemical",
"monthly_mmbtu": [],
"fuel_loads_mmbtu_per_hour": [],
"blended_industrial_reference_names": [],
"blended_industrial_reference_percents": [],
"normalize_and_scale_load_profile_input": false,
"year": 2017
},
"Boiler": {
"min_mmbtu_per_hour": 5.0,
Expand Down
2 changes: 1 addition & 1 deletion reoptjl/test/posts/test_thermal_in_results.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
"annual_mmbtu": 500.0
},
"ProcessHeatLoad": {
"industry_reference_name": "FlatLoad",
"industrial_reference_name": "FlatLoad",
"annual_mmbtu": 100
},
"ExistingBoiler": {
Expand Down
Loading

0 comments on commit bd79024

Please sign in to comment.