Skip to main content

Storage Deployment Optimization Model (SDOM)

Tests codecov Documentation DOI PyPI Downloads

SDOM (Storage Deployment Optimization Model) is an open-source, high-resolution grid capacity-expansion framework developed by the National Lab of the Rockies (NLR). It’s purpose-built to optimize the deployment and operation of energy storage technologies, leveraging hourly temporal resolution and granular spatial representation of Variable Renewable Energy (VRE) sources such as solar and wind.

SDOM estimates technology portfolios required to meet a specified carbon-free generation mix target by:

  • 📆 Evaluating long-duration and seasonal storage technologies
  • 🌦 Analyzing complementarity and synergies among diverse VRE resources and load profiles
  • 📉 Assessing curtailment and operational strategies under various grid scenarios

Table of contents

How SDOM Works?

🔍 At its core, SDOM models the gap between electricity demand and fixed generation:

Inputs include time series data for:

  • Load profiles
  • Fixed generation (e.g., hydropower, nuclear, biomass, and geothermal). Hydropower can be configured as a fixed profile or operated flexibly with hydro-budget formulations.
  • Optional grid-import and grid-export capacity and price data

Outputs include:

  • The optimal technology portfolio capacity (PV solar, wind, storage types, thermal capacity) to reliably meet demand.
  • Dispatch profiles for each technology, highlighting how resources are operated hour-by-hour.
  • Insights on operational metrics like VRE curtailment, storage cycling, and thermal usage.

An illustrative figure below shows the flow from inputs to optimization results, enabling exploration of storage needs under varying renewable integration levels.

Illustration about how SDOM works

Key Features

⚙️

  • Accurate Representation of Storage Technologies Diversity: SDOM is strongly focused in providing a framework able to represent different storage technologies by:

    • Representation of short-, long-duration, and seasonal storage technologies,
    • Charging/discharging capacity decoupling,
    • Optimization of both energy and power capacity,
    • Full temporal chronology.
  • Temporal Resolution: Hourly time steps over a full-year, 8,760-hour default horizon enable precise modeling of storage dynamics and VRE variability. The n_hours argument can select a shorter horizon for testing or sensitivity analysis.

  • Spatial Resolution: Fine-grained representation of VRE sources (e.g., solar, wind) captures geographic diversity and enhances system fidelity.

  • Copper Plate Modeling: SDOM Model neglects transmission constraints to keep the model tractable from the computational standpoint. Future SDOM releases should include inter-regional transmission constraints.

  • Fixed Generation Profiles: Nuclear, hydropower, and other fixed-generation resources (e.g., biomass and geothermal) are represented with time-series inputs. Hydropower is configurable as run-of-river or as flexible daily- and monthly-budget operations through the hydro-budget formulations.

  • System Optimization Objective: Minimizes total system cost, including capital, fixed/variable O&M, and fuel costs. Enabled import/export formulations can add grid-exchange energy costs; the demand-charge formulation adds applicable demand charges. The model satisfies user-defined carbon-free or renewable energy targets.

  • Modeling approach: Formulated as a Mixed-Integer Linear Programming (MILP) model to allow rigorous optimization of investment and capacity decisions.

  • Parametric & Sensitivity Analysis: Built-in ParametricStudy API to run multi-dimensional parameter sweeps in parallel — sweep GenMix_Target, technologies CAPEX costs, load growth factors, and more.

  • Platforms:

    • SDOM was originally developed in GAMS.

    • To provide a full open-source solution, SDOM is also available as a Python package. It supports Python >=3.11,<3.14.

  • Solver Compatibility: Currently the SDOM python version has been tested using the open-source CBC solver, the HiGHS open-source solver through the highspy module, and FICO Xpress through the xpress Python interface. In this repo the Windows executable for CBC is provided. You will need to provide the path of the CBC solver to run SDOM as illustrated in our simple script example. Xpress is commercial and requires a valid license.

Optimization Scope

📉 SDOM performs cost minimization across a 1-year operation window using a copper plate assumption—i.e., no internal transmission constraints—making it computationally efficient while capturing major cost drivers. Conventional generators are used as balancing resources, and storage technologies serve to meet carbon or renewable penetration goals.

Notes on Model Expansion

While SDOM currently supports a 1-year horizon, multiyear analyses could provide deeper insights into how interannual variability affects storage needs. Chronological, simulation-based approaches are better suited for this but present significant computational challenges—especially at hourly resolution. Extending SDOM to support multiyear optimization is left as future work.

Sample Results

The figures below show example outputs from a 3-dimensional parametric study (3 carbon-free targets × 2 storage CAPEX factors × 2 load scaling factors = 12 cases) run against the Data/no_exchange_run_of_river/ dataset included in this repository.

Installed capacity by technology across all cases

Capacity comparison

Total generation by technology across all cases

Generation comparison

VRE curtailment across all cases

Curtailment — absolute

Per-case capacity and generation mix (GenMix_Target = 1.0, base CAPEX, base load)

Capacity and generation donuts

Hourly dispatch heatmap — net load

Heatmap — net load

GETTING STARTED WITH SDOM

System Setup and Prerequisites

  • a. You'll need to install python
  • b. Also, You'll need an IDE (Integrated Development Environment), we recommend to install MS VS code
  • d. We alse recommend to install extensions such as:
    • Python (required): Provides Python language support, debugging, environment selection, and IntelliSense in VS Code.
    • edit CSV: To edit and interact with input csv files for SDOM directly in vs code.
    • vscode-pdf: to read and see pdf files directly in vscode.

Install SDOM

