stride Data API

This page displays the data API methods available in stride for computing energy demand calculations.

Data API

class stride.api.APIClient(project: Project | None = None)

Singleton API client for querying STRIDE electricity load and demand data.

This class provides a thread-safe singleton interface to a DuckDB database containing electricity consumption, demand, and related metrics data. It ensures only one database connection exists throughout the application lifecycle while providing convenient methods for common data queries used in dashboard visualizations.

The client supports various data retrieval patterns including: - Annual consumption and peak demand metrics with optional breakdowns - Load duration curves for capacity planning analysis - Time series data with flexible resampling and grouping - Seasonal load pattern analysis - Secondary metrics integration (economic, demographic, weather data)

db

The underlying DuckDB database connection

Type:

duckdb.DuckDBPyConnection

project_config

The project configuration if provided

Type:

ProjectConfig, optional

energy_proj_table

Name of the energy projection table

Type:

str

project_country

Country identifier for the project

Type:

str

Examples

>>> # Initialize with database path
>>> client = APIClient("/path/to/database.db")
>>>
>>> # Initialize with ProjectConfig
>>> client = APIClient(project_config=config, db_connection=conn)
>>>
>>> # Query annual consumption by sector
>>> consumption = client.get_annual_electricity_consumption(
...     scenarios=["baseline", "high_growth"], group_by="Sector"
... )
property years: list[int]

Get cached list of valid model years.

Returns:

A list of valid model years from the database.

Return type:

list[int]

property scenarios: list[str]

Get cached list of valid scenarios.

Returns:

A list of valid scenarios from the database.

Return type:

list[str]

refresh_metadata() None

Refresh cached years and scenarios by re-reading from database. Call this if the database content has changed.

get_unique_sectors() list[str]

Get unique sectors from the energy projection table.

Returns:

Sorted list of unique sectors from the database

Return type:

list[str]

get_unique_end_uses() list[str]

Get unique end uses (metrics) from the energy projection table.

Returns:

Sorted list of unique end uses/metrics from the database

Return type:

list[str]

get_years() list[int]
Returns:

A list of valid model years. Used for validating inputs into api query functions.

Return type:

list[int]

Examples

>>> client = APIClient(path_or_conn)
>>> years = client.get_years()
>>> print(years)
[2025, 2030, 2035, 2040, 2045, 2050]
get_annual_electricity_consumption(scenarios: list[str] | None = None, years: list[int] | None = None, group_by: Literal['End Use', 'Sector'] | None = None) DataFrame

Queries the Total Annual Consumption for each scenario.

Parameters:
  • years (list[int], optional) – Valid projection years for the opened project. If None, uses all projection years.

  • group_by (ConsumptionBreakdown, optional) – Optionally breakdown by Sector and end Use. If None, uses total.

  • scenarios (list[str], optional) – Optional list of scenarios to filter by. If None, uses all scenarios available.

Returns:

DataFrame with consumption values in tall format.

Columns: - scenario: str, scenario name - year: int, projection year - sector/end_use: str, breakdown category (if group_by specified) - value: float, consumption value in MWh

Return type:

pd.DataFrame

Examples

>>> client = APIClient(path_or_conn)
>>> # Get total consumption for all scenarios and years
>>> df = client.get_annual_electricity_consumption()
|-------------|------|-------------|-------|
| scenario    | year | value |
|-------------|------|-------|
| baseline    | 2025 | 5500  |
| baseline    | 2030 | 5900  |
| high_growth | 2025 | 6000  |
| high_growth | 2030 | 6500  |
|-------------|------|-------------|-------|
>>> # Get consumption by sector for specific scenarios
>>> df = client.get_annual_electricity_consumption(
...     scenarios=["baseline", "high_growth"], group_by="Sector"
... )
|-------------|------|-------------|-------|
| scenario    | year | sector      | value |
|-------------|------|-------------|-------|
| baseline    | 2025 | Commercial  | 1500  |
| baseline    | 2025 | Industrial  | 2200  |
| baseline    | 2025 | Residential | 1800  |
| baseline    | 2030 | Commercial  | 1650  |
| high_growth | 2025 | Commercial  | 1600  |
|-------------|------|-------------|-------|
get_annual_peak_demand(scenarios: list[str] | None = None, years: list[int] | None = None, group_by: Literal['End Use', 'Sector'] | None = None) DataFrame

Queries the peak annual consumption for each scenario. If group_by is specified, uses the peak timestamp to look up corresponding End Use or Sector values.

