dynamic_characterization

June 23, 2026 · View on GitHub

Read the Docs PyPI - Version Conda Version Conda - License Binder

This is a package for the dynamic characterization of Life Cycle Inventories with temporal information. It includes a collection of dynamic characterization functions for various environmental flows. We also provide a simple interface to apply these functions to an existing dynamic LCI (coming from, e.g., bw_temporalis or bw_timex).

The following dynamic characterization functions are currently included:

moduleimpact categorymetriccovered emissionssource
ipcc_ar6climate changeradiative forcing, GWP247 GHGsradiative efficiencies & lifetimes from IPCC AR6 Ch.7
prospectiveclimate changeprospective radiative forcing, pGWP, pGTPCO2, CH4, N2OWatanabe et al. (2026)
original_temporalis_functionsclimate changeradiative forcingCO2, CH4bw_temporalis

What do dynamic characterization functions do?

The functions are meant to work with a common input format of the dynamic inventory, collected in a pandas DataFrame that looks like this:

dateamountflowactivity
1013312
3122142

Each function takes one row of this dynamic inventory dataframe (i.e., one emission at one point in time) and transform it according to some metric. The output generated by applying a very simple function to both rows of the input dataframe could look like:

dateamountflowactivity
1013312
1023112
1033112
3122142
3132042
3141942

How do I use this package?

The workflow could look like this:

import pandas as pd
from dynamic_characterization import characterize
from dynamic_characterization.ipcc_ar6 import characterize_co2, characterize_ch4

# defining a dummy dynamic inventory that you somehow got
dynamic_inventory_df = pd.DataFrame(
        data={
            "date": pd.Series(
                data=[
                    "15-12-2020",
                    "20-12-2020",
                    "25-05-2022",
                ],
                dtype="datetime64[s]",
            ),
            "amount": pd.Series(data=[10.0, 20.0, 50.0], dtype="float64"),
            "flow": pd.Series(data=[1, 1, 3], dtype="int"),
            "activity": pd.Series(data=[2, 2, 4], dtype="int"),
        }
    )

df_characterized = characterize(
        dynamic_inventory_df,
        metric="radiative_forcing", # could also be GWP
        characterization_functions={
            1: characterize_co2,
            3: characterize_ch4,
        },
        time_horizon=2,
    )

If you use this package with Brightway, stuff can get even easier: if you have an impact assessment method at hand, you can pass it to the characterize function via the base_lcia_method attribute and we'll try to automatically match the flows that are characterized in that method to the flows we have characterization functions for. This matching is based on the names or the CAS numbers, depending on the flow. The function call could look like this then:

method = ('EF v3.1', 'climate change', 'global warming potential (GWP100)')

df_characterized = characterize(
        dynamic_inventory_df,
        metric="radiative_forcing", # could also be GWP
        base_lcia_method=method,
        time_horizon=2,

)

Prospective Characterization

The prospective module provides prospective characterization factors based on Watanabe et al. (2026). These factors account for how radiative efficiencies change over time under different climate scenarios, making them more appropriate for future-oriented LCA studies. This particularly enables more temporally consistent results when used alongside IAM-based prospective LCI adaptations, e.g., in time-explicit LCA.

For an interactive demo of pLCIA, open the example notebook on Binder

NOTE: Currently, only the IAM scenarios and pathways from Watanabe et al. (2026) are supported. This will change soon.

Setting up a scenario

Before using prospective metrics, you must set a background scenario:

import dynamic_characterization.prospective as prospective

# Set the scenario (required before using pGWP or pGTP)
prospective.set_scenario(iam="IMAGE", ssp="SSP1", rcp="2.6")

Available scenario combinations:

  • IAMs: IMAGE, AIM, GCAM4, MESSAGE, REMIND
  • SSPs: SSP1-SSP5 (availability depends on IAM)
  • RCPs: 2.6, 4.5, 6.0, 8.5

Using prospective metrics

from dynamic_characterization import characterize

# Using prospective radiative forcing (W/m² time series)
df_prf = characterize(
    dynamic_inventory_df,
    metric="prospective_radiative_forcing",
    base_lcia_method=method,
    time_horizon=100,
)

# Using prospective GWP (kg CO2eq)
df_pgwp = characterize(
    dynamic_inventory_df,
    metric="pGWP",
    base_lcia_method=method,
    time_horizon=100,
)

# Using prospective GTP (kg CO2eq)
df_pgtp = characterize(
    dynamic_inventory_df,
    metric="pGTP",
    base_lcia_method=method,
    time_horizon=100,
)

Fallback to IPCC for unsupported GHGs

The Watanabe module only provides prospective characterization for CO2, CH4, and N2O. By default, other GHGs (like CO and the 244 other GHGs from IPCC AR6) fall back to standard IPCC characterization:

# Default: use IPCC for GHGs not in Watanabe (fallback_to_ipcc=True)
df_prf = characterize(
    dynamic_inventory_df,
    metric="prospective_radiative_forcing",
    base_lcia_method=method,
    fallback_to_ipcc=True,  # default
)

# Strict mode: only characterize CO2, CH4, N2O
df_prf_strict = characterize(
    dynamic_inventory_df,
    metric="prospective_radiative_forcing",
    base_lcia_method=method,
    fallback_to_ipcc=False,  # skip GHGs not in Watanabe
)

Time-varying radiative efficiency

By default, the radiative efficiency (RE) is fixed at the emission year (IPCC standard approach). You can enable time-varying RE for more physical accuracy:

df_pgwp = characterize(
    dynamic_inventory_df,
    metric="pGWP",
    base_lcia_method=method,
    time_varying_re=True,  # RE evolves as gas decays
)

FAIR climate model (optional)

