From 43c0721cfd69148ec3c138343126c11db2ce5d60 Mon Sep 17 00:00:00 2001 From: bill-becker Date: Wed, 1 Mar 2023 08:56:21 -0700 Subject: [PATCH 01/23] Define hybrid GHP inputs in nested_inputs --- reo/nested_inputs.py | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/reo/nested_inputs.py b/reo/nested_inputs.py index 0f6574713..8aad84ef1 100644 --- a/reo/nested_inputs.py +++ b/reo/nested_inputs.py @@ -2510,6 +2510,42 @@ def list_of_dict(input): "type": "bool", "default": False, "description": "If GHP can serve the domestic hot water (DHW) portion of the heating load" }, + # TODO move is_hybrid_ghx into ghpghx_inputs (and an input of GhpGhx.jl) + "is_hybrid_ghx": { + "type": "bool", "default": False, + "description": "If the GHP system uses a hybrid GHX with auxiliary heater or cooler" + }, + + "aux_heater_type": { + "type": "str", "default": "electric", + "description": "The type of auxiliary heater, 'electric' or 'natural_gas'" + }, + "aux_heater_installed_cost_us_dollars_per_mmbtu_per_hr": { + "type": "float", + "min": 0.0, + "max": 1.0e6, + "default": 1000.0, + "description": "Installed cost of auxiliary heater for hybrid ghx in $/MMBtu/hr based on peak thermal production" + }, + "aux_heater_thermal_efficiency": { + "type": "float", + "min": 0.001, + "max": 10.0, + "description": "The thermal efficiency (thermal_out/fuel_in) of the auxiliary heater" + }, + "aux_cooler_installed_cost_us_dollars_per_ton": { + "type": "float", + "min": 0.0, + "max": 1.0e6, + "default": 80.0, + "description": "Installed cost of auxiliary cooler (e.g. cooling tower) for hybrid ghx in $/ton based on peak thermal production" + }, + "aux_cooler_energy_use_intensity_kwe_per_kwt": { + "type": "float", + "min": 0.001, + "max": 10.0, + "description": "The thermal efficiency (thermal_out/fuel_in) of the auxiliary heater" + }, "macrs_option_years": { "type": "int", "restrict_to": macrs_schedules, From 783d0d73d8d21d28653365cd87c174cf9a2405d1 Mon Sep 17 00:00:00 2001 From: bill-becker Date: Wed, 1 Mar 2023 08:56:49 -0700 Subject: [PATCH 02/23] Add hybrid GHP django model fields and makemigration file --- ...ergy_use_intensity_kwe_per_kwt_and_more.py | 43 +++++++++++++++++++ reo/models.py | 9 ++++ 2 files changed, 52 insertions(+) create mode 100644 reo/migrations/0149_ghpmodel_aux_cooler_energy_use_intensity_kwe_per_kwt_and_more.py diff --git a/reo/migrations/0149_ghpmodel_aux_cooler_energy_use_intensity_kwe_per_kwt_and_more.py b/reo/migrations/0149_ghpmodel_aux_cooler_energy_use_intensity_kwe_per_kwt_and_more.py new file mode 100644 index 000000000..a1e093c3e --- /dev/null +++ b/reo/migrations/0149_ghpmodel_aux_cooler_energy_use_intensity_kwe_per_kwt_and_more.py @@ -0,0 +1,43 @@ +# Generated by Django 4.0.7 on 2023-02-28 23:24 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('reo', '0148_alter_electrictariffmodel_coincident_peak_load_active_timesteps'), + ] + + operations = [ + migrations.AddField( + model_name='ghpmodel', + name='aux_cooler_energy_use_intensity_kwe_per_kwt', + field=models.FloatField(blank=True, null=True), + ), + migrations.AddField( + model_name='ghpmodel', + name='aux_cooler_installed_cost_us_dollars_per_ton', + field=models.FloatField(blank=True, null=True), + ), + migrations.AddField( + model_name='ghpmodel', + name='aux_heater_installed_cost_us_dollars_per_mmbtu_per_hr', + field=models.FloatField(blank=True, null=True), + ), + migrations.AddField( + model_name='ghpmodel', + name='aux_heater_thermal_efficiency', + field=models.FloatField(blank=True, null=True), + ), + migrations.AddField( + model_name='ghpmodel', + name='aux_heater_type', + field=models.TextField(blank=True, null=True), + ), + migrations.AddField( + model_name='ghpmodel', + name='is_hybrid_ghx', + field=models.BooleanField(blank=True, null=True), + ), + ] diff --git a/reo/models.py b/reo/models.py index ab217e984..0a6d9554b 100644 --- a/reo/models.py +++ b/reo/models.py @@ -1141,6 +1141,15 @@ class GHPModel(models.Model): ghpghx_response_uuids = ArrayField(models.TextField(null=True, blank=True), default=list, null=True) ghpghx_responses = ArrayField(PickledObjectField(null=True, editable=True), null=True, default=list) can_serve_dhw = models.BooleanField(null=True, blank=True) + + # TODO move is_hybrid_ghx to ghpghx_inputs because we want to compete non-hybrid vs hybrid within single REopt run + is_hybrid_ghx = models.BooleanField(null=True, blank=True) + + aux_heater_type = models.TextField(null=True, blank=True) + aux_heater_installed_cost_us_dollars_per_mmbtu_per_hr = models.FloatField(null=True, blank=True) + aux_heater_thermal_efficiency = models.FloatField(null=True, blank=True) + aux_cooler_installed_cost_us_dollars_per_ton = models.FloatField(null=True, blank=True) + aux_cooler_energy_use_intensity_kwe_per_kwt = models.FloatField(null=True, blank=True) macrs_option_years = models.IntegerField(null=True, blank=True) macrs_bonus_pct = models.FloatField(null=True, blank=True) macrs_itc_reduction = models.FloatField(null=True, blank=True) From 9a181fc241c2144aabea82bf29a9cb3fbdf111cf Mon Sep 17 00:00:00 2001 From: bill-becker Date: Wed, 1 Mar 2023 08:57:35 -0700 Subject: [PATCH 03/23] Add hybrid GHP fields to GHPGHX class --- reo/src/ghp.py | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/reo/src/ghp.py b/reo/src/ghp.py index e35ffa5e4..40cd76ee4 100644 --- a/reo/src/ghp.py +++ b/reo/src/ghp.py @@ -21,7 +21,11 @@ def __init__(self, dfm, response, **kwargs): self.length_boreholes_ft = response["outputs"]["length_boreholes_ft"] self.yearly_total_electric_consumption_series_kw = response["outputs"]["yearly_total_electric_consumption_series_kw"] self.peak_combined_heatpump_thermal_ton = response["outputs"]["peak_combined_heatpump_thermal_ton"] - + # TODO replace dummy values below with GhpGhx.jl response fields + self.yearly_auxiliary_boiler_consumption_series_mmbtu_per_hour = [5.0] * 8760 #response["outputs"]["yearly_auxiliary_boiler_consumption_series_mmbtu_per_hour"] + self.yearly_auxiliary_cooling_tower_consumption_series_ton = [20.0] * 8760 #response["outputs"]["yearly_auxiliary_cooling_tower_consumption_series_ton"] + self.peak_auxiliary_boiler_mmbtu_per_hour = 5.0 #response["outputs"]["peak_auxiliary_boiler_mmbtu_per_hour"] + self.peak_auxiliary_cooling_tower_ton = 20.0 #response["outputs"]["peak_auxiliary_cooling_tower_ton"] if kwargs.get("require_ghp_purchase"): self.require_ghp_purchase = 1 @@ -33,6 +37,13 @@ def __init__(self, dfm, response, **kwargs): self.installed_cost_building_hydronic_loop_us_dollars_per_sqft = kwargs.get("installed_cost_building_hydronic_loop_us_dollars_per_sqft") self.om_cost_us_dollars_per_sqft_year = kwargs.get("om_cost_us_dollars_per_sqft_year") self.building_sqft = kwargs.get("building_sqft") + # TODO is_hybrid_ghx will be moved to the ghpghx_inputs field + self.is_hybrid_ghx = kwargs.get("is_hybrid_ghx") + self.aux_heater_type = kwargs.get("aux_heater_type") + self.aux_heater_installed_cost_us_dollars_per_mmbtu_per_hr = kwargs.get("aux_heater_installed_cost_us_dollars_per_mmbtu_per_hr") + self.aux_heater_thermal_efficiency = kwargs.get("aux_heater_thermal_efficiency") + self.aux_cooler_installed_cost_us_dollars_per_ton = kwargs.get("aux_cooler_installed_cost_us_dollars_per_ton") + self.aux_cooler_energy_use_intensity_kwe_per_kwt = kwargs.get("aux_cooler_energy_use_intensity_kwe_per_kwt") # Heating and cooling loads served and electricity consumed by GHP # TODO with hybrid with auxiliary/supplemental heating/cooling devices, we may want to separate out/distiguish that energy @@ -50,6 +61,17 @@ def __init__(self, dfm, response, **kwargs): self.setup_om_cost() + # TODO finish fuel/cost/emissions + # if self.aux_heater_type == "natural_gas": + # self.fuel_burn_series_mmbtu_per_hour = [self.yearly_auxiliary_boiler_consumption_series_mmbtu_per_hour[i] / self.aux_heater_thermal_efficiency + # for i in range(len(self.yearly_auxiliary_boiler_consumption_series_mmbtu_per_hour))] + # self.aux_heater_yearly_fuel_burn_mmbtu = sum(self.fuel_burn_series_mmbtu_per_hour) + # self.yearly_emissions_lb_CO2 = dfm.boiler.emissions_factor_lb_CO2_per_mmbtu * self.aux_heater_yearly_fuel_burn_mmbtu + # self.yearly_emissions_lb_NOx = dfm.boiler.emissions_factor_lb_NOx_per_mmbtu * self.aux_heater_yearly_fuel_burn_mmbtu + # self.yearly_emissions_lb_SOx = dfm.boiler.emissions_factor_lb_SOx_per_mmbtu * self.aux_heater_yearly_fuel_burn_mmbtu + # self.yearly_emissions_lb_PM25 = dfm.boiler.emissions_factor_lb_PM25_per_mmbtu * self.aux_heater_yearly_fuel_burn_mmbtu + + dfm.add_ghp(self) def setup_installed_cost_curve(self): @@ -61,14 +83,17 @@ def setup_installed_cost_curve(self): # The GHX and hydronic loop cost are the y-intercepts ([$]) of the cost for each design self.ghx_cost = self.total_ghx_ft * self.installed_cost_ghx_us_dollars_per_ft self.hydronic_loop_cost = self.building_sqft * self.installed_cost_building_hydronic_loop_us_dollars_per_sqft + self.aux_heater_cost = self.aux_heater_installed_cost_us_dollars_per_mmbtu_per_hr * self.peak_auxiliary_boiler_mmbtu_per_hour + self.aux_cooler_cost = self.aux_cooler_installed_cost_us_dollars_per_ton * self.peak_auxiliary_cooling_tower_ton # The DataManager._get_REopt_cost_curve method expects at least a two-point tech_size_for_cost_curve to # to use the first value of installed_cost_us_dollars_per_kw as an absolute $ value and # the initial slope is based on the heat pump size (e.g. $/ton) of the cost curve for # building a rebate-based cost curve if there are less-than big_number maximum incentives self.tech_size_for_cost_curve = [0.0, big_number] - self.installed_cost_us_dollars_per_kw = [self.ghx_cost + self.hydronic_loop_cost, - self.installed_cost_heatpump_us_dollars_per_ton] + self.installed_cost_us_dollars_per_kw = [self.ghx_cost + self.hydronic_loop_cost + + self.aux_heater_cost + self.aux_cooler_cost, + self.installed_cost_heatpump_us_dollars_per_ton] # Using a separate call to _get_REopt_cost_curve in data_manager for "ghp" (not included in "available_techs") # and then use the value below for heat pump capacity to calculate the final absolute cost for GHP From c1a6b40694e4518056c7ce78d45fd0e4ba5fe1ad Mon Sep 17 00:00:00 2001 From: bill-becker Date: Wed, 1 Mar 2023 09:00:17 -0700 Subject: [PATCH 04/23] Add WIP test/notes for hybrid GHP to test_ghp_job --- ghpghx/tests/posts/test_ghpghx_POST.json | 4 ++-- reo/tests/posts/test_ghp_POST.json | 9 +++++++-- reo/tests/test_ghp_job.py | 5 +++++ 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/ghpghx/tests/posts/test_ghpghx_POST.json b/ghpghx/tests/posts/test_ghpghx_POST.json index 0e6a7b2a6..3675c9ef3 100644 --- a/ghpghx/tests/posts/test_ghpghx_POST.json +++ b/ghpghx/tests/posts/test_ghpghx_POST.json @@ -4,6 +4,6 @@ "solver_eft_tolerance_f": 2.0, "ghx_model": "TESS", "tess_ghx_minimum_timesteps_per_hour": 1, - "max_sizing_iterations": 10, - "init_sizing_factor_ft_per_peak_ton": 300.0 + "max_sizing_iterations": 1, + "init_sizing_factor_ft_per_peak_ton": 300.0 } \ No newline at end of file diff --git a/reo/tests/posts/test_ghp_POST.json b/reo/tests/posts/test_ghp_POST.json index dfe2336cc..cf2214be8 100644 --- a/reo/tests/posts/test_ghp_POST.json +++ b/reo/tests/posts/test_ghp_POST.json @@ -43,8 +43,13 @@ "building_sqft": 50000.0, "can_serve_dhw": false, "space_heating_efficiency_thermal_factor": 0.85, - "cooling_efficiency_thermal_factor": 0.6 - + "cooling_efficiency_thermal_factor": 0.6, + "is_hybrid_ghx": true, + "aux_heater_type": "electric", + "aux_heater_installed_cost_us_dollars_per_mmbtu_per_hr": 0.0, + "aux_heater_thermal_efficiency": 0.95, + "aux_cooler_installed_cost_us_dollars_per_ton": 0.0, + "aux_cooler_energy_use_intensity_kwe_per_kwt": 0.02 }, "PV":{ "max_kw": 0.0 diff --git a/reo/tests/test_ghp_job.py b/reo/tests/test_ghp_job.py index 05bba3414..17888316b 100644 --- a/reo/tests/test_ghp_job.py +++ b/reo/tests/test_ghp_job.py @@ -88,6 +88,11 @@ def test_ghp(self): ghp_uuid = d["outputs"]["Scenario"]["Site"]["GHP"]["ghp_chosen_uuid"] print("GHP uuid chosen = ", ghp_uuid) + # TODO add natural_gas option with fuel cost and emissions - FIX to "boiler" fuel cost/emissions + # Still need to confirm the first cut at passing boiler emissions from dfm in ghp works, but also + # need to create a separate yearly GHP emissions param for reopt_model and zero out when no GHP + # also need to link aux heater fuel cost with fuel_params and fuel_tariff to get pwf_fuel for LCC cost + # Test GHP serving addressable fraction of space heating with VAV efficiency thermal knockdown heating_served_mmbtu = sum(d["outputs"]["Scenario"]["Site"]["GHP"]["ghpghx_chosen_outputs"]["heating_thermal_load_mmbtu_per_hr"]) expected_heating_served_mmbtu = 12000 * 0.8 * 0.9 * 0.7 * 0.85 # (fuel_mmbtu * boiler_effic * addressable_load * space_heat_frac * space_heating_efficiency_thermal_factor) From 61217d47ff151bb2e146caa105c1db4941cb29f6 Mon Sep 17 00:00:00 2001 From: lixiangk1 <72464565+lixiangk1@users.noreply.github.com> Date: Fri, 10 Mar 2023 00:41:12 -0700 Subject: [PATCH 05/23] Enable hybrid GHX --- Manifest.toml | 7 + Project.toml | 1 + ...hpghxoutputs_end_of_year_eft_f_and_more.py | 39 ++++ ...uxiliary_boiler_mmbtu_per_hour_and_more.py | 75 +++++++ .../0006_alter_ghpghxinputs_is_hybrid_ghx.py | 18 ++ ...ts_aux_heat_exchange_unit_type_and_more.py | 23 ++ ghpghx/models.py | 32 ++- ghpghx/tests/posts/test_ghpghx_POST.json | 3 +- .../tests/posts/test_hybrid_ghpghx_POST.json | 9 + julia_src/Manifest.toml | 197 ++++++++++-------- .../0150_remove_ghpmodel_is_hybrid_ghx.py | 17 ++ reo/models.py | 3 - reo/nested_inputs.py | 8 +- reo/scenario.py | 36 ++++ reo/src/ghp.py | 25 ++- reo/tests/posts/test_ghp_POST.json | 1 - 16 files changed, 383 insertions(+), 111 deletions(-) create mode 100644 Manifest.toml create mode 100644 Project.toml create mode 100644 ghpghx/migrations/0004_ghpghxoutputs_end_of_year_eft_f_and_more.py create mode 100644 ghpghx/migrations/0005_remove_ghpghxoutputs_peak_auxiliary_boiler_mmbtu_per_hour_and_more.py create mode 100644 ghpghx/migrations/0006_alter_ghpghxinputs_is_hybrid_ghx.py create mode 100644 ghpghx/migrations/0007_ghpghxoutputs_aux_heat_exchange_unit_type_and_more.py create mode 100644 ghpghx/tests/posts/test_hybrid_ghpghx_POST.json create mode 100644 reo/migrations/0150_remove_ghpmodel_is_hybrid_ghx.py diff --git a/Manifest.toml b/Manifest.toml new file mode 100644 index 000000000..0c8e995aa --- /dev/null +++ b/Manifest.toml @@ -0,0 +1,7 @@ +# This file is machine-generated - editing it directly is not advised + +julia_version = "1.8.0" +manifest_format = "2.0" +project_hash = "da39a3ee5e6b4b0d3255bfef95601890afd80709" + +[deps] diff --git a/Project.toml b/Project.toml new file mode 100644 index 000000000..81648c0b1 --- /dev/null +++ b/Project.toml @@ -0,0 +1 @@ +[deps] diff --git a/ghpghx/migrations/0004_ghpghxoutputs_end_of_year_eft_f_and_more.py b/ghpghx/migrations/0004_ghpghxoutputs_end_of_year_eft_f_and_more.py new file mode 100644 index 000000000..f84210ca9 --- /dev/null +++ b/ghpghx/migrations/0004_ghpghxoutputs_end_of_year_eft_f_and_more.py @@ -0,0 +1,39 @@ +# Generated by Django 4.0.7 on 2023-03-07 01:46 + +import django.contrib.postgres.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ghpghx', '0003_auto_20211222_1817'), + ] + + operations = [ + migrations.AddField( + model_name='ghpghxoutputs', + name='end_of_year_eft_f', + field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), blank=True, default=list, help_text='End of year entering fluid temperature for all years in the last iteration of GHX sizing [degF]', null=True, size=None), + ), + migrations.AddField( + model_name='ghpghxoutputs', + name='peak_auxiliary_boiler_mmbtu_per_hour', + field=models.FloatField(blank=True, help_text='Peak auxiliary boiler consumption for boiler sizing [MMBtu/hr]', null=True), + ), + migrations.AddField( + model_name='ghpghxoutputs', + name='peak_auxiliary_cooling_tower_ton', + field=models.FloatField(blank=True, help_text='Peak auxiliary cooling tower consumption for cooling tower sizing [ton]', null=True), + ), + migrations.AddField( + model_name='ghpghxoutputs', + name='yearly_auxiliary_boiler_consumption_series_mmbtu_per_hour', + field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), blank=True, default=list, help_text='Hourly auxiliary boiler consumption, average across simulation years [MMBtu/hr]', null=True, size=None), + ), + migrations.AddField( + model_name='ghpghxoutputs', + name='yearly_auxiliary_cooling_tower_consumption_series_ton', + field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), blank=True, default=list, help_text='Hourly auxiliary cooling tower consumption, average across simulation years [ton]', null=True, size=None), + ), + ] diff --git a/ghpghx/migrations/0005_remove_ghpghxoutputs_peak_auxiliary_boiler_mmbtu_per_hour_and_more.py b/ghpghx/migrations/0005_remove_ghpghxoutputs_peak_auxiliary_boiler_mmbtu_per_hour_and_more.py new file mode 100644 index 000000000..ead1708c6 --- /dev/null +++ b/ghpghx/migrations/0005_remove_ghpghxoutputs_peak_auxiliary_boiler_mmbtu_per_hour_and_more.py @@ -0,0 +1,75 @@ +# Generated by Django 4.0.7 on 2023-03-10 02:48 + +import django.contrib.postgres.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ghpghx', '0004_ghpghxoutputs_end_of_year_eft_f_and_more'), + ] + + operations = [ + migrations.RemoveField( + model_name='ghpghxoutputs', + name='peak_auxiliary_boiler_mmbtu_per_hour', + ), + migrations.RemoveField( + model_name='ghpghxoutputs', + name='peak_auxiliary_cooling_tower_ton', + ), + migrations.RemoveField( + model_name='ghpghxoutputs', + name='yearly_auxiliary_boiler_consumption_series_mmbtu_per_hour', + ), + migrations.RemoveField( + model_name='ghpghxoutputs', + name='yearly_auxiliary_cooling_tower_consumption_series_ton', + ), + migrations.AddField( + model_name='ghpghxinputs', + name='is_hybrid_ghx', + field=models.BooleanField(blank=True, default=True, help_text='If the GHP system uses a hybrid GHX with auxiliary heater or cooler', null=True), + ), + migrations.AddField( + model_name='ghpghxoutputs', + name='annual_aux_cooler_electric_consumption_kwh', + field=models.FloatField(blank=True, help_text='Annual auxiliary cooler electrical consumption [kWh]', null=True), + ), + migrations.AddField( + model_name='ghpghxoutputs', + name='annual_aux_heater_electric_consumption_kwh', + field=models.FloatField(blank=True, help_text='Annual auxiliary heater electrical consumption [kWh]', null=True), + ), + migrations.AddField( + model_name='ghpghxoutputs', + name='peak_aux_cooler_thermal_production_ton', + field=models.FloatField(blank=True, help_text='Peak auxiliary cooler thermal production for cooler sizing [ton]', null=True), + ), + migrations.AddField( + model_name='ghpghxoutputs', + name='peak_aux_heater_thermal_production_mmbtu_per_hour', + field=models.FloatField(blank=True, help_text='Peak auxiliary heater thermal production for heater sizing [MMBtu/hr]', null=True), + ), + migrations.AddField( + model_name='ghpghxoutputs', + name='yearly_aux_cooler_electric_consumption_series_kw', + field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), blank=True, default=list, help_text='Hourly auxiliary cooler electrical consumption, average across simulation years [kW]', null=True, size=None), + ), + migrations.AddField( + model_name='ghpghxoutputs', + name='yearly_aux_cooler_thermal_production_series_kwt', + field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), blank=True, default=list, help_text='Hourly auxiliary cooler thermal production, average across simulation years [kW-thermal]', null=True, size=None), + ), + migrations.AddField( + model_name='ghpghxoutputs', + name='yearly_aux_heater_electric_consumption_series_kw', + field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), blank=True, default=list, help_text='Hourly auxiliary heater electrical consumption, average across simulation years [kW]', null=True, size=None), + ), + migrations.AddField( + model_name='ghpghxoutputs', + name='yearly_aux_heater_thermal_production_series_mmbtu_per_hour', + field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), blank=True, default=list, help_text='Hourly auxiliary heater thermal production, average across simulation years [MMBtu/hr]', null=True, size=None), + ), + ] diff --git a/ghpghx/migrations/0006_alter_ghpghxinputs_is_hybrid_ghx.py b/ghpghx/migrations/0006_alter_ghpghxinputs_is_hybrid_ghx.py new file mode 100644 index 000000000..7be5d3e83 --- /dev/null +++ b/ghpghx/migrations/0006_alter_ghpghxinputs_is_hybrid_ghx.py @@ -0,0 +1,18 @@ +# Generated by Django 4.0.7 on 2023-03-10 05:48 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ghpghx', '0005_remove_ghpghxoutputs_peak_auxiliary_boiler_mmbtu_per_hour_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='ghpghxinputs', + name='is_hybrid_ghx', + field=models.BooleanField(blank=True, help_text='If the GHP system uses a hybrid GHX with auxiliary heater or cooler', null=True), + ), + ] diff --git a/ghpghx/migrations/0007_ghpghxoutputs_aux_heat_exchange_unit_type_and_more.py b/ghpghx/migrations/0007_ghpghxoutputs_aux_heat_exchange_unit_type_and_more.py new file mode 100644 index 000000000..bab32e3cd --- /dev/null +++ b/ghpghx/migrations/0007_ghpghxoutputs_aux_heat_exchange_unit_type_and_more.py @@ -0,0 +1,23 @@ +# Generated by Django 4.0.7 on 2023-03-10 07:19 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ghpghx', '0006_alter_ghpghxinputs_is_hybrid_ghx'), + ] + + operations = [ + migrations.AddField( + model_name='ghpghxoutputs', + name='aux_heat_exchange_unit_type', + field=models.TextField(blank=True, help_text='Specifies if the auxiliary heat exchange unit is a heater or cooler', null=True), + ), + migrations.AlterField( + model_name='ghpghxinputs', + name='is_hybrid_ghx', + field=models.BooleanField(blank=True, default=True, help_text='If the GHP system uses a hybrid GHX with auxiliary heater or cooler', null=True), + ), + ] diff --git a/ghpghx/models.py b/ghpghx/models.py index 68f2ecbe3..154e21827 100644 --- a/ghpghx/models.py +++ b/ghpghx/models.py @@ -216,7 +216,12 @@ def clean(self): init_sizing_factor_ft_per_peak_ton = models.FloatField(blank=True, default=246.1, validators=[MinValueValidator(1.0), MaxValueValidator(5000.0)], help_text="Initial guess of total feet of GHX boreholes (total feet = N bores * Length bore) based on peak ton heating/cooling [ft/ton]") + # TODO move is_hybrid_ghx to ghpghx_inputs because we want to compete non-hybrid vs hybrid within single REopt run + # Hybrid flag + is_hybrid_ghx = models.BooleanField(null=True, blank=True, default=True, + help_text="If the GHP system uses a hybrid GHX with auxiliary heater or cooler") + class GHPGHXOutputs(models.Model): # Outputs/results @@ -258,7 +263,32 @@ class GHPGHXOutputs(models.Model): help_text="Average cooling heatpump system coefficient of performance (COP) (includes ghx pump allocation)") solved_eft_error_f = models.FloatField(null=True, blank=True, help_text="Error between the solved GHPGHX system EFT and the max or min limit for EFT") - + # Hybrid + yearly_aux_heater_thermal_production_series_mmbtu_per_hour = ArrayField(models.FloatField(null=True, blank=True), + default=list, null=True, blank=True, + help_text="Hourly auxiliary heater thermal production, average across simulation years [MMBtu/hr]") + yearly_aux_cooler_thermal_production_series_kwt = ArrayField(models.FloatField(null=True, blank=True), + default=list, null=True, blank=True, + help_text="Hourly auxiliary cooler thermal production, average across simulation years [kW-thermal]") + yearly_aux_heater_electric_consumption_series_kw = ArrayField(models.FloatField(null=True, blank=True), + default=list, null=True, blank=True, + help_text="Hourly auxiliary heater electrical consumption, average across simulation years [kW]") + yearly_aux_cooler_electric_consumption_series_kw = ArrayField(models.FloatField(null=True, blank=True), + default=list, null=True, blank=True, + help_text="Hourly auxiliary cooler electrical consumption, average across simulation years [kW]") + peak_aux_heater_thermal_production_mmbtu_per_hour = models.FloatField(null=True, blank=True, + help_text="Peak auxiliary heater thermal production for heater sizing [MMBtu/hr]") + peak_aux_cooler_thermal_production_ton = models.FloatField(null=True, blank=True, + help_text="Peak auxiliary cooler thermal production for cooler sizing [ton]") + annual_aux_heater_electric_consumption_kwh = models.FloatField(null=True, blank=True, + help_text="Annual auxiliary heater electrical consumption [kWh]") + annual_aux_cooler_electric_consumption_kwh = models.FloatField(null=True, blank=True, + help_text="Annual auxiliary cooler electrical consumption [kWh]") + end_of_year_eft_f = ArrayField(models.FloatField(null=True, blank=True), + default=list, null=True, blank=True, + help_text="End of year entering fluid temperature for all years in the last iteration of GHX sizing [degF]") + aux_heat_exchange_unit_type = models.TextField(null=True, blank=True, + help_text="Specifies if the auxiliary heat exchange unit is a heater or cooler") class ModelManager(object): diff --git a/ghpghx/tests/posts/test_ghpghx_POST.json b/ghpghx/tests/posts/test_ghpghx_POST.json index 3675c9ef3..14b604b01 100644 --- a/ghpghx/tests/posts/test_ghpghx_POST.json +++ b/ghpghx/tests/posts/test_ghpghx_POST.json @@ -5,5 +5,6 @@ "ghx_model": "TESS", "tess_ghx_minimum_timesteps_per_hour": 1, "max_sizing_iterations": 1, - "init_sizing_factor_ft_per_peak_ton": 300.0 + "init_sizing_factor_ft_per_peak_ton": 300.0, + "is_hybrid_ghx": true } \ No newline at end of file diff --git a/ghpghx/tests/posts/test_hybrid_ghpghx_POST.json b/ghpghx/tests/posts/test_hybrid_ghpghx_POST.json new file mode 100644 index 000000000..0e6a7b2a6 --- /dev/null +++ b/ghpghx/tests/posts/test_hybrid_ghpghx_POST.json @@ -0,0 +1,9 @@ +{ + "borehole_depth_ft": 400.0, + "simulation_years": 20, + "solver_eft_tolerance_f": 2.0, + "ghx_model": "TESS", + "tess_ghx_minimum_timesteps_per_hour": 1, + "max_sizing_iterations": 10, + "init_sizing_factor_ft_per_peak_ton": 300.0 +} \ No newline at end of file diff --git a/julia_src/Manifest.toml b/julia_src/Manifest.toml index 043fc7793..8bc0f8abf 100644 --- a/julia_src/Manifest.toml +++ b/julia_src/Manifest.toml @@ -1,8 +1,8 @@ # This file is machine-generated - editing it directly is not advised -julia_version = "1.8.3" +julia_version = "1.8.0" manifest_format = "2.0" -project_hash = "c2cdadb4ef175368e043d03d8dd75e25e020ef11" +project_hash = "90e66426e70d2bf97b8a3e99508fcb404743b11d" [[deps.AbstractFFTs]] deps = ["ChainRulesCore", "LinearAlgebra"] @@ -11,10 +11,10 @@ uuid = "621f4979-c628-5d54-868e-fcf4e3e8185c" version = "1.2.1" [[deps.Adapt]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "195c5505521008abea5aee4f96930717958eac6f" +deps = ["LinearAlgebra", "Requires"] +git-tree-sha1 = "cc37d689f599e8df4f464b2fa3870ff7db7492ef" uuid = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" -version = "3.4.0" +version = "3.6.1" [[deps.ArchGDAL]] deps = ["CEnum", "ColorTypes", "Dates", "DiskArrays", "Extents", "GDAL", "GeoFormatTypes", "GeoInterface", "GeoInterfaceRecipes", "ImageCore", "Tables"] @@ -24,6 +24,7 @@ version = "0.9.3" [[deps.ArgTools]] uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" +version = "1.1.1" [[deps.Arrow_jll]] deps = ["Artifacts", "Bzip2_jll", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Lz4_jll", "Pkg", "Thrift_jll", "Zlib_jll", "boost_jll", "snappy_jll"] @@ -57,9 +58,9 @@ version = "0.7.3" [[deps.Blosc_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Lz4_jll", "Pkg", "Zlib_jll", "Zstd_jll"] -git-tree-sha1 = "91d6baa911283650df649d0aea7c28639273ae7b" +git-tree-sha1 = "e94024822c0a5b14989abbdba57820ad5b177b95" uuid = "0b7ba130-8d10-5ba8-a3d6-c5182647fed9" -version = "1.21.1+0" +version = "1.21.2+0" [[deps.Bzip2_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] @@ -80,15 +81,15 @@ version = "0.10.9" [[deps.ChainRulesCore]] deps = ["Compat", "LinearAlgebra", "SparseArrays"] -git-tree-sha1 = "e7ff6cadf743c098e08fca25c91103ee4303c9bb" +git-tree-sha1 = "c6d890a52d2c4d55d326439580c3b8d0875a77d9" uuid = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" -version = "1.15.6" +version = "1.15.7" [[deps.ChangesOfVariables]] deps = ["ChainRulesCore", "LinearAlgebra", "Test"] -git-tree-sha1 = "38f7a08f19d8810338d4f5085211c7dfa5d5bdd8" +git-tree-sha1 = "485193efd2176b88e6622a39a246f8c5b600e74e" uuid = "9e997f8a-9a97-42d5-a9f1-ce6bfc15e2c0" -version = "0.1.4" +version = "0.1.6" [[deps.CodecBzip2]] deps = ["Bzip2_jll", "Libdl", "TranscodingStreams"] @@ -98,9 +99,9 @@ version = "0.7.2" [[deps.CodecZlib]] deps = ["TranscodingStreams", "Zlib_jll"] -git-tree-sha1 = "ded953804d019afa9a3f98981d99b33e3db7b6da" +git-tree-sha1 = "9c209fb7536406834aa938fb149964b985de6c83" uuid = "944b1d66-785c-5afd-91f1-9de20f533193" -version = "0.7.0" +version = "0.7.1" [[deps.ColorTypes]] deps = ["FixedPointNumbers", "Random"] @@ -133,19 +134,20 @@ version = "0.3.0" [[deps.Compat]] deps = ["Dates", "LinearAlgebra", "UUIDs"] -git-tree-sha1 = "00a2cccc7f098ff3b66806862d275ca3db9e6e5a" +git-tree-sha1 = "7a60c856b9fa189eb34f5f8a6f6b5529b7942957" uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" -version = "4.5.0" +version = "4.6.1" [[deps.CompilerSupportLibraries_jll]] deps = ["Artifacts", "Libdl"] uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" +version = "0.5.2+0" [[deps.ConstructionBase]] deps = ["LinearAlgebra"] -git-tree-sha1 = "fb21ddd70a051d882a1686a5a550990bbe371a95" +git-tree-sha1 = "89a9db8d28102b094992472d333674bd1a83ce2a" uuid = "187b0558-2788-49d3-abe0-74a17ed4e7c9" -version = "1.4.1" +version = "1.5.1" [[deps.CoolProp]] deps = ["CoolProp_jll", "Markdown", "Unitful"] @@ -191,15 +193,15 @@ version = "1.1.0" [[deps.DiffRules]] deps = ["IrrationalConstants", "LogExpFunctions", "NaNMath", "Random", "SpecialFunctions"] -git-tree-sha1 = "c5b6685d53f933c11404a3ae9822afe30d522494" +git-tree-sha1 = "a4ad7ef19d2cdc2eff57abbbe68032b1cd0bd8f8" uuid = "b552c78f-8df3-52c6-915a-8e097449b14b" -version = "1.12.2" +version = "1.13.0" [[deps.DiskArrays]] deps = ["OffsetArrays"] -git-tree-sha1 = "27ebdcf03ca847fa484f28273db57de3c8514920" +git-tree-sha1 = "3f87990e0882e44c0f4e5c9699d09a0edbfa25c8" uuid = "3c3547ce-8d99-4f5e-a174-61eb10b00ae3" -version = "0.3.8" +version = "0.3.9" [[deps.Distributed]] deps = ["Random", "Serialization", "Sockets"] @@ -212,8 +214,9 @@ uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" version = "0.9.3" [[deps.Downloads]] -deps = ["ArgTools", "LibCURL", "NetworkOptions"] +deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"] uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" +version = "1.6.0" [[deps.Expat_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] @@ -244,6 +247,9 @@ git-tree-sha1 = "e27c4ebe80e8699540f2d6c805cc12203b614f12" uuid = "48062228-2e41-5def-b9a4-89aafe57970f" version = "0.9.20" +[[deps.FileWatching]] +uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" + [[deps.FixedPointNumbers]] deps = ["Statistics"] git-tree-sha1 = "335bfdceacc84c5cdf16aadc768aa5ddfc5383cc" @@ -252,9 +258,9 @@ version = "0.8.4" [[deps.ForwardDiff]] deps = ["CommonSubexpressions", "DiffResults", "DiffRules", "LinearAlgebra", "LogExpFunctions", "NaNMath", "Preferences", "Printf", "Random", "SpecialFunctions", "StaticArrays"] -git-tree-sha1 = "a69dd6db8a809f78846ff259298678f0d6212180" +git-tree-sha1 = "00e252f4d706b3d55a8863432e742bf5717b498d" uuid = "f6369f11-7733-5829-9624-2563aa707210" -version = "0.10.34" +version = "0.10.35" [[deps.Future]] deps = ["Random"] @@ -268,9 +274,9 @@ version = "1.5.1" [[deps.GDAL_jll]] deps = ["Arrow_jll", "Artifacts", "Expat_jll", "GEOS_jll", "HDF5_jll", "JLLWrappers", "LibCURL_jll", "LibPQ_jll", "Libdl", "Libtiff_jll", "NetCDF_jll", "OpenJpeg_jll", "PROJ_jll", "Pkg", "SQLite_jll", "Zlib_jll", "Zstd_jll", "libgeotiff_jll"] -git-tree-sha1 = "46641669463e45077ea6f08e5c8bd07189173acf" +git-tree-sha1 = "aa913bff49c25482fe3db2c357cb5f8127a6d2ba" uuid = "a7073274-a066-55f0-b90d-d619367d196c" -version = "301.600.100+0" +version = "301.600.200+0" [[deps.GEOS_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] @@ -285,9 +291,9 @@ version = "0.4.1" [[deps.GeoInterface]] deps = ["Extents"] -git-tree-sha1 = "fb28b5dc239d0174d7297310ef7b84a11804dfab" +git-tree-sha1 = "e07a1b98ed72e3cdd02c6ceaab94b8a606faca40" uuid = "cf35fbd7-0cd7-5166-be24-54bfbe79505f" -version = "1.0.1" +version = "1.2.1" [[deps.GeoInterfaceRecipes]] deps = ["GeoInterface", "RecipesBase"] @@ -295,6 +301,13 @@ git-tree-sha1 = "29e1ec25cfb6762f503a19495aec347acf867a9e" uuid = "0329782f-3d07-4b52-b9f6-d3137cf03c7a" version = "1.0.0" +[[deps.GhpGhx]] +git-tree-sha1 = "21f8352f3797abcae3314bac3b44ca73c4acbd91" +repo-rev = "update_names" +repo-url = "https://github.com/NREL/GhpGhx.jl.git" +uuid = "7ce85f02-24a8-4d69-a3f0-14b5daa7d30c" +version = "0.1.0" + [[deps.Glob]] git-tree-sha1 = "4df9f7e06108728ebf00a0a11edee4b29a482bb2" uuid = "c27321d9-0574-5035-807b-f59d2c89b15c" @@ -308,15 +321,15 @@ version = "1.1.2" [[deps.H5Zblosc]] deps = ["Blosc", "HDF5"] -git-tree-sha1 = "26b22c9039b039e29ec4f4f989946de722e87ab9" +git-tree-sha1 = "d3966da25e48c05c31cd9786fd201627877612a2" uuid = "c8ec2601-a99c-407f-b158-e79c03c2f5f7" -version = "0.1.0" +version = "0.1.1" [[deps.HDF5]] deps = ["Compat", "HDF5_jll", "Libdl", "Mmap", "Random", "Requires", "UUIDs"] -git-tree-sha1 = "b5df7c3cab3a00c33c2e09c6bd23982a75e2fbb2" +git-tree-sha1 = "3dab31542b3da9f25a6a1d11159d4af8fdce7d67" uuid = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f" -version = "0.16.13" +version = "0.16.14" [[deps.HDF5_jll]] deps = ["Artifacts", "JLLWrappers", "LibCURL_jll", "Libdl", "OpenSSL_jll", "Pkg", "Zlib_jll"] @@ -324,13 +337,6 @@ git-tree-sha1 = "4cc2bb72df6ff40b055295fdef6d92955f9dede8" uuid = "0234f1f7-429e-5d53-9886-15a909be8d59" version = "1.12.2+2" -[[deps.GhpGhx]] -git-tree-sha1 = "19a4afe42a79f22612478a87d9d938c6a5087af2" -repo-rev = "main" -repo-url = "https://github.com/NREL/GhpGhx.jl" -uuid = "7ce85f02-24a8-4d69-a3f0-14b5daa7d30c" -version = "0.1.0" - [[deps.HTTP]] deps = ["Base64", "Dates", "IniFile", "Logging", "MbedTLS", "NetworkOptions", "Sockets", "URIs"] git-tree-sha1 = "0fa77022fe4b511826b39c894c90daf5fce3334a" @@ -345,9 +351,9 @@ version = "0.9.4" [[deps.InfrastructureModels]] deps = ["JuMP", "Memento"] -git-tree-sha1 = "cca034ca1ae73e08bf4db63f158474d40d7dad9a" +git-tree-sha1 = "88da90ad5d8ca541350c156bea2715f3a23836ce" uuid = "2030c09a-7f63-5d83-885d-db604e0e9cc0" -version = "0.7.5" +version = "0.7.6" [[deps.IniFile]] git-tree-sha1 = "f550e6e32074c939295eb5ea6de31849ac2c9625" @@ -356,9 +362,9 @@ version = "0.5.1" [[deps.InlineStrings]] deps = ["Parsers"] -git-tree-sha1 = "0cf92ec945125946352f3d46c96976ab972bde6f" +git-tree-sha1 = "9cc2baf75c6d09f9da536ddf58eb2f29dedaf461" uuid = "842dd82b-1e85-43dc-bf29-5d0ee9dffc48" -version = "1.3.2" +version = "1.4.0" [[deps.InteractiveUtils]] deps = ["Markdown"] @@ -377,9 +383,9 @@ uuid = "3587e190-3f89-42d0-90ee-14403ec27112" version = "0.1.8" [[deps.IrrationalConstants]] -git-tree-sha1 = "7fd44fd4ff43fc60815f8e764c0f352b83c49151" +git-tree-sha1 = "630b497eafcc20001bba38a4651b327dcfc491d2" uuid = "92d709cd-6900-40b7-9082-c6be49f344b6" -version = "0.1.1" +version = "0.2.2" [[deps.IterTools]] git-tree-sha1 = "fa6287a4469f5e048d763df38279ee729fbd44e5" @@ -410,10 +416,10 @@ uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" version = "0.21.3" [[deps.JpegTurbo_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "b53380851c6e6664204efb2e62cd24fa5c47e4ba" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "6f2675ef130a300a112286de91973805fcc5ffbc" uuid = "aacddb02-875f-59d6-b918-886e6ef4fbf8" -version = "2.1.2+0" +version = "2.1.91+0" [[deps.JuMP]] deps = ["LinearAlgebra", "MathOptInterface", "MutableArithmetics", "OrderedCollections", "Printf", "SparseArrays"] @@ -442,10 +448,12 @@ version = "2.10.1+0" [[deps.LibCURL]] deps = ["LibCURL_jll", "MozillaCACerts_jll"] uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" +version = "0.6.3" [[deps.LibCURL_jll]] deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"] uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" +version = "7.84.0+0" [[deps.LibGit2]] deps = ["Base64", "NetworkOptions", "Printf", "SHA"] @@ -460,6 +468,7 @@ version = "14.3.0+1" [[deps.LibSSH2_jll]] deps = ["Artifacts", "Libdl", "MbedTLS_jll"] uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" +version = "1.10.2+0" [[deps.Libdl]] uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" @@ -494,9 +503,9 @@ version = "2.12.0+0" [[deps.LogExpFunctions]] deps = ["ChainRulesCore", "ChangesOfVariables", "DocStringExtensions", "InverseFunctions", "IrrationalConstants", "LinearAlgebra"] -git-tree-sha1 = "946607f84feb96220f480e0422d3484c49c00239" +git-tree-sha1 = "0a1b7c2863e44523180fdb3146534e265a91870b" uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688" -version = "0.3.19" +version = "0.3.23" [[deps.Logging]] uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" @@ -543,6 +552,7 @@ version = "1.1.7" [[deps.MbedTLS_jll]] deps = ["Artifacts", "Libdl"] uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" +version = "2.28.0+0" [[deps.Memento]] deps = ["Dates", "Distributed", "Requires", "Serialization", "Sockets", "Test", "UUIDs"] @@ -561,6 +571,7 @@ version = "0.3.4" [[deps.MozillaCACerts_jll]] uuid = "14a3606d-f60d-562e-9121-12d972cd8159" +version = "2022.2.1" [[deps.MutableArithmetics]] deps = ["LinearAlgebra", "SparseArrays", "Test"] @@ -570,9 +581,9 @@ version = "1.0.5" [[deps.NaNMath]] deps = ["OpenLibm_jll"] -git-tree-sha1 = "a7c3d1da1189a1c2fe843a3bfa04d18d20eb3211" +git-tree-sha1 = "0877504529a3e5c3343c6f8b4c0381e57e4387e4" uuid = "77ba4419-2d1f-58cd-9bb1-8ffee604a2e3" -version = "1.0.1" +version = "1.0.2" [[deps.NetCDF_jll]] deps = ["Artifacts", "HDF5_jll", "JLLWrappers", "LibCURL_jll", "Libdl", "Pkg", "XML2_jll", "Zlib_jll"] @@ -582,16 +593,18 @@ version = "400.902.5+1" [[deps.NetworkOptions]] uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" +version = "1.2.0" [[deps.OffsetArrays]] deps = ["Adapt"] -git-tree-sha1 = "f71d8950b724e9ff6110fc948dff5a329f901d64" +git-tree-sha1 = "82d7c9e310fe55aa54996e6f7f94674e2a38fcb4" uuid = "6fe1bfb0-de20-5000-8ca7-80f57d26f881" -version = "1.12.8" +version = "1.12.9" [[deps.OpenBLAS_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" +version = "0.3.20+0" [[deps.OpenJpeg_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Libtiff_jll", "LittleCMS_jll", "Pkg", "libpng_jll"] @@ -602,12 +615,13 @@ version = "2.4.0+0" [[deps.OpenLibm_jll]] deps = ["Artifacts", "Libdl"] uuid = "05823500-19ac-5b8b-9628-191a04bc5112" +version = "0.8.1+0" [[deps.OpenSSL_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "f6e9dba33f9f2c44e08a020b0caf6903be540004" +git-tree-sha1 = "9ff31d101d987eb9d66bd8b176ac7c277beccd09" uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95" -version = "1.1.19+0" +version = "1.1.20+0" [[deps.OpenSpecFun_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"] @@ -634,13 +648,14 @@ version = "0.5.11" [[deps.Parsers]] deps = ["Dates", "SnoopPrecompile"] -git-tree-sha1 = "6466e524967496866901a78fca3f2e9ea445a559" +git-tree-sha1 = "478ac6c952fddd4399e71d4779797c538d0ff2bf" uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" -version = "2.5.2" +version = "2.5.8" [[deps.Pkg]] deps = ["Artifacts", "Dates", "Downloads", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" +version = "1.8.0" [[deps.PolyhedralRelaxations]] deps = ["DataStructures", "ForwardDiff", "JuMP", "Logging", "LoggingExtras"] @@ -655,10 +670,10 @@ uuid = "2dfb63ee-cc39-5dd5-95bd-886bf059d720" version = "1.4.2" [[deps.PowerModelsDistribution]] -deps = ["CSV", "Dates", "FilePaths", "Glob", "InfrastructureModels", "JSON", "JuMP", "LinearAlgebra", "Logging", "LoggingExtras", "PolyhedralRelaxations", "Statistics"] -git-tree-sha1 = "497384070b047898b763a7e9c883c5bc196582ad" +deps = ["CSV", "Dates", "FilePaths", "Glob", "InfrastructureModels", "JSON", "JuMP", "LinearAlgebra", "Logging", "LoggingExtras", "PolyhedralRelaxations", "SpecialFunctions", "Statistics"] +git-tree-sha1 = "fd2a5efc06acb1b449a985c48d4b3d8004a3b371" uuid = "d7431456-977f-11e9-2de3-97ff7677985e" -version = "0.14.5" +version = "0.14.7" [[deps.Preferences]] deps = ["TOML"] @@ -695,9 +710,9 @@ version = "0.3.2" [[deps.RecipesBase]] deps = ["SnoopPrecompile"] -git-tree-sha1 = "18c35ed630d7229c5584b945641a73ca83fb5213" +git-tree-sha1 = "261dddd3b862bd2c940cf6ca4d1c8fe593e457c8" uuid = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" -version = "1.3.2" +version = "1.3.3" [[deps.Reexport]] git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b" @@ -712,24 +727,25 @@ version = "1.3.0" [[deps.Roots]] deps = ["ChainRulesCore", "CommonSolve", "Printf", "Setfield"] -git-tree-sha1 = "a3db467ce768343235032a1ca0830fc64158dadf" +git-tree-sha1 = "9c2f5d3768804ed465f0c51540c6074ae9f63900" uuid = "f2b01f46-fcfa-551c-844a-d8ac1e96c665" -version = "2.0.8" +version = "2.0.9" [[deps.SHA]] uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" +version = "0.7.0" [[deps.SQLite_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] -git-tree-sha1 = "2c761a91fb503e94bd0130fcf4352166c3c555bc" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Zlib_jll"] +git-tree-sha1 = "54d66b0f69f4578f4988fc08d579783fcdcd764f" uuid = "76ed43ae-9a5d-5a62-8c75-30186b810ce8" -version = "3.40.0+1" +version = "3.41.0+0" [[deps.SentinelArrays]] deps = ["Dates", "Random"] -git-tree-sha1 = "efd23b378ea5f2db53a55ae53d3133de4e080aa9" +git-tree-sha1 = "77d3c4726515dca71f6d80fbb5e251088defe305" uuid = "91c51154-3ec4-41a3-a24f-3f23e20d615c" -version = "1.3.16" +version = "1.3.18" [[deps.Serialization]] uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" @@ -741,9 +757,10 @@ uuid = "efcf1570-3423-57d1-acb7-fd33fddbac46" version = "1.1.1" [[deps.SnoopPrecompile]] -git-tree-sha1 = "f604441450a3c0569830946e5b33b78c928e1a85" +deps = ["Preferences"] +git-tree-sha1 = "e760a70afdcd461cf01a575947738d359234665c" uuid = "66db9d55-30c0-4569-8b51-7e840670fc0c" -version = "1.0.1" +version = "1.0.3" [[deps.Sockets]] uuid = "6462fe0b-24de-5631-8697-dd941f90decc" @@ -754,9 +771,9 @@ uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" [[deps.SpecialFunctions]] deps = ["ChainRulesCore", "IrrationalConstants", "LogExpFunctions", "OpenLibm_jll", "OpenSpecFun_jll"] -git-tree-sha1 = "d75bda01f8c31ebb72df80a46c88b25d1c79c56d" +git-tree-sha1 = "ef28127915f4229c971eb43f3fc075dd3fe91880" uuid = "276daf66-3868-5448-9aa4-cd146d93841b" -version = "2.1.7" +version = "2.2.0" [[deps.StackViews]] deps = ["OffsetArrays"] @@ -766,9 +783,9 @@ version = "0.1.1" [[deps.StaticArrays]] deps = ["LinearAlgebra", "Random", "StaticArraysCore", "Statistics"] -git-tree-sha1 = "6954a456979f23d05085727adb17c4551c19ecd1" +git-tree-sha1 = "2d7d9e1ddadc8407ffd460e24218e37ef52dd9a3" uuid = "90137ffa-7385-5640-81b9-e52037218182" -version = "1.5.12" +version = "1.5.16" [[deps.StaticArraysCore]] git-tree-sha1 = "6b7ba252635a5eff6a0b0664a41ee140a1c9e72a" @@ -782,6 +799,7 @@ uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" [[deps.TOML]] deps = ["Dates"] uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" +version = "1.0.0" [[deps.TableTraits]] deps = ["IteratorInterfaceExtensions"] @@ -798,6 +816,7 @@ version = "1.10.0" [[deps.Tar]] deps = ["ArgTools", "SHA"] uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" +version = "1.10.0" [[deps.TensorCore]] deps = ["LinearAlgebra"] @@ -811,9 +830,9 @@ uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [[deps.TestEnv]] deps = ["Pkg"] -git-tree-sha1 = "df6f1b5f13936504ba45088e13a62a6930cdabf7" +git-tree-sha1 = "b5a483bcc8c9f0df569101df166601e5e699f346" uuid = "1e6cf692-eddd-4d53-88a5-2d735e33781b" -version = "1.8.1" +version = "1.9.3" [[deps.Thrift_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "boost_jll"] @@ -823,14 +842,14 @@ version = "0.16.0+0" [[deps.TranscodingStreams]] deps = ["Random", "Test"] -git-tree-sha1 = "e4bdc63f5c6d62e80eb1c0043fcc0360d5950ff7" +git-tree-sha1 = "94f38103c984f89cf77c402f2a68dbd870f8165f" uuid = "3bb67fe8-82b1-5028-8e26-92a6c54297fa" -version = "0.9.10" +version = "0.9.11" [[deps.URIs]] -git-tree-sha1 = "ac00576f90d8a259f2c9d823e91d1de3fd44d348" +git-tree-sha1 = "074f993b0ca030848b897beff716d93aca60f06a" uuid = "5c2747f8-b7ea-4ff2-ba2e-563bfd36b1d4" -version = "1.4.1" +version = "1.4.2" [[deps.UUIDs]] deps = ["Random", "SHA"] @@ -841,9 +860,9 @@ uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" [[deps.Unitful]] deps = ["ConstructionBase", "Dates", "LinearAlgebra", "Random"] -git-tree-sha1 = "d670a70dd3cdbe1c1186f2f17c9a68a7ec24838c" +git-tree-sha1 = "bb37ed24f338bc59b83e3fc9f32dd388e5396c53" uuid = "1986cc42-f94f-5a68-af5c-568840ba703d" -version = "1.12.2" +version = "1.12.4" [[deps.WeakRefStrings]] deps = ["DataAPI", "InlineStrings", "Parsers"] @@ -871,12 +890,13 @@ version = "0.15.5" [[deps.Zlib_jll]] deps = ["Libdl"] uuid = "83775a58-1f1d-513f-b197-d71354ab007a" +version = "1.2.12+3" [[deps.Zstd_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "e45044cd873ded54b6a5bac0eb5c971392cf1927" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "c6edfe154ad7b313c01aceca188c05c835c67360" uuid = "3161d3a3-bdf6-5164-811a-617609db77b4" -version = "1.5.2+0" +version = "1.5.4+0" [[deps.boost_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] @@ -887,6 +907,7 @@ version = "1.76.0+1" [[deps.libblastrampoline_jll]] deps = ["Artifacts", "Libdl", "OpenBLAS_jll"] uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" +version = "5.1.1+0" [[deps.libgeotiff_jll]] deps = ["Artifacts", "JLLWrappers", "LibCURL_jll", "Libdl", "Libtiff_jll", "PROJ_jll", "Pkg"] @@ -903,10 +924,12 @@ version = "1.6.38+0" [[deps.nghttp2_jll]] deps = ["Artifacts", "Libdl"] uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" +version = "1.48.0+0" [[deps.p7zip_jll]] deps = ["Artifacts", "Libdl"] uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" +version = "17.4.0+0" [[deps.snappy_jll]] deps = ["Artifacts", "JLLWrappers", "LZO_jll", "Libdl", "Pkg", "Zlib_jll"] diff --git a/reo/migrations/0150_remove_ghpmodel_is_hybrid_ghx.py b/reo/migrations/0150_remove_ghpmodel_is_hybrid_ghx.py new file mode 100644 index 000000000..2de31488c --- /dev/null +++ b/reo/migrations/0150_remove_ghpmodel_is_hybrid_ghx.py @@ -0,0 +1,17 @@ +# Generated by Django 4.0.7 on 2023-03-10 02:48 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('reo', '0149_ghpmodel_aux_cooler_energy_use_intensity_kwe_per_kwt_and_more'), + ] + + operations = [ + migrations.RemoveField( + model_name='ghpmodel', + name='is_hybrid_ghx', + ), + ] diff --git a/reo/models.py b/reo/models.py index 0a6d9554b..aec33c5e5 100644 --- a/reo/models.py +++ b/reo/models.py @@ -1142,9 +1142,6 @@ class GHPModel(models.Model): ghpghx_responses = ArrayField(PickledObjectField(null=True, editable=True), null=True, default=list) can_serve_dhw = models.BooleanField(null=True, blank=True) - # TODO move is_hybrid_ghx to ghpghx_inputs because we want to compete non-hybrid vs hybrid within single REopt run - is_hybrid_ghx = models.BooleanField(null=True, blank=True) - aux_heater_type = models.TextField(null=True, blank=True) aux_heater_installed_cost_us_dollars_per_mmbtu_per_hr = models.FloatField(null=True, blank=True) aux_heater_thermal_efficiency = models.FloatField(null=True, blank=True) diff --git a/reo/nested_inputs.py b/reo/nested_inputs.py index 8aad84ef1..a0a6fe078 100644 --- a/reo/nested_inputs.py +++ b/reo/nested_inputs.py @@ -2509,13 +2509,7 @@ def list_of_dict(input): "can_serve_dhw": { "type": "bool", "default": False, "description": "If GHP can serve the domestic hot water (DHW) portion of the heating load" - }, - # TODO move is_hybrid_ghx into ghpghx_inputs (and an input of GhpGhx.jl) - "is_hybrid_ghx": { - "type": "bool", "default": False, - "description": "If the GHP system uses a hybrid GHX with auxiliary heater or cooler" - }, - + }, "aux_heater_type": { "type": "str", "default": "electric", "description": "The type of auxiliary heater, 'electric' or 'natural_gas'" diff --git a/reo/scenario.py b/reo/scenario.py index 6c9e622a0..8f86d0d75 100644 --- a/reo/scenario.py +++ b/reo/scenario.py @@ -389,6 +389,8 @@ def setup_pv(pv_dict, latitude, longitude, time_steps_per_hour): number_of_ghpghx = len(inputs_dict["Site"]["GHP"]["ghpghx_inputs"]) for i in range(number_of_ghpghx): ghpghx_post = inputs_dict["Site"]["GHP"]["ghpghx_inputs"][i] + is_hybrid_ghx = ghpghx_post.pop("is_hybrid_ghx", None) + ghpghx_post["latitude"] = inputs_dict["Site"]["latitude"] ghpghx_post["longitude"] = inputs_dict["Site"]["longitude"] # Only SpaceHeating portion of Heating Load gets served by GHP, unless allowed by can_serve_dhw @@ -410,6 +412,40 @@ def setup_pv(pv_dict, latitude, longitude, time_steps_per_hour): k_by_zone = copy.deepcopy(GHPGHXInputs.ground_k_by_climate_zone) climate_zone, nearest_city, geometric_flag = get_climate_zone_and_nearest_city(ghpghx_post["latitude"], ghpghx_post["longitude"], BuiltInProfile.default_cities) ghpghx_post["ground_thermal_conductivity_btu_per_hr_ft_f"] = k_by_zone[climate_zone] + + # Hybrid + # Determine if location is heating or cooling dominated + if is_hybrid_ghx: + determine_heat_cool_post = copy.deepcopy(ghpghx_post) + determine_heat_cool_post["simulation_years"] = 2 + determine_heat_cool_post["max_sizing_iterations"] = 1 + + determine_heat_cool_post_resp = client.post('/v1/ghpghx/', data=determine_heat_cool_post) + determine_heat_cool_post_resp_dict = json.loads(determine_heat_cool_post_resp.content) + + determine_heat_cool_uuid = determine_heat_cool_post_resp_dict.get('ghp_uuid') + determine_heat_cool_results_url = "/v1/ghpghx/"+determine_heat_cool_uuid+"/results/" + determine_heat_cool_results_resp = client.get(determine_heat_cool_results_url) + determine_heat_cool_results_resp_dict = json.loads(determine_heat_cool_results_resp.content) + temp_diff = determine_heat_cool_results_resp_dict["outputs"]["end_of_year_eft_f"][1] - determine_heat_cool_results_resp_dict["outputs"]["end_of_year_eft_f"][0] + temp_diff = 0 + # TODO - Implement fractional sizing + hybrid_sizing_flag = 1.0 + if temp_diff > 0: + hybrid_sizing_flag = -2.0 + elif temp_diff < 0: + hybrid_sizing_flag = -1.0 + + ghpghx_post["hybrid_sizing_flag"] = hybrid_sizing_flag + + # Other hybrid inputs + ghpghx_post["is_heating_electric"] = False + if inputs_dict["Site"]["GHP"]["aux_heater_type"] == "electric": + ghpghx_post["is_heating_electric"] = True + + ghpghx_post["aux_heater_thermal_efficiency"] = inputs_dict["Site"]["GHP"]["aux_heater_thermal_efficiency"] + ghpghx_post["aux_cooler_energy_use_intensity_kwe_per_kwt"] = inputs_dict["Site"]["GHP"]["aux_cooler_energy_use_intensity_kwe_per_kwt"] + # Call /ghpghx endpoint to size GHP and GHX ghpghx_post_resp = client.post('/v1/ghpghx/', data=ghpghx_post) ghpghx_post_resp_dict = json.loads(ghpghx_post_resp.content) diff --git a/reo/src/ghp.py b/reo/src/ghp.py index 40cd76ee4..c1292298d 100644 --- a/reo/src/ghp.py +++ b/reo/src/ghp.py @@ -21,11 +21,16 @@ def __init__(self, dfm, response, **kwargs): self.length_boreholes_ft = response["outputs"]["length_boreholes_ft"] self.yearly_total_electric_consumption_series_kw = response["outputs"]["yearly_total_electric_consumption_series_kw"] self.peak_combined_heatpump_thermal_ton = response["outputs"]["peak_combined_heatpump_thermal_ton"] - # TODO replace dummy values below with GhpGhx.jl response fields - self.yearly_auxiliary_boiler_consumption_series_mmbtu_per_hour = [5.0] * 8760 #response["outputs"]["yearly_auxiliary_boiler_consumption_series_mmbtu_per_hour"] - self.yearly_auxiliary_cooling_tower_consumption_series_ton = [20.0] * 8760 #response["outputs"]["yearly_auxiliary_cooling_tower_consumption_series_ton"] - self.peak_auxiliary_boiler_mmbtu_per_hour = 5.0 #response["outputs"]["peak_auxiliary_boiler_mmbtu_per_hour"] - self.peak_auxiliary_cooling_tower_ton = 20.0 #response["outputs"]["peak_auxiliary_cooling_tower_ton"] + # Hybrid fields + self.yearly_aux_heater_thermal_production_series_mmbtu_per_hour = response["outputs"]["yearly_aux_heater_thermal_production_series_mmbtu_per_hour"] + self.yearly_aux_cooler_thermal_production_series_kwt = response["outputs"]["yearly_aux_cooler_thermal_production_series_kwt"] + self.yearly_aux_heater_electric_consumption_series_kw = response["outputs"]["yearly_aux_heater_electric_consumption_series_kw"] + self.yearly_aux_cooler_electric_consumption_series_kw = response["outputs"]["yearly_aux_cooler_electric_consumption_series_kw"] + self.peak_aux_heater_thermal_production_mmbtu_per_hour = response["outputs"]["peak_aux_heater_thermal_production_mmbtu_per_hour"] + self.peak_aux_cooler_thermal_production_ton = response["outputs"]["peak_aux_cooler_thermal_production_ton"] + self.annual_aux_heater_electric_consumption_kwh = response["outputs"]["annual_aux_heater_electric_consumption_kwh"] + self.annual_aux_cooler_electric_consumption_kwh = response["outputs"]["annual_aux_cooler_electric_consumption_kwh"] + self.aux_heat_exchange_unit_type = response["outputs"]["aux_heat_exchange_unit_type"] if kwargs.get("require_ghp_purchase"): self.require_ghp_purchase = 1 @@ -37,8 +42,6 @@ def __init__(self, dfm, response, **kwargs): self.installed_cost_building_hydronic_loop_us_dollars_per_sqft = kwargs.get("installed_cost_building_hydronic_loop_us_dollars_per_sqft") self.om_cost_us_dollars_per_sqft_year = kwargs.get("om_cost_us_dollars_per_sqft_year") self.building_sqft = kwargs.get("building_sqft") - # TODO is_hybrid_ghx will be moved to the ghpghx_inputs field - self.is_hybrid_ghx = kwargs.get("is_hybrid_ghx") self.aux_heater_type = kwargs.get("aux_heater_type") self.aux_heater_installed_cost_us_dollars_per_mmbtu_per_hr = kwargs.get("aux_heater_installed_cost_us_dollars_per_mmbtu_per_hr") self.aux_heater_thermal_efficiency = kwargs.get("aux_heater_thermal_efficiency") @@ -63,8 +66,8 @@ def __init__(self, dfm, response, **kwargs): # TODO finish fuel/cost/emissions # if self.aux_heater_type == "natural_gas": - # self.fuel_burn_series_mmbtu_per_hour = [self.yearly_auxiliary_boiler_consumption_series_mmbtu_per_hour[i] / self.aux_heater_thermal_efficiency - # for i in range(len(self.yearly_auxiliary_boiler_consumption_series_mmbtu_per_hour))] + # self.fuel_burn_series_mmbtu_per_hour = [self.yearly_aux_heater_thermal_production_series_mmbtu_per_hour[i] / self.aux_heater_thermal_efficiency + # for i in range(len(self.yearly_aux_heater_thermal_production_series_mmbtu_per_hour))] # self.aux_heater_yearly_fuel_burn_mmbtu = sum(self.fuel_burn_series_mmbtu_per_hour) # self.yearly_emissions_lb_CO2 = dfm.boiler.emissions_factor_lb_CO2_per_mmbtu * self.aux_heater_yearly_fuel_burn_mmbtu # self.yearly_emissions_lb_NOx = dfm.boiler.emissions_factor_lb_NOx_per_mmbtu * self.aux_heater_yearly_fuel_burn_mmbtu @@ -83,8 +86,8 @@ def setup_installed_cost_curve(self): # The GHX and hydronic loop cost are the y-intercepts ([$]) of the cost for each design self.ghx_cost = self.total_ghx_ft * self.installed_cost_ghx_us_dollars_per_ft self.hydronic_loop_cost = self.building_sqft * self.installed_cost_building_hydronic_loop_us_dollars_per_sqft - self.aux_heater_cost = self.aux_heater_installed_cost_us_dollars_per_mmbtu_per_hr * self.peak_auxiliary_boiler_mmbtu_per_hour - self.aux_cooler_cost = self.aux_cooler_installed_cost_us_dollars_per_ton * self.peak_auxiliary_cooling_tower_ton + self.aux_heater_cost = self.aux_heater_installed_cost_us_dollars_per_mmbtu_per_hr * self.peak_aux_heater_thermal_production_mmbtu_per_hour + self.aux_cooler_cost = self.aux_cooler_installed_cost_us_dollars_per_ton * self.peak_aux_cooler_thermal_production_ton # The DataManager._get_REopt_cost_curve method expects at least a two-point tech_size_for_cost_curve to # to use the first value of installed_cost_us_dollars_per_kw as an absolute $ value and diff --git a/reo/tests/posts/test_ghp_POST.json b/reo/tests/posts/test_ghp_POST.json index cf2214be8..382f4e732 100644 --- a/reo/tests/posts/test_ghp_POST.json +++ b/reo/tests/posts/test_ghp_POST.json @@ -44,7 +44,6 @@ "can_serve_dhw": false, "space_heating_efficiency_thermal_factor": 0.85, "cooling_efficiency_thermal_factor": 0.6, - "is_hybrid_ghx": true, "aux_heater_type": "electric", "aux_heater_installed_cost_us_dollars_per_mmbtu_per_hr": 0.0, "aux_heater_thermal_efficiency": 0.95, From aa84c27e34c9eaace0fb64a7d35c9941e3585afd Mon Sep 17 00:00:00 2001 From: lixiangk1 <72464565+lixiangk1@users.noreply.github.com> Date: Mon, 13 Mar 2023 12:01:48 -0600 Subject: [PATCH 06/23] Add GhpGhx.jl variables to the REopt ghpghx model --- ...ergy_use_intensity_kwe_per_kwt_and_more.py | 24 +++++++++++++++++++ ...hpghxinputs_hybrid_sizing_flag_and_more.py | 23 ++++++++++++++++++ ghpghx/models.py | 12 +++++++++- julia_src/Manifest.toml | 4 ++-- ...ergy_use_intensity_kwe_per_kwt_and_more.py | 21 ++++++++++++++++ reo/models.py | 2 -- reo/nested_inputs.py | 12 ---------- reo/scenario.py | 7 ++---- reo/src/ghp.py | 4 +--- 9 files changed, 84 insertions(+), 25 deletions(-) create mode 100644 ghpghx/migrations/0008_ghpghxinputs_aux_cooler_energy_use_intensity_kwe_per_kwt_and_more.py create mode 100644 ghpghx/migrations/0009_ghpghxinputs_hybrid_sizing_flag_and_more.py create mode 100644 reo/migrations/0151_remove_ghpmodel_aux_cooler_energy_use_intensity_kwe_per_kwt_and_more.py diff --git a/ghpghx/migrations/0008_ghpghxinputs_aux_cooler_energy_use_intensity_kwe_per_kwt_and_more.py b/ghpghx/migrations/0008_ghpghxinputs_aux_cooler_energy_use_intensity_kwe_per_kwt_and_more.py new file mode 100644 index 000000000..f655db42c --- /dev/null +++ b/ghpghx/migrations/0008_ghpghxinputs_aux_cooler_energy_use_intensity_kwe_per_kwt_and_more.py @@ -0,0 +1,24 @@ +# Generated by Django 4.0.7 on 2023-03-12 04:18 + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ghpghx', '0007_ghpghxoutputs_aux_heat_exchange_unit_type_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='ghpghxinputs', + name='aux_cooler_energy_use_intensity_kwe_per_kwt', + field=models.FloatField(blank=True, default=0.2, help_text='The energy use intensity of the auxiliary cooler [kWe/kWt]', null=True, validators=[django.core.validators.MinValueValidator(0.001), django.core.validators.MaxValueValidator(10.0)]), + ), + migrations.AddField( + model_name='ghpghxinputs', + name='aux_heater_thermal_efficiency', + field=models.FloatField(blank=True, default=0.98, help_text='The thermal efficiency (thermal_out/fuel_in) of the auxiliary heater', null=True, validators=[django.core.validators.MinValueValidator(0.001), django.core.validators.MaxValueValidator(10.0)]), + ), + ] diff --git a/ghpghx/migrations/0009_ghpghxinputs_hybrid_sizing_flag_and_more.py b/ghpghx/migrations/0009_ghpghxinputs_hybrid_sizing_flag_and_more.py new file mode 100644 index 000000000..365e4efaa --- /dev/null +++ b/ghpghx/migrations/0009_ghpghxinputs_hybrid_sizing_flag_and_more.py @@ -0,0 +1,23 @@ +# Generated by Django 4.0.7 on 2023-03-13 16:57 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ghpghx', '0008_ghpghxinputs_aux_cooler_energy_use_intensity_kwe_per_kwt_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='ghpghxinputs', + name='hybrid_sizing_flag', + field=models.FloatField(blank=True, default=1.0, help_text='Possible values: -2 (size for heating), -1.0 (size for cooling), 1.0 (non-hybrid), value between 0-1 (fraction of full GHE size)', null=True), + ), + migrations.AddField( + model_name='ghpghxinputs', + name='is_heating_electric', + field=models.BooleanField(blank=True, default=True, help_text='Set to True if heating is electric, false otherwise', null=True), + ), + ] diff --git a/ghpghx/models.py b/ghpghx/models.py index 154e21827..d27ab6915 100644 --- a/ghpghx/models.py +++ b/ghpghx/models.py @@ -220,7 +220,17 @@ def clean(self): # Hybrid flag is_hybrid_ghx = models.BooleanField(null=True, blank=True, default=True, - help_text="If the GHP system uses a hybrid GHX with auxiliary heater or cooler") + help_text="If the GHP system uses a hybrid GHX with auxiliary heater or cooler") + hybrid_sizing_flag = models.FloatField(null=True, blank=True, default=1.0, + help_text="Possible values: -2 (size for heating), -1.0 (size for cooling), 1.0 (non-hybrid), value between 0-1 (fraction of full GHE size)") + is_heating_electric = models.BooleanField(null=True, blank=True, default=True, + help_text="Set to True if heating is electric, false otherwise") + aux_heater_thermal_efficiency = models.FloatField(null=True, blank=True, + default=0.98, validators=[MinValueValidator(0.001), MaxValueValidator(10.0)], + help_text="The thermal efficiency (thermal_out/fuel_in) of the auxiliary heater") + aux_cooler_energy_use_intensity_kwe_per_kwt = models.FloatField(null=True, blank=True, + default=0.2, validators=[MinValueValidator(0.001), MaxValueValidator(10.0)], + help_text="The energy use intensity of the auxiliary cooler [kWe/kWt]") class GHPGHXOutputs(models.Model): # Outputs/results diff --git a/julia_src/Manifest.toml b/julia_src/Manifest.toml index 8bc0f8abf..010306d81 100644 --- a/julia_src/Manifest.toml +++ b/julia_src/Manifest.toml @@ -302,8 +302,8 @@ uuid = "0329782f-3d07-4b52-b9f6-d3137cf03c7a" version = "1.0.0" [[deps.GhpGhx]] -git-tree-sha1 = "21f8352f3797abcae3314bac3b44ca73c4acbd91" -repo-rev = "update_names" +git-tree-sha1 = "d6d00dbdba7391470d6d7d7ad08fa64c030f5806" +repo-rev = "main" repo-url = "https://github.com/NREL/GhpGhx.jl.git" uuid = "7ce85f02-24a8-4d69-a3f0-14b5daa7d30c" version = "0.1.0" diff --git a/reo/migrations/0151_remove_ghpmodel_aux_cooler_energy_use_intensity_kwe_per_kwt_and_more.py b/reo/migrations/0151_remove_ghpmodel_aux_cooler_energy_use_intensity_kwe_per_kwt_and_more.py new file mode 100644 index 000000000..f70cb286a --- /dev/null +++ b/reo/migrations/0151_remove_ghpmodel_aux_cooler_energy_use_intensity_kwe_per_kwt_and_more.py @@ -0,0 +1,21 @@ +# Generated by Django 4.0.7 on 2023-03-12 04:18 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('reo', '0150_remove_ghpmodel_is_hybrid_ghx'), + ] + + operations = [ + migrations.RemoveField( + model_name='ghpmodel', + name='aux_cooler_energy_use_intensity_kwe_per_kwt', + ), + migrations.RemoveField( + model_name='ghpmodel', + name='aux_heater_thermal_efficiency', + ), + ] diff --git a/reo/models.py b/reo/models.py index aec33c5e5..8ac7f46c2 100644 --- a/reo/models.py +++ b/reo/models.py @@ -1144,9 +1144,7 @@ class GHPModel(models.Model): aux_heater_type = models.TextField(null=True, blank=True) aux_heater_installed_cost_us_dollars_per_mmbtu_per_hr = models.FloatField(null=True, blank=True) - aux_heater_thermal_efficiency = models.FloatField(null=True, blank=True) aux_cooler_installed_cost_us_dollars_per_ton = models.FloatField(null=True, blank=True) - aux_cooler_energy_use_intensity_kwe_per_kwt = models.FloatField(null=True, blank=True) macrs_option_years = models.IntegerField(null=True, blank=True) macrs_bonus_pct = models.FloatField(null=True, blank=True) macrs_itc_reduction = models.FloatField(null=True, blank=True) diff --git a/reo/nested_inputs.py b/reo/nested_inputs.py index a0a6fe078..0f7024b20 100644 --- a/reo/nested_inputs.py +++ b/reo/nested_inputs.py @@ -2521,24 +2521,12 @@ def list_of_dict(input): "default": 1000.0, "description": "Installed cost of auxiliary heater for hybrid ghx in $/MMBtu/hr based on peak thermal production" }, - "aux_heater_thermal_efficiency": { - "type": "float", - "min": 0.001, - "max": 10.0, - "description": "The thermal efficiency (thermal_out/fuel_in) of the auxiliary heater" - }, "aux_cooler_installed_cost_us_dollars_per_ton": { "type": "float", "min": 0.0, "max": 1.0e6, "default": 80.0, "description": "Installed cost of auxiliary cooler (e.g. cooling tower) for hybrid ghx in $/ton based on peak thermal production" - }, - "aux_cooler_energy_use_intensity_kwe_per_kwt": { - "type": "float", - "min": 0.001, - "max": 10.0, - "description": "The thermal efficiency (thermal_out/fuel_in) of the auxiliary heater" }, "macrs_option_years": { "type": "int", diff --git a/reo/scenario.py b/reo/scenario.py index 8f86d0d75..22dd15104 100644 --- a/reo/scenario.py +++ b/reo/scenario.py @@ -419,7 +419,7 @@ def setup_pv(pv_dict, latitude, longitude, time_steps_per_hour): determine_heat_cool_post = copy.deepcopy(ghpghx_post) determine_heat_cool_post["simulation_years"] = 2 determine_heat_cool_post["max_sizing_iterations"] = 1 - + determine_heat_cool_post_resp = client.post('/v1/ghpghx/', data=determine_heat_cool_post) determine_heat_cool_post_resp_dict = json.loads(determine_heat_cool_post_resp.content) @@ -428,7 +428,7 @@ def setup_pv(pv_dict, latitude, longitude, time_steps_per_hour): determine_heat_cool_results_resp = client.get(determine_heat_cool_results_url) determine_heat_cool_results_resp_dict = json.loads(determine_heat_cool_results_resp.content) temp_diff = determine_heat_cool_results_resp_dict["outputs"]["end_of_year_eft_f"][1] - determine_heat_cool_results_resp_dict["outputs"]["end_of_year_eft_f"][0] - temp_diff = 0 + # TODO - Implement fractional sizing hybrid_sizing_flag = 1.0 if temp_diff > 0: @@ -443,9 +443,6 @@ def setup_pv(pv_dict, latitude, longitude, time_steps_per_hour): if inputs_dict["Site"]["GHP"]["aux_heater_type"] == "electric": ghpghx_post["is_heating_electric"] = True - ghpghx_post["aux_heater_thermal_efficiency"] = inputs_dict["Site"]["GHP"]["aux_heater_thermal_efficiency"] - ghpghx_post["aux_cooler_energy_use_intensity_kwe_per_kwt"] = inputs_dict["Site"]["GHP"]["aux_cooler_energy_use_intensity_kwe_per_kwt"] - # Call /ghpghx endpoint to size GHP and GHX ghpghx_post_resp = client.post('/v1/ghpghx/', data=ghpghx_post) ghpghx_post_resp_dict = json.loads(ghpghx_post_resp.content) diff --git a/reo/src/ghp.py b/reo/src/ghp.py index c1292298d..53d518bba 100644 --- a/reo/src/ghp.py +++ b/reo/src/ghp.py @@ -44,9 +44,7 @@ def __init__(self, dfm, response, **kwargs): self.building_sqft = kwargs.get("building_sqft") self.aux_heater_type = kwargs.get("aux_heater_type") self.aux_heater_installed_cost_us_dollars_per_mmbtu_per_hr = kwargs.get("aux_heater_installed_cost_us_dollars_per_mmbtu_per_hr") - self.aux_heater_thermal_efficiency = kwargs.get("aux_heater_thermal_efficiency") - self.aux_cooler_installed_cost_us_dollars_per_ton = kwargs.get("aux_cooler_installed_cost_us_dollars_per_ton") - self.aux_cooler_energy_use_intensity_kwe_per_kwt = kwargs.get("aux_cooler_energy_use_intensity_kwe_per_kwt") + self.aux_cooler_installed_cost_us_dollars_per_ton = kwargs.get("aux_cooler_installed_cost_us_dollars_per_ton") # Heating and cooling loads served and electricity consumed by GHP # TODO with hybrid with auxiliary/supplemental heating/cooling devices, we may want to separate out/distiguish that energy From 84e794fb6ef0720f7982f53d1193faf27c3ff253 Mon Sep 17 00:00:00 2001 From: lixiangk1 <72464565+lixiangk1@users.noreply.github.com> Date: Mon, 13 Mar 2023 12:49:18 -0600 Subject: [PATCH 07/23] Update GHP test posts after moving GHP inputs --- ghpghx/tests/posts/test_ghpghx_POST.json | 4 +++- reo/tests/posts/test_ghp_POST.json | 4 +--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ghpghx/tests/posts/test_ghpghx_POST.json b/ghpghx/tests/posts/test_ghpghx_POST.json index 14b604b01..abce1ae08 100644 --- a/ghpghx/tests/posts/test_ghpghx_POST.json +++ b/ghpghx/tests/posts/test_ghpghx_POST.json @@ -6,5 +6,7 @@ "tess_ghx_minimum_timesteps_per_hour": 1, "max_sizing_iterations": 1, "init_sizing_factor_ft_per_peak_ton": 300.0, - "is_hybrid_ghx": true + "is_hybrid_ghx": true, + "aux_heater_thermal_efficiency": 0.95, + "aux_cooler_energy_use_intensity_kwe_per_kwt": 0.02 } \ No newline at end of file diff --git a/reo/tests/posts/test_ghp_POST.json b/reo/tests/posts/test_ghp_POST.json index 382f4e732..28a525857 100644 --- a/reo/tests/posts/test_ghp_POST.json +++ b/reo/tests/posts/test_ghp_POST.json @@ -46,9 +46,7 @@ "cooling_efficiency_thermal_factor": 0.6, "aux_heater_type": "electric", "aux_heater_installed_cost_us_dollars_per_mmbtu_per_hr": 0.0, - "aux_heater_thermal_efficiency": 0.95, - "aux_cooler_installed_cost_us_dollars_per_ton": 0.0, - "aux_cooler_energy_use_intensity_kwe_per_kwt": 0.02 + "aux_cooler_installed_cost_us_dollars_per_ton": 0.0 }, "PV":{ "max_kw": 0.0 From 3ea82b7004acf55b27114e66587c6125a4aed87c Mon Sep 17 00:00:00 2001 From: lixiangk1 <72464565+lixiangk1@users.noreply.github.com> Date: Mon, 13 Mar 2023 15:40:27 -0600 Subject: [PATCH 08/23] Add sizing factor for GHP auxiliary heater/cooler --- ...unit_capacity_sizing_factor_on_peak_load.py | 18 ++++++++++++++++++ reo/models.py | 1 + reo/nested_inputs.py | 7 +++++++ reo/src/ghp.py | 7 ++++--- reo/tests/posts/test_ghp_POST.json | 3 ++- 5 files changed, 32 insertions(+), 4 deletions(-) create mode 100644 reo/migrations/0152_ghpmodel_aux_unit_capacity_sizing_factor_on_peak_load.py diff --git a/reo/migrations/0152_ghpmodel_aux_unit_capacity_sizing_factor_on_peak_load.py b/reo/migrations/0152_ghpmodel_aux_unit_capacity_sizing_factor_on_peak_load.py new file mode 100644 index 000000000..cdd6dd6f1 --- /dev/null +++ b/reo/migrations/0152_ghpmodel_aux_unit_capacity_sizing_factor_on_peak_load.py @@ -0,0 +1,18 @@ +# Generated by Django 4.0.7 on 2023-03-13 21:31 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('reo', '0151_remove_ghpmodel_aux_cooler_energy_use_intensity_kwe_per_kwt_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='ghpmodel', + name='aux_unit_capacity_sizing_factor_on_peak_load', + field=models.FloatField(blank=True, null=True), + ), + ] diff --git a/reo/models.py b/reo/models.py index 8ac7f46c2..e33d3629d 100644 --- a/reo/models.py +++ b/reo/models.py @@ -1145,6 +1145,7 @@ class GHPModel(models.Model): aux_heater_type = models.TextField(null=True, blank=True) aux_heater_installed_cost_us_dollars_per_mmbtu_per_hr = models.FloatField(null=True, blank=True) aux_cooler_installed_cost_us_dollars_per_ton = models.FloatField(null=True, blank=True) + aux_unit_capacity_sizing_factor_on_peak_load = models.FloatField(null=True, blank=True) macrs_option_years = models.IntegerField(null=True, blank=True) macrs_bonus_pct = models.FloatField(null=True, blank=True) macrs_itc_reduction = models.FloatField(null=True, blank=True) diff --git a/reo/nested_inputs.py b/reo/nested_inputs.py index 0f7024b20..3b120115e 100644 --- a/reo/nested_inputs.py +++ b/reo/nested_inputs.py @@ -2527,6 +2527,13 @@ def list_of_dict(input): "max": 1.0e6, "default": 80.0, "description": "Installed cost of auxiliary cooler (e.g. cooling tower) for hybrid ghx in $/ton based on peak thermal production" + }, + "aux_unit_capacity_sizing_factor_on_peak_load": { + "type": "float", + "min": 1.0, + "max": 5.0, + "default": 1.2, + "description": "Factor on peak heating and cooling load served by the auxiliary heater/cooler used for determining heater/cooler installed capacity" }, "macrs_option_years": { "type": "int", diff --git a/reo/src/ghp.py b/reo/src/ghp.py index 53d518bba..2f83eb72a 100644 --- a/reo/src/ghp.py +++ b/reo/src/ghp.py @@ -44,7 +44,8 @@ def __init__(self, dfm, response, **kwargs): self.building_sqft = kwargs.get("building_sqft") self.aux_heater_type = kwargs.get("aux_heater_type") self.aux_heater_installed_cost_us_dollars_per_mmbtu_per_hr = kwargs.get("aux_heater_installed_cost_us_dollars_per_mmbtu_per_hr") - self.aux_cooler_installed_cost_us_dollars_per_ton = kwargs.get("aux_cooler_installed_cost_us_dollars_per_ton") + self.aux_cooler_installed_cost_us_dollars_per_ton = kwargs.get("aux_cooler_installed_cost_us_dollars_per_ton") + self.aux_unit_capacity_sizing_factor_on_peak_load = kwargs.get("aux_unit_capacity_sizing_factor_on_peak_load") # Heating and cooling loads served and electricity consumed by GHP # TODO with hybrid with auxiliary/supplemental heating/cooling devices, we may want to separate out/distiguish that energy @@ -84,8 +85,8 @@ def setup_installed_cost_curve(self): # The GHX and hydronic loop cost are the y-intercepts ([$]) of the cost for each design self.ghx_cost = self.total_ghx_ft * self.installed_cost_ghx_us_dollars_per_ft self.hydronic_loop_cost = self.building_sqft * self.installed_cost_building_hydronic_loop_us_dollars_per_sqft - self.aux_heater_cost = self.aux_heater_installed_cost_us_dollars_per_mmbtu_per_hr * self.peak_aux_heater_thermal_production_mmbtu_per_hour - self.aux_cooler_cost = self.aux_cooler_installed_cost_us_dollars_per_ton * self.peak_aux_cooler_thermal_production_ton + self.aux_heater_cost = self.aux_heater_installed_cost_us_dollars_per_mmbtu_per_hr * self.peak_aux_heater_thermal_production_mmbtu_per_hour * self.aux_unit_capacity_sizing_factor_on_peak_load + self.aux_cooler_cost = self.aux_cooler_installed_cost_us_dollars_per_ton * self.peak_aux_cooler_thermal_production_ton * self.aux_unit_capacity_sizing_factor_on_peak_load # The DataManager._get_REopt_cost_curve method expects at least a two-point tech_size_for_cost_curve to # to use the first value of installed_cost_us_dollars_per_kw as an absolute $ value and diff --git a/reo/tests/posts/test_ghp_POST.json b/reo/tests/posts/test_ghp_POST.json index 28a525857..90b8f87ce 100644 --- a/reo/tests/posts/test_ghp_POST.json +++ b/reo/tests/posts/test_ghp_POST.json @@ -46,7 +46,8 @@ "cooling_efficiency_thermal_factor": 0.6, "aux_heater_type": "electric", "aux_heater_installed_cost_us_dollars_per_mmbtu_per_hr": 0.0, - "aux_cooler_installed_cost_us_dollars_per_ton": 0.0 + "aux_cooler_installed_cost_us_dollars_per_ton": 0.0, + "aux_unit_capacity_sizing_factor_on_peak_load": 1.3 }, "PV":{ "max_kw": 0.0 From 41eb4a0daf6db7fbcc7517be174490f9b9234c82 Mon Sep 17 00:00:00 2001 From: lixiangk1 <72464565+lixiangk1@users.noreply.github.com> Date: Tue, 14 Mar 2023 09:30:17 -0600 Subject: [PATCH 09/23] Update aux heater default thermal efficiency --- ghpghx/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ghpghx/models.py b/ghpghx/models.py index d27ab6915..afb2e1da4 100644 --- a/ghpghx/models.py +++ b/ghpghx/models.py @@ -226,7 +226,7 @@ def clean(self): is_heating_electric = models.BooleanField(null=True, blank=True, default=True, help_text="Set to True if heating is electric, false otherwise") aux_heater_thermal_efficiency = models.FloatField(null=True, blank=True, - default=0.98, validators=[MinValueValidator(0.001), MaxValueValidator(10.0)], + default=0.99, validators=[MinValueValidator(0.001), MaxValueValidator(10.0)], help_text="The thermal efficiency (thermal_out/fuel_in) of the auxiliary heater") aux_cooler_energy_use_intensity_kwe_per_kwt = models.FloatField(null=True, blank=True, default=0.2, validators=[MinValueValidator(0.001), MaxValueValidator(10.0)], From 37d97496e2215d33120cdc849f532d3c11637f2b Mon Sep 17 00:00:00 2001 From: bill-becker Date: Tue, 14 Mar 2023 13:13:26 -0600 Subject: [PATCH 10/23] Increase development server resource limits to production Keep replicas at 2, not the 20 in production --- .helm/values.development.yaml | 11 +++++++++++ config/gunicorn.conf.py | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/.helm/values.development.yaml b/.helm/values.development.yaml index e69de29bb..41241f202 100644 --- a/.helm/values.development.yaml +++ b/.helm/values.development.yaml @@ -0,0 +1,11 @@ +appEnv: development +djangoSettingsModule: reopt_api.dev_settings +djangoReplicas: 2 +djangoMemoryRequest: "400Mi" +djangoMemoryLimit: "1400Mi" +celeryReplicas: 2 +celeryMemoryRequest: "200Mi" +celeryMemoryLimit: "900Mi" +juliaReplicas: 2 +juliaMemoryRequest: "2000Mi" +juliaMemoryLimit: "8000Mi" \ No newline at end of file diff --git a/config/gunicorn.conf.py b/config/gunicorn.conf.py index 879818060..4d125349b 100644 --- a/config/gunicorn.conf.py +++ b/config/gunicorn.conf.py @@ -14,7 +14,7 @@ if os.environ.get('K8S_DEPLOY') is None: workers = multiprocessing.cpu_count() else: - workers = 4 + workers = 8 # Note that the app currently has threading issues, so we explicitly want a # non-thread worker process model. From 7f2a6dda4b1e3f880d2bb692e2f6fa5de1bd5ca3 Mon Sep 17 00:00:00 2001 From: lixiangk1 <72464565+lixiangk1@users.noreply.github.com> Date: Wed, 15 Mar 2023 22:03:10 -0600 Subject: [PATCH 11/23] Update default hybrid GHP values --- ghpghx/models.py | 4 ++-- reo/nested_inputs.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ghpghx/models.py b/ghpghx/models.py index afb2e1da4..3616870f3 100644 --- a/ghpghx/models.py +++ b/ghpghx/models.py @@ -226,10 +226,10 @@ def clean(self): is_heating_electric = models.BooleanField(null=True, blank=True, default=True, help_text="Set to True if heating is electric, false otherwise") aux_heater_thermal_efficiency = models.FloatField(null=True, blank=True, - default=0.99, validators=[MinValueValidator(0.001), MaxValueValidator(10.0)], + default=0.98, validators=[MinValueValidator(0.001), MaxValueValidator(1.0)], help_text="The thermal efficiency (thermal_out/fuel_in) of the auxiliary heater") aux_cooler_energy_use_intensity_kwe_per_kwt = models.FloatField(null=True, blank=True, - default=0.2, validators=[MinValueValidator(0.001), MaxValueValidator(10.0)], + default=0.02, validators=[MinValueValidator(0.001), MaxValueValidator(1.0)], help_text="The energy use intensity of the auxiliary cooler [kWe/kWt]") class GHPGHXOutputs(models.Model): diff --git a/reo/nested_inputs.py b/reo/nested_inputs.py index 3b120115e..4925a53d7 100644 --- a/reo/nested_inputs.py +++ b/reo/nested_inputs.py @@ -2518,14 +2518,14 @@ def list_of_dict(input): "type": "float", "min": 0.0, "max": 1.0e6, - "default": 1000.0, + "default": 26000.0, "description": "Installed cost of auxiliary heater for hybrid ghx in $/MMBtu/hr based on peak thermal production" }, "aux_cooler_installed_cost_us_dollars_per_ton": { "type": "float", "min": 0.0, "max": 1.0e6, - "default": 80.0, + "default": 400.0, "description": "Installed cost of auxiliary cooler (e.g. cooling tower) for hybrid ghx in $/ton based on peak thermal production" }, "aux_unit_capacity_sizing_factor_on_peak_load": { From bb8c4bd506982df5b638777afb76f26c54093e6e Mon Sep 17 00:00:00 2001 From: lixiangk1 <72464565+lixiangk1@users.noreply.github.com> Date: Mon, 20 Mar 2023 12:03:53 -0600 Subject: [PATCH 12/23] Add new GhpGhx output variables --- ...hpghxoutputs_end_of_year_eft_f_and_more.py | 59 +++++++++++++++++++ ghpghx/models.py | 19 ++++-- julia_src/Manifest.toml | 55 ++++++++++++++++- reo/scenario.py | 2 +- 4 files changed, 128 insertions(+), 7 deletions(-) create mode 100644 ghpghx/migrations/0010_remove_ghpghxoutputs_end_of_year_eft_f_and_more.py diff --git a/ghpghx/migrations/0010_remove_ghpghxoutputs_end_of_year_eft_f_and_more.py b/ghpghx/migrations/0010_remove_ghpghxoutputs_end_of_year_eft_f_and_more.py new file mode 100644 index 000000000..bd6cd9406 --- /dev/null +++ b/ghpghx/migrations/0010_remove_ghpghxoutputs_end_of_year_eft_f_and_more.py @@ -0,0 +1,59 @@ +# Generated by Django 4.0.7 on 2023-03-20 17:02 + +import django.contrib.postgres.fields +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ghpghx', '0009_ghpghxinputs_hybrid_sizing_flag_and_more'), + ] + + operations = [ + migrations.RemoveField( + model_name='ghpghxoutputs', + name='end_of_year_eft_f', + ), + migrations.AddField( + model_name='ghpghxoutputs', + name='end_of_year_ghx_lft_f', + field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), blank=True, default=list, help_text='End of year GHX leaving fluid temperature for all years in the last iteration of GHX sizing [degF]', null=True, size=None), + ), + migrations.AddField( + model_name='ghpghxoutputs', + name='ghx_soln_number_of_iterations', + field=models.IntegerField(blank=True, help_text='The number of iterations taken to get GHX sizing', null=True), + ), + migrations.AddField( + model_name='ghpghxoutputs', + name='max_yearly_ghx_lft_f', + field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), blank=True, default=list, help_text='Maximum GHX leaving fluid temperature for all years in the last iteration of GHX sizing [degF]', null=True, size=None), + ), + migrations.AddField( + model_name='ghpghxoutputs', + name='min_yearly_ghx_lft_f', + field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), blank=True, default=list, help_text='Minimum GHX leaving fluid temperature for all years in the last iteration of GHX sizing [degF]', null=True, size=None), + ), + migrations.AddField( + model_name='ghpghxoutputs', + name='yearly_ghx_lft_series_f', + field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), blank=True, default=list, help_text='Hourly GHX leaving fluid temperature (lft), average across simulation years [kW]', null=True, size=None), + ), + migrations.AlterField( + model_name='ghpghxinputs', + name='aux_cooler_energy_use_intensity_kwe_per_kwt', + field=models.FloatField(blank=True, default=0.02, help_text='The energy use intensity of the auxiliary cooler [kWe/kWt]', null=True, validators=[django.core.validators.MinValueValidator(0.001), django.core.validators.MaxValueValidator(1.0)]), + ), + migrations.AlterField( + model_name='ghpghxinputs', + name='aux_heater_thermal_efficiency', + field=models.FloatField(blank=True, default=0.98, help_text='The thermal efficiency (thermal_out/fuel_in) of the auxiliary heater', null=True, validators=[django.core.validators.MinValueValidator(0.001), django.core.validators.MaxValueValidator(1.0)]), + ), + migrations.AlterField( + model_name='ghpghxinputs', + name='hybrid_sizing_flag', + field=models.FloatField(blank=True, default=1.0, help_text='Possible values: -2 (size for heating), -1.0 (size for cooling), 1.0 (non-hybrid), value between 0-1 (fraction of full GHX size)', null=True), + ), + ] diff --git a/ghpghx/models.py b/ghpghx/models.py index 3616870f3..427f930a8 100644 --- a/ghpghx/models.py +++ b/ghpghx/models.py @@ -222,7 +222,7 @@ def clean(self): is_hybrid_ghx = models.BooleanField(null=True, blank=True, default=True, help_text="If the GHP system uses a hybrid GHX with auxiliary heater or cooler") hybrid_sizing_flag = models.FloatField(null=True, blank=True, default=1.0, - help_text="Possible values: -2 (size for heating), -1.0 (size for cooling), 1.0 (non-hybrid), value between 0-1 (fraction of full GHE size)") + help_text="Possible values: -2 (size for heating), -1.0 (size for cooling), 1.0 (non-hybrid), value between 0-1 (fraction of full GHX size)") is_heating_electric = models.BooleanField(null=True, blank=True, default=True, help_text="Set to True if heating is electric, false otherwise") aux_heater_thermal_efficiency = models.FloatField(null=True, blank=True, @@ -294,12 +294,23 @@ class GHPGHXOutputs(models.Model): help_text="Annual auxiliary heater electrical consumption [kWh]") annual_aux_cooler_electric_consumption_kwh = models.FloatField(null=True, blank=True, help_text="Annual auxiliary cooler electrical consumption [kWh]") - end_of_year_eft_f = ArrayField(models.FloatField(null=True, blank=True), + end_of_year_ghx_lft_f = ArrayField(models.FloatField(null=True, blank=True), default=list, null=True, blank=True, - help_text="End of year entering fluid temperature for all years in the last iteration of GHX sizing [degF]") + help_text="End of year GHX leaving fluid temperature for all years in the last iteration of GHX sizing [degF]") + max_yearly_ghx_lft_f = ArrayField(models.FloatField(null=True, blank=True), + default=list, null=True, blank=True, + help_text="Maximum GHX leaving fluid temperature for all years in the last iteration of GHX sizing [degF]") + min_yearly_ghx_lft_f = ArrayField(models.FloatField(null=True, blank=True), + default=list, null=True, blank=True, + help_text="Minimum GHX leaving fluid temperature for all years in the last iteration of GHX sizing [degF]") aux_heat_exchange_unit_type = models.TextField(null=True, blank=True, help_text="Specifies if the auxiliary heat exchange unit is a heater or cooler") - + yearly_ghx_lft_series_f = ArrayField(models.FloatField(null=True, blank=True), + default=list, null=True, blank=True, + help_text="Hourly GHX leaving fluid temperature (lft), average across simulation years [kW]") + ghx_soln_number_of_iterations = models.IntegerField(null=True, blank=True, + help_text="The number of iterations taken to get GHX sizing") + class ModelManager(object): def __init__(self): diff --git a/julia_src/Manifest.toml b/julia_src/Manifest.toml index 010306d81..c4cec9eac 100644 --- a/julia_src/Manifest.toml +++ b/julia_src/Manifest.toml @@ -161,11 +161,22 @@ git-tree-sha1 = "6b492e9aafcd6016d5aca105567ed2271c423015" uuid = "3351c21f-4feb-5f29-afb9-f4fcb0e27549" version = "6.4.3+0" +[[deps.Crayons]] +git-tree-sha1 = "249fe38abf76d48563e2f4556bebd215aa317e15" +uuid = "a8cc5b0e-0ffa-5ad4-8c14-923d3ee1735f" +version = "4.1.1" + [[deps.DataAPI]] git-tree-sha1 = "e8119c1a33d267e16108be441a287a6981ba1630" uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a" version = "1.14.0" +[[deps.DataFrames]] +deps = ["Compat", "DataAPI", "Future", "InlineStrings", "InvertedIndices", "IteratorInterfaceExtensions", "LinearAlgebra", "Markdown", "Missings", "PooledArrays", "PrettyTables", "Printf", "REPL", "Random", "Reexport", "SentinelArrays", "SnoopPrecompile", "SortingAlgorithms", "Statistics", "TableTraits", "Tables", "Unicode"] +git-tree-sha1 = "aa51303df86f8626a962fccb878430cdb0a97eee" +uuid = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" +version = "1.5.0" + [[deps.DataStructures]] deps = ["Compat", "InteractiveUtils", "OrderedCollections"] git-tree-sha1 = "d1fff3a548102f48987a52a2e0d114fa97d730f0" @@ -256,6 +267,12 @@ git-tree-sha1 = "335bfdceacc84c5cdf16aadc768aa5ddfc5383cc" uuid = "53c48c17-4a7d-5ca2-90c5-79b7896eea93" version = "0.8.4" +[[deps.Formatting]] +deps = ["Printf"] +git-tree-sha1 = "8339d61043228fdd3eb658d86c926cb282ae72a8" +uuid = "59287772-0a20-5a39-b81b-1366585eb4c0" +version = "0.4.2" + [[deps.ForwardDiff]] deps = ["CommonSubexpressions", "DiffResults", "DiffRules", "LinearAlgebra", "LogExpFunctions", "NaNMath", "Preferences", "Printf", "Random", "SpecialFunctions", "StaticArrays"] git-tree-sha1 = "00e252f4d706b3d55a8863432e742bf5717b498d" @@ -302,8 +319,9 @@ uuid = "0329782f-3d07-4b52-b9f6-d3137cf03c7a" version = "1.0.0" [[deps.GhpGhx]] -git-tree-sha1 = "d6d00dbdba7391470d6d7d7ad08fa64c030f5806" -repo-rev = "main" +deps = ["CSV", "DataFrames", "HTTP", "JSON"] +git-tree-sha1 = "eaa76a32cd4d3fc84abef44b82c6c8a50f8547dd" +repo-rev = "add_ground_temp" repo-url = "https://github.com/NREL/GhpGhx.jl.git" uuid = "7ce85f02-24a8-4d69-a3f0-14b5daa7d30c" version = "0.1.0" @@ -382,6 +400,11 @@ git-tree-sha1 = "49510dfcb407e572524ba94aeae2fced1f3feb0f" uuid = "3587e190-3f89-42d0-90ee-14403ec27112" version = "0.1.8" +[[deps.InvertedIndices]] +git-tree-sha1 = "0dc7b50b8d436461be01300fd8cd45aa0274b038" +uuid = "41ab1584-1d38-5bbf-9106-f11c6c58b48f" +version = "1.3.0" + [[deps.IrrationalConstants]] git-tree-sha1 = "630b497eafcc20001bba38a4651b327dcfc491d2" uuid = "92d709cd-6900-40b7-9082-c6be49f344b6" @@ -445,6 +468,11 @@ git-tree-sha1 = "e5b909bcf985c5e2605737d2ce278ed791b89be6" uuid = "dd4b983a-f0e5-5f8d-a1b7-129d4a5fb1ac" version = "2.10.1+0" +[[deps.LaTeXStrings]] +git-tree-sha1 = "f2355693d6778a178ade15952b7ac47a4ff97996" +uuid = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f" +version = "1.3.0" + [[deps.LibCURL]] deps = ["LibCURL_jll", "MozillaCACerts_jll"] uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" @@ -560,6 +588,12 @@ git-tree-sha1 = "bb2e8f4d9f400f6e90d57b34860f6abdc51398e5" uuid = "f28f55f0-a522-5efc-85c2-fe41dfb9b2d9" version = "1.4.1" +[[deps.Missings]] +deps = ["DataAPI"] +git-tree-sha1 = "f66bdc5de519e8f8ae43bdc598782d35a25b1272" +uuid = "e1d29d7a-bbdc-5cf2-9ac0-f12de2c33e28" +version = "1.1.0" + [[deps.Mmap]] uuid = "a63ad114-7e13-5084-954f-fe012c677804" @@ -681,6 +715,12 @@ git-tree-sha1 = "47e5f437cc0e7ef2ce8406ce1e7e24d44915f88d" uuid = "21216c6a-2e73-6563-6e65-726566657250" version = "1.3.0" +[[deps.PrettyTables]] +deps = ["Crayons", "Formatting", "LaTeXStrings", "Markdown", "Reexport", "StringManipulation", "Tables"] +git-tree-sha1 = "548793c7859e28ef026dba514752275ee871169f" +uuid = "08abe8d2-0d0c-5749-adfa-8a2ac140af0d" +version = "2.2.3" + [[deps.Printf]] deps = ["Unicode"] uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" @@ -765,6 +805,12 @@ version = "1.0.3" [[deps.Sockets]] uuid = "6462fe0b-24de-5631-8697-dd941f90decc" +[[deps.SortingAlgorithms]] +deps = ["DataStructures"] +git-tree-sha1 = "a4ada03f999bd01b3a25dcaa30b2d929fe537e00" +uuid = "a2af1166-a08f-5f64-846c-94a0d3cef48c" +version = "1.1.0" + [[deps.SparseArrays]] deps = ["LinearAlgebra", "Random"] uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" @@ -796,6 +842,11 @@ version = "1.4.0" deps = ["LinearAlgebra", "SparseArrays"] uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" +[[deps.StringManipulation]] +git-tree-sha1 = "46da2434b41f41ac3594ee9816ce5541c6096123" +uuid = "892a3eda-7b42-436c-8928-eab12a02cf0e" +version = "0.3.0" + [[deps.TOML]] deps = ["Dates"] uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" diff --git a/reo/scenario.py b/reo/scenario.py index 22dd15104..3833757c9 100644 --- a/reo/scenario.py +++ b/reo/scenario.py @@ -427,7 +427,7 @@ def setup_pv(pv_dict, latitude, longitude, time_steps_per_hour): determine_heat_cool_results_url = "/v1/ghpghx/"+determine_heat_cool_uuid+"/results/" determine_heat_cool_results_resp = client.get(determine_heat_cool_results_url) determine_heat_cool_results_resp_dict = json.loads(determine_heat_cool_results_resp.content) - temp_diff = determine_heat_cool_results_resp_dict["outputs"]["end_of_year_eft_f"][1] - determine_heat_cool_results_resp_dict["outputs"]["end_of_year_eft_f"][0] + temp_diff = determine_heat_cool_results_resp_dict["outputs"]["end_of_year_ghx_lft_f"][1] - determine_heat_cool_results_resp_dict["outputs"]["end_of_year_ghx_lft_f"][0] # TODO - Implement fractional sizing hybrid_sizing_flag = 1.0 From 05cb7a17940cc403eb40bd440dedc4238325c21e Mon Sep 17 00:00:00 2001 From: lixiangk1 <72464565+lixiangk1@users.noreply.github.com> Date: Wed, 22 Mar 2023 11:16:56 -0600 Subject: [PATCH 13/23] Add new inputs for fractional GHX sizing --- ...uts_hybrid_ghx_sizing_fraction_and_more.py | 24 +++++++++++++++++++ ghpghx/models.py | 6 ++++- julia_src/Manifest.toml | 24 +++++++++---------- reo/scenario.py | 24 +++++++++++-------- 4 files changed, 55 insertions(+), 23 deletions(-) create mode 100644 ghpghx/migrations/0011_ghpghxinputs_hybrid_ghx_sizing_fraction_and_more.py diff --git a/ghpghx/migrations/0011_ghpghxinputs_hybrid_ghx_sizing_fraction_and_more.py b/ghpghx/migrations/0011_ghpghxinputs_hybrid_ghx_sizing_fraction_and_more.py new file mode 100644 index 000000000..c654e6f75 --- /dev/null +++ b/ghpghx/migrations/0011_ghpghxinputs_hybrid_ghx_sizing_fraction_and_more.py @@ -0,0 +1,24 @@ +# Generated by Django 4.0.7 on 2023-03-22 03:34 + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ghpghx', '0010_remove_ghpghxoutputs_end_of_year_eft_f_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='ghpghxinputs', + name='hybrid_ghx_sizing_fraction', + field=models.FloatField(blank=True, default=0.6, help_text='Applies fraction to full GHX size for hybrid sizing (value between 0.1 - 1.0)', null=True, validators=[django.core.validators.MinValueValidator(0.1), django.core.validators.MaxValueValidator(1.0)]), + ), + migrations.AddField( + model_name='ghpghxinputs', + name='hybrid_ghx_sizing_method', + field=models.TextField(blank=True, default='None', help_text="Possible values: 'Fractional' (user inputs fraction of full GHX size), 'Automatic' (REopt determines based on the smaller heating or cooling load), 'None' (non-hybrid)", null=True), + ), + ] diff --git a/ghpghx/models.py b/ghpghx/models.py index 427f930a8..513423081 100644 --- a/ghpghx/models.py +++ b/ghpghx/models.py @@ -216,13 +216,17 @@ def clean(self): init_sizing_factor_ft_per_peak_ton = models.FloatField(blank=True, default=246.1, validators=[MinValueValidator(1.0), MaxValueValidator(5000.0)], help_text="Initial guess of total feet of GHX boreholes (total feet = N bores * Length bore) based on peak ton heating/cooling [ft/ton]") - # TODO move is_hybrid_ghx to ghpghx_inputs because we want to compete non-hybrid vs hybrid within single REopt run # Hybrid flag is_hybrid_ghx = models.BooleanField(null=True, blank=True, default=True, help_text="If the GHP system uses a hybrid GHX with auxiliary heater or cooler") + hybrid_ghx_sizing_method = models.TextField(null=True, blank=True, default="None", + help_text="Possible values: 'Fractional' (user inputs fraction of full GHX size), 'Automatic' (REopt determines based on the smaller heating or cooling load), 'None' (non-hybrid)") hybrid_sizing_flag = models.FloatField(null=True, blank=True, default=1.0, help_text="Possible values: -2 (size for heating), -1.0 (size for cooling), 1.0 (non-hybrid), value between 0-1 (fraction of full GHX size)") + hybrid_ghx_sizing_fraction = models.FloatField(null=True, blank=True, default=0.6, + validators=[MinValueValidator(0.1), MaxValueValidator(1.0)], + help_text="Applies fraction to full GHX size for hybrid sizing (value between 0.1 - 1.0)") is_heating_electric = models.BooleanField(null=True, blank=True, default=True, help_text="Set to True if heating is electric, false otherwise") aux_heater_thermal_efficiency = models.FloatField(null=True, blank=True, diff --git a/julia_src/Manifest.toml b/julia_src/Manifest.toml index c4cec9eac..cb5e1c887 100644 --- a/julia_src/Manifest.toml +++ b/julia_src/Manifest.toml @@ -6,9 +6,9 @@ project_hash = "90e66426e70d2bf97b8a3e99508fcb404743b11d" [[deps.AbstractFFTs]] deps = ["ChainRulesCore", "LinearAlgebra"] -git-tree-sha1 = "69f7020bd72f069c219b5e8c236c1fa90d2cb409" +git-tree-sha1 = "16b6dbc4cf7caee4e1e75c49485ec67b667098a0" uuid = "621f4979-c628-5d54-868e-fcf4e3e8185c" -version = "1.2.1" +version = "1.3.1" [[deps.Adapt]] deps = ["LinearAlgebra", "Requires"] @@ -296,10 +296,10 @@ uuid = "a7073274-a066-55f0-b90d-d619367d196c" version = "301.600.200+0" [[deps.GEOS_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "f4c0cafb093b62d5a5d8447a9b2306555385c0d9" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "818ab247de98d8848a022c7be084b1283d912326" uuid = "d604d12d-fa86-5845-992e-78dc15976526" -version = "3.11.0+0" +version = "3.11.2+0" [[deps.GeoFormatTypes]] git-tree-sha1 = "434166198434a5c2fcc0a1a59d22c3b0ad460889" @@ -320,7 +320,7 @@ version = "1.0.0" [[deps.GhpGhx]] deps = ["CSV", "DataFrames", "HTTP", "JSON"] -git-tree-sha1 = "eaa76a32cd4d3fc84abef44b82c6c8a50f8547dd" +git-tree-sha1 = "938c2bc87c0714f4e995283cca60b9a4ef4f6db6" repo-rev = "add_ground_temp" repo-url = "https://github.com/NREL/GhpGhx.jl.git" uuid = "7ce85f02-24a8-4d69-a3f0-14b5daa7d30c" @@ -767,9 +767,9 @@ version = "1.3.0" [[deps.Roots]] deps = ["ChainRulesCore", "CommonSolve", "Printf", "Setfield"] -git-tree-sha1 = "9c2f5d3768804ed465f0c51540c6074ae9f63900" +git-tree-sha1 = "b45deea4566988994ebb8fb80aa438a295995a6e" uuid = "f2b01f46-fcfa-551c-844a-d8ac1e96c665" -version = "2.0.9" +version = "2.0.10" [[deps.SHA]] uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" @@ -829,9 +829,9 @@ version = "0.1.1" [[deps.StaticArrays]] deps = ["LinearAlgebra", "Random", "StaticArraysCore", "Statistics"] -git-tree-sha1 = "2d7d9e1ddadc8407ffd460e24218e37ef52dd9a3" +git-tree-sha1 = "7756ce473bd10b67245bdebdc8d8670a85f6230b" uuid = "90137ffa-7385-5640-81b9-e52037218182" -version = "1.5.16" +version = "1.5.18" [[deps.StaticArraysCore]] git-tree-sha1 = "6b7ba252635a5eff6a0b0664a41ee140a1c9e72a" @@ -860,9 +860,9 @@ version = "1.0.1" [[deps.Tables]] deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "LinearAlgebra", "OrderedCollections", "TableTraits", "Test"] -git-tree-sha1 = "c79322d36826aa2f4fd8ecfa96ddb47b174ac78d" +git-tree-sha1 = "1544b926975372da01227b382066ab70e574a3ec" uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" -version = "1.10.0" +version = "1.10.1" [[deps.Tar]] deps = ["ArgTools", "SHA"] diff --git a/reo/scenario.py b/reo/scenario.py index 3833757c9..10c332fe0 100644 --- a/reo/scenario.py +++ b/reo/scenario.py @@ -389,8 +389,8 @@ def setup_pv(pv_dict, latitude, longitude, time_steps_per_hour): number_of_ghpghx = len(inputs_dict["Site"]["GHP"]["ghpghx_inputs"]) for i in range(number_of_ghpghx): ghpghx_post = inputs_dict["Site"]["GHP"]["ghpghx_inputs"][i] - is_hybrid_ghx = ghpghx_post.pop("is_hybrid_ghx", None) - + hybrid_ghx_sizing_method = ghpghx_post.pop("hybrid_ghx_sizing_method", None) + ghpghx_post["latitude"] = inputs_dict["Site"]["latitude"] ghpghx_post["longitude"] = inputs_dict["Site"]["longitude"] # Only SpaceHeating portion of Heating Load gets served by GHP, unless allowed by can_serve_dhw @@ -415,7 +415,7 @@ def setup_pv(pv_dict, latitude, longitude, time_steps_per_hour): # Hybrid # Determine if location is heating or cooling dominated - if is_hybrid_ghx: + if hybrid_ghx_sizing_method == "Automatic": determine_heat_cool_post = copy.deepcopy(ghpghx_post) determine_heat_cool_post["simulation_years"] = 2 determine_heat_cool_post["max_sizing_iterations"] = 1 @@ -429,20 +429,24 @@ def setup_pv(pv_dict, latitude, longitude, time_steps_per_hour): determine_heat_cool_results_resp_dict = json.loads(determine_heat_cool_results_resp.content) temp_diff = determine_heat_cool_results_resp_dict["outputs"]["end_of_year_ghx_lft_f"][1] - determine_heat_cool_results_resp_dict["outputs"]["end_of_year_ghx_lft_f"][0] - # TODO - Implement fractional sizing hybrid_sizing_flag = 1.0 if temp_diff > 0: hybrid_sizing_flag = -2.0 elif temp_diff < 0: hybrid_sizing_flag = -1.0 - + ghpghx_post["hybrid_sizing_flag"] = hybrid_sizing_flag - # Other hybrid inputs - ghpghx_post["is_heating_electric"] = False - if inputs_dict["Site"]["GHP"]["aux_heater_type"] == "electric": - ghpghx_post["is_heating_electric"] = True - + elif hybrid_ghx_sizing_method == "Fractional": + hybrid_ghx_sizing_fraction = ghpghx_post.pop("hybrid_ghx_sizing_fraction", None) + if hybrid_ghx_sizing_fraction is not None: + ghpghx_post["hybrid_sizing_flag"] = hybrid_ghx_sizing_fraction + + # Other hybrid inputs + ghpghx_post["is_heating_electric"] = False + if inputs_dict["Site"]["GHP"]["aux_heater_type"] == "electric": + ghpghx_post["is_heating_electric"] = True + # Call /ghpghx endpoint to size GHP and GHX ghpghx_post_resp = client.post('/v1/ghpghx/', data=ghpghx_post) ghpghx_post_resp_dict = json.loads(ghpghx_post_resp.content) From 5449904725b3f8fbe9796dd92e20e1d5e005c3c8 Mon Sep 17 00:00:00 2001 From: lixiangk1 <72464565+lixiangk1@users.noreply.github.com> Date: Thu, 23 Mar 2023 15:55:05 -0600 Subject: [PATCH 14/23] Add error handling for when GHX sizing reaches max iterations --- .../0012_remove_ghpghxinputs_is_hybrid_ghx.py | 17 +++++++++++++++++ ghpghx/models.py | 4 +--- reo/exceptions.py | 15 +++++++++++++++ reo/scenario.py | 17 +++++++++++++---- 4 files changed, 46 insertions(+), 7 deletions(-) create mode 100644 ghpghx/migrations/0012_remove_ghpghxinputs_is_hybrid_ghx.py diff --git a/ghpghx/migrations/0012_remove_ghpghxinputs_is_hybrid_ghx.py b/ghpghx/migrations/0012_remove_ghpghxinputs_is_hybrid_ghx.py new file mode 100644 index 000000000..40c479cb9 --- /dev/null +++ b/ghpghx/migrations/0012_remove_ghpghxinputs_is_hybrid_ghx.py @@ -0,0 +1,17 @@ +# Generated by Django 4.0.7 on 2023-03-23 21:53 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('ghpghx', '0011_ghpghxinputs_hybrid_ghx_sizing_fraction_and_more'), + ] + + operations = [ + migrations.RemoveField( + model_name='ghpghxinputs', + name='is_hybrid_ghx', + ), + ] diff --git a/ghpghx/models.py b/ghpghx/models.py index 513423081..1b1a126ee 100644 --- a/ghpghx/models.py +++ b/ghpghx/models.py @@ -216,10 +216,8 @@ def clean(self): init_sizing_factor_ft_per_peak_ton = models.FloatField(blank=True, default=246.1, validators=[MinValueValidator(1.0), MaxValueValidator(5000.0)], help_text="Initial guess of total feet of GHX boreholes (total feet = N bores * Length bore) based on peak ton heating/cooling [ft/ton]") - + # Hybrid flag - is_hybrid_ghx = models.BooleanField(null=True, blank=True, default=True, - help_text="If the GHP system uses a hybrid GHX with auxiliary heater or cooler") hybrid_ghx_sizing_method = models.TextField(null=True, blank=True, default="None", help_text="Possible values: 'Fractional' (user inputs fraction of full GHX size), 'Automatic' (REopt determines based on the smaller heating or cooling load), 'None' (non-hybrid)") hybrid_sizing_flag = models.FloatField(null=True, blank=True, default=1.0, diff --git a/reo/exceptions.py b/reo/exceptions.py index 62a54423b..225e6fe43 100644 --- a/reo/exceptions.py +++ b/reo/exceptions.py @@ -257,3 +257,18 @@ def __init__(self, exc_type, exc_value, exc_traceback, task='', run_uuid='', use message = "Error saving to database." super(SaveToDatabase, self).__init__(task=task, name=self.__name__, run_uuid=run_uuid, user_uuid=user_uuid, message=message, traceback=debug_msg) + +class GHXMaxIterationsError(REoptError): + """ + Catches the case where GHX sizing hits the maximum number of iterations indicating GHP solution may not have converged + + Attributes: + message - explanation of the error + """ + + __name__ = 'GHXMaxIterationsError' + + def __init__(self, task='', run_uuid='', user_uuid=''): + message = "The GHX sizing solution did not converge. This particular scenario may be difficult or impossible for the current REopt model to solve. Try adjusting the 'GHX simulation solver initial guess (ft/ton)' variable." + super(GHXMaxIterationsError, self).__init__(task=task, name=self.__name__, run_uuid=run_uuid, user_uuid=user_uuid, + message=message, traceback='') diff --git a/reo/scenario.py b/reo/scenario.py index 10c332fe0..dbfd8d6df 100644 --- a/reo/scenario.py +++ b/reo/scenario.py @@ -49,7 +49,7 @@ from reo.src import ghp from celery import shared_task, Task from reo.models import ModelManager -from reo.exceptions import REoptError, UnexpectedError, LoadProfileError, WindDownloadError, PVWattsDownloadError, RequestError +from reo.exceptions import REoptError, UnexpectedError, LoadProfileError, WindDownloadError, PVWattsDownloadError, RequestError, GHXMaxIterationsError from tastypie.test import TestApiClient from reo.utilities import TONHOUR_TO_KWHT, get_climate_zone_and_nearest_city from ghpghx.models import GHPGHXInputs @@ -438,9 +438,9 @@ def setup_pv(pv_dict, latitude, longitude, time_steps_per_hour): ghpghx_post["hybrid_sizing_flag"] = hybrid_sizing_flag elif hybrid_ghx_sizing_method == "Fractional": - hybrid_ghx_sizing_fraction = ghpghx_post.pop("hybrid_ghx_sizing_fraction", None) - if hybrid_ghx_sizing_fraction is not None: - ghpghx_post["hybrid_sizing_flag"] = hybrid_ghx_sizing_fraction + # TODO: Update if default fractional sizing changes + hybrid_ghx_sizing_fraction = ghpghx_post.pop("hybrid_ghx_sizing_fraction", 0.6) + ghpghx_post["hybrid_sizing_flag"] = hybrid_ghx_sizing_fraction # Other hybrid inputs ghpghx_post["is_heating_electric"] = False @@ -454,6 +454,12 @@ def setup_pv(pv_dict, latitude, longitude, time_steps_per_hour): ghpghx_results_url = "/v1/ghpghx/"+ghpghx_uuid_list[i]+"/results/" ghpghx_results_resp = client.get(ghpghx_results_url) # same as doing ghpModelManager.make_response(ghp_uuid) ghpghx_results_resp_dict = json.loads(ghpghx_results_resp.content) + + if ghpghx_results_resp_dict["outputs"]["ghx_soln_number_of_iterations"] == ghpghx_results_resp_dict["inputs"]["max_sizing_iterations"]: + ghx_error = GHXMaxIterationsError(task=self.name, run_uuid=run_uuid, user_uuid=inputs_dict.get('user_uuid')) + ghx_error.save_to_db() + raise ghx_error + ghp_option_list.append(ghp.GHPGHX(dfm=dfm, response=ghpghx_results_resp_dict, **inputs_dict["Site"]["GHP"])) @@ -525,6 +531,9 @@ def setup_pv(pv_dict, latitude, longitude, time_steps_per_hour): e.save_to_db() raise e + if isinstance(e, GHXMaxIterationsError): + raise e + if hasattr(e, 'args'): if len(e.args) > 0: if e.args[0] == 'Unable to download wind data': From 71f98db7761bd11d65251ae39122f0b54fba0604 Mon Sep 17 00:00:00 2001 From: lixiangk1 <72464565+lixiangk1@users.noreply.github.com> Date: Thu, 23 Mar 2023 20:20:33 -0600 Subject: [PATCH 15/23] Update GHP test post --- ghpghx/tests/posts/test_ghpghx_POST.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ghpghx/tests/posts/test_ghpghx_POST.json b/ghpghx/tests/posts/test_ghpghx_POST.json index abce1ae08..f96ad945b 100644 --- a/ghpghx/tests/posts/test_ghpghx_POST.json +++ b/ghpghx/tests/posts/test_ghpghx_POST.json @@ -4,9 +4,9 @@ "solver_eft_tolerance_f": 2.0, "ghx_model": "TESS", "tess_ghx_minimum_timesteps_per_hour": 1, - "max_sizing_iterations": 1, + "max_sizing_iterations": 10, "init_sizing_factor_ft_per_peak_ton": 300.0, - "is_hybrid_ghx": true, + "hybrid_ghx_sizing_method": "None", "aux_heater_thermal_efficiency": 0.95, "aux_cooler_energy_use_intensity_kwe_per_kwt": 0.02 } \ No newline at end of file From 29ee077992596a970b69d4e84aef989fc81c1f13 Mon Sep 17 00:00:00 2001 From: lixiangk1 <72464565+lixiangk1@users.noreply.github.com> Date: Mon, 27 Mar 2023 08:59:51 -0600 Subject: [PATCH 16/23] Update packages, add hybrid GHP test --- .../tests/posts/test_hybrid_ghpghx_POST.json | 17 ++-- julia_src/Manifest.toml | 55 +------------ reo/tests/posts/test_ghp_hybrid_POST.json | 56 +++++++++++++ reo/tests/test_ghp_hybrid.py | 78 +++++++++++++++++++ 4 files changed, 146 insertions(+), 60 deletions(-) create mode 100644 reo/tests/posts/test_ghp_hybrid_POST.json create mode 100644 reo/tests/test_ghp_hybrid.py diff --git a/ghpghx/tests/posts/test_hybrid_ghpghx_POST.json b/ghpghx/tests/posts/test_hybrid_ghpghx_POST.json index 0e6a7b2a6..3d89cacce 100644 --- a/ghpghx/tests/posts/test_hybrid_ghpghx_POST.json +++ b/ghpghx/tests/posts/test_hybrid_ghpghx_POST.json @@ -1,9 +1,12 @@ { - "borehole_depth_ft": 400.0, - "simulation_years": 20, - "solver_eft_tolerance_f": 2.0, - "ghx_model": "TESS", - "tess_ghx_minimum_timesteps_per_hour": 1, - "max_sizing_iterations": 10, - "init_sizing_factor_ft_per_peak_ton": 300.0 + "borehole_depth_ft": 400.0, + "simulation_years": 25, + "solver_eft_tolerance_f": 2, + "ghx_model": "TESS", + "tess_ghx_minimum_timesteps_per_hour": 1, + "max_sizing_iterations": 15, + "init_sizing_factor_ft_per_peak_ton": 246.1, + "aux_heater_thermal_efficiency": 0.98, + "aux_cooler_energy_use_intensity_kwe_per_kwt": 0.02, + "hybrid_ghx_sizing_method": "Automatic" } \ No newline at end of file diff --git a/julia_src/Manifest.toml b/julia_src/Manifest.toml index cb5e1c887..bb3b3137d 100644 --- a/julia_src/Manifest.toml +++ b/julia_src/Manifest.toml @@ -161,22 +161,11 @@ git-tree-sha1 = "6b492e9aafcd6016d5aca105567ed2271c423015" uuid = "3351c21f-4feb-5f29-afb9-f4fcb0e27549" version = "6.4.3+0" -[[deps.Crayons]] -git-tree-sha1 = "249fe38abf76d48563e2f4556bebd215aa317e15" -uuid = "a8cc5b0e-0ffa-5ad4-8c14-923d3ee1735f" -version = "4.1.1" - [[deps.DataAPI]] git-tree-sha1 = "e8119c1a33d267e16108be441a287a6981ba1630" uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a" version = "1.14.0" -[[deps.DataFrames]] -deps = ["Compat", "DataAPI", "Future", "InlineStrings", "InvertedIndices", "IteratorInterfaceExtensions", "LinearAlgebra", "Markdown", "Missings", "PooledArrays", "PrettyTables", "Printf", "REPL", "Random", "Reexport", "SentinelArrays", "SnoopPrecompile", "SortingAlgorithms", "Statistics", "TableTraits", "Tables", "Unicode"] -git-tree-sha1 = "aa51303df86f8626a962fccb878430cdb0a97eee" -uuid = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" -version = "1.5.0" - [[deps.DataStructures]] deps = ["Compat", "InteractiveUtils", "OrderedCollections"] git-tree-sha1 = "d1fff3a548102f48987a52a2e0d114fa97d730f0" @@ -267,12 +256,6 @@ git-tree-sha1 = "335bfdceacc84c5cdf16aadc768aa5ddfc5383cc" uuid = "53c48c17-4a7d-5ca2-90c5-79b7896eea93" version = "0.8.4" -[[deps.Formatting]] -deps = ["Printf"] -git-tree-sha1 = "8339d61043228fdd3eb658d86c926cb282ae72a8" -uuid = "59287772-0a20-5a39-b81b-1366585eb4c0" -version = "0.4.2" - [[deps.ForwardDiff]] deps = ["CommonSubexpressions", "DiffResults", "DiffRules", "LinearAlgebra", "LogExpFunctions", "NaNMath", "Preferences", "Printf", "Random", "SpecialFunctions", "StaticArrays"] git-tree-sha1 = "00e252f4d706b3d55a8863432e742bf5717b498d" @@ -319,9 +302,8 @@ uuid = "0329782f-3d07-4b52-b9f6-d3137cf03c7a" version = "1.0.0" [[deps.GhpGhx]] -deps = ["CSV", "DataFrames", "HTTP", "JSON"] -git-tree-sha1 = "938c2bc87c0714f4e995283cca60b9a4ef4f6db6" -repo-rev = "add_ground_temp" +git-tree-sha1 = "1af6c5587bf0e72b5053e0bf651078f7919005a7" +repo-rev = "main" repo-url = "https://github.com/NREL/GhpGhx.jl.git" uuid = "7ce85f02-24a8-4d69-a3f0-14b5daa7d30c" version = "0.1.0" @@ -400,11 +382,6 @@ git-tree-sha1 = "49510dfcb407e572524ba94aeae2fced1f3feb0f" uuid = "3587e190-3f89-42d0-90ee-14403ec27112" version = "0.1.8" -[[deps.InvertedIndices]] -git-tree-sha1 = "0dc7b50b8d436461be01300fd8cd45aa0274b038" -uuid = "41ab1584-1d38-5bbf-9106-f11c6c58b48f" -version = "1.3.0" - [[deps.IrrationalConstants]] git-tree-sha1 = "630b497eafcc20001bba38a4651b327dcfc491d2" uuid = "92d709cd-6900-40b7-9082-c6be49f344b6" @@ -468,11 +445,6 @@ git-tree-sha1 = "e5b909bcf985c5e2605737d2ce278ed791b89be6" uuid = "dd4b983a-f0e5-5f8d-a1b7-129d4a5fb1ac" version = "2.10.1+0" -[[deps.LaTeXStrings]] -git-tree-sha1 = "f2355693d6778a178ade15952b7ac47a4ff97996" -uuid = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f" -version = "1.3.0" - [[deps.LibCURL]] deps = ["LibCURL_jll", "MozillaCACerts_jll"] uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" @@ -588,12 +560,6 @@ git-tree-sha1 = "bb2e8f4d9f400f6e90d57b34860f6abdc51398e5" uuid = "f28f55f0-a522-5efc-85c2-fe41dfb9b2d9" version = "1.4.1" -[[deps.Missings]] -deps = ["DataAPI"] -git-tree-sha1 = "f66bdc5de519e8f8ae43bdc598782d35a25b1272" -uuid = "e1d29d7a-bbdc-5cf2-9ac0-f12de2c33e28" -version = "1.1.0" - [[deps.Mmap]] uuid = "a63ad114-7e13-5084-954f-fe012c677804" @@ -715,12 +681,6 @@ git-tree-sha1 = "47e5f437cc0e7ef2ce8406ce1e7e24d44915f88d" uuid = "21216c6a-2e73-6563-6e65-726566657250" version = "1.3.0" -[[deps.PrettyTables]] -deps = ["Crayons", "Formatting", "LaTeXStrings", "Markdown", "Reexport", "StringManipulation", "Tables"] -git-tree-sha1 = "548793c7859e28ef026dba514752275ee871169f" -uuid = "08abe8d2-0d0c-5749-adfa-8a2ac140af0d" -version = "2.2.3" - [[deps.Printf]] deps = ["Unicode"] uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" @@ -805,12 +765,6 @@ version = "1.0.3" [[deps.Sockets]] uuid = "6462fe0b-24de-5631-8697-dd941f90decc" -[[deps.SortingAlgorithms]] -deps = ["DataStructures"] -git-tree-sha1 = "a4ada03f999bd01b3a25dcaa30b2d929fe537e00" -uuid = "a2af1166-a08f-5f64-846c-94a0d3cef48c" -version = "1.1.0" - [[deps.SparseArrays]] deps = ["LinearAlgebra", "Random"] uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" @@ -842,11 +796,6 @@ version = "1.4.0" deps = ["LinearAlgebra", "SparseArrays"] uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" -[[deps.StringManipulation]] -git-tree-sha1 = "46da2434b41f41ac3594ee9816ce5541c6096123" -uuid = "892a3eda-7b42-436c-8928-eab12a02cf0e" -version = "0.3.0" - [[deps.TOML]] deps = ["Dates"] uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" diff --git a/reo/tests/posts/test_ghp_hybrid_POST.json b/reo/tests/posts/test_ghp_hybrid_POST.json new file mode 100644 index 000000000..c5424a32e --- /dev/null +++ b/reo/tests/posts/test_ghp_hybrid_POST.json @@ -0,0 +1,56 @@ +{ + "Scenario": { + "timeout_seconds": 400, + "optimality_tolerance_techs": 0.001, + "Site": { + "latitude": 39.73951619, + "longitude": -104.9890941762, + "land_acres": null , + "roof_squarefeet": null , + "Financial": { + "om_cost_escalation_pct": 0.0, + "escalation_pct": 0.0, + "boiler_fuel_escalation_pct": 0.0, + "offtaker_tax_pct": 0.0 , + "offtaker_discount_pct": 0.08 , + "third_party_ownership": false, + "owner_tax_pct": 0.0 , + "owner_discount_pct": 0.08, + "analysis_years": 25 + } , + "LoadProfile": { + "doe_reference_name": "LargeOffice" + } , + "LoadProfileBoilerFuel": { + "doe_reference_name": "LargeOffice" + } , + "LoadProfileChillerThermal": { + "doe_reference_name": "LargeOffice" + } , + "ElectricTariff": { + "blended_annual_rates_us_dollars_per_kwh": 0.10, + "blended_annual_demand_charges_us_dollars_per_kw": 10.0 + } , + "FuelTariff": { + "existing_boiler_fuel_type": "natural_gas" , + "boiler_fuel_blended_annual_rates_us_dollars_per_mmbtu": 10.0 + } , + "GHP": { + "require_ghp_purchase": true, + "building_sqft": 498588.0, + "can_serve_dhw": false, + "aux_heater_type": "electric", + "aux_heater_installed_cost_us_dollars_per_mmbtu_per_hr": 0.0, + "aux_cooler_installed_cost_us_dollars_per_ton": 0.0, + "aux_unit_capacity_sizing_factor_on_peak_load": 1.2 + }, + "PV":{ + "max_kw": 0.0 + }, + "Storage":{ + "max_kw": 0.0, + "max_kwh": 0.0 + } + } + } +} \ No newline at end of file diff --git a/reo/tests/test_ghp_hybrid.py b/reo/tests/test_ghp_hybrid.py new file mode 100644 index 000000000..5e3b7c583 --- /dev/null +++ b/reo/tests/test_ghp_hybrid.py @@ -0,0 +1,78 @@ +# ********************************************************************************* +# REopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# Redistributions of source code must retain the above copyright notice, this list +# of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above copyright notice, this +# list of conditions and the following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# Neither the name of the copyright holder nor the names of its contributors may be +# used to endorse or promote products derived from this software without specific +# prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +# OF THE POSSIBILITY OF SUCH DAMAGE. +# ********************************************************************************* +import json +import os +import pandas as pd +from tastypie.test import ResourceTestCaseMixin +from django.test import TestCase +from reo.models import ModelManager + +class GHPTest(ResourceTestCaseMixin, TestCase): + REopt_tol = 1e-2 + + def setUp(self): + super(GHPTest, self).setUp() + self.reopt_base = '/v1/job/' + self.test_reopt_post = os.path.join('reo', 'tests', 'posts', 'test_ghp_hybrid_POST.json') + self.test_ghpghx_post = os.path.join('ghpghx', 'tests', 'posts', 'test_hybrid_ghpghx_POST.json') + + def get_ghpghx_response(self, data): + return self.api_client.post(self.ghpghx_base, format='json', data=data) + + def get_reopt_response(self, data): + return self.api_client.post(self.reopt_base, format='json', data=data) + + def test_ghp(self): + """ + + This tests the automatic sizing functionality of hybrid GHP + + """ + nested_data = json.load(open(self.test_reopt_post, 'rb')) + ghpghx_post = json.load(open(self.test_ghpghx_post, 'rb')) + + nested_data["Scenario"]["Site"]["GHP"]["ghpghx_inputs"] = [ghpghx_post] + + # Call REopt + resp = self.get_reopt_response(data=nested_data) + self.assertHttpCreated(resp) + r = json.loads(resp.content) + run_uuid = r.get('run_uuid') + d = ModelManager.make_response(run_uuid=run_uuid) + + ghp_uuid = d["outputs"]["Scenario"]["Site"]["GHP"]["ghp_chosen_uuid"] + print("GHP uuid chosen = ", ghp_uuid) + + # Output number of boreholes and heatpump sizing + n_boreholes = d["outputs"]["Scenario"]["Site"]["GHP"]["ghpghx_chosen_outputs"]["number_of_boreholes"] + heatpump_tons = d["outputs"]["Scenario"]["Site"]["GHP"]["ghpghx_chosen_outputs"]["peak_combined_heatpump_thermal_ton"] + # Comparison to TESS exe range + self.assertAlmostEqual(n_boreholes, 45) + self.assertAlmostEqual(heatpump_tons, 824.927) From 90a166c195ed8273dd480bb9c15518da85056fa8 Mon Sep 17 00:00:00 2001 From: lixiangk1 <72464565+lixiangk1@users.noreply.github.com> Date: Mon, 27 Mar 2023 10:20:43 -0600 Subject: [PATCH 17/23] Update hybrid GHP test --- reo/tests/test_ghp_hybrid.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/reo/tests/test_ghp_hybrid.py b/reo/tests/test_ghp_hybrid.py index 5e3b7c583..db011720e 100644 --- a/reo/tests/test_ghp_hybrid.py +++ b/reo/tests/test_ghp_hybrid.py @@ -74,5 +74,6 @@ def test_ghp(self): n_boreholes = d["outputs"]["Scenario"]["Site"]["GHP"]["ghpghx_chosen_outputs"]["number_of_boreholes"] heatpump_tons = d["outputs"]["Scenario"]["Site"]["GHP"]["ghpghx_chosen_outputs"]["peak_combined_heatpump_thermal_ton"] # Comparison to TESS exe range - self.assertAlmostEqual(n_boreholes, 45) + # TODO: Number of boreholes vary between runs with the same input + # self.assertAlmostEqual(n_boreholes, 45) self.assertAlmostEqual(heatpump_tons, 824.927) From 4ea0f3cff214f10a5765041003e123a9bfcb6c6e Mon Sep 17 00:00:00 2001 From: lixiangk1 <72464565+lixiangk1@users.noreply.github.com> Date: Tue, 28 Mar 2023 12:40:54 -0600 Subject: [PATCH 18/23] Update GHP initial sizing factor default value --- ...puts_init_sizing_factor_ft_per_peak_ton.py | 19 +++++++++++++++++++ ghpghx/models.py | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 ghpghx/migrations/0013_alter_ghpghxinputs_init_sizing_factor_ft_per_peak_ton.py diff --git a/ghpghx/migrations/0013_alter_ghpghxinputs_init_sizing_factor_ft_per_peak_ton.py b/ghpghx/migrations/0013_alter_ghpghxinputs_init_sizing_factor_ft_per_peak_ton.py new file mode 100644 index 000000000..726fc9c83 --- /dev/null +++ b/ghpghx/migrations/0013_alter_ghpghxinputs_init_sizing_factor_ft_per_peak_ton.py @@ -0,0 +1,19 @@ +# Generated by Django 4.0.7 on 2023-03-28 18:32 + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ghpghx', '0012_remove_ghpghxinputs_is_hybrid_ghx'), + ] + + operations = [ + migrations.AlterField( + model_name='ghpghxinputs', + name='init_sizing_factor_ft_per_peak_ton', + field=models.FloatField(blank=True, default=75, help_text='Initial guess of total feet of GHX boreholes (total feet = N bores * Length bore) based on peak ton heating/cooling [ft/ton]', validators=[django.core.validators.MinValueValidator(1.0), django.core.validators.MaxValueValidator(5000.0)]), + ), + ] diff --git a/ghpghx/models.py b/ghpghx/models.py index 1b1a126ee..70cfca520 100644 --- a/ghpghx/models.py +++ b/ghpghx/models.py @@ -214,7 +214,7 @@ def clean(self): default=15, validators=[MinValueValidator(1), MaxValueValidator(15)], help_text="Maximum number of sizing iterations before the GHPGHX model times out") init_sizing_factor_ft_per_peak_ton = models.FloatField(blank=True, - default=246.1, validators=[MinValueValidator(1.0), MaxValueValidator(5000.0)], + default=75, validators=[MinValueValidator(1.0), MaxValueValidator(5000.0)], help_text="Initial guess of total feet of GHX boreholes (total feet = N bores * Length bore) based on peak ton heating/cooling [ft/ton]") # Hybrid flag From b7657f4b23ef61e626c2fd356d6279509db49558 Mon Sep 17 00:00:00 2001 From: bill-becker Date: Wed, 29 Mar 2023 11:39:43 -0600 Subject: [PATCH 19/23] Update Julia env GhpGhx and REopt --- julia_src/Manifest.toml | 84 +++++++++++++++++----- reo/migrations/0153_merge_20230329_1652.py | 14 ++++ 2 files changed, 81 insertions(+), 17 deletions(-) create mode 100644 reo/migrations/0153_merge_20230329_1652.py diff --git a/julia_src/Manifest.toml b/julia_src/Manifest.toml index bb3b3137d..05405f471 100644 --- a/julia_src/Manifest.toml +++ b/julia_src/Manifest.toml @@ -1,6 +1,6 @@ # This file is machine-generated - editing it directly is not advised -julia_version = "1.8.0" +julia_version = "1.8.5" manifest_format = "2.0" project_hash = "90e66426e70d2bf97b8a3e99508fcb404743b11d" @@ -141,7 +141,7 @@ version = "4.6.1" [[deps.CompilerSupportLibraries_jll]] deps = ["Artifacts", "Libdl"] uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" -version = "0.5.2+0" +version = "1.0.1+0" [[deps.ConstructionBase]] deps = ["LinearAlgebra"] @@ -161,11 +161,22 @@ git-tree-sha1 = "6b492e9aafcd6016d5aca105567ed2271c423015" uuid = "3351c21f-4feb-5f29-afb9-f4fcb0e27549" version = "6.4.3+0" +[[deps.Crayons]] +git-tree-sha1 = "249fe38abf76d48563e2f4556bebd215aa317e15" +uuid = "a8cc5b0e-0ffa-5ad4-8c14-923d3ee1735f" +version = "4.1.1" + [[deps.DataAPI]] git-tree-sha1 = "e8119c1a33d267e16108be441a287a6981ba1630" uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a" version = "1.14.0" +[[deps.DataFrames]] +deps = ["Compat", "DataAPI", "Future", "InlineStrings", "InvertedIndices", "IteratorInterfaceExtensions", "LinearAlgebra", "Markdown", "Missings", "PooledArrays", "PrettyTables", "Printf", "REPL", "Random", "Reexport", "SentinelArrays", "SnoopPrecompile", "SortingAlgorithms", "Statistics", "TableTraits", "Tables", "Unicode"] +git-tree-sha1 = "aa51303df86f8626a962fccb878430cdb0a97eee" +uuid = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" +version = "1.5.0" + [[deps.DataStructures]] deps = ["Compat", "InteractiveUtils", "OrderedCollections"] git-tree-sha1 = "d1fff3a548102f48987a52a2e0d114fa97d730f0" @@ -256,6 +267,12 @@ git-tree-sha1 = "335bfdceacc84c5cdf16aadc768aa5ddfc5383cc" uuid = "53c48c17-4a7d-5ca2-90c5-79b7896eea93" version = "0.8.4" +[[deps.Formatting]] +deps = ["Printf"] +git-tree-sha1 = "8339d61043228fdd3eb658d86c926cb282ae72a8" +uuid = "59287772-0a20-5a39-b81b-1366585eb4c0" +version = "0.4.2" + [[deps.ForwardDiff]] deps = ["CommonSubexpressions", "DiffResults", "DiffRules", "LinearAlgebra", "LogExpFunctions", "NaNMath", "Preferences", "Printf", "Random", "SpecialFunctions", "StaticArrays"] git-tree-sha1 = "00e252f4d706b3d55a8863432e742bf5717b498d" @@ -291,9 +308,9 @@ version = "0.4.1" [[deps.GeoInterface]] deps = ["Extents"] -git-tree-sha1 = "e07a1b98ed72e3cdd02c6ceaab94b8a606faca40" +git-tree-sha1 = "0eb6de0b312688f852f347171aba888658e29f20" uuid = "cf35fbd7-0cd7-5166-be24-54bfbe79505f" -version = "1.2.1" +version = "1.3.0" [[deps.GeoInterfaceRecipes]] deps = ["GeoInterface", "RecipesBase"] @@ -302,16 +319,16 @@ uuid = "0329782f-3d07-4b52-b9f6-d3137cf03c7a" version = "1.0.0" [[deps.GhpGhx]] -git-tree-sha1 = "1af6c5587bf0e72b5053e0bf651078f7919005a7" +git-tree-sha1 = "c74e436a02552a1d20bae5c6b84bb27cf33e8eb9" repo-rev = "main" repo-url = "https://github.com/NREL/GhpGhx.jl.git" uuid = "7ce85f02-24a8-4d69-a3f0-14b5daa7d30c" version = "0.1.0" [[deps.Glob]] -git-tree-sha1 = "4df9f7e06108728ebf00a0a11edee4b29a482bb2" +git-tree-sha1 = "97285bbd5230dd766e9ef6749b80fc617126d496" uuid = "c27321d9-0574-5035-807b-f59d2c89b15c" -version = "1.3.0" +version = "1.3.1" [[deps.Graphics]] deps = ["Colors", "LinearAlgebra", "NaNMath"] @@ -351,9 +368,9 @@ version = "0.9.4" [[deps.InfrastructureModels]] deps = ["JuMP", "Memento"] -git-tree-sha1 = "88da90ad5d8ca541350c156bea2715f3a23836ce" +git-tree-sha1 = "dc1e2eba1a98aa457b629fe1723d9078ecb74340" uuid = "2030c09a-7f63-5d83-885d-db604e0e9cc0" -version = "0.7.6" +version = "0.7.7" [[deps.IniFile]] git-tree-sha1 = "f550e6e32074c939295eb5ea6de31849ac2c9625" @@ -382,6 +399,11 @@ git-tree-sha1 = "49510dfcb407e572524ba94aeae2fced1f3feb0f" uuid = "3587e190-3f89-42d0-90ee-14403ec27112" version = "0.1.8" +[[deps.InvertedIndices]] +git-tree-sha1 = "0dc7b50b8d436461be01300fd8cd45aa0274b038" +uuid = "41ab1584-1d38-5bbf-9106-f11c6c58b48f" +version = "1.3.0" + [[deps.IrrationalConstants]] git-tree-sha1 = "630b497eafcc20001bba38a4651b327dcfc491d2" uuid = "92d709cd-6900-40b7-9082-c6be49f344b6" @@ -445,6 +467,11 @@ git-tree-sha1 = "e5b909bcf985c5e2605737d2ce278ed791b89be6" uuid = "dd4b983a-f0e5-5f8d-a1b7-129d4a5fb1ac" version = "2.10.1+0" +[[deps.LaTeXStrings]] +git-tree-sha1 = "f2355693d6778a178ade15952b7ac47a4ff97996" +uuid = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f" +version = "1.3.0" + [[deps.LibCURL]] deps = ["LibCURL_jll", "MozillaCACerts_jll"] uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" @@ -560,6 +587,12 @@ git-tree-sha1 = "bb2e8f4d9f400f6e90d57b34860f6abdc51398e5" uuid = "f28f55f0-a522-5efc-85c2-fe41dfb9b2d9" version = "1.4.1" +[[deps.Missings]] +deps = ["DataAPI"] +git-tree-sha1 = "f66bdc5de519e8f8ae43bdc598782d35a25b1272" +uuid = "e1d29d7a-bbdc-5cf2-9ac0-f12de2c33e28" +version = "1.1.0" + [[deps.Mmap]] uuid = "a63ad114-7e13-5084-954f-fe012c677804" @@ -630,9 +663,9 @@ uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e" version = "0.5.5+0" [[deps.OrderedCollections]] -git-tree-sha1 = "85f8e6578bf1f9ee0d11e7bb1b1456435479d47c" +git-tree-sha1 = "d78db6df34313deaca15c5c0b9ff562c704fe1ab" uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" -version = "1.4.1" +version = "1.5.0" [[deps.PROJ_jll]] deps = ["Artifacts", "JLLWrappers", "LibCURL_jll", "Libdl", "Libtiff_jll", "Pkg", "SQLite_jll"] @@ -681,6 +714,12 @@ git-tree-sha1 = "47e5f437cc0e7ef2ce8406ce1e7e24d44915f88d" uuid = "21216c6a-2e73-6563-6e65-726566657250" version = "1.3.0" +[[deps.PrettyTables]] +deps = ["Crayons", "Formatting", "LaTeXStrings", "Markdown", "Reexport", "StringManipulation", "Tables"] +git-tree-sha1 = "548793c7859e28ef026dba514752275ee871169f" +uuid = "08abe8d2-0d0c-5749-adfa-8a2ac140af0d" +version = "2.2.3" + [[deps.Printf]] deps = ["Unicode"] uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" @@ -694,10 +733,10 @@ deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"] uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" [[deps.REopt]] -deps = ["ArchGDAL", "CoolProp", "Dates", "DelimitedFiles", "HTTP", "JLD", "JSON", "JuMP", "LinDistFlow", "LinearAlgebra", "Logging", "MathOptInterface", "Requires", "Roots", "Statistics", "TestEnv"] -git-tree-sha1 = "9c5a19161f6ae306782dc613a8d6c1360247a377" +deps = ["ArchGDAL", "CSV", "CoolProp", "DataFrames", "Dates", "DelimitedFiles", "HTTP", "JLD", "JSON", "JuMP", "LinDistFlow", "LinearAlgebra", "Logging", "MathOptInterface", "Requires", "Roots", "Statistics", "TestEnv"] +git-tree-sha1 = "305dd26c9bce888d2e68a19bea428b541c0a05d9" uuid = "d36ad4e8-d74a-4f7a-ace1-eaea049febf6" -version = "0.24.0" +version = "0.29.0" [[deps.Random]] deps = ["SHA", "Serialization"] @@ -765,6 +804,12 @@ version = "1.0.3" [[deps.Sockets]] uuid = "6462fe0b-24de-5631-8697-dd941f90decc" +[[deps.SortingAlgorithms]] +deps = ["DataStructures"] +git-tree-sha1 = "a4ada03f999bd01b3a25dcaa30b2d929fe537e00" +uuid = "a2af1166-a08f-5f64-846c-94a0d3cef48c" +version = "1.1.0" + [[deps.SparseArrays]] deps = ["LinearAlgebra", "Random"] uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" @@ -783,9 +828,9 @@ version = "0.1.1" [[deps.StaticArrays]] deps = ["LinearAlgebra", "Random", "StaticArraysCore", "Statistics"] -git-tree-sha1 = "7756ce473bd10b67245bdebdc8d8670a85f6230b" +git-tree-sha1 = "b8d897fe7fa688e93aef573711cb207c08c9e11e" uuid = "90137ffa-7385-5640-81b9-e52037218182" -version = "1.5.18" +version = "1.5.19" [[deps.StaticArraysCore]] git-tree-sha1 = "6b7ba252635a5eff6a0b0664a41ee140a1c9e72a" @@ -796,6 +841,11 @@ version = "1.4.0" deps = ["LinearAlgebra", "SparseArrays"] uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" +[[deps.StringManipulation]] +git-tree-sha1 = "46da2434b41f41ac3594ee9816ce5541c6096123" +uuid = "892a3eda-7b42-436c-8928-eab12a02cf0e" +version = "0.3.0" + [[deps.TOML]] deps = ["Dates"] uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" @@ -816,7 +866,7 @@ version = "1.10.1" [[deps.Tar]] deps = ["ArgTools", "SHA"] uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" -version = "1.10.0" +version = "1.10.1" [[deps.TensorCore]] deps = ["LinearAlgebra"] diff --git a/reo/migrations/0153_merge_20230329_1652.py b/reo/migrations/0153_merge_20230329_1652.py new file mode 100644 index 000000000..47d8dbc46 --- /dev/null +++ b/reo/migrations/0153_merge_20230329_1652.py @@ -0,0 +1,14 @@ +# Generated by Django 4.0.7 on 2023-03-29 16:52 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('reo', '0149_alter_messagemodel_index_together_and_more'), + ('reo', '0152_ghpmodel_aux_unit_capacity_sizing_factor_on_peak_load'), + ] + + operations = [ + ] From b069f2b8ff04b810c460b59d0787e1729e9ed8bb Mon Sep 17 00:00:00 2001 From: bill-becker Date: Wed, 29 Mar 2023 15:41:09 -0600 Subject: [PATCH 20/23] Remove development server deploy resource values Development will use values.yaml --- .helm/values.development.yaml | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/.helm/values.development.yaml b/.helm/values.development.yaml index 41241f202..e69de29bb 100644 --- a/.helm/values.development.yaml +++ b/.helm/values.development.yaml @@ -1,11 +0,0 @@ -appEnv: development -djangoSettingsModule: reopt_api.dev_settings -djangoReplicas: 2 -djangoMemoryRequest: "400Mi" -djangoMemoryLimit: "1400Mi" -celeryReplicas: 2 -celeryMemoryRequest: "200Mi" -celeryMemoryLimit: "900Mi" -juliaReplicas: 2 -juliaMemoryRequest: "2000Mi" -juliaMemoryLimit: "8000Mi" \ No newline at end of file From ca1c4710ac479295b213074310baf9c85f660c8c Mon Sep 17 00:00:00 2001 From: lixiangk1 <72464565+lixiangk1@users.noreply.github.com> Date: Wed, 29 Mar 2023 22:43:38 -0600 Subject: [PATCH 21/23] Remove toml files --- Manifest.toml | 7 ------- Project.toml | 1 - 2 files changed, 8 deletions(-) delete mode 100644 Manifest.toml delete mode 100644 Project.toml diff --git a/Manifest.toml b/Manifest.toml deleted file mode 100644 index 0c8e995aa..000000000 --- a/Manifest.toml +++ /dev/null @@ -1,7 +0,0 @@ -# This file is machine-generated - editing it directly is not advised - -julia_version = "1.8.0" -manifest_format = "2.0" -project_hash = "da39a3ee5e6b4b0d3255bfef95601890afd80709" - -[deps] diff --git a/Project.toml b/Project.toml deleted file mode 100644 index 81648c0b1..000000000 --- a/Project.toml +++ /dev/null @@ -1 +0,0 @@ -[deps] From 42feee6962a2a40fea40c0dbad2bb0c79b409655 Mon Sep 17 00:00:00 2001 From: lixiangk1 <72464565+lixiangk1@users.noreply.github.com> Date: Thu, 30 Mar 2023 10:27:31 -0600 Subject: [PATCH 22/23] Update hybrid GHX test --- reo/tests/test_ghp_hybrid.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/reo/tests/test_ghp_hybrid.py b/reo/tests/test_ghp_hybrid.py index db011720e..cc07cee58 100644 --- a/reo/tests/test_ghp_hybrid.py +++ b/reo/tests/test_ghp_hybrid.py @@ -73,7 +73,12 @@ def test_ghp(self): # Output number of boreholes and heatpump sizing n_boreholes = d["outputs"]["Scenario"]["Site"]["GHP"]["ghpghx_chosen_outputs"]["number_of_boreholes"] heatpump_tons = d["outputs"]["Scenario"]["Site"]["GHP"]["ghpghx_chosen_outputs"]["peak_combined_heatpump_thermal_ton"] + aux_cooler_annual_thermal_production_kwht = sum(d["outputs"]["Scenario"]["Site"]["GHP"]["ghpghx_chosen_outputs"]["yearly_aux_cooler_thermal_production_series_kwt"]) + aux_heater_annual_thermal_production_mmbtu = sum(d["outputs"]["Scenario"]["Site"]["GHP"]["ghpghx_chosen_outputs"]["yearly_aux_heater_thermal_production_series_mmbtu_per_hour"]) + # Comparison to TESS exe range # TODO: Number of boreholes vary between runs with the same input # self.assertAlmostEqual(n_boreholes, 45) self.assertAlmostEqual(heatpump_tons, 824.927) + self.assertAlmostEqual(aux_cooler_annual_thermal_production_kwht, 0.0) + self.assertGreater(aux_heater_annual_thermal_production_mmbtu, 480.0) From 3137c3eb39a4682342d3342b3ede0606f4df2be5 Mon Sep 17 00:00:00 2001 From: lixiangk1 <72464565+lixiangk1@users.noreply.github.com> Date: Thu, 30 Mar 2023 14:45:12 -0600 Subject: [PATCH 23/23] Update CHANGELOG.md --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe992a55b..c4b978314 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,16 @@ Classify the change according to the following categories: ##### Removed ### Patches +## v2.11.0 +### Minor Updates +##### Added +- Enabled hybrid GHX sizing within the GHP model through the **hybrid_ghx_sizing_method** variable + - User is able to select "Automatic" (REopt sizes GHX based on the smaller of the heating or cooling load), "Fractional" (GHX size is a user-defined fraction of the non-hybrid GHX size), or "None" (non-hybrid) + - Auxiliary heater and cooler are both currently only electric + - Outputs added to track the thermal production, electrical consumption, and size of the auxiliary unit +##### Changed +- Updated default value **init_sizing_factor_ft_per_peak_ton** from 246.1 to 75 for the `/ghpghx` endpoint + ## v2.10.1 ### Patches - Make **ERPOutageInputs** field **max_outage_duration** required