Skip to content

Commit

Permalink
Merge pull request #514 from NREL/develop
Browse files Browse the repository at this point in the history
v3.1.0 Defaults Updates and Hybrid+Central GHP
  • Loading branch information
Bill-Becker authored Oct 4, 2023
2 parents c971e01 + 2a3f949 commit 02fa6ab
Show file tree
Hide file tree
Showing 31 changed files with 71,160 additions and 208 deletions.
33 changes: 33 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,39 @@ Classify the change according to the following categories:
##### Removed
### Patches

## v3.1.0
### Major Updates
#### Changed
- ANNUAL UPDATE TO DEFAULT VALUES. Changes outlined below with (old value) --> (new value). See user manual for references.
- Owner Discount rate, nominal (%): : **Financial** **owner_discount_rate_fraction** 0.0564 --> 0.0638
- Offtaker Discount rate, nominal (%): **Financial** **offtaker_discount_rate_fraction** 0.0564 --> 0.0638
- Electricity cost escalation rate, nominal (%): **Financial** **elec_cost_escalation_rate_fraction** 0.019 --> 0.017
- Existing boiler fuel cost escalation rate, nominal (%): **Financial** **existing_boiler_fuel_cost_escalation_rate_fraction** 0.034 --> 0.015
- Boiler fuel cost escalation rate, nominal (%): **Financial** **boiler_fuel_cost_escalation_rate_fraction** 0.034 --> 0.015
- CHP fuel cost escalation rate, nominal (%): **Financial** **chp_fuel_cost_escalation_rate_fraction** 0.034 --> 0.015
- Generator fuel cost escalation rate, nominal (%): **Financial** **generator_fuel_cost_escalation_rate_fraction** 0.027 --> 0.012
- Array tilt – Ground mount, Fixed: **PV** **tilt** latitude --> 20
- O&M cost ($/kW/year): **PV** **om_cost_per_kw** 17 --> 18
- System capital cost ($/kW): **PV** **installed_cost_per_kw** 1592 --> 1790
- Energy capacity cost ($/kWh): **ElectricStorage** **installed_cost_per_kwh** 388 --> 455
- Power capacity cost ($/kW): **ElectricStorage** **installed_cost_per_kw** 775 --> 910
- Energy capacity replacement cost ($/kWh): **ElectricStorage** **replace_cost_per_kwh** 220 --> 318
- Power capacity replacement cost ($/kW): **ElectricStorage** **replace_cost_per_kw** 440 --> 715
- Fuel burn rate by generator capacity (gal/kWh): **Generator** **fuel_slope_gal_per_kwh** 0.076 --> removed and replaced with full and half-load efficiencies
- Electric efficiency at 100% load (% HHV-basis): **Generator** **electric_efficiency_full_load** N/A - new input --> 0.322
- Electric efficiency at 50% load (% HHV-basis): **Generator** **electric_efficiency_half_load** N/A - new input --> 0.322
- Generator fuel higher heating value (HHV): **Generator** **fuel_higher_heating_value_kwh_per_gal** N/A - new input --> 40.7
- System capital cost ($/kW): **Generator** **installed_cost_per_kw** 500 --> $650 if the generator only runs during outages; $800 if it is allowed to run parallel with the grid; $880 for off-grid
- Fixed O&M ($/kW/yr): **Generator** **om_cost_per_kw** Grid connected: 10 Off-grid: 20 --> Grid connected: 20 Off-grid: 10
- System capital cost ($/kW) by Class: **Wind** **size_class_to_installed_cost** residential - 5675 commercial - 4300 medium - 2766 large - 2239 --> residential - 6339 commercial - 4760 medium - 3137 large - 2386
- O&M cost ($/kW/year): **Wind** **om_cost_per_kw** 35 --> 36
### Minor Updates
##### Added
- Added ability to run hybrid GHX using REopt API v3.
- Added ability to run centralized GHP scenarios using REopt API.
##### Fixed
- Fixed `test_thermal_in_results` to account for missing required inputs.

## v3.0.0
### Major Updates
##### Changed
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 4.0.7 on 2023-09-28 17:54

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('ghpghx', '0013_alter_ghpghxinputs_init_sizing_factor_ft_per_peak_ton'),
]

