Skip to content

Build transport demand

Build transport charging demand per clustered model region. Simplified version that only generates charging demand time series.

build_transport_demand(traffic_profile, nodes, sector_load_data, snapshots)

Build transport charging demand time series (MWh) from annual totals.

Parameters:

Name Type Description Default
traffic_profile Series

Normalised weighting of weekday hours (count profile) defining the intraday pattern. The series length must match the expected weekly resolution of generate_periodic_profiles. Values do not need to be normalised; the function will normalise internally.

required
nodes list

Provinces / nodes for which the time series are generated.

required
sector_load_data DataFrame

Annual transport load by province (index) and year (columns) as provided by REMIND processing.

required
snapshots DatetimeIndex

Target temporal resolution for the output.

required

Returns:

Type Description
DataFrame

pd.DataFrame: Provincial transport demand time series (MWh) whose annual

DataFrame

totals per node match sector_load_data for the target year.

Source code in workflow/scripts/build_transport_demand.py
def build_transport_demand(
    traffic_profile: pd.Series,
    nodes: list,
    sector_load_data: pd.DataFrame,
    snapshots: pd.DatetimeIndex,
) -> pd.DataFrame:
    """Build transport charging demand time series (MWh) from annual totals.

    Args:
        traffic_profile (pd.Series): Normalised weighting of weekday hours (``count``
            profile) defining the intraday pattern. The series length must match the
            expected weekly resolution of ``generate_periodic_profiles``. Values do
            not need to be normalised; the function will normalise internally.
        nodes (list): Provinces / nodes for which the time series are generated.
        sector_load_data (pd.DataFrame): Annual transport load by province (index)
            and year (columns) as provided by REMIND processing.
        snapshots (pd.DatetimeIndex): Target temporal resolution for the output.

    Returns:
        pd.DataFrame: Provincial transport demand time series (MWh) whose annual
        totals per node match ``sector_load_data`` for the target year.
    """

    if traffic_profile.empty:
        raise ValueError("traffic_profile must contain at least one value")

    traffic = traffic_profile / traffic_profile.sum()

    base_shape = generate_periodic_profiles(
        dt_index=snapshots,
        col_tzs=pd.Series(index=nodes, data=[TIMEZONE] * len(nodes)),
        weekly_profile=traffic.values,
    )

    # 2. Get annual totals
    target_year = str(snapshots[0].year)
    nodal_totals = sector_load_data[target_year]

    # 3. Build time series per province
    result = pd.DataFrame(index=snapshots, columns=nodes, dtype=float)
    for node in nodes:
        total = nodal_totals[node]
        shape = base_shape[node] / base_shape[node].sum()  # normalize
        result[node] = shape * total

    return result