# Theory
This section explains some of the theory behind time-explicit LCAs with `bw_timex`. In contrast to the [Getting Started section](getting_started/index.md), 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](api/index).
## 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](decisiontree.md) might also help to understand the differences between the different types of LCA.
```{image} data/timeexplicit_lca_dark.svg
:class: only-dark
```
```{image} data/timeexplicit_lca_light.svg
:class: only-light
```
## 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](https://github.com/brightway-lca/bw_temporalis/tree/main),
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`](https://docs.brightway.dev/projects/bw-temporalis/en/stable/content/api/bw_temporalis/temporal_distribution/index.html#bw_temporalis.temporal_distribution.TemporalDistribution) class from [`bw_temporalis`](https://github.com/brightway-lca/bw_temporalis). This class carries the information how the amount of the exchange is spread over time in two numpy arrays (`date`and `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](https://en.wikipedia.org/wiki/Convolution), combining the two temporal profiles.
````{admonition} Example: Convolution
:class: admonition-example
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:
```python
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")
```
~~~{image} data/td_two_and_four_years_ahead.svg
:align: center
~~~
The other distribution spreads an amount over the following 4 months, with decreasing shares:
```python
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")
```
~~~{image} data/td_spread_over_four_months.svg
:align: center
~~~
Now, let's see what happens when we convolute these temporal distributions:
```python
convolved_distribution = two_and_four_years_ahead * spread_over_four_months
convolved_distribution.graph(resolution="M")
```
~~~{image} data/td_convolved.svg
:align: center
~~~
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](https://github.com/brightway-lca/bw_temporalis/tree/main). An in-depth
description of how this works is available in the [brightway-docs](https://docs.brightway.dev/en/latest/content/theory/graph_traversal.html). 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.
````{admonition} Example: temporal evolution
:class: admonition-example
Foreground exchange amounts can be modified with a dictionary of either time-dependent scaling factors or absolute amounts:
```python
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.
````{admonition} Example: Timeline
:class: admonition-example
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.
```{mermaid}
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(CO2):::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%] * 11 kg\n dates:[0]" years|CO2
B_2030-.->|"amounts: [100%] * 7 kg\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](https://github.com/brightway-lca/bw_processing?tab=readme-ov-file). 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`.
````{admonition} Example: Matrix modifications
:class: admonition-example
:name: example-matrix-modifications
For the simple system above, these are the modifications we apply to the matrices:
:::{div} only-light
```{carousel}
:show_controls:
:show_indicators:
:show_dark:
:show_fade:
:data-bs-interval: false
~~~{image} data/matrix1_light.svg
:align: center
~~~
~~~{image} data/matrix2_light.svg
:align: center
~~~
~~~{image} data/matrix3_light.svg
:align: center
~~~
~~~{image} data/matrix4_light.svg
:align: center
~~~
```
:::
:::{div} only-dark
```{carousel}
:show_controls:
:show_indicators:
:show_fade:
:data-bs-interval: false
~~~{image} data/matrix1_dark.svg
:align: center
:class: only-dark
~~~
~~~{image} data/matrix2_dark.svg
:align: center
:class: only-dark
~~~
~~~{image} data/matrix3_dark.svg
:align: center
:class: only-dark
~~~
~~~{image} data/matrix4_dark.svg
:align: center
:class: only-dark
~~~
```
:::
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](https://www.ipcc.ch/report/ar6/wg1/chapter/chapter-7/) from the brightway package [`dynamic_characterization`](https://github.com/brightway-lca/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](https://pubs.acs.org/doi/10.1021/es9030003).
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](decisiontree.md) for guidance for your time-explicit LCA.