The fair module provides FAIR-based dynamic characterization using the FaIR simple climate model. Unlike the per-species impulse-response approach used by the other metrics, FAIR runs a full climate-model ensemble and returns probabilistic results across hundreds of calibrated configurations.

Install the optional dependency with:

$ pip install dynamic_characterization[fair]

Calibration data: FAIR's uncertainty comes from the calibrated, constrained AR6 ensemble (calibration1.4.1, 841 members), which is not bundled in the fair package. If the calibration CSVs are not found in your fair install, they are downloaded from the FaIR example data and cached locally (via pooch) on the first FAIR run, so the first call needs network access. FAIR is simulated from 1750 to spin up the climate state; your inventory is applied as a perturbation on top of the chosen SSP marker background.

FAIR metrics

Two metrics are available:

  • fair_radiative_forcing — change in total radiative forcing (ΔRF, W/m²) attributable to the inventory emissions, per year.
  • fair_temperature — change in global mean surface temperature (ΔT, K) attributable to the inventory emissions, per year.

Both metrics return a long DataFrame with an extra quantile column compared to the standard output format:

dateamountflowactivityquantile
2020-01-013.2e-14122.5
2020-01-014.1e-141250.0
...............

Each row represents the characterization result for one (year, flow, activity, quantile) combination. The default quantiles are (2.5, 25, 50, 75, 97.5). The output is attributed per flow and per activity, allowing disaggregated analysis of the inventory. For FAIR metrics, time_horizon sets the analysis end as (last emission year + time_horizon) and defaults to the scenario end year (2100) when not given or None.

Setting up a FAIR scenario

FAIR metrics require a scenario to be set first (same set_scenario call as for prospective metrics):

import dynamic_characterization.prospective as prospective

# FAIR-native scenario (IAM-agnostic marker):
prospective.set_scenario(iam="FAIR", ssp="SSP2", rcp="4.5")

# Or a dual prospective+FAIR scenario (also supports pGWP etc.):
prospective.set_scenario(iam="IMAGE", ssp="SSP1", rcp="2.6")

FAIR-native scenarios use iam="FAIR". Dual scenarios (IAM-based) support both prospective and FAIR metrics.

If the current scenario does not support FAIR metrics (or if no scenario has been set), the metrics raise a clear ValueError.

Available FAIR-capable scenarios

Use available_scenarios("fair") to list FAIR-capable scenarios programmatically:

from dynamic_characterization.prospective import available_scenarios

# Scenarios that support fair_radiative_forcing / fair_temperature:
available_scenarios("fair")

# Scenarios that support pGWP / pGTP / prospective_radiative_forcing:
available_scenarios("prospective")

The following scenarios currently support FAIR metrics (12 total: 8 FAIR-native markers + 4 dual prospective+FAIR scenarios):

IAMSSPRCPNotes
FAIRSSP11.9FAIR-native
FAIRSSP12.6FAIR-native
FAIRSSP24.5FAIR-native
FAIRSSP37.0FAIR-native
FAIRSSP43.4FAIR-native
FAIRSSP46.0FAIR-native
FAIRSSP53.4-overFAIR-native
FAIRSSP58.5FAIR-native
GCAM4SSP46.0dual (prospective + FAIR)
IMAGESSP12.6dual (prospective + FAIR)
MESSAGESSP24.5dual (prospective + FAIR)
REMINDSSP58.5dual (prospective + FAIR)

Running FAIR characterization

from dynamic_characterization import characterize
from dynamic_characterization.prospective import set_scenario, available_scenarios

# Pick a FAIR-capable scenario (see available_scenarios("fair"))
set_scenario("FAIR", "SSP2", "4.5")

# Returns a long DataFrame with columns
# ["date", "amount", "flow", "activity", "quantile"]
df_rf = characterize(
    dynamic_inventory_df,
    metric="fair_radiative_forcing",   # ΔRF in W/m²
    base_lcia_method=("EF v3.1", "climate change", "global warming potential (GWP100)"),
    time_horizon=100,   # analysis end = last emission year + time_horizon; None -> 2100
)

# Temperature anomaly (ΔT in K):
df_temp = characterize(
    dynamic_inventory_df,
    metric="fair_temperature",
    base_lcia_method=("EF v3.1", "climate change", "global warming potential (GWP100)"),
)

What do dynamic characterization functions look like?

Here's an example of what such a function could look like:

def example_characterization_function(series: namedtuple, period: int = 2) -> namedtuple:
    date_beginning: np.datetime64 = series.date.to_numpy()
    dates_characterized: np.ndarray = date_beginning + np.arange(
        start=0, stop=period, dtype="timedelta64[D]"
    ).astype("timedelta64[s]")

    amount_beginning: float = series.amount

    # in reality, this would probably something more complex like an exponential decay function
    amount_characterized: np.ndarray = amount_beginning - np.arange(
        start=0, stop=period, dtype="int"
    )

    return namedtuple("CharacterizedRow", ["date", "amount", "flow", "activity"])(
        date=np.array(dates_characterized, dtype="datetime64[s]"),
        amount=amount_characterized,
        flow=series.flow,
        activity=series.activity,
    )

Installation

You can install dynamic_characterization via [pip] from [PyPI]:

$ pip install dynamic_characterization

Alternatively, you can also use conda:

$ conda install -c diepers dynamic_characterization

Contributing

Contributions are very welcome. To learn more, see the Contributor Guide.

License

Distributed under the terms of the BSD 3 Clause license, dynamic_characterization is free and open source software.

Issues

If you encounter any problems, please file an issue along with a detailed description.

Support

If you have any questions or need help, do not hesitate to contact Timo Diepers (timo.diepers@ltt.rwth-aachen.de)