It is recommended to load the packages in a virtual enviroment.

We recommend to use uv, a Python manager for virtual environments and packages.

  • a. Install uv following the instructions at uv on PyPI.

    Windows only — verify that python and uv are on your PATH before creating the virtual environment. In PowerShell (or cmd) run:

    where.exe python
    where.exe uv
    

    Each command should print a full path. If you see INFO: Could not find files for the given pattern(s)., the executable is not on your PATH — re-check the Python installer option Add python.exe to PATH, or reinstall uv and open a new terminal so PATH changes take effect.

    Fix it manually (no admin required) — add the missing folders to your User PATH:

    1. Locate the install folder(s). Common defaults are:

      • Python: %LOCALAPPDATA%\Programs\Python\Python3xx\ and %LOCALAPPDATA%\Programs\Python\Python3xx\Scripts\
      • uv: %USERPROFILE%\.local\bin\ (official installer) or the Scripts folder of the Python you used with pip install uv

      You can list installed Python versions with:

      Get-ChildItem "$env:LOCALAPPDATA\Programs\Python" -Directory
      
    2. Append the folder(s) to your User PATH (persists across sessions, no admin needed). Edit the $newPaths list to match what you found in step 1, then run:

      $newPaths = @(
          "$env:LOCALAPPDATA\Programs\Python\Python312",
          "$env:LOCALAPPDATA\Programs\Python\Python312\Scripts",
          "$env:USERPROFILE\.local\bin"
      )
      $userPath = [Environment]::GetEnvironmentVariable("Path", "User")
      $updated  = (@($userPath.TrimEnd(';')) + $newPaths) -join ';'
      [Environment]::SetEnvironmentVariable("Path", $updated, "User")
      
    3. Close and reopen your terminal (and VS Code) so the new PATH is picked up, then re-run where.exe python and where.exe uv to confirm both now resolve.

  • b. Create a new virtual environment named .venv:

    uv venv .venv
    
      This command creates a Python virtual environment in the `.venv` directory.
    
  • c. Activate your virtual environment and install the SDOM package:

    uv pip install sdom
    
  • d. Install the python module according to your solver. We'll use here HiGHS open-source solver

    uv pip install highspy
    

    For FICO Xpress, install SDOM with the optional Xpress dependency and make sure your Xpress license is configured:

    uv pip install "sdom[xpress]"
    
  • e. Verify your environment by listing installed packages:

    In your terminal or powershell run:

    uv pip list
    

    You should see output similar to:

      Package         Version
      --------------- -----------
      contourpy       1.3.3
      cycler          0.12.1
      fonttools       4.63.0
      highspy         1.14.0
      kiwisolver      1.5.0
      matplotlib      3.10.9
      numpy           2.4.6
      packaging       26.2
      pandas          2.3.3
      pillow          12.2.0
      pyomo           6.10.0
      pyparsing       3.3.2
      python-dateutil 2.9.0.post0
      pytz            2026.2
      sdom            0.2.3
      six             1.17.0
      tzdata          2026.2
    

SDOM Input Files

For detailed information about SDOM input files, please refer to the SDOM Input Documentation.

SDOM simple script example

System-first quick start

Install the Infrasys integration with sdom[infrasys] before running this example.

from sdom import get_default_solver_config_dict, run_solver
from sdom.infrasys_integration import add_results_to_system, plot_system_results
from sdom.infrasys_integration.make_system import load_system
from sdom.infrasys_integration.pyomo_builder import initialize_copperplate_model_from_system

system = load_system("Data/no_exchange_run_of_river", name="copperplate")
model = initialize_copperplate_model_from_system(system, n_hours=24).create_instance()
solver_config = get_default_solver_config_dict(solver_name="highs", executable_path="")
results = run_solver(model, solver_config, case_name="copperplate")

if results.is_optimal:
  add_results_to_system(system, results, run_id="copperplate-24h")
  plot_system_results(system, run_id="copperplate-24h", output_dir="results/copperplate")

The recommended copperplate and zonal examples are in the Infrasys System workflows guide. The legacy dict-based example remains available in the running and outputs guide.

PUBLICATIONS AND USE CASES OF SDOM

📄 For a comprehensive list of publications and use cases, please refer to the SDOM Publications Documentation.

CONTRIBUTING GUIDELINES

💻 Developers Guide

Metadata

Release files for sdom 0.2.9

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for sdom 0.2.9
File Size Uploaded Provenance
sdom-0.2.9.tar.gz 188.7 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for sdom 0.2.9
File Interpreter ABI Platform
sdom-0.2.9-py3-none-any.whl Python 3 none any Details

Total release size: 412.9 kB

Release files / sdom-0.2.9.tar.gz

Download URL sdom-0.2.9.tar.gz
Size 188.7 kB
Tags Source
SHA-256 checksum
How to use checksums
6c71d9ba990aa43a5d9a51426fb8d8abfe416eba38cd0da8fe3ef1d0bc400e1a
BLAKE2b-256 checksum
How to use checksums
11e991f91a223e5e413fa5e94aec6563cb095690a45dd8e1692d6bef07386d23
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.12

Release files / sdom-0.2.9-py3-none-any.whl

Download URL sdom-0.2.9-py3-none-any.whl
Size 224.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
aeed6c7141fdb4f474c60fc20d47971c5d86dd4648f6eb7d66122190816cac35
BLAKE2b-256 checksum
How to use checksums
6dc8576fc0595b5c81a0c042ecc38c0b2377c970bd9a73750e3f5ce4fb2586ab
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.12

Security for sdom 0.2.9

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page