Skip to content

Commit

Permalink
Merge branch 'develop' into ghx-defaults-standardization
Browse files Browse the repository at this point in the history
  • Loading branch information
atpham88 committed Dec 24, 2024
2 parents a55cb72 + 051c088 commit 5ba222e
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 13 deletions.
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,21 @@ Classify the change according to the following categories:
##### Removed
### Patches

## v3.10.2
### Minor Updates
##### Changed
- Summary focus can now be a string with multiple types of focus such as `A,B,C`
##### Fixed
- Issue with `CHP.installed_cost_per_kw` not being an array when updating the inputs model object (which expects an array) in process_results.py, from Julia

## v3.10.1
### Minor Updates
##### Fixed
- ASHP min allowable sizing
- Prevent battery simultaneous charge/discharge
##### Changed
- Updated GHP to allow costs to be calculated for GHP and GHX separately and without running GhpGhx.jl, for district energy applications

## v3.10.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 = "32499f329265d270e9f77c8831892772b5fbf28f"
git-tree-sha1 = "79c315746fe8274cf047d5d8d04be1b75020065b"
uuid = "d36ad4e8-d74a-4f7a-ace1-eaea049febf6"
version = "0.48.0"
version = "0.48.1"

[[deps.Random]]
deps = ["SHA"]
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 @@ -137,8 +137,8 @@
{
"label" : "Results URL",
"key" : "url",
"bau_value" : lambda df: f'=HYPERLINK("https://custom-table-download-reopt-stage.its.nrel.gov/tool/results/{safe_get(df, "webtool_uuid")}", "Results Link")',
"scenario_value": lambda df: f'=HYPERLINK("https://custom-table-download-reopt-stage.its.nrel.gov/tool/results/{safe_get(df, "webtool_uuid")}", "Results Link")'
"bau_value" : lambda df: f'=HYPERLINK("https://reopt.nrel.gov/tool/results/{safe_get(df, "webtool_uuid")}", "Results Link")',
"scenario_value": lambda df: f'=HYPERLINK("https://reopt.nrel.gov/tool/results/{safe_get(df, "webtool_uuid")}", "Results Link")'
},
#####################################################################################################
######################### System Capacities #############################
Expand Down
14 changes: 14 additions & 0 deletions reoptjl/src/process_results.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ def update_inputs_in_database(inputs_to_update: dict, run_uuid: str) -> None:
SiteInputs.objects.filter(meta__run_uuid=run_uuid).update(**inputs_to_update["Site"])

if inputs_to_update["CHP"]: # Will be an empty dictionary if CHP is not considered
if inputs_to_update["CHP"].get("installed_cost_per_kw") and type(inputs_to_update["CHP"].get("installed_cost_per_kw")) == float:
inputs_to_update["CHP"]["installed_cost_per_kw"] = [inputs_to_update["CHP"]["installed_cost_per_kw"]]
CHPInputs.objects.filter(meta__run_uuid=run_uuid).update(**inputs_to_update["CHP"])
if inputs_to_update["SteamTurbine"]: # Will be an empty dictionary if SteamTurbine is not considered
SteamTurbineInputs.objects.filter(meta__run_uuid=run_uuid).update(**inputs_to_update["SteamTurbine"])
Expand All @@ -139,8 +141,10 @@ def update_inputs_in_database(inputs_to_update: dict, run_uuid: str) -> None:
else:
ExistingChillerInputs.objects.filter(meta__run_uuid=run_uuid).update(**inputs_to_update["ExistingChiller"])
if inputs_to_update["ASHPSpaceHeater"]:
prune_update_fields(ASHPSpaceHeaterInputs, inputs_to_update["ASHPSpaceHeater"])
ASHPSpaceHeaterInputs.objects.filter(meta__run_uuid=run_uuid).update(**inputs_to_update["ASHPSpaceHeater"])
if inputs_to_update["ASHPWaterHeater"]:
prune_update_fields(ASHPWaterHeaterInputs, inputs_to_update["ASHPWaterHeater"])
ASHPWaterHeaterInputs.objects.filter(meta__run_uuid=run_uuid).update(**inputs_to_update["ASHPWaterHeater"])
except Exception as e:
exc_type, exc_value, exc_traceback = sys.exc_info()
Expand All @@ -150,3 +154,13 @@ def update_inputs_in_database(inputs_to_update: dict, run_uuid: str) -> None:
tb.format_tb(exc_traceback)
)
log.debug(debug_msg)

