Theory#

This section explains some of the theory behind time-explicit LCAs with bw_timex. In contrast to the Getting Started section, we explain a bit more of what`s going on in the background here. If this is still too vague for you, you can always check out our API reference.

Terminology#

LCA terminology can be confusing sometimes, also for experienced practitioners. Particularly, we found the terminology around temporal aspects of LCA confusing, where different terms have been used to describe the same temporal aspect or the same term has been used to describe different temporal aspects.

In an attempt for improved clarity, we use the term “time-explicit LCA”. Essentially, time-explicit LCA jointly considers “temporal distribution”, i.e., how processes, emissions and environmental responses are spread out over time, and “temporal evolution”, i.e., how processes, emissions and environmental responses change over time. Very broadly speaking, the former is frequently considered in dynamic LCA, while the latter is frequently considered in prospective LCA.

Below, you find a visualization of the time-explicit approach. Our decision tree might also help to understand the differences between the different types of LCA.

../_images/timeexplicit_lca_dark.svg../_images/timeexplicit_lca_light.svg

Data requirements#

For a time-explicit LCA, three inputs are required:

  1. a static product system model with

  2. temporal information using the attribute temporal_distribution on technosphere or biosphere exchanges in the product system model, and

  3. a set of time-specific background databases, which must have a reference in time.

Note

  • The foreground system must have exchanges linked to one of the time-specific background databases. These exchanges at the intersection between foreground and background databases will be relinked by bw_timex.

  • Temporal distributions can occur at technosphere and biosphere exchanges and can be given in various forms, see bw_temporalis, including absolute (e.g. 2024-03-18) or relative (e.g. 3 years before) types and can have different temporal resolution (down to seconds but later aggregation supports resolutions down to hours).

  • Temporal distributions are optional. If none are provided, no delay between producing and consuming process is assumed and the timing of the consuming process is adopted also for the producing process.

Temporal distributions and graph traversal#

To determine the timing of the exchanges within the production system, we add the temporal_distribution attribute to the respective exchanges, using the TemporalDistribution class from bw_temporalis. This class carries the information how the amount of the exchange is spread over time in two numpy arrays (dateand amount). So, it tells you what share of an exchange happens at what point in time. If two consecutive edges in the supply chain graph carry a TemporalDistribution, they are convolved, combining the two temporal profiles.

Example: Convolution

Let’s say we have two temporal distributions. The first dates 30% of some exchange two years into the future, and 70% of that exchange four years into the future:

import numpy as np
from bw_temporalis import TemporalDistribution

two_and_four_years_ahead = TemporalDistribution(
    date=np.array([2, 4], dtype="timedelta64[Y]"),
    amount=np.array([0.3, 0.7])
)

two_and_four_years_ahead.graph(resolution="Y")
../_images/td_two_and_four_years_ahead.svg

The other distribution spreads an amount over the following 4 months, with decreasing shares:

spread_over_four_months = TemporalDistribution(
    date=np.array([0, 1, 2, 3], dtype="timedelta64[M]"),
    amount=np.array([0.4, 0.3, 0.2, 0.1])
)

spread_over_four_months.graph(resolution="M")
../_images/td_spread_over_four_months.svg

Now, let’s see what happens when we convolute these temporal distributions:

convolved_distribution = two_and_four_years_ahead * spread_over_four_months

convolved_distribution.graph(resolution="M")
../_images/td_convolved.svg

Note how both the dates and the amounts of the output distribution get scaled.

To convolute all the temporal information from the supply chain graph, bw_timex uses the graph traversal from bw_temporalis. An in-depth description of how this works is available in the brightway-docs. The default is a best-first supply chain graph traversal, following the most impactful node in the graph based on the static pre-calculated LCIA score for a chosen impact category. A breadth-first traversal is an optional alternative. Several input arguments for the graph traversal, such as maximum calculation count or cut-off, can be passed to the TimexLCA instance.

By default, only the foreground system is traversed, but nodes to be skipped during traversal can be specified by a edge_filter_function. At each process, the graph traversal uses convolution to combine the temporal distributions of the process and the exchange it consumes into the resulting combined temporal distribution of the upstream producer of the exchange.

Temporal evolution in the foreground system#

bw_timex (>0.3.4) allows you to also implement time-dependent modifications to the foreground exchanges. This optional feature is useful if you want to simulate changes in the foreground system over time, such as technological learning, efficiency improvements or changing market structures. Such changes are not covered by the linking to time-specific background processes.

You can specify temporal evolution attributes on exchanges using either time-dependent scaling factors or absolute amounts. bw_timex, then, adjusts the base exchange amount based on the timing of the process, interpolating if needed between closest available scaling data. For scaling factors, it will multiply the base exchange amount with the time-specific scaling factor and for absolute amounts, it will directly set the value to the time-specific amount. These modified time-specific exchange amounts are then used throughout the rest of the timexLCA.

This temporal evolution of exchanges is optional, and if no temporal evolution is given, the exchange amounts remain constant over time.

Example: temporal evolution

Foreground exchange amounts can be modified with a dictionary of either time-dependent scaling factors or absolute amounts:

from datetime import datetime

exchange["temporal_evolution_factors"] = {
    datetime(2020, 1, 1): 1.0,   # 100% of base amount in 2020
    datetime(2030, 1, 1): 0.75,  # 75% of base amount in 2030
    datetime(2040, 1, 1): 0.6,   # 60% of base amount in 2040
}

exchange["temporal_evolution_amounts"] = {
    datetime(2020, 1, 1): 60,   # 60 MJ in 2020
    datetime(2030, 1, 1): 45,   # 45 MJ in 2030
    datetime(2040, 1, 1): 36,   # 36 MJ in 2040
}

Building the process timeline#

The graph traversal returns a timeline that lists the time of each technosphere exchange in the temporalized foreground system. Exchanges that flow from same producer to the same consumer within a certain time-window (temporal_grouping, default is ‘year’) are grouped together. This is done to avoid countless exchanges in the timeline, as the temporal distributions at the exchange level can have temporal resolutions down to seconds while one may not have a similar temporal resolution for the databases. We recommend aligning temporal_grouping to the temporal resolution of the available databases.

Example: Timeline

Let’s consider the following system: a process A consumes an exchange b from a process B. Both A and B emit CO2. The emission of CO2 from B decreases in the future. All exchanges occur at a certain point in time, relative to process A, which takes place “now” (2024). This example does not contain include temporal evolution of foregound exchanges.

flowchart LR subgraph background[" "] B_2020(Process B \n 2020):::bg B_2030(Process B \n 2030):::bg end subgraph foreground[" "] A(Process A):::fg end subgraph biosphere[" "] CO2(CO<sub>2</sub>):::b end B_2020-->|"amounts: [30%,50%,20%] * 3 kg\n dates:[-2,0,+4]" years|A A-.->|"amounts: [60%, 40%] * 5 kg\n dates: [0,+1]" years|CO2 B_2020-.->|"amounts: [100%] * <span style='color:#9c5ffd'><b>11 kg</b></span>\n dates:[0]" years|CO2 B_2030-.->|"amounts: [100%] * <span style='color:#9c5ffd'><b>7 kg</b></span>\n dates:[0]" years|CO2 classDef bg color:#222832, fill:#3fb1c5, stroke:none; classDef fg color:#222832, fill:#3fb1c5, stroke:none; classDef b color:#222832, fill:#9c5ffd, stroke:none; style foreground fill:none, stroke:none; style background fill:none, stroke:none; style biosphere fill:none, stroke:none;

The resulting timeline looks like this:

date_producer

producer_name

date_consumer

consumer_name

amount

temporal_market_shares

2022-01-01

B

2024-01-01

A

0.9

{‘background’: 0.8, ‘background_2030’: 0.2}

2024-01-01

B

2024-01-01

A

1.5

{‘background’: 0.6, ‘background_2030’: 0.4}

2024-01-01

A

2024-01-01

-1

1.0

None

2028-01-01

B

2024-01-01

A

0.6

{‘background’: 0.2, ‘background_2030’: 0.8}

Time mapping#

Based on the timing of the processes in the timeline, bw_timex matches the processes at the intersection between foreground and background to the best available, time-specific background databases. Available matching strategies are closest database or linear interpolation between two closest databases based on temporal proximity. The column temporal_market_shares in the timeline specifies how much of that temporalized exchange is taken from the respective background databases. As only exchanges between foreground and background are relinked, this field is None for exchanges within the foreground system, e.g., A to -1 (the functional unit). The new best-fitting background producer(s) are mapped on the same name, reference product and location as the old background producer.

Modifying the matrices#

bw_timex now modifies the technosphere and biosphere matrices using datapackages from bw_processing. In order to store reference to multiple time points in a single matrix, we add a new element (row-column pair) for each process at a specific time to the matrices.

Technosphere matrix modifications#

  1. For each temporalized process in the timeline, a new process copy, a “temporalized process” is created, which links to its new temporalized producer and consumer. The timing of the processes is stored in the activity_time_mapping, which maps the process ids to process timing.

  2. For those processes linking to the background databases, bw_timex creates “temporal markets”, a new row-column pair that relinks the exchanges to the new producing processes from the best-fitting background database(s). Temporal markets are similar to regional markets in LCI databases, that distribute a commodity between different regional providers, only here across different temporal providers.

Biosphere matrix modifications#

Depending on the user’s choice, two different biosphere matrices are created:

  1. The basic command TimexLCA.lci() defaults to creating the dynamic biosphere matrix (TimexLCA.lci(build_dynamic_biosphere=True)). The dynamic biosphere matrix contains both the time-explicit inventories from the links to the time-specific background databases as well as the timing of the emissions. The timing of the emissions is realized through adding a separate biosphere flow for each time of emission. The inventories from the time-specific background databases are aggregated at the temporal markets as these have the correct timing from the timeline. The matrix TimexLCA.dynamic_inventory and the more readable DataFrame TimexLCA.dynamic_inventory_df contain the emissions of the system per biosphere flow including its timestamp and its emitting process.

  2. If you are only interested in the new inventories, but not their timings, you can set build_dynamic_biosphere=False, which will only create an inventory linking to the new background processes but without the timing of the biosphere flows and is stored under TimexLCA.lca.inventory.

Example: Matrix modifications

For the simple system above, these are the modifications we apply to the matrices:


The timings from the timeline and the inventory information from the time-specific databases is inserted into the new time-explicit matrices. For each specific point in time that product b is demanded, temporal markets are created, distributing the demand for b between the time-specific background databases. The dynamic biosphere matrix is created, containing the timing of emissions. You can see that the CO2 emission at process A occurs both in 2024 and 2025, based on the temporal distribution on this biosphere exchange.

Static or dynamic impact assessment#

bw_timex allows to use conventional static impact assessment methods (LCIA), which are executed using TimexLCA.static_lcia(). Conventional LCIA methods have one characterization factor per substance, regardless of the timing of emission.

To take advantage of the detailed temporal information at the inventory level of TimexLCA.dynamic_inventory, dynamic LCIA can be applied, using TimexLCA.dynamic_lcia(). Users can define or import their own dynamic LCIA functions. Out of the box, we provide dynamic LCIA functions for the climate change metrics ‘radiative forcing’ and ‘global warming potential (GWP)’ for all greenhouse gases in the IPCC AR6 report Chapter 7 Table 7.SM.7 from the brightway package dynamic_characterization.

The time_horizon, over which both metrics are evaluated, defaults to 100 years, but can be set flexibly in years. Additionally, both metrics can be applied with a fixed or flexible time horizon. Fixed time horizon means that the all emissions are evaluated starting from the timing of the functional unit until the end of the time horizon, meaning that later emissions are counted for shorter, and flexible time horizon means that each emission is evaluated starting from its own timing until the end of the time horizon. The former is the approach of Levasseur et al. 2010. This behavior is set with the boolean fixed_time_horizon.

Contribution assessment of impacts#

Sometimes it might be helpful to understand where the time-explicit environmental impacts actually originate from. While the default is to aggregate background emissions at the respective temporal markets (to keep the correct timing), there is the option to disaggregate them to their original emitting processes from the time-specific background databases. This is helpful for an in-depth contribution analysis and can be executed with TimexLCA.dynamic_lcia(use_disaggregated_lci=True). This overwrites the TimexLCA.dynamic_inventory and TimexLCA.dynamic_inventory_df with versions, in which the background emissions are retained at corresponding background processes, and uses these when calculating the dynamic LCIA results.

Unsure about the different options? Check out the decision tree for guidance for your time-explicit LCA.