From 10179d2242dde6ecbb7983f2edec4420b65b0aab Mon Sep 17 00:00:00 2001 From: An Pham Date: Tue, 10 Dec 2024 12:14:19 -0700 Subject: [PATCH 1/4] update inputs --- reo/models.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/reo/models.py b/reo/models.py index 6865c0233..a548c0d35 100644 --- a/reo/models.py +++ b/reo/models.py @@ -662,7 +662,8 @@ class StorageModel(models.Model): total_itc_pct = models.FloatField(null=True, blank=True) total_rebate_us_dollars_per_kw = models.IntegerField(null=True, blank=True) total_rebate_us_dollars_per_kwh = models.IntegerField(null=True, blank=True) - + min_duration_hours = models.IntegerField(null=True, blank=True) + max_duration_hours = models.IntegerField(null=True, blank=True) # Outputs size_kw = models.FloatField(null=True, blank=True) From 0788e6edf554405d935a3546bef74f8d3eaab312 Mon Sep 17 00:00:00 2001 From: An Pham Date: Tue, 10 Dec 2024 14:19:06 -0700 Subject: [PATCH 2/4] added battery duration constraints --- julia_src/reopt_model.jl | 4 ++++ julia_src/utils.jl | 18 ++++++++++-------- reo/models.py | 4 ++-- reo/src/data_manager.py | 12 ++++++++++-- 4 files changed, 26 insertions(+), 12 deletions(-) diff --git a/julia_src/reopt_model.jl b/julia_src/reopt_model.jl index 6c26ebd2c..1c6549cc8 100644 --- a/julia_src/reopt_model.jl +++ b/julia_src/reopt_model.jl @@ -451,6 +451,10 @@ function add_storage_size_constraints(m, p) @constraint(m, StoragePowerLBCon[b in p.Storage], m[:dvStorageCapPower][b] >= p.StorageMinSizePower[b]) # Constraint (4c)-2: Upper bound on Storage Power Capacity @constraint(m, StoragePowerUBCon[b in p.Storage], m[:dvStorageCapPower][b] <= p.StorageMaxSizePower[b]) + # Constraint (4c)-3: Lower bound on Storage Energy Capacity based on Battery Duration + constraint(m, StorageEnergyLBCon["Elec"], m[:dvStorageCapEnergy]["Elec"] >= m[:dvStorageCapPower]["Elec"] * p.MinDurationHours) + # Constraint (4c)-4: Upper bound on Storage Energy Capacity based on Battery Duration + constraint(m, StorageEnergyLBCon["Elec"], m[:dvStorageCapEnergy]["Elec"] <= m[:dvStorageCapPower]["Elec"] * p.MaxDurationHours) end diff --git a/julia_src/utils.jl b/julia_src/utils.jl index a1d05ae15..effe96b13 100644 --- a/julia_src/utils.jl +++ b/julia_src/utils.jl @@ -140,17 +140,19 @@ Base.@kwdef struct Parameter GridChargeEfficiency::Float64 # \eta^{esig}: Efficiency of charging electrical storage using grid power [fraction] (NEW) DischargeEfficiency::AxisArray # \eta^{eso}_{b}: Efficiency of discharging storage system b [fraction] (NEW) - ### Storage Parameters ### # \ubar{w}^{bkW}_{b}: Minimum power capacity of storage system b (needs to be indexed on b ) + ### Storage Parameters ### # \ubar{w}^{bkW}_{b}: Minimum power capacity of storage system b (needs to be indexed on b ) StorageMinChargePcent::Float64 # \ubar{w}^{mcp}_{b}: Minimum state of charge of storage system b - InitSOC::Float64 # w^{i}_{b} Initial percent state of charge for storage system b - StorageMinSizeEnergy::AxisArray # \bar{w}^{bkWh}_{b}: Maximum energy capacity of storage system b [kWh] - StorageMaxSizeEnergy::AxisArray # \ubar{w}^{bkWh}_{b}: Minimum energy capacity of storage system b [kWh] + InitSOC::Float64 # w^{i}_{b} Initial percent state of charge for storage system b + StorageMinSizeEnergy::AxisArray # \bar{w}^{bkWh}_{b}: Maximum energy capacity of storage system b [kWh] + StorageMaxSizeEnergy::AxisArray # \ubar{w}^{bkWh}_{b}: Minimum energy capacity of storage system b [kWh] StorageMinSizePower::AxisArray # \bar{w}^{bkW}_{b}: Maximum power capacity of storage system b [kW] StorageMaxSizePower::AxisArray # \ubar{w}^{bkW}_{b}: Minimum power capacity of storage system b [kW] - StorageMinSOC::AxisArray # \ubar{w}^{mcp}_{b}: Minimum state of charge of storage system b [fraction] - StorageInitSOC::AxisArray #Initial state of charge of storage system b [fraction] - StorageCanGridCharge::Bool # Boolean for storage system [fraction] - + StorageMinSOC::AxisArray # \ubar{w}^{mcp}_{b}: Minimum state of charge of storage system b [fraction] + StorageInitSOC::AxisArray # Initial state of charge of storage system b [fraction] + StorageCanGridCharge::Bool # Boolean for storage system [fraction] + MinDurationHours::Float64 # Minimum amount of time electric storage can discharge at its rated power capacity + MaxDurationHours::Float64 # Maximum amount of time storage can discharge at its rated power capacity + ### Fuel Burn Parameters ### FuelBurnSlope::AxisArray # m^\text{fm}_{t}: Fuel burn rate slope parameter for technology t FuelBurnYInt::AxisArray # m^\text{fb}_{t}: Fuel burn rate slope parameter for technology t diff --git a/reo/models.py b/reo/models.py index a548c0d35..d833e5b33 100644 --- a/reo/models.py +++ b/reo/models.py @@ -662,8 +662,8 @@ class StorageModel(models.Model): total_itc_pct = models.FloatField(null=True, blank=True) total_rebate_us_dollars_per_kw = models.IntegerField(null=True, blank=True) total_rebate_us_dollars_per_kwh = models.IntegerField(null=True, blank=True) - min_duration_hours = models.IntegerField(null=True, blank=True) - max_duration_hours = models.IntegerField(null=True, blank=True) + min_duration_hours = models.FloatField(null=True, blank=True) + max_duration_hours = models.FloatField(null=True, blank=True) # Outputs size_kw = models.FloatField(null=True, blank=True) diff --git a/reo/src/data_manager.py b/reo/src/data_manager.py index 95be50eb0..1e99dce3c 100644 --- a/reo/src/data_manager.py +++ b/reo/src/data_manager.py @@ -1154,6 +1154,8 @@ def _get_REopt_storage_techs_and_params(self): storage_min_energy = [self.storage.min_kwh] storage_max_energy = [self.storage.max_kwh] storage_decay_rate = [0.0] + storage_min_duration_hours = [self.storage.min_duration_hours] + storage_max_duration_hours = [self.storage.max_duration_hours] # Obtain storage costs and params sf = self.site.financial @@ -1263,7 +1265,8 @@ def _get_REopt_storage_techs_and_params(self): return storage_techs, thermal_storage_techs, hot_tes_techs, \ cold_tes_techs, storage_power_cost, storage_energy_cost, \ storage_min_power, storage_max_power, storage_min_energy, \ - storage_max_energy, storage_decay_rate + storage_max_energy, storage_decay_rate, storage_min_duration_hours, \ + storage_max_duration_hours def _get_export_curtailment_params(self, techs, export_rates, net_metering_limit_kw): """ @@ -1429,7 +1432,8 @@ def finalize(self): storage_techs, thermal_storage_techs, hot_tes_techs, \ cold_tes_techs, storage_power_cost, storage_energy_cost, \ storage_min_power, storage_max_power, storage_min_energy, \ - storage_max_energy, storage_decay_rate = self._get_REopt_storage_techs_and_params() + storage_max_energy, storage_decay_rate, storage_min_duration_hours, \ + storage_max_duration_hours = self._get_REopt_storage_techs_and_params() parser = UrdbParse(big_number=big_number, elec_tariff=self.elec_tariff, techs=get_techs_not_none(self.available_techs, self), @@ -1752,6 +1756,8 @@ def finalize(self): 'StorageMaxSizePower': storage_max_power, 'StorageMinSOC': [self.storage.soc_min_pct, self.hot_tes.soc_min_pct, self.cold_tes.soc_min_pct], 'StorageInitSOC': [self.storage.soc_init_pct, self.hot_tes.soc_init_pct, self.cold_tes.soc_init_pct], + 'MinDurationHours': storage_min_duration_hours, + 'MaxDurationHours': storage_max_duration_hours, 'StorageCanGridCharge': self.storage.canGridCharge, 'SegmentMinSize': segment_min_size, 'SegmentMaxSize': segment_max_size, @@ -1947,6 +1953,8 @@ def finalize(self): 'StorageMaxSizeEnergy': [0.0 for _ in storage_techs], 'StorageMinSizePower': [0.0 for _ in storage_techs], 'StorageMaxSizePower': [0.0 for _ in storage_techs], + 'MinDurationHours': [0.0 for _ in storage_techs], + 'MaxDurationHours': [0.0 for _ in storage_techs], 'StorageMinSOC': [0.0 for _ in storage_techs], 'StorageInitSOC': [0.0 for _ in storage_techs], 'StorageCanGridCharge': self.storage.canGridCharge, From a7dc3b28962db7162db5ad159fb02e8c4fc2ba0b Mon Sep 17 00:00:00 2001 From: An Pham Date: Tue, 10 Dec 2024 19:18:47 -0700 Subject: [PATCH 3/4] Create 0154_storagemodel_max_duration_hours_and_more.py --- ...toragemodel_max_duration_hours_and_more.py | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 reo/migrations/0154_storagemodel_max_duration_hours_and_more.py diff --git a/reo/migrations/0154_storagemodel_max_duration_hours_and_more.py b/reo/migrations/0154_storagemodel_max_duration_hours_and_more.py new file mode 100644 index 000000000..c5cb64ee6 --- /dev/null +++ b/reo/migrations/0154_storagemodel_max_duration_hours_and_more.py @@ -0,0 +1,23 @@ +# Generated by Django 4.0.7 on 2024-12-11 02:16 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('reo', '0153_merge_20230329_1652'), + ] + + operations = [ + migrations.AddField( + model_name='storagemodel', + name='max_duration_hours', + field=models.FloatField(blank=True, null=True), + ), + migrations.AddField( + model_name='storagemodel', + name='min_duration_hours', + field=models.FloatField(blank=True, null=True), + ), + ] From b6c40136f1573ca966d96d17750ffa24f57f00f3 Mon Sep 17 00:00:00 2001 From: An Pham Date: Tue, 10 Dec 2024 21:20:48 -0700 Subject: [PATCH 4/4] Update reopt_model.jl --- julia_src/reopt_model.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/julia_src/reopt_model.jl b/julia_src/reopt_model.jl index 1c6549cc8..6ab285f52 100644 --- a/julia_src/reopt_model.jl +++ b/julia_src/reopt_model.jl @@ -452,9 +452,9 @@ function add_storage_size_constraints(m, p) # Constraint (4c)-2: Upper bound on Storage Power Capacity @constraint(m, StoragePowerUBCon[b in p.Storage], m[:dvStorageCapPower][b] <= p.StorageMaxSizePower[b]) # Constraint (4c)-3: Lower bound on Storage Energy Capacity based on Battery Duration - constraint(m, StorageEnergyLBCon["Elec"], m[:dvStorageCapEnergy]["Elec"] >= m[:dvStorageCapPower]["Elec"] * p.MinDurationHours) + constraint(m, StorageMinDurationCon["Elec"], m[:dvStorageCapEnergy]["Elec"] >= m[:dvStorageCapPower]["Elec"] * p.MinDurationHours) # Constraint (4c)-4: Upper bound on Storage Energy Capacity based on Battery Duration - constraint(m, StorageEnergyLBCon["Elec"], m[:dvStorageCapEnergy]["Elec"] <= m[:dvStorageCapPower]["Elec"] * p.MaxDurationHours) + constraint(m, StorageMaxDurationCon["Elec"], m[:dvStorageCapEnergy]["Elec"] <= m[:dvStorageCapPower]["Elec"] * p.MaxDurationHours) end