Quickstart¶
This quickstart shows how to load a Frictionless data package, run a temporal LCA, and plot impact scores over time.
Install¶
pip install trails
You can also install from conda:
conda install romainsacchi::trails
Solver note: TRAILS relies on fast sparse solvers for large LCAs. Install the recommended solver for your platform (see the README) to avoid slow SciPy fallbacks.
Run a temporal LCA¶
from datapackage import Package
from trails import Trails, lca, get_lcia_method_names, plot_temporal_scores
# Load a Frictionless data package exported by premise (or compatible tooling)
package = Package("path/to/datapackage.json")
# Choose an LCIA method bundled with TRAILS
method = get_lcia_method_names(ei_version="3.11")[0]
# Initialize TRAILS (annual interpolation is optional).
# Default interpolation bounds are [min_year-1, max_year+1], where
# out-of-range years duplicate the nearest endpoint inventory.
trails = Trails(
package,
interpolate_annual=True,
methods=[method],
ei_version="3.11",
)
# Optional: widen interpolation bounds (e.g., +/-20 years)
# trails = Trails(
# package,
# interpolate_annual=True,
# interpolation_start_year_offset=-20,
# interpolation_end_year_offset=20,
# methods=[method],
# ei_version="3.11",
# )
# Pick an activity index from the metadata
activity_indices = next(iter(trails.activity_indices.values()))
start_act_idx = next(iter(activity_indices.keys()))
# Run temporal routing (builds the traversal graph).
# By default this uses adaptive routing with a relative cutoff of 1e-4.
trails.temporal_routing(
start_year=2030,
start_act_idx=start_act_idx,
min_amount=1e-18,
)
# Run temporal LCA (stores scores on trails.scores)
lca(
trails=trails,
# defaults shown explicitly:
solver_mode="iterative",
iterative_rtol=1e-3,
)
# Plot temporal impact scores
fig = plot_temporal_scores(trails, method_label=method)
fig.show()
Adaptive routing is the default. To make the default criterion explicit:
trails.temporal_routing(
start_year=2030,
start_act_idx=start_act_idx,
max_depth=None,
adaptive_relative_score_cutoff=1e-4,
)
Branches below the cutoff remain in the matrix-solved frontier, so they are still included in the final LCA.
To run fixed-depth routing instead, pass an integer max_depth and omit
adaptive cutoffs.
What you get¶
Temporal LCA results are stored on the Trails instance. Use trails.scores
for impact scores (when compute_score=True). If you run
lca(..., store_inventory=True), TRAILS also stores trails.inventory;
with compute_score=True and store_inventory=True, it also stores
trails.characterized_inventory.
Importing Excel inventories¶
You can import user-provided inventories from Excel using bw2io. When you
omit year and scenario_label, the exchanges are applied to all template
years and interpolated across annual years.
Sign conventions for Excel imports: - Technosphere exchanges are sign-flipped on import (positive becomes negative, and vice versa). - Production and biosphere exchanges are stored as-is.
from trails import Trails
trails = Trails(package)
trails.import_excel_inventory("path/to/inventory.xlsx")
# Target a single scenario slice instead
trails.import_excel_inventory("path/to/inventory.xlsx", year=2020)
Excel column meanings (from the exchanges table):
name: exchange name.amount: base exchange amount (used when no year-specific columns exist).Year-specific columns (e.g.,
2020,2040): optional numeric columns that set year-specific amounts. TRAILS writes these values into the matching years and linearly interpolates between them; years outside the provided range are clamped to the nearest endpoint.location: exchange location (required for technosphere/production).categories: biosphere categories (tuple orcompartment/subcompartment).unit: exchange unit.type:production,technosphere, orbiosphere.reference product: required for technosphere/production exchanges.temporal_distribution: temporal distribution code.temporal_loc/temporal_scale: distribution parameters.temporal_min/temporal_max: integer offset bounds.temporal_offsets/temporal_weights: JSON lists used fortemporal_distribution=6(discrete empirical), e.g.[0, 5, 12]and[0.5, 0.3, 0.2].temporal_amount_source:port(ported amount) ormatrix(use matrix values).comment: optional notes.
FaIR radiative forcing¶
After running a temporal LCA, you can translate the inventory into radiative forcing and temperature anomalies using the FaIR climate model. This uses a baseline IAMC scenario and per-species perturbations derived from the Trails inventory. Outputs are aggregated across all FaIR configurations and stored as quantiles (2.5, 25, 50, 75, 97.5).
from trails import lca
from trails.fair_rf import run_fair_delta_rf
# Ensure an inventory with root attribution is available
lca(
trails=trails,
store_inventory=True,
)
rf = run_fair_delta_rf(
trails,
scenario="REMIND|SSP2-PkBudg650",
# defaults shown explicitly:
per_species_runs=True,
per_species_workers=None, # auto: min(4, cpu_count, n_work_items)
)
The resulting outputs are stored on the Trails instance:
trails.instant_radiative_forcingwith dims(quantile, year, flow, root activity)trails.delta_temperaturewith dims(quantile, year, flow, root activity)
Notes:
run_fair_delta_rfrequirestrails.inventorywith aroot activitydimension. Runlca(..., store_inventory=True)first.scenariomust match a scenario label present in the emissions CSV used byrun_fair_delta_rf.If
config_nameandconfig_namesare omitted, TRAILS evaluates all available FaIR configurations and stores quantiles across the ensemble.
You can visualize these with the built-in plotting helpers (defaults to the 50th quantile):
from trails import plot_rf, plot_temp
plot_rf(trails, year_range=(2000, 2100))
plot_temp(trails, year_range=(2000, 2100))