Parameters:
  • years (list[int], optional) – Valid projection years for the opened project. If None, uses all projection years.

  • group_by (ConsumptionBreakdown, optional) – Optionally breakdown by Sector and end Use. If None, uses total.

  • scenarios (list[str], optional) – Optional list of scenarios to filter by. If None, uses all scenarios available.

Returns:

DataFrame with peak demand values in tall format.

Columns: - scenario: str, scenario name - year: int, projection year - sector/end_use: str, breakdown category (if group_by specified) - value: float, peak demand value in MW

Return type:

pd.DataFrame

Examples

>>> client = APIClient(path_or_conn)
>>> # Get peak demand for all scenarios and years (no breakdown)
>>> df = client.get_annual_peak_demand()
|-------------|------|-------|
| scenario    | year | value |
|-------------|------|-------|
| baseline    | 2025 | 5500  |
| baseline    | 2030 | 5900  |
| high_growth | 2025 | 6000  |
| high_growth | 2030 | 6500  |
|-------------|------|-------|
>>> # Get peak demand by sector for specific scenarios
>>> df = client.get_annual_peak_demand(
...     scenarios=["baseline", "high_growth"], group_by="Sector"
... )
|-------------|------|-------------|-------|
| scenario    | year | sector      | value |
|-------------|------|-------------|-------|
| baseline    | 2025 | Commercial  | 1500  |
| baseline    | 2025 | Industrial  | 2200  |
| baseline    | 2025 | Residential | 1800  |
| baseline    | 2030 | Commercial  | 1650  |
| high_growth | 2025 | Commercial  | 1600  |
|-------------|------|-------------|-------|
get_secondary_metric(scenario: str, metric: Literal['GDP', 'GDP Per Capita', 'Human Development Index', 'Population'], years: list[int] | None = None) DataFrame

Queries the database for the secondary metric to overlay against a particular plot on the secondary axis

!!!Must be able to handle multiple overrides of a particular metric to differentiate between scenarios!!!

Parameters:
  • scenario (str) – A valid scenario for the project.

  • metric (SecondaryMetric) – The secondary metric to query.

  • years (list[int], optional) – A list of valid model years to filter by. Uses all model years if None specified.

Returns:

DataFrame with secondary metric values.

Columns: - year: int, model year - value: float, metric value for the specified scenario and metric type

Return type:

pd.DataFrame

Examples

>>> client = APIClient(path_or_conn)
>>> df = client.get_secondary_metric("baseline", "GDP", [2025, 2030, 2035])
|------|-------|
| year | value |
|------|-------|
| 2025 | 1250.5|
| 2030 | 1380.2|
| 2035 | 1520.8|
|------|-------|
get_load_duration_curve(years: int | list[int] | None = None, scenarios: list[str] | None = None) DataFrame

Gets the load duration curve for each scenario or year

Parameters:
  • years (int | list[int], optional) – A valid year or list of years for the given project. If None, uses first year.

  • scenarios (list[str], optional) – List of scenarios to filter by. If None, uses all scenarios.

Returns:

DataFrame with load duration curve data.

Columns:
  • {scenario_name} or {year}: float, demand values sorted from highest to lowest for each scenario (if multiple scenarios) or year (if multiple years)

Index: row number (0 to 8759 for hourly data)

Return type:

pd.DataFrame

Raises:

ValueError – If both years and scenarios are lists with more than one item

Examples

>>> client = APIClient(path_or_conn)
>>> # Single year, multiple scenarios
>>> df = client.get_load_duration_curve(2030, ["baseline", "high_growth"])
>>> # Multiple years, single scenario
>>> df = client.get_load_duration_curve([2025, 2030], ["baseline"])
>>> # Single year, single scenario
>>> df = client.get_load_duration_curve(2030, ["baseline"])
get_scenario_summary(scenario: str, year: int) dict[str, float]
Parameters:
  • scenario (str) – A valid scenario from the project.

  • year (int) – The projection year to get the summary.

Returns:

Dictionary of KPI metrics with metric names as keys and values as floats.

Keys: - TOTAL_CONSUMPTION: float, total electricity consumption (MWh) - PERCENT_GROWTH: float, percentage growth from base year - PEAK_DEMAND: float, peak demand (MW) - Additional KPIs to be defined

Return type:

dict[str, float]

Examples

>>> client = APIClient(path_or_conn)
>>> summary = client.get_scenario_summary("baseline", 2030)
>>> print(summary)
{
    'TOTAL_CONSUMPTION': 45.2,
    'PERCENT_GROWTH': 12.5,
    'PEAK_DEMAND': 5500.0
}
get_weather_metric(scenario: str, year: int, wvar: Literal['BAIT', 'HDD', 'CDD', 'Temperature', 'Solar_Radiation', 'Wind_Speed', 'Dew_Point', 'Humidity'], resample: Literal['Hourly', 'Daily Mean', 'Weekly Mean'], timegroup: Literal['Seasonal', 'Seasonal and Weekday/Weekend', 'Weekday/Weekend'] | None = None) DataFrame