operations = [
migrations.AddField(
model_name='ghpghxoutputs',
name='hybrid_auto_ghx_sizing_flag',
field=models.BooleanField(blank=True, default=False, null=True),
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Generated by Django 4.0.7 on 2023-09-28 17:55

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('ghpghx', '0014_ghpghxoutputs_hybrid_auto_ghx_sizing_flag'),
]

operations = [
migrations.RemoveField(
model_name='ghpghxoutputs',
name='hybrid_auto_ghx_sizing_flag',
),
migrations.AddField(
model_name='ghpghxinputs',
name='hybrid_auto_ghx_sizing_flag',
field=models.BooleanField(blank=True, default=False, null=True),
),
]
18 changes: 18 additions & 0 deletions ghpghx/migrations/0016_ghpghxoutputs_heat_pump_configuration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 4.0.7 on 2023-09-28 18:00

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('ghpghx', '0015_remove_ghpghxoutputs_hybrid_auto_ghx_sizing_flag_and_more'),
]

operations = [
migrations.AddField(
model_name='ghpghxoutputs',
name='heat_pump_configuration',
field=models.TextField(blank=True, help_text='Specifies if the auxiliary heat exchange unit is a heater or cooler', null=True),
),
]
18 changes: 18 additions & 0 deletions ghpghx/migrations/0017_ghpghxinputs_heat_pump_configuration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 4.0.7 on 2023-10-01 16:42

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('ghpghx', '0016_ghpghxoutputs_heat_pump_configuration'),
]

