# Step 1 - Adding temporal information To get you started with time-explicit LCA, we'll investigate this very simple production system with two "technosphere" nodes A and B and a "biosphere" node representing some CO2 emissions. For the sake of this example, we'll assume that we demand Process A to run exactly once. ```{mermaid} :caption: Example production system flowchart LR subgraph background[background] B(Process B):::bg end subgraph foreground[foreground] A(Process A):::fg end subgraph biosphere[biosphere] CO2(CO2):::bio end B-->|"3 kg \n  "|A A-.->|"5 kg \n  "|CO2 B-.->|"11 kg \n  "|CO2 classDef fg color:#222832, fill:#3fb1c5, stroke:none; classDef bg color:#222832, fill:#3fb1c5, stroke:none; classDef bio color:#222832, fill:#9c5ffd, stroke:none; style background fill:none, stroke:none; style foreground fill:none, stroke:none; style biosphere fill:none, stroke:none; ``` :::{dropdown} Here's the code to set this up with brightway - but this is not essential here :icon: codescan ```python import bw2data as bd bd.projects.set_current("getting_started_with_timex") bd.Database("biosphere").write( { ("biosphere", "CO2"): { "type": "emission", "name": "CO2", }, } ) bd.Database("background").write( { ("background", "B"): { "name": "B", "location": "somewhere", "reference product": "B", "exchanges": [ { "amount": 1, "type": "production", "input": ("background", "B"), }, { "amount": 11, "type": "biosphere", "input": ("biosphere", "CO2"), }, ], }, } ) bd.Database("foreground").write( { ("foreground", "A"): { "name": "A", "location": "somewhere", "reference product": "A", "exchanges": [ { "amount": 1, "type": "production", "input": ("foreground", "A"), }, { "amount": 3, "type": "technosphere", "input": ("background", "B"), }, { "amount": 5, "type": "biosphere", "input": ("biosphere", "CO2"), } ], }, } ) bd.Method(("our", "method")).write( [ (("biosphere", "CO2"), 1), ] ) ``` ::: Now, if you want to consider time in your LCA, you need to somehow add temporal information. For time-explicit LCA, we consider two kinds of temporal information, that will be discussed in the following. ## Temporal distributions To determine the timing of the exchanges within the production system, we add the `temporal_distribution` attribute to the respective exchanges. To carry the temporal information, we use 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 is a *container for a series of amount spread over time*, so it tells you what share of an exchange happens at what point in time. So, let's include this information in our production system - first visually: ```{mermaid} :caption: Temporalized example production system flowchart LR subgraph background[" "] B_2020(Process B):::bg end subgraph foreground[" "] A(Process A):::fg end subgraph biosphere[" "] CO2(CO2):::b end B_2020-->|"dates:[-2,0,+4] years \n shares: [30%,50%,20%] * 3 kg "|A A-.->|"dates: [0,+1] years\n shares: [60%,40%] * 5 kg"|CO2 B_2020-.->|"dates:[0] years\n shares: [100%] * 11 kg"|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; ``` :::{dropdown} Here's the code to add this information to our modeled production system in Brightway :icon: codescan ```python import numpy as np from bw_temporalis import TemporalDistribution from bw_timex.utils import add_temporal_distribution_to_exchange # Starting with the exchange between A and B # First, create a TemporalDistribution with the time information from above td_b_to_a = TemporalDistribution( date=np.array([-2, 0, 4], dtype="timedelta64[Y]"), amount=np.array([0.3, 0.5, 0.2]), ) # Now add the temporal distribution to the corresponding exchange. In # principle, you just have to do the following: # exchange_object["temporal_distribution"] = TemporalDistribution # We currently don't have the exchange_object at hand here, but we can # use the utility function add_temporal_distribution_to_exchange to help. add_temporal_distribution_to_exchange( temporal_distribution=td_b_to_a, input_code="B", input_database="background", output_code="A", output_database="foreground" ) # Now we do the same for our other temporalized exchange between A and CO2 td_a_to_co2 = TemporalDistribution( date=np.array([0, 1], dtype="timedelta64[Y]"), amount=np.array([0.6, 0.4]), ) # We actually only have to define enough fields to uniquely identify the # exchange here add_temporal_distribution_to_exchange( temporal_distribution=td_a_to_co2, input_code="CO2", output_code="A" ) ``` ::: ## Time-specific process data While the temporal information above tells us when the processes occur, we also need information on how our processes change over time. So, for our simple example, let's say our background process B somehow evolves, so that it emits less CO2 in the future. To make it precise, we assume that the original process we modeled above represents the process state in the year 2020, emitting 11 kg CO2, which reduces to 7 kg CO2 by 2030: ```{mermaid} :caption: Temporalized example production system with two time-specific background processes B 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-->|"dates:[-2,0,+4] years \n shares: [30%,50%,20%] * 3 kg"|A A-.->|"dates: [0,+1] years\n shares: [60%,40%] * 5 kg"|CO2 B_2020-.->|"dates:[0] years\n shares: [100%] * 11 kg"|CO2 B_2030-.->|"dates:[0] years\n shares: [100%] * 7 kg"|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; ``` :::{dropdown} Again, here's the code in case you're interested :icon: codescan ```python bd.Database("background_2030").write( { ("background_2030", "B"): { "name": "B", "location": "somewhere", "reference product": "B", "exchanges": [ { "amount": 1, "type": "production", "input": ("background_2030", "B"), }, { "amount": 7, "type": "biosphere", "input": ("biosphere", "CO2"), }, ], }, } ) ``` ::: So, as you can see, the processes at specific time steps reside within a separate normal Brightway database. To hand them to `bw_timex`, we just need to define a dictionary that maps the names of time-specific databases to the point in time that they represent: ```python from datetime import datetime # Note: The foreground does not represent a specific point in time, but should # later be dynamically distributed over time database_dates = { "background": datetime.strptime("2020", "%Y"), "background_2030": datetime.strptime("2030", "%Y"), "foreground": "dynamic", } ``` :::{note} You can use whatever data source you want for the time-specific process data. A nice package from the Brightway cosmos that can help you is [premise](https://premise.readthedocs.io/en/latest/introduction.html). ::: ## Temporal evolution of foreground exchanges (`bw_timex>0.3.4`) The approaches above handle temporal variation in the *background* system — different database snapshots for different points in time. But what if a *foreground* exchange itself changes over time? For example, an industrial process might become more energy-efficient over the years, so its electricity consumption per unit of output decreases. `bw_timex` supports this via **temporal evolution** attributes on exchanges. These are optional — if you don't add them, exchange amounts remain constant over time as before. There are two ways to specify temporal evolution: **Scaling factors** — multiply the base exchange amount by a time-dependent factor: ```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 } ``` **Absolute amounts** — directly specify the exchange amount at each point in time: ```python 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 } ``` For dates between the specified points, values are linearly interpolated. For dates outside the range, the nearest boundary value is used. You can specify either `temporal_evolution_amounts` or `temporal_evolution_amounts` for the same exchange, but not both. A convenience function is available to add temporal evolution to an existing exchange: ```python from bw_timex.utils import add_temporal_evolution_to_exchange add_temporal_evolution_to_exchange( temporal_evolution_factors={ datetime(2020, 1, 1): 1.0, datetime(2030, 1, 1): 0.75, }, input_code="B", input_database="background", output_code="A", output_database="foreground", ) ``` :::{note} Temporal evolution only applies to foreground exchanges. Background process evolution is handled by the database interpolation mechanism described above. :::