Gets the weather time series data to use as a secondary axis. Optionally Resample to Daily or weekly mean

Parameters:
  • scenario (str) – The scenario specific weather source data

  • year (int) – The valid model year to choose for the weather metric

  • wvar (WeatherVar) – The weather variable to choose (currently only “BAIT” is supported)

  • resample (ResampleOptions, optional) – Resampling option for the data

  • timegroup (TimeGroup, optional) – Time grouping option

Returns:

Pandas DataFrame with weather values

Columns:
  • datetime: Datetime64, datetime or time period depending on resample option

  • value: float, BAIT (Balance Point Adjusted Integrated Temperature) values

Return type:

pd.DataFrame

Examples

>>> client = APIClient(path_or_conn)
>>> weather = client.get_weather_metric("baseline", 2030, "BAIT", "Daily Mean")
>>> print(weather.head())
|------------|-------|
|  datetime  | value |
|------------|-------|
| 2030-01-01 |  5.2  |
| 2030-01-02 |  6.1  |
| 2030-01-03 |  4.8  |
| 2030-01-04 |  7.3  |
| 2030-01-05 |  8.9  |
|------------|-------|
get_time_series_comparison(scenario: str, years: int | list[int], group_by: Literal['End Use', 'Sector'] | None = None, resample: Literal['Hourly', 'Daily Mean', 'Weekly Mean'] = 'Daily Mean') DataFrame

User selects 1 or more than model years. Returns tall format data with time period information.

Parameters:
  • scenario (str) – A valid scenario for the project.

  • years (Union[int, list[int]]) – 1 or 2 model years to view on the same chart.

  • group_by (ConsumptionBreakdown) – The load broken down by sector or end use.

  • resample (ResampleOptions, optional) – Resampling option for the data. Use None for raw hourly data.

Returns:

DataFrame with electricity consumption time series data in tall format.

Columns: - scenario: str, scenario name - year: int, projection year - time_period: int, time period (hour of year for raw data, day/week for resampled) - sector/end_use: str, breakdown category (if group_by specified) - value: float, consumption value

Return type:

pd.DataFrame

Examples

>>> client = APIClient(path_or_conn)
>>> # Raw hourly data with group_by
>>> df = client.get_time_series_comparison(
...     "baseline", [2025, 2030], "Sector", resample=None
... )
|----------|------|-------------|-------------|--------|
| scenario | year | time_period | sector      | value  |
|----------|------|-------------|-------------|--------|
| baseline | 2025 | 1           | Commercial  | 125.5  |
| baseline | 2025 | 1           | Industrial  | 210.3  |
| baseline | 2025 | 1           | Residential | 180.7  |
| baseline | 2025 | 2           | Commercial  | 124.8  |
|----------|------|-------------|-------------|--------|
>>> # Resampled data with group_by specified
>>> df = client.get_time_series_comparison("baseline", [2025, 2030], "Sector")
|----------|------|-------------|-------------|--------|
| scenario | year | time_period | sector      | value  |
|----------|------|-------------|-------------|--------|
| baseline | 2025 | 1           | Commercial  | 1250.5 |
| baseline | 2025 | 1           | Industrial  | 2100.3 |
| baseline | 2025 | 1           | Residential | 1800.7 |
| baseline | 2025 | 2           | Commercial  | 1245.8 |
| baseline | 2030 | 1           | Commercial  | 1380.2 |
|----------|------|-------------|-------------|--------|
>>> # Raw hourly data without group_by
>>> df = client.get_time_series_comparison("baseline", [2025, 2030], resample=None)
|----------|------|-------------|--------|
| scenario | year | time_period | value  |
|----------|------|-------------|--------|
| baseline | 2025 | 1           | 5150.5 |
| baseline | 2025 | 2           | 5136.2 |
| baseline | 2030 | 1           | 5675.4 |
| baseline | 2030 | 2           | 5666.5 |
|----------|------|-------------|--------|
get_seasonal_load_lines(scenario: str, years: int | list[int] | None = None, group_by: Literal['Seasonal', 'Seasonal and Weekday/Weekend', 'Weekday/Weekend'] = 'Seasonal', agg: Literal['Average Day', 'Peak Day', 'Minimum Day', 'Median Day'] = 'Average Day') DataFrame
Parameters:
  • scenario (str) – A valid scenario within the project.

  • group_by (TimeGroup) – Seasonal, Weekday/Weekend, or Both.

  • agg (TimeGroupAgg) – How to aggregate each hour of the day.

  • years (int | list[int]] | None) – A single or list of valid model years.

