Add input for ElectricStorage SOC timeseries#486
Conversation
|
I reviewed the changes and ran some tests and everything seems to make sense. I was wondering if you've noticed in your runs that the cases with the fixed SOC dispatch returns a slightly lower LCC compared to the fixed sizes but unconstrained dispatch? We'd expect it to be slightly higher if results are different right? Is that just a mathematical tolerance thing? |
that are there for constraint function compatibility and don't want to expose to users
@lixiangk1 I've found that if I have a run1 with fixed PV and BESS sizes, then do a run2 where the SOC is fixed to run1 SOC with a 1-5% tolerance (and all else held equal):
|
|
|
||
| for t in p.s.storage.types.elec | ||
| fix(m[:dvGridToStorage][t, ts], 0.0, force=true) | ||
| for b in p.s.storage.types.elec |
There was a problem hiding this comment.
changing for consistency in how we index storage
There was a problem hiding this comment.
Pull request overview
Adds support for fixing ElectricStorage state-of-charge (SOC) as a time series (with tolerance), and updates degradation-related naming for battery results.
Changes:
- Add
fixed_soc_series_fractionandfixed_soc_series_fraction_toleranceinputs for ElectricStorage, plus corresponding dispatch constraints to enforce a fixed SOC trajectory within tolerance. - Rename ElectricStorage degradation output
state_of_healthtostate_of_health_series_fraction. - Add a new scenario + test coverage for fixed SOC behavior.
Reviewed changes
Copilot reviewed 13 out of 14 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| test/scenarios/fixed_pv_bess.json | Adds a simple PV+BESS scenario used by the new fixed-SOC test. |
| test/runtests.jl | Adds a HiGHS testset validating fixed SOC behavior. |
| src/constraints/storage_constraints.jl | Implements fixed SOC constraints (and refactors a power-capacity constraint). |
| src/core/energy_storage/electric_storage.jl | Adds ElectricStorage inputs for fixed SOC series and tolerance; adjusts SOC-related defaults when fixed series is provided. |
| src/core/utils.jl | Extends JSON/Dict key conversion list to include fixed_soc_series_fraction (and degradation segment arrays). |
| src/results/electric_storage.jl | Renames degradation output key to state_of_health_series_fraction. |
| src/results/thermal_storage.jl | Clarifies docstrings to describe outputs as “average year.” |
| src/results/electric_utility.jl | Fixes formatting/indentation around storage-dependent results. |
| src/mpc/structs.jl | Adds fixed SOC series/tolerance fields to MPC electric storage structs. |
| src/mpc/model.jl | Minor variable naming fix in a storage loop. |
| src/core/reopt.jl | Minor loop variable rename and updates a degradation-related log message. |
| src/core/electric_tariff.jl | Docstring tweak (adds unit detail for wholesale export rate). |
| src/core/scenario.jl | Minor docstring list adjustment in Scenario docs. |
| CHANGELOG.md | Records the new inputs and the degradation output rename. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| # Constraint (4l): Constrain to fixed_soc_series_fraction | ||
| if hasproperty(p.s.storage.attr[b], :fixed_soc_series_fraction) && !isnothing(p.s.storage.attr[b].fixed_soc_series_fraction) | ||
| # Allow for a percentage point (fractional) buffer on user-provided fixed_soc_series_fraction | ||
| @constraint(m, [ts in p.time_steps], | ||
| m[Symbol("dvStoredEnergy"*_n)][b, ts] <= (p.s.storage.attr[b].fixed_soc_series_fraction_tolerance + p.s.storage.attr[b].fixed_soc_series_fraction[ts]) * m[Symbol("dvStorageEnergy"*_n)][b] | ||
| ) | ||
| @constraint(m, [ts in p.time_steps], | ||
| m[Symbol("dvStoredEnergy"*_n)][b, ts] >= (-p.s.storage.attr[b].fixed_soc_series_fraction_tolerance + p.s.storage.attr[b].fixed_soc_series_fraction[ts]) * m[Symbol("dvStorageEnergy"*_n)][b] | ||
| ) |
There was a problem hiding this comment.
The fixed-SOC constraints only apply for ts in p.time_steps and do not constrain dvStoredEnergy[b, 0]. In MPC (and any case where soc_init_fraction isn’t auto-aligned to the fixed series), this can allow an inconsistent initial SOC or force a large first-step correction that may make the problem infeasible. Consider also constraining dvStoredEnergy[b, 0] to match the first fixed SOC value (within tolerance), or aligning soc_init_fraction automatically whenever a fixed series is provided.
There was a problem hiding this comment.
soc_init_fraction is set equal to fixed_soc_series_fraction[1] in electric_storage.jl. @lixiangk1 does this approach work for MPC?
Added
Changed
Cherry-picked from branch
h2-tldrd-mpcby @lixiangk1 with a few small mods