Code, data and reproduction notebook for comparing four ways of representing energy storage in OSeMOSYS when the year is reduced to representative days.
This project implements four storage state-of-charge (SoC) formulations for temporally aggregated OSeMOSYS models.
| Paper (Borba, Islam & Niet, Environ. Res. Commun.) | Linkage / verification | Code directory |
|---|---|---|
| Niet | direct link between representative time slices | models/Model_Niet |
| Welsch | fixed intra-day pattern per day type | models/Model_Welsch |
| Kotzur | chronological days, SoC verified at day boundaries | models/Model_Kotzur |
| Novo | chronological hours, SoC verified every time step | models/Model_Cluster |
Everything needed to regenerate the published results is in this repository:
conda env create -f env/environment-crossplatform.yml # Python 3.13.5, GLPK 5.0, otoole 1.1.5, scikit-learn 1.7.1
conda activate storage_osemosys
jupyter lab notebooks/reproduce_study.ipynb # step-by-step reproduction (fast steps on by default)The notebook checks the environment, documents the inputs, reruns the clustering (including a multi-seed
robustness check), runs the independent hand-calculated SoC verification (validation/soc/), reproduces
selected cases bit-for-bit against the archived results, and rebuilds Table 4, the cost-error curves,
the stress-test statistics and the SoC fidelity metrics. Switches at the top enable the full-year benchmarks,
the 108-configuration sweep and the stress tests. See REPRODUCIBILITY.md for details,
runtimes and the list of corrections made for the published version.
Command-line alternative:
python workflow/automation.py --resources WND SOL HYD --k all --h all # 108 aggregated configurations x 3 systems
python workflow/automation.py --resources WND SOL HYD --k 365 --h 1 # full-year hourly benchmarks
python workflow/automation.py --resources WND SOL --k 365 --h 1 --cap 0.2 # binding-capacity stress test
python validation/soc/run.py # independent SoC verification
python workflow/reproducibility_proof.py # rerun main.py, compare with the saved reference instance# Create and activate the environment (any OS); or: make setup
conda env create -f env/environment-crossplatform.yml
conda activate storage_osemosysEdit config/config.yaml to set your parameters:
scenario_name: k4h1WND
resource: WND # WND | SOL | HYD
n_clusters: 4 # representative days k
hour_grouping: 1 # hours per intra-day block h
random_state: 0 # k-means seed used in the paper
StorageMaxCapacity: 1000.0 # total storage energy cap, kW.yr (0.2 in the stress test)# Run the main analysis
make run-main
# Or run directly
python workflow/main.pystorage-in-OSeMOSYS/
├── config/
│ └── config.yaml # Scenario and path configuration (one scenario per run)
├── data/
│ ├── timeseries/ # Hourly time series (wind, solar, hydro CF; demand profile)
│ ├── system/ # OSeMOSYS parameter CSVs shared by all formulations (Table 3)
│ ├── published_results/ # Archived result tables and key trajectories of the paper
│ └── full_archive/ # Optional: full results archive, Zenodo 10.5281/zenodo.23598757 (not tracked)
├── models/
│ └── Model_*/ # OSeMOSYS model file and otoole config of each formulation
├── src/ # Python package: clustering, input generation, solve, post-processing
│ ├── cluster.py # Representative-day clustering
│ ├── simulation.py # GLPK runner
│ ├── reproduce.py # Helpers used by the notebook and the batch runner
│ └── ...
├── workflow/
│ ├── main.py # Run one scenario (all four formulations)
│ └── automation.py # Command-line batch runner for scenario grids
├── notebooks/
│ └── reproduce_study.ipynb # Step-by-step reproduction of the study
├── validation/
│ └── soc/ # Independent hand-calculated SoC verification (Section 4.1)
├── env/ # Conda environments
├── docs/ # Documentation site (Sphinx)
├── archive/ # Superseded scripts, notebooks and data (not used by the workflow)
├── results/ # Fresh outputs (created on first run, not tracked)
└── REPRODUCIBILITY.md # Reproduction guide, runtimes, corrections log
python workflow/main.py solves one scenario with all four formulations and writes to results/:
| File | Content |
|---|---|
Simulation_Results.xlsx |
One row per scenario and formulation: LP size, total cost, run time, memory, new VRE and storage capacity, solver status |
<scenario>_Storage_Level_Model_<name>.csv |
State of charge of each formulation (native, plus reconstructed _intraday series for Kotzur and Welsch) |
<scenario>_clustering.json |
Representative days and the day-to-representative mapping used by all four formulations |
<scenario>_Figure*.png |
Daily, hourly and two-week SoC comparison |
To load and compare results:
import src.reproduce as rp
res = rp.load_results("results/Simulation_Results.xlsx") # one row per scenario x formulation
rp.add_errors(res) # cost and capacity error vs the k365h1 Novo benchmark
rp.table4(res) # Table 4 of the paper (needs the k365h1 runs)https://deltae.github.io/storage-in-OSeMOSYS/ explains the four formulations, the workflow, every
configuration setting and the output files. The API reference is generated from src/.
| Problem | Fix |
|---|---|
glpsol: command not found |
conda install -c conda-forge glpk=5.0 in the storage_osemosys environment |
ModuleNotFoundError: src |
Run python workflow/main.py (works from any folder); start Jupyter in the repository root or notebooks/ |
| A long run needs too much memory | Novo at k = 365 needs about 12 GB; all aggregated cases need less than 3 GB |
MIT, see LICENSE.
Please cite the article (see also CITATION.cff):
Borba, B., Islam, M. E., & Niet, T. Storage in Long-Term Energy Planning Models with Temporal Aggregation: Best Practices and Algorithms. Environmental Research Communications (2026). [under review]
Code, inputs and all results of the study are archived on Zenodo: 10.5281/zenodo.23598757 (v1.2.0).
Developed by Bruno Borba, Md Eliasinul Islam and Taco Niet, Delta E+ Research Lab, Simon Fraser University, BC, Canada.
Note:
- The results and figures generated by this workflow are intended for research and reproducibility purposes.
- Ensure that the
results/directory exists and is writable before running the workflow. - The memory requirements mentioned in the troubleshooting section are approximate and may vary depending on the system and solver configuration.
- The workflow assumes that all necessary dependencies are installed and accessible in the current environment.
- Methods are authorized and validated as described in the paper. Claude agent is used for code standardization, docstring hygene, workflow automation, and formatting within this repository.