operations = [
migrations.AddField(
model_name='ghpghxinputs',
name='heat_pump_configuration',
field=models.TextField(blank=True, help_text='Specifies if the auxiliary heat exchange unit is a heater or cooler', null=True),
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Generated by Django 4.0.7 on 2023-10-02 18:20

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


class Migration(migrations.Migration):

dependencies = [
('ghpghx', '0017_ghpghxinputs_heat_pump_configuration'),
]

operations = [
migrations.AddField(
model_name='ghpghxinputs',
name='wwhp_cooling_pump_fluid_flow_rate_gpm_per_ton',
field=models.FloatField(blank=True, default=3.0, help_text='Volumetric flow rate of the fluid in the hydronic chilled water cooling loop per peak ton cooling [GPM/ton]', validators=[django.core.validators.MinValueValidator(0.1), django.core.validators.MaxValueValidator(10.0)]),
),
migrations.AddField(
model_name='ghpghxinputs',
name='wwhp_cooling_pump_min_speed_fraction',
field=models.FloatField(blank=True, default=0.1, help_text='The minimum turndown fraction of the WWHP cooling pump. 1.0 is a constant speed pump.', validators=[django.core.validators.MinValueValidator(0.0), django.core.validators.MaxValueValidator(1.0)]),
),
migrations.AddField(
model_name='ghpghxinputs',
name='wwhp_cooling_pump_power_exponent',
field=models.FloatField(blank=True, default=2.2, help_text='The WWHP cooling pump power curve exponent', validators=[django.core.validators.MinValueValidator(0.1), django.core.validators.MaxValueValidator(10.0)]),
),
migrations.AddField(
model_name='ghpghxinputs',
name='wwhp_cooling_pump_power_watt_per_gpm',
field=models.FloatField(blank=True, default=15.0, help_text='Pumping power required for a given volumetric flow rate of the fluid through the cooling pump [Watt/GPM]', validators=[django.core.validators.MinValueValidator(0.0), django.core.validators.MaxValueValidator(100.0)]),
),
migrations.AddField(
model_name='ghpghxinputs',
name='wwhp_cooling_setpoint_f',
field=models.FloatField(blank=True, default=55.0, help_text='Setpoint temperature of the chilled water cooling loop [degF]', validators=[django.core.validators.MinValueValidator(0.0), django.core.validators.MaxValueValidator(100.0)]),
),
migrations.AddField(
model_name='ghpghxinputs',
name='wwhp_cop_map_eft_cooling',
field=django.contrib.postgres.fields.ArrayField(base_field=picklefield.fields.PickledObjectField(editable=False), default=ghpghx.models.GHPGHXInputs._get_wwhp_cooling_cop_map, help_text="WWHP cooling heat pump coefficient of performance (COP) map: list of dictionaries, each with the key 'EFT' followed by keys representing temperature setpoints", null=True, size=None),
),
migrations.AddField(
model_name='ghpghxinputs',
name='wwhp_cop_map_eft_heating',
field=django.contrib.postgres.fields.ArrayField(base_field=picklefield.fields.PickledObjectField(editable=False), default=ghpghx.models.GHPGHXInputs._get_wwhp_heating_cop_map, help_text="WWHP heating heat pump coefficient of performance (COP) map: list of dictionaries, each with the key 'EFT' followed by keys representing temperature setpoints", null=True, size=None),
),
migrations.AddField(
model_name='ghpghxinputs',
name='wwhp_heating_pump_fluid_flow_rate_gpm_per_ton',
field=models.FloatField(blank=True, default=3.0, help_text='Volumetric flow rate of the fluid in the hydronic space heating loop per peak ton heating [GPM/ton]', validators=[django.core.validators.MinValueValidator(0.1), django.core.validators.MaxValueValidator(10.0)]),
),
migrations.AddField(
model_name='ghpghxinputs',
name='wwhp_heating_pump_min_speed_fraction',
field=models.FloatField(blank=True, default=0.1, help_text='The minimum turndown fraction of the WWHP heating pump. 1.0 is a constant speed pump.', validators=[django.core.validators.MinValueValidator(0.0), django.core.validators.MaxValueValidator(1.0)]),
),
migrations.AddField(
model_name='ghpghxinputs',
name='wwhp_heating_pump_power_exponent',
field=models.FloatField(blank=True, default=2.2, help_text='The WWHP heating pump power curve exponent', validators=[django.core.validators.MinValueValidator(0.1), django.core.validators.MaxValueValidator(10.0)]),
),
migrations.AddField(
model_name='ghpghxinputs',
name='wwhp_heating_pump_power_watt_per_gpm',
field=models.FloatField(blank=True, default=15.0, help_text='Pumping power required for a given volumetric flow rate of the fluid through the heating pump [Watt/GPM]', validators=[django.core.validators.MinValueValidator(0.0), django.core.validators.MaxValueValidator(100.0)]),
),
migrations.AddField(
model_name='ghpghxinputs',
name='wwhp_heating_setpoint_f',
field=models.FloatField(blank=True, default=140.0, help_text='Setpoint temperature of the space heating hot water loop [degF]', validators=[django.core.validators.MinValueValidator(0.0), django.core.validators.MaxValueValidator(250.0)]),
),
migrations.AlterField(
model_name='ghpghxinputs',
name='heat_pump_configuration',
field=models.TextField(blank=True, default='WSHP', help_text='Specifies if the GHP system is centralized (WWHP) or decentralized (WSHP)', null=True),
),
]
60 changes: 60 additions & 0 deletions ghpghx/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,28 @@ def _get_cop_map():
default=_get_cop_map,
help_text="Heat pump coefficient of performance (COP) map: list of dictionaries, each with 3 keys: 1) EFT, 2) HeatingCOP, 3) CoolingCOP")

def _get_wwhp_heating_cop_map():
hp_cop_filepath = os.path.join('ghpghx', 'tests', 'posts', "wwhp_heating_heatpump_cop_map.csv" )
heatpump_copmap_df = pd.read_csv(hp_cop_filepath)
heatpump_copmap_list_of_dict = heatpump_copmap_df.to_dict('records')
return heatpump_copmap_list_of_dict

wwhp_cop_map_eft_heating = ArrayField(
PickledObjectField(editable=True), null=True,
default=_get_wwhp_heating_cop_map,
help_text="WWHP heating heat pump coefficient of performance (COP) map: list of dictionaries, each with the key 'EFT' followed by keys representing temperature setpoints")

def _get_wwhp_cooling_cop_map():
hp_cop_filepath = os.path.join('ghpghx', 'tests', 'posts', "wwhp_cooling_heatpump_cop_map.csv" )
heatpump_copmap_df = pd.read_csv(hp_cop_filepath)
heatpump_copmap_list_of_dict = heatpump_copmap_df.to_dict('records')
return heatpump_copmap_list_of_dict

wwhp_cop_map_eft_cooling = ArrayField(
PickledObjectField(editable=True), null=True,
default=_get_wwhp_cooling_cop_map,
help_text="WWHP cooling heat pump coefficient of performance (COP) map: list of dictionaries, each with the key 'EFT' followed by keys representing temperature setpoints")

"""
TODO define custom clean_cop_map()
def clean_cop_map(self):
Expand Down Expand Up @@ -174,6 +196,7 @@ def clean(self):
# Hybrid flag
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_auto_ghx_sizing_flag = models.BooleanField(blank=True, null=True, default=False)
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,
Expand All @@ -188,6 +211,41 @@ def clean(self):
default=0.02, validators=[MinValueValidator(0.001), MaxValueValidator(1.0)],
help_text="The energy use intensity of the auxiliary cooler [kWe/kWt]")

# Central plant variables
heat_pump_configuration = models.TextField(null=True, blank=True, default="WSHP",
help_text="Specifies if the GHP system is centralized (WWHP) or decentralized (WSHP)")
wwhp_cooling_setpoint_f = models.FloatField(blank=True,
default=55.0, validators=[MinValueValidator(0.0), MaxValueValidator(100.0)],
help_text="Setpoint temperature of the chilled water cooling loop [degF]")
wwhp_heating_setpoint_f = models.FloatField(blank=True,
default=140.0, validators=[MinValueValidator(0.0), MaxValueValidator(250.0)],
help_text="Setpoint temperature of the space heating hot water loop [degF]")
wwhp_heating_pump_fluid_flow_rate_gpm_per_ton = models.FloatField(blank=True,
default=3.0, validators=[MinValueValidator(0.1), MaxValueValidator(10.0)],
help_text="Volumetric flow rate of the fluid in the hydronic space heating loop per peak ton heating [GPM/ton]")
wwhp_cooling_pump_fluid_flow_rate_gpm_per_ton = models.FloatField(blank=True,
default=3.0, validators=[MinValueValidator(0.1), MaxValueValidator(10.0)],
help_text="Volumetric flow rate of the fluid in the hydronic chilled water cooling loop per peak ton cooling [GPM/ton]")
wwhp_heating_pump_power_watt_per_gpm = models.FloatField(blank=True,
default=15.0, validators=[MinValueValidator(0.0), MaxValueValidator(100.0)],
help_text="Pumping power required for a given volumetric flow rate of the fluid through the heating pump [Watt/GPM]")
wwhp_cooling_pump_power_watt_per_gpm = models.FloatField(blank=True,
default=15.0, validators=[MinValueValidator(0.0), MaxValueValidator(100.0)],
help_text="Pumping power required for a given volumetric flow rate of the fluid through the cooling pump [Watt/GPM]")
wwhp_heating_pump_min_speed_fraction = models.FloatField(blank=True,
default=0.1, validators=[MinValueValidator(0.0), MaxValueValidator(1.0)],
help_text="The minimum turndown fraction of the WWHP heating pump. 1.0 is a constant speed pump.")
wwhp_cooling_pump_min_speed_fraction = models.FloatField(blank=True,
default=0.1, validators=[MinValueValidator(0.0), MaxValueValidator(1.0)],
help_text="The minimum turndown fraction of the WWHP cooling pump. 1.0 is a constant speed pump.")
wwhp_heating_pump_power_exponent = models.FloatField(blank=True,
default=2.2, validators=[MinValueValidator(0.1), MaxValueValidator(10.0)],
help_text="The WWHP heating pump power curve exponent")
wwhp_cooling_pump_power_exponent = models.FloatField(blank=True,
default=2.2, validators=[MinValueValidator(0.1), MaxValueValidator(10.0)],
help_text="The WWHP cooling pump power curve exponent")


class GHPGHXOutputs(models.Model):
# Outputs/results

Expand Down Expand Up @@ -266,6 +324,8 @@ class GHPGHXOutputs(models.Model):
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")
heat_pump_configuration = models.TextField(null=True, blank=True,
help_text="Specifies if the auxiliary heat exchange unit is a heater or cooler")

class ModelManager(object):

Expand Down
1 change: 1 addition & 0 deletions ghpghx/resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ def obj_create(self, bundle, **kwargs):
ghpghxOutputsM = GHPGHXOutputs(ghp_uuid=ghp_uuid, **results)
ghpghxOutputsM.save()
except Exception as e:
print(e)
exc_type, exc_value, exc_traceback = sys.exc_info()
message = "Error saving the results to the database"
data["status"] = message
Expand Down
3 changes: 2 additions & 1 deletion ghpghx/tests/posts/test_ghpghx_POST.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@
"init_sizing_factor_ft_per_peak_ton": 300.0,
"hybrid_ghx_sizing_method": "None",
"aux_heater_thermal_efficiency": 0.95,
"aux_cooler_energy_use_intensity_kwe_per_kwt": 0.02
"aux_cooler_energy_use_intensity_kwe_per_kwt": 0.02,
"heat_pump_configuration": "WSHP"
}
3 changes: 2 additions & 1 deletion ghpghx/tests/posts/test_hybrid_ghpghx_POST.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@
"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"
"hybrid_ghx_sizing_method": "Automatic",
"heat_pump_configuration": "WSHP"
}
7 changes: 7 additions & 0 deletions ghpghx/tests/posts/wwhp_cooling_heatpump_cop_map.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
eft,40,50,60
50,6.9,8,9.1
60,6.2,7.2,8.3
70,5.6,6.5,7.4
80,4.9,5.8,6.6
90,4.3,5.1,5.8
100,3.6,4.4,5
7 changes: 7 additions & 0 deletions ghpghx/tests/posts/wwhp_heating_heatpump_cop_map.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
eft,120,130,140
30,2.7,2.3,2.2
40,3.2,2.8,2.7
50,3.7,3.2,3.1
60,4.2,3.7,3.5
70,4.7,4.1,4
80,5.2,4.6,4.4
Loading

0 comments on commit 02fa6ab

Please sign in to comment.