def prune_update_fields(model_obj, dict_to_update):
"""
REopt.jl may return more fields than the API has to update, so prune those extra ones before updating the model/db object
"""
field_names = [field.name for field in model_obj._meta.get_fields()]
dict_to_update_keys = list(dict_to_update.keys())
for key in dict_to_update_keys:
if key not in field_names:
del dict_to_update[key]
51 changes: 51 additions & 0 deletions reoptjl/test/posts/ashp_defaults_update.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{
"user_uuid": "1d2ef71e-fd93-4c4a-b5c3-1485a87f772e",
"webtool_uuid": "1ab7530f-74b8-4ea8-b8ed-f11bd953f61f",
"Settings": {
"optimality_tolerance": 0.001,
"solver_name": "HiGHS",
"off_grid_flag": false,
"include_climate_in_objective": false,
"include_health_in_objective": false
},
"Meta": {
"address": "San Francisco CA USA"
},
"Site": {
"latitude": 37.7749295,
"longitude": -122.4194155,
"include_exported_renewable_electricity_in_total": true,
"include_exported_elec_emissions_in_total": true,
"land_acres": 1000000.0,
"roof_squarefeet": 0
},
"ElectricLoad": {
"doe_reference_name": "Hospital"
},
"ElectricTariff": {
"blended_annual_energy_rate": 0.15,
"blended_annual_demand_rate": 0.0
},
"ElectricUtility": {
"cambium_location_type": "GEA Regions",
"cambium_metric_col": "lrmer_co2e",
"cambium_scenario": "Mid-case",
"cambium_grid_level": "enduse"
},
"SpaceHeatingLoad": {
"annual_mmbtu": 11570.916,
"doe_reference_name": "Hospital"
},
"DomesticHotWaterLoad": {
"annual_mmbtu": 671.405,
"doe_reference_name": "Hospital"
},
"ExistingBoiler": {
"fuel_type": "natural_gas",
"fuel_cost_per_mmbtu": 25.0
},
"ASHPSpaceHeater": {
"force_into_system": true,
"can_serve_cooling": false
}
}
17 changes: 16 additions & 1 deletion reoptjl/test/test_job_endpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -311,4 +311,19 @@ def test_centralghp(self):
resp = self.api_client.get(f'/v3/job/{run_uuid}/results')
r = json.loads(resp.content)

self.assertAlmostEqual(r["outputs"]["Financial"]["lifecycle_capital_costs"], 1046066.8, delta=1000)
self.assertAlmostEqual(r["outputs"]["Financial"]["lifecycle_capital_costs"], 1046066.8, delta=1000)

def test_ashp_defaults_update_from_julia(self):
# Test that the inputs_with_defaults_set_in_julia feature worked for ASHPSpaceHeater
post_file = os.path.join('reoptjl', 'test', 'posts', 'ashp_defaults_update.json')
post = json.load(open(post_file, 'r'))
resp = self.api_client.post('/stable/job/', format='json', data=post)
self.assertHttpCreated(resp)
r = json.loads(resp.content)
run_uuid = r.get('run_uuid')

resp = self.api_client.get(f'/stable/job/{run_uuid}/results')
r = json.loads(resp.content)

self.assertEquals(r["inputs"]["ASHPSpaceHeater"]["om_cost_per_ton"], 0.0)
self.assertEquals(r["inputs"]["ASHPSpaceHeater"]["sizing_factor"], 1.1)
23 changes: 15 additions & 8 deletions reoptjl/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -1066,17 +1066,18 @@ def queryset_for_summary(api_metas,summary_dict:dict):
)
if len(utility) > 0:
for m in utility:

if 'focus' not in summary_dict[str(m.meta.run_uuid)].keys():
summary_dict[str(m.meta.run_uuid)]['focus'] = ''
if m.outage_start_time_step is None:
if len(m.outage_start_time_steps) == 0:
summary_dict[str(m.meta.run_uuid)]['focus'] = "Financial"
summary_dict[str(m.meta.run_uuid)]['focus'] += "Financial,"
else:
summary_dict[str(m.meta.run_uuid)]['focus'] = "Resilience"
summary_dict[str(m.meta.run_uuid)]['focus'] += "Resilience,"
summary_dict[str(m.meta.run_uuid)]['outage_duration'] = m.outage_durations[0] # all durations are same.
else:
# outage start timestep was provided, is 1 or more
summary_dict[str(m.meta.run_uuid)]['outage_duration'] = m.outage_end_time_step - m.outage_start_time_step + 1
summary_dict[str(m.meta.run_uuid)]['focus'] = "Resilience"
summary_dict[str(m.meta.run_uuid)]['focus'] += "Resilience,"

site = SiteOutputs.objects.filter(meta__run_uuid__in=run_uuids).only(
'meta__run_uuid',
Expand All @@ -1097,15 +1098,18 @@ def queryset_for_summary(api_metas,summary_dict:dict):
)
if len(site_inputs) > 0:
for m in site_inputs:
# if focus key doesnt exist, create it
if 'focus' not in summary_dict[str(m.meta.run_uuid)].keys():
summary_dict[str(m.meta.run_uuid)]['focus'] = ''
try: # can be NoneType
if m.renewable_electricity_min_fraction > 0:
summary_dict[str(m.meta.run_uuid)]['focus'] = "Clean-energy"
summary_dict[str(m.meta.run_uuid)]['focus'] += "Clean-energy,"
except:
pass # is NoneType

try: # can be NoneType
if m.renewable_electricity_max_fraction > 0:
summary_dict[str(m.meta.run_uuid)]['focus'] = "Clean-energy"
summary_dict[str(m.meta.run_uuid)]['focus'] += "Clean-energy,"
except:
pass # is NoneType

Expand All @@ -1118,11 +1122,14 @@ def queryset_for_summary(api_metas,summary_dict:dict):
)
if len(settings) > 0:
for m in settings:
# if focus key doesnt exist, create it
if 'focus' not in summary_dict[str(m.meta.run_uuid)].keys():
summary_dict[str(m.meta.run_uuid)]['focus'] = ''
if m.off_grid_flag:
summary_dict[str(m.meta.run_uuid)]['focus'] = "Off-grid"
summary_dict[str(m.meta.run_uuid)]['focus'] += "Off-grid,"

if m.include_climate_in_objective or m.include_health_in_objective:
summary_dict[str(m.meta.run_uuid)]['focus'] = "Clean-energy"
summary_dict[str(m.meta.run_uuid)]['focus'] += "Clean-energy,"

tariffInputs = ElectricTariffInputs.objects.filter(meta__run_uuid__in=run_uuids).only(
'meta__run_uuid',
Expand Down

0 comments on commit 5ba222e

Please sign in to comment.