Returns:

DataFrame with seasonal load line data in tall format.

Columns: - scenario: str, scenario name - year: int, projection year - season: str, season name (Winter, Spring, Summer, Fall) - if group_by includes “Seasonal” - day_type: str, day type (Weekday, Weekend) - if group_by includes “Weekday/Weekend” - hour_of_day: int, hour of day (0-23) - value: float, aggregated load value

Return type:

pd.DataFrame

Examples

>>> client = APIClient(path_or_conn)
>>> # Seasonal grouping only
>>> df = client.get_seasonal_load_lines(
...     "baseline", [2025, 2030], "Seasonal", "Average Day"
... )
|----------|------|--------|-------------|--------|
| scenario | year | season | hour_of_day | value  |
|----------|------|--------|-------------|--------|
| baseline | 2025 | Winter | 0           | 3200.5 |
| baseline | 2025 | Winter | 1           | 3100.2 |
| baseline | 2025 | Winter | 2           | 3050.8 |
| baseline | 2025 | Spring | 0           | 2800.3 |
| baseline | 2030 | Winter | 0           | 3450.2 |
|----------|------|--------|-------------|--------|
>>> # Both seasonal and weekday/weekend grouping
>>> df = client.get_seasonal_load_lines(
...     "baseline", [2025], "Seasonal and Weekday/Weekend", "Average Day"
... )
|----------|------|--------|----------|-------------|--------|
| scenario | year | season | day_type | hour_of_day | value  |
|----------|------|--------|----------|-------------|--------|
| baseline | 2025 | Winter | Weekday  | 0           | 3400.5 |
| baseline | 2025 | Winter | Weekday  | 1           | 3350.2 |
| baseline | 2025 | Winter | Weekend  | 0           | 3000.3 |
| baseline | 2025 | Spring | Weekday  | 0           | 2900.7 |
|----------|------|--------|----------|-------------|--------|
get_seasonal_load_area(scenario: str, year: int, group_by: Literal['Seasonal', 'Seasonal and Weekday/Weekend', 'Weekday/Weekend'] = 'Seasonal', agg: Literal['Average Day', 'Peak Day', 'Minimum Day', 'Median Day'] = 'Average Day', breakdown: Literal['End Use', 'Sector'] | None = None) DataFrame

Get seasonal load area data for a single year with optional breakdown by sector/end_use.

Parameters:
  • scenario (str) – A valid scenario within the project.

  • year (int) – A single valid model year.

  • group_by (TimeGroup) – Seasonal, Weekday/Weekend, or Both.

  • agg (TimeGroupAgg) – How to aggregate each hour of the day.

  • breakdown (ConsumptionBreakdown, optional) – Optional breakdown by Sector or End Use.

Returns:

DataFrame with seasonal load area data in tall format.

Columns: - scenario: str, scenario name - year: int, projection year - season: str, season name (Winter, Spring, Summer, Fall) - if group_by includes “Seasonal” - day_type: str, day type (Weekday, Weekend) - if group_by includes “Weekday/Weekend” - hour_of_day: int, hour of day (0-23) - sector/end_use: str, breakdown category (if breakdown specified) - value: float, aggregated load value

Return type:

pd.DataFrame

Examples

>>> client = APIClient(path_or_conn)
>>> df = client.get_seasonal_load_area("baseline", 2030)
|-------------|------|--------|-------------|-------|
| scenario    | year | season | hour_of_day | value |
|-------------|------|--------|-------------|-------|
| baseline    | 2030 | Winter | 0           | 3400.5|
| baseline    | 2030 | Winter | 1           | 3350.2|
| baseline    | 2030 | Spring | 0           | 2900.7|
| baseline    | 2030 | Spring | 1           | 2850.3|
|-------------|------|--------|-------------|-------|
>>> df = client.get_seasonal_load_area("baseline", 2030, breakdown="Sector")
|-------------|------|--------|-------------|-------------|-------|
| scenario    | year | season | hour_of_day | sector      | value |
|-------------|------|--------|-------------|-------------|-------|
| baseline    | 2030 | Winter | 0           | Commercial  | 1200.5|
| baseline    | 2030 | Winter | 0           | Industrial  | 1500.2|
| baseline    | 2030 | Winter | 0           | Residential | 1700.8|
| baseline    | 2030 | Spring | 0           | Commercial  | 1300.7|
| baseline    | 2030 | Spring | 0           | Industrial  | 1600.3|
|-------------|------|--------|-------------|-------------|-------|