Skip to content

Commit

Permalink
Merge pull request #587 from NREL/develop
Browse files Browse the repository at this point in the history
REopt.jl v0.47.1
  • Loading branch information
Bill-Becker authored Jun 4, 2024
2 parents 4456bcf + 539d481 commit d736bb2
Show file tree
Hide file tree
Showing 7 changed files with 165 additions and 10 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ Classify the change according to the following categories:
##### Removed
### Patches

## v3.9.1
### Minor Updates
#### Added
- Added `ProcessHeatLoadInputs` for new ways to input `ProcessHeatLoad`, similar to other loads
#### Fixed
- See fixes and changes here: https://github.com/NREL/REopt.jl/releases/tag/v0.47.0

## v3.9.0
### Minor Updates
#### Added
Expand Down
4 changes: 2 additions & 2 deletions julia_src/Manifest.toml
Original file line number Diff line number Diff line change
Expand Up @@ -917,9 +917,9 @@ 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 = "3c40f3939f79c3f66df69e9acc503fef614cdd63"
git-tree-sha1 = "b51d56a6398f302100004184b64bbe3d1e137277"
uuid = "d36ad4e8-d74a-4f7a-ace1-eaea049febf6"
version = "0.46.1"
version = "0.47.1"

[[deps.Random]]
deps = ["SHA"]
Expand Down
2 changes: 1 addition & 1 deletion reoptjl/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ def obj_create(self, bundle, **kwargs):
meta = {
"run_uuid": run_uuid,
"api_version": 3,
"reopt_version": "0.45.0",
"reopt_version": "0.47.1",
"status": "Validating..."
}
bundle.data.update({"APIMeta": meta})
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Generated by Django 4.0.7 on 2024-06-01 20:15

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


class Migration(migrations.Migration):

dependencies = [
('reoptjl', '0059_processheatloadinputs_and_more'),
]

operations = [
migrations.AddField(
model_name='processheatloadinputs',
name='addressable_load_fraction',
field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1.0)]), blank=True, default=list, help_text='Fraction of input fuel load which is addressable by heating technologies (default is 1.0).Can be a scalar or vector with length aligned with use of monthly_mmbtu (12) or fuel_loads_mmbtu_per_hour.', size=None),
),
migrations.AddField(
model_name='processheatloadinputs',
name='blended_industry_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_industry_reference_percents to create a blended load profile from multiple Industrial reference facility/sector types.', size=None),
),
migrations.AddField(
model_name='processheatloadinputs',
name='blended_industry_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_industry_reference_names to create a blended load profile from multiple Industrial reference facility/sector types. Must sum to 1.0.', size=None),
),
migrations.AddField(
model_name='processheatloadinputs',
name='industry_reference_name',
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')], help_text='Industrial process heat load reference facility/sector type', null=True),
),
migrations.AddField(
model_name='processheatloadinputs',
name='monthly_mmbtu',
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 process heat fuel consumption in [MMbtu], used to scale simulated default building load profile for the site's climate zone", size=None),
),
migrations.AlterField(
model_name='absorptionchillerinputs',
name='heating_load_input',
field=models.TextField(blank=True, choices=[('DomesticHotWater', 'Domestichotwater'), ('SpaceHeating', 'Spaceheating'), ('ProcessHeat', 'Processheat')], help_text='Absorption chiller heat input - determines what heating load is added to by absorption chiller use', null=True),
),
migrations.AlterField(
model_name='processheatloadinputs',
name='annual_mmbtu',
field=models.FloatField(blank=True, help_text='Annual site process heat fuel consumption, used to scale simulated default industry load profile [MMBtu]', null=True, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(100000000.0)]),
),
migrations.AlterField(
model_name='processheatloadinputs',
name='fuel_loads_mmbtu_per_hour',
field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True), blank=True, default=list, help_text='Vector of process heat fuel loads [mmbtu/hr] over one year. Must be hourly (8,760 samples), 30 minute (17,520 samples), or 15 minute (35,040 samples). All non-net load values must be greater than or equal to zero. ', size=None),
),
]
102 changes: 97 additions & 5 deletions reoptjl/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -6384,19 +6384,53 @@ class ProcessHeatLoadInputs(BaseModel, models.Model):

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

