diff --git a/CHANGELOG.md b/CHANGELOG.md index 4cee93ce4..09bc917a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,32 @@ Classify the change according to the following categories: ##### Removed ### Patches +## misc-defaults +### Minor Updates +#### Changed +Update the following inputs from the previous --> new values: +- `Financial.offtaker_discount_rate_fraction`: 0.0638 --> 0.0624 +- `Financial.owner_discount_rate_fraction`: 0.0638 --> 0.0624 +- `Financial.elec_cost_escalation_rate_fraction`: 0.017 --> 0.0166 +- `Financial.existing_boiler_fuel_cost_escalation_rate_fraction `: 0.015 --> 0.0348 +- `Financial.boiler_fuel_cost_escalation_rate_fraction `: 0.015 --> 0.0348 +- `Financial.chp_fuel_cost_escalation_rate_fraction `: 0.015 --> 0.0348 +- `Financial.generator_fuel_cost_escalation_rate_fraction `: 0.012 --> 0.0197 +- `Generator.fuel_cost_per_gallon`: 3.61 --> 2.25 +- `ColdThermalStorage`, `HotThermalStorage`, `ElectricStorage` `macrs_option_years`: 7 --> 5 +- `CHP`, `ColdThermalStorage`, `HotThermalStorage`, `ElectricStorage`, `PV`, `Wind` `macrs_bonus_fraction` 0.6 --> 1.0 +- `GHP.macrs_bonus_fraction`: 0.4 --> 0.0 +- `GHP.macrs_option_years`: 5 --> 0 +- `SteamTurbine.macrs_bonus_fraction`: 0 --> 1.0 +- `SteamTurbine.macrs_option_years`: 0 --> 5 (in order for 100% bonus depr to apply) +- `CHP.federal_itc_fraction`: 0.3 --> 0.0 +- `Wind.om_cost_per_kw`: 36.0 --> 42.0 +- `Wind.size_class_to_installed_cost` = Dict( + "residential"=> 6339.0, --> 7692.0 + "commercial"=> 4760.0, --> 5776.0 + "medium"=> 3137.0, --> 3807.0 + "large"=> 2386.0 --> 2896.0) + ## v3.13.0 ### Minor Updates #### Added diff --git a/julia_src/Manifest.toml b/julia_src/Manifest.toml index 499e0c00a..756844116 100644 --- a/julia_src/Manifest.toml +++ b/julia_src/Manifest.toml @@ -922,9 +922,9 @@ uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" [[deps.REopt]] deps = ["ArchGDAL", "CSV", "CoolProp", "DataFrames", "Dates", "DelimitedFiles", "HTTP", "JLD", "JSON", "JuMP", "LinDistFlow", "LinearAlgebra", "Logging", "MathOptInterface", "Requires", "Roots", "Statistics", "TestEnv"] -git-tree-sha1 = "4db74039055f6084d8bd45304de9de856462d917" +git-tree-sha1 = "f9fd5a8419a3a1c057403fb34fd4f47c15afe28e" uuid = "d36ad4e8-d74a-4f7a-ace1-eaea049febf6" -version = "0.53.2" +version = "0.54.0" [[deps.Random]] deps = ["SHA"] diff --git a/reoptjl/migrations/0093_alter_chpinputs_federal_itc_fraction_and_more.py b/reoptjl/migrations/0093_alter_chpinputs_federal_itc_fraction_and_more.py new file mode 100644 index 000000000..354937af6 --- /dev/null +++ b/reoptjl/migrations/0093_alter_chpinputs_federal_itc_fraction_and_more.py @@ -0,0 +1,124 @@ +# Generated by Django 4.0.7 on 2025-09-03 16:20 + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('reoptjl', '0092_merge_20250613_0525'), + ] + + operations = [ + migrations.AlterField( + model_name='chpinputs', + name='federal_itc_fraction', + field=models.FloatField(blank=True, default=0.0, help_text='Percentage of capital costs that are credited towards federal taxes', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1)]), + ), + migrations.AlterField( + model_name='chpinputs', + name='macrs_bonus_fraction', + field=models.FloatField(blank=True, default=0.0, help_text='Percent of upfront project costs to depreciate in year one in addition to scheduled depreciation', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1)]), + ), + migrations.AlterField( + model_name='chpinputs', + name='macrs_option_years', + field=models.IntegerField(blank=True, choices=[(0, 'Zero'), (5, 'Five'), (7, 'Seven')], default=0, help_text='Duration over which accelerated depreciation will occur. Set to zero to disable'), + ), + migrations.AlterField( + model_name='coldthermalstorageinputs', + name='macrs_bonus_fraction', + field=models.FloatField(blank=True, default=1.0, help_text='Percent of upfront project costs to depreciate in year one in addition to scheduled depreciation', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1)]), + ), + migrations.AlterField( + model_name='coldthermalstorageinputs', + name='macrs_option_years', + field=models.IntegerField(blank=True, choices=[(0, 'Zero'), (5, 'Five'), (7, 'Seven')], default=5, help_text='Duration over which accelerated depreciation will occur. Set to zero to disable'), + ), + migrations.AlterField( + model_name='electricstorageinputs', + name='macrs_bonus_fraction', + field=models.FloatField(blank=True, default=1.0, help_text='Percent of upfront project costs to depreciate in year one in addition to scheduled depreciation', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1)]), + ), + migrations.AlterField( + model_name='electricstorageinputs', + name='macrs_option_years', + field=models.IntegerField(blank=True, choices=[(0, 'Zero'), (5, 'Five'), (7, 'Seven')], default=5, help_text='Duration over which accelerated depreciation will occur. Set to zero to disable'), + ), + migrations.AlterField( + model_name='financialinputs', + name='boiler_fuel_cost_escalation_rate_fraction', + field=models.FloatField(blank=True, default=0.0348, help_text='Annual nominal boiler fuel cost escalation rate', validators=[django.core.validators.MinValueValidator(-1), django.core.validators.MaxValueValidator(1)]), + ), + migrations.AlterField( + model_name='financialinputs', + name='chp_fuel_cost_escalation_rate_fraction', + field=models.FloatField(blank=True, default=0.0348, help_text='Annual nominal chp fuel cost escalation rate', validators=[django.core.validators.MinValueValidator(-1), django.core.validators.MaxValueValidator(1)]), + ), + migrations.AlterField( + model_name='financialinputs', + name='elec_cost_escalation_rate_fraction', + field=models.FloatField(blank=True, default=0.0166, help_text='Annual nominal utility electricity cost escalation rate.', validators=[django.core.validators.MinValueValidator(-1), django.core.validators.MaxValueValidator(1)]), + ), + migrations.AlterField( + model_name='financialinputs', + name='existing_boiler_fuel_cost_escalation_rate_fraction', + field=models.FloatField(blank=True, default=0.0348, help_text='Annual nominal existing boiler fuel cost escalation rate', validators=[django.core.validators.MinValueValidator(-1), django.core.validators.MaxValueValidator(1)]), + ), + migrations.AlterField( + model_name='financialinputs', + name='generator_fuel_cost_escalation_rate_fraction', + field=models.FloatField(blank=True, default=0.0197, help_text='Annual nominal boiler fuel cost escalation rate', validators=[django.core.validators.MinValueValidator(-1), django.core.validators.MaxValueValidator(1)]), + ), + migrations.AlterField( + model_name='financialinputs', + name='offtaker_discount_rate_fraction', + field=models.FloatField(blank=True, default=0.0624, help_text='Nominal energy offtaker discount rate. In single ownership model the offtaker is also the generation owner.', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1)]), + ), + migrations.AlterField( + model_name='financialinputs', + name='owner_discount_rate_fraction', + field=models.FloatField(blank=True, default=0.0624, help_text='Nominal generation owner discount rate. Used for two party financing model. In two party ownership model the offtaker does not own the generator(s).', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1)]), + ), + migrations.AlterField( + model_name='generatorinputs', + name='fuel_cost_per_gallon', + field=models.FloatField(blank=True, default=2.25, help_text='Diesel cost in $/gallon', validators=[django.core.validators.MinValueValidator(0.0), django.core.validators.MaxValueValidator(100.0)]), + ), + migrations.AlterField( + model_name='ghpinputs', + name='macrs_bonus_fraction', + field=models.FloatField(blank=True, default=0.0, help_text='Percent of upfront project costs to depreciate in year one in addition to scheduled depreciation', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1)]), + ), + migrations.AlterField( + model_name='ghpinputs', + name='macrs_option_years', + field=models.IntegerField(blank=True, choices=[(0, 'Zero'), (5, 'Five'), (7, 'Seven')], default=0, help_text='Duration over which accelerated depreciation will occur. Set to zero to disable'), + ), + migrations.AlterField( + model_name='hotthermalstorageinputs', + name='macrs_bonus_fraction', + field=models.FloatField(blank=True, default=1.0, help_text='Percent of upfront project costs to depreciate in year one in addition to scheduled depreciation', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1)]), + ), + migrations.AlterField( + model_name='hotthermalstorageinputs', + name='macrs_option_years', + field=models.IntegerField(blank=True, choices=[(0, 'Zero'), (5, 'Five'), (7, 'Seven')], default=5, help_text='Duration over which accelerated depreciation will occur. Set to zero to disable'), + ), + migrations.AlterField( + model_name='pvinputs', + name='macrs_bonus_fraction', + field=models.FloatField(blank=True, default=1.0, help_text='Percent of upfront project costs to depreciate in year one in addition to scheduled depreciation', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1)]), + ), + migrations.AlterField( + model_name='windinputs', + name='macrs_bonus_fraction', + field=models.FloatField(blank=True, default=1.0, help_text='Percent of upfront project costs to depreciate in year one in addition to scheduled depreciation', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1)]), + ), + migrations.AlterField( + model_name='windinputs', + name='om_cost_per_kw', + field=models.FloatField(blank=True, default=42, help_text='Annual operations and maintenance costs in $/kW', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1000.0)]), + ), + ] diff --git a/reoptjl/migrations/0094_alter_chpinputs_macrs_bonus_fraction_and_more.py b/reoptjl/migrations/0094_alter_chpinputs_macrs_bonus_fraction_and_more.py new file mode 100644 index 000000000..2af077603 --- /dev/null +++ b/reoptjl/migrations/0094_alter_chpinputs_macrs_bonus_fraction_and_more.py @@ -0,0 +1,34 @@ +# Generated by Django 4.0.7 on 2025-09-05 17:55 + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('reoptjl', '0093_alter_chpinputs_federal_itc_fraction_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='chpinputs', + name='macrs_bonus_fraction', + field=models.FloatField(blank=True, default=1.0, help_text='Percent of upfront project costs to depreciate in year one in addition to scheduled depreciation', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1)]), + ), + migrations.AlterField( + model_name='chpinputs', + name='macrs_option_years', + field=models.IntegerField(blank=True, choices=[(0, 'Zero'), (5, 'Five'), (7, 'Seven')], default=5, help_text='Duration over which accelerated depreciation will occur. Set to zero to disable'), + ), + migrations.AlterField( + model_name='steamturbineinputs', + name='macrs_bonus_fraction', + field=models.FloatField(blank=True, default=1.0, help_text='Percent of upfront project costs to depreciate in year one in addition to scheduled depreciation', null=True, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1)]), + ), + migrations.AlterField( + model_name='steamturbineinputs', + name='macrs_option_years', + field=models.IntegerField(blank=True, choices=[(0, 'Zero'), (5, 'Five'), (7, 'Seven')], default=5, help_text='Duration over which accelerated depreciation will occur. Set to zero to disable', null=True), + ), + ] diff --git a/reoptjl/models.py b/reoptjl/models.py index 2d48782ea..b128ff11f 100644 --- a/reoptjl/models.py +++ b/reoptjl/models.py @@ -76,10 +76,10 @@ class MACRS_YEARS_CHOICES(models.IntegerChoices): } WIND_COST_DEFAULTS = { # size_class_to_installed_cost - "residential" : 6339.0, - "commercial" : 4760.0, - "medium" : 3137.0, - "large" : 2386.0 + "residential" : 7692.0, + "commercial" : 5776.0, + "medium" : 3807.0, + "large" : 2896.0 } def at_least_one_set(model, possible_sets): @@ -677,7 +677,7 @@ class FinancialInputs(BaseModel, models.Model): help_text="Analysis period in years. Must be integer." ) elec_cost_escalation_rate_fraction = models.FloatField( - default=0.017, + default=0.0166, validators=[ MinValueValidator(-1), MaxValueValidator(1) @@ -686,7 +686,7 @@ class FinancialInputs(BaseModel, models.Model): help_text="Annual nominal utility electricity cost escalation rate." ) offtaker_discount_rate_fraction = models.FloatField( - default=0.0638, + default=0.0624, validators=[ MinValueValidator(0), MaxValueValidator(1) @@ -714,7 +714,7 @@ class FinancialInputs(BaseModel, models.Model): help_text="Annual nominal O&M cost escalation rate" ) owner_discount_rate_fraction = models.FloatField( - default=0.0638, + default=0.0624, validators=[ MinValueValidator(0), MaxValueValidator(1) @@ -906,7 +906,7 @@ class FinancialInputs(BaseModel, models.Model): help_text=("Annual nominal escalation rate of the public health cost of 1 tonne of PM2.5 emissions (as a decimal). The default value is calculated from the EASIUR model for a height of 150m.") ) generator_fuel_cost_escalation_rate_fraction = models.FloatField( - default=0.012, + default=0.0197, validators=[ MinValueValidator(-1), MaxValueValidator(1) @@ -915,7 +915,7 @@ class FinancialInputs(BaseModel, models.Model): help_text=("Annual nominal boiler fuel cost escalation rate") ) existing_boiler_fuel_cost_escalation_rate_fraction = models.FloatField( - default=0.015, + default=0.0348, validators=[ MinValueValidator(-1), MaxValueValidator(1) @@ -924,7 +924,7 @@ class FinancialInputs(BaseModel, models.Model): help_text=("Annual nominal existing boiler fuel cost escalation rate") ) boiler_fuel_cost_escalation_rate_fraction = models.FloatField( - default=0.015, + default=0.0348, validators=[ MinValueValidator(-1), MaxValueValidator(1) @@ -933,7 +933,7 @@ class FinancialInputs(BaseModel, models.Model): help_text=("Annual nominal boiler fuel cost escalation rate") ) chp_fuel_cost_escalation_rate_fraction = models.FloatField( - default=0.015, + default=0.0348, validators=[ MinValueValidator(-1), MaxValueValidator(1) @@ -2785,7 +2785,7 @@ class PV_LOCATION_CHOICES(models.TextChoices): help_text="Duration over which accelerated depreciation will occur. Set to zero to disable" ) macrs_bonus_fraction = models.FloatField( - default=0.6, + default=1.0, validators=[ MinValueValidator(0), MaxValueValidator(1) @@ -3214,7 +3214,7 @@ class WIND_SIZE_CLASS_CHOICES(models.TextChoices): help_text="Installed cost in $/kW. Default cost is determined based on size_class." ) om_cost_per_kw = models.FloatField( - default=36, + default=42, validators=[ MinValueValidator(0), MaxValueValidator(1.0e3) @@ -3229,7 +3229,7 @@ class WIND_SIZE_CLASS_CHOICES(models.TextChoices): help_text="Duration over which accelerated depreciation will occur. Set to zero to disable" ) macrs_bonus_fraction = models.FloatField( - default=0.6, + default=1.0, validators=[ MinValueValidator(0), MaxValueValidator(1) @@ -3642,13 +3642,13 @@ class ElectricStorageInputs(BaseModel, models.Model): help_text="Annual O&M cost as a fraction of installed cost." ) macrs_option_years = models.IntegerField( - default=MACRS_YEARS_CHOICES.SEVEN, + default=MACRS_YEARS_CHOICES.FIVE, choices=MACRS_YEARS_CHOICES.choices, blank=True, help_text="Duration over which accelerated depreciation will occur. Set to zero to disable" ) macrs_bonus_fraction = models.FloatField( - default=0.6, + default=1.0, validators=[ MinValueValidator(0), MaxValueValidator(1) @@ -3809,7 +3809,7 @@ class GeneratorInputs(BaseModel, models.Model): help_text="Diesel generator per unit production (variable) operations and maintenance costs in $/kWh" ) fuel_cost_per_gallon = models.FloatField( - default=3.0, + default=2.25, validators=[ MinValueValidator(0.0), MaxValueValidator(1.0e2) @@ -4438,7 +4438,7 @@ class CHPInputs(BaseModel, models.Model): help_text="Duration over which accelerated depreciation will occur. Set to zero to disable" ) macrs_bonus_fraction = models.FloatField( - default=0.6, + default=1.0, validators=[ MinValueValidator(0), MaxValueValidator(1) @@ -4456,7 +4456,7 @@ class CHPInputs(BaseModel, models.Model): help_text="Percent of the ITC value by which depreciable basis is reduced" ) federal_itc_fraction = models.FloatField( - default=0.3, + default=0.0, validators=[ MinValueValidator(0), MaxValueValidator(1) @@ -6693,7 +6693,7 @@ class SIZE_CLASS_LIST(models.IntegerChoices): ) macrs_option_years = models.IntegerField( - default=MACRS_YEARS_CHOICES.ZERO, + default=MACRS_YEARS_CHOICES.FIVE, choices=MACRS_YEARS_CHOICES.choices, null=True, blank=True, @@ -6701,7 +6701,7 @@ class SIZE_CLASS_LIST(models.IntegerChoices): ) macrs_bonus_fraction = models.FloatField( - default=0.0, + default=1.0, validators=[ MinValueValidator(0), MaxValueValidator(1) @@ -6894,13 +6894,13 @@ class HotThermalStorageInputs(BaseModel, models.Model): help_text="Thermal energy-based cost of TES (e.g. volume of the tank)" ) macrs_option_years = models.IntegerField( - default=MACRS_YEARS_CHOICES.SEVEN, + default=MACRS_YEARS_CHOICES.FIVE, choices=MACRS_YEARS_CHOICES.choices, blank=True, help_text="Duration over which accelerated depreciation will occur. Set to zero to disable" ) macrs_bonus_fraction = models.FloatField( - default=0.6, + default=1.0, validators=[ MinValueValidator(0), MaxValueValidator(1) @@ -7098,13 +7098,13 @@ class ColdThermalStorageInputs(BaseModel, models.Model): help_text="Thermal energy-based cost of TES (e.g. volume of the tank)" ) macrs_option_years = models.IntegerField( - default=MACRS_YEARS_CHOICES.SEVEN, + default=MACRS_YEARS_CHOICES.FIVE, choices=MACRS_YEARS_CHOICES.choices, blank=True, help_text="Duration over which accelerated depreciation will occur. Set to zero to disable" ) macrs_bonus_fraction = models.FloatField( - default=0.6, + default=1.0, validators=[ MinValueValidator(0), MaxValueValidator(1) @@ -8399,13 +8399,13 @@ class GHPInputs(BaseModel, models.Model): ) macrs_option_years = models.IntegerField( - default=MACRS_YEARS_CHOICES.FIVE, + default=MACRS_YEARS_CHOICES.ZERO, choices=MACRS_YEARS_CHOICES.choices, blank=True, help_text="Duration over which accelerated depreciation will occur. Set to zero to disable" ) macrs_bonus_fraction = models.FloatField( - default=0.6, + default=0.0, validators=[ MinValueValidator(0), MaxValueValidator(1) diff --git a/reoptjl/test/posts/outage.json b/reoptjl/test/posts/outage.json index 5658437c5..b7a501c47 100644 --- a/reoptjl/test/posts/outage.json +++ b/reoptjl/test/posts/outage.json @@ -34,7 +34,8 @@ "min_kw": 150.0, "max_kw": 150.0, "macrs_bonus_fraction": 0.8, - "macrs_option_years": 0 + "macrs_option_years": 0, + "federal_itc_fraction": 0.3 }, "PV": { "min_kw": 282.3629, diff --git a/reoptjl/test/test_http_endpoints.py b/reoptjl/test/test_http_endpoints.py index 8104a6197..14771a2c3 100644 --- a/reoptjl/test/test_http_endpoints.py +++ b/reoptjl/test/test_http_endpoints.py @@ -35,7 +35,7 @@ def test_chp_defaults(self): self.assertEqual(http_response["prime_mover"], "combustion_turbine") self.assertEqual(http_response["size_class"], 2) self.assertGreater(http_response["chp_elec_size_heuristic_kw"], 3500.0) - self.assertEqual(http_response["default_inputs"]["federal_itc_fraction"], 0.3) + self.assertEqual(http_response["default_inputs"]["federal_itc_fraction"], 0.0) inputs = { "prime_mover": "micro_turbine", @@ -50,7 +50,7 @@ def test_chp_defaults(self): http_response = response.json() # Check the endpoint logic with the expected selection - self.assertEqual(http_response["default_inputs"]["federal_itc_fraction"], 0.3) + self.assertEqual(http_response["default_inputs"]["federal_itc_fraction"], 0.0) inputs = { "prime_mover": "combustion_turbine", diff --git a/reoptjl/test/test_validator.py b/reoptjl/test/test_validator.py index 8e0c1c6d3..2e9d5c24b 100644 --- a/reoptjl/test/test_validator.py +++ b/reoptjl/test/test_validator.py @@ -102,7 +102,7 @@ def test_off_grid_defaults_overrides(self): self.assertAlmostEqual(validator.models["Wind"].operating_reserve_required_fraction, 0.5) self.assertAlmostEqual(validator.models["PV"].operating_reserve_required_fraction, 0.25) - self.assertEqual(validator.models["Wind"].installed_cost_per_kw, 4760.0) # set based on size_class + self.assertEqual(validator.models["Wind"].installed_cost_per_kw, 5776.0) # set based on size_class (commercial) self.assertAlmostEqual(validator.models["ElectricLoad"].operating_reserve_required_fraction, 0.1) self.assertAlmostEqual(validator.models["ElectricLoad"].critical_load_fraction, 1.0)