INDUSTRY_REFERENCE_NAME = models.TextChoices('INDUSTRY_REFERENCE_NAME', (
'Chemical '
'Warehouse '
'FlatLoad '
'FlatLoad_24_5 '
'FlatLoad_16_7 '
'FlatLoad_16_5 '
'FlatLoad_8_7 '
'FlatLoad_8_5'
))

annual_mmbtu = models.FloatField(
validators=[
MinValueValidator(1),
MaxValueValidator(MAX_BIG_NUMBER)
],
null=True,
blank=True,
help_text=("Annual site process heat consumption, used "
"to scale simulated load profile [MMBtu]")
help_text=("Annual site process heat fuel consumption, used "
"to scale simulated default industry load profile [MMBtu]")
)

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

monthly_mmbtu = ArrayField(
models.FloatField(
validators=[
MinValueValidator(0),
MaxValueValidator(MAX_BIG_NUMBER)
],
blank=True
),
default=list, blank=True,
help_text=("Monthly site process heat fuel consumption in [MMbtu], used "
"to scale simulated default building load profile for the site's climate zone")
)

fuel_loads_mmbtu_per_hour = ArrayField(
Expand All @@ -6405,11 +6439,50 @@ class ProcessHeatLoadInputs(BaseModel, models.Model):
),
default=list,
blank=True,
help_text=("Typical load over all hours in one year. Must be hourly (8,760 samples), 30 minute (17,"
help_text=("Vector of process heat fuel loads [mmbtu/hr] over one year. Must be hourly (8,760 samples), 30 minute (17,"
"520 samples), or 15 minute (35,040 samples). All non-net load values must be greater than or "
"equal to zero. "
)
)

blended_industry_reference_names = ArrayField(
models.TextField(
choices=INDUSTRY_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 "
"Industrial reference facility/sector types.")
)

blended_industry_reference_percents = ArrayField(
models.FloatField(
null=True, blank=True,
validators=[
MinValueValidator(0),
MaxValueValidator(1.0)
],
),
default=list,
blank=True,
help_text=("Used in concert with blended_industry_reference_names to create a blended load profile from multiple "
"Industrial reference facility/sector types. Must sum to 1.0.")
)

addressable_load_fraction = ArrayField(
models.FloatField(
validators=[
MinValueValidator(0),
MaxValueValidator(1.0)
],
blank=True
),
default=list,
blank=True,
help_text=( "Fraction of input fuel load which is addressable by heating technologies (default is 1.0)."
"Can be a scalar or vector with length aligned with use of monthly_mmbtu (12) or fuel_loads_mmbtu_per_hour.")
)

def clean(self):
Expand All @@ -6420,6 +6493,25 @@ 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 self.industry_reference_name != "" or \
len(self.blended_industry_reference_names) > 0:
self.year = 2017 # the validator provides an "info" message regarding this)

if self.addressable_load_fraction == None:
self.addressable_load_fraction = list([1.0]) # should not convert to timeseries, in case it is to be used with monthly_mmbtu or annual_mmbtu

# possible sets for defining load profile
if not at_least_one_set(self.dict, self.possible_sets):
error_messages["required inputs"] = \
"Must provide at least one set of valid inputs from {}.".format(self.possible_sets)

class HeatingLoadOutputs(BaseModel, models.Model):

key = "HeatingLoadOutputs"
Expand Down
1 change: 1 addition & 0 deletions reoptjl/test/posts/test_thermal_in_results.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"annual_mmbtu": 500.0
},
"ProcessHeatLoad": {
"industry_reference_name": "FlatLoad",
"annual_mmbtu": 100
},
"ExistingBoiler": {
Expand Down
4 changes: 2 additions & 2 deletions reoptjl/test/test_http_endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,8 @@ def test_simulated_load(self):
inputs["annual_kwh"] = 1.5E7
inputs["doe_reference_name[0]"] = "Hospital"
inputs["doe_reference_name[1]"] = "LargeOffice"
inputs["percent_share[0]"] = 25.0
inputs["percent_share[1]"] = 100.0 - inputs["percent_share[0]"]
inputs["percent_share[0]"] = 0.25
inputs["percent_share[1]"] = 1.0 - inputs["percent_share[0]"]

# The /v3/simulated_load endpoint calls the http.jl /simulated_load endpoint
response = self.api_client.get(f'/v3/simulated_load', data=inputs)
Expand Down

0 comments on commit d736bb2

Please sign in to comment.