cafein.lca 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cafein_lca-0.1.0/.coveragerc +6 -0
- cafein_lca-0.1.0/.flake8 +4 -0
- cafein_lca-0.1.0/.gitignore +15 -0
- cafein_lca-0.1.0/.zenodo.json +26 -0
- cafein_lca-0.1.0/CHANGELOG.md +58 -0
- cafein_lca-0.1.0/CITATION.cff +29 -0
- cafein_lca-0.1.0/LICENSE +28 -0
- cafein_lca-0.1.0/PKG-INFO +141 -0
- cafein_lca-0.1.0/README.md +98 -0
- cafein_lca-0.1.0/RELEASING.md +19 -0
- cafein_lca-0.1.0/cafein/lca/__init__.py +23 -0
- cafein_lca-0.1.0/cafein/lca/config.py +187 -0
- cafein_lca-0.1.0/cafein/lca/data/itf-2020/assembly_disposal_intensities.csv +3 -0
- cafein_lca-0.1.0/cafein/lca/data/itf-2020/battery_chemistries.csv +10 -0
- cafein_lca-0.1.0/cafein/lca/data/itf-2020/constants.csv +25 -0
- cafein_lca-0.1.0/cafein/lca/data/itf-2020/delivery_legs.csv +11 -0
- cafein_lca-0.1.0/cafein/lca/data/itf-2020/electricity_pathways.csv +7 -0
- cafein_lca-0.1.0/cafein/lca/data/itf-2020/fuel_ghg.csv +17 -0
- cafein_lca-0.1.0/cafein/lca/data/itf-2020/fuel_wtt_energy.csv +3 -0
- cafein_lca-0.1.0/cafein/lca/data/itf-2020/hydrogen_pathways.csv +8 -0
- cafein_lca-0.1.0/cafein/lca/data/itf-2020/infrastructure_categories.csv +8 -0
- cafein_lca-0.1.0/cafein/lca/data/itf-2020/infrastructure_types.csv +7 -0
- cafein_lca-0.1.0/cafein/lca/data/itf-2020/manifest.toml +7 -0
- cafein_lca-0.1.0/cafein/lca/data/itf-2020/material_intensities.csv +15 -0
- cafein_lca-0.1.0/cafein/lca/data/itf-2020/modes.csv +131 -0
- cafein_lca-0.1.0/cafein/lca/data/itf-2020/power_gen_mix.csv +19 -0
- cafein_lca-0.1.0/cafein/lca/data/itf-2020/service_vehicles.csv +15 -0
- cafein_lca-0.1.0/cafein/lca/data/scenarios/finland_2020.toml +14 -0
- cafein_lca-0.1.0/cafein/lca/data/scenarios/india_metropolitan.toml +324 -0
- cafein_lca-0.1.0/cafein/lca/engine/__init__.py +1 -0
- cafein_lca-0.1.0/cafein/lca/engine/delivery.py +30 -0
- cafein_lca-0.1.0/cafein/lca/engine/infrastructure.py +86 -0
- cafein_lca-0.1.0/cafein/lca/engine/manufacturing.py +101 -0
- cafein_lca-0.1.0/cafein/lca/engine/services.py +81 -0
- cafein_lca-0.1.0/cafein/lca/engine/use.py +65 -0
- cafein_lca-0.1.0/cafein/lca/export.py +238 -0
- cafein_lca-0.1.0/cafein/lca/lca.py +185 -0
- cafein_lca-0.1.0/cafein/lca/modes.py +226 -0
- cafein_lca-0.1.0/cafein/lca/parameters.py +72 -0
- cafein_lca-0.1.0/cafein/lca/results.py +88 -0
- cafein_lca-0.1.0/cafein/lca/scenarios.py +205 -0
- cafein_lca-0.1.0/pyproject.toml +57 -0
cafein_lca-0.1.0/.flake8
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "cafein.lca: Life-cycle assessment of urban passenger transport in Python",
|
|
3
|
+
"description": "Life-cycle energy use and greenhouse-gas emissions per passenger-km, vehicle-km and vehicle for 56 urban transport modes, decomposed into manufacturing, delivery, use, operational services and infrastructure. The calculation model and default coefficients are adapted from the life-cycle assessment model published by the International Transport Forum with Cazzola & Crist (2020), 'Good to Go? Assessing the Environmental Performance of New Mobility'. This library is an adaptation of an original work by the OECD/ITF; the opinions expressed and arguments employed in it should not be reported as representing the official views of the OECD or of its Member countries.",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"upload_type": "software",
|
|
6
|
+
"creators": [
|
|
7
|
+
{
|
|
8
|
+
"name": "Tenkanen, Henrikki",
|
|
9
|
+
"affiliation": "Aalto University"
|
|
10
|
+
}
|
|
11
|
+
],
|
|
12
|
+
"keywords": [
|
|
13
|
+
"life-cycle assessment",
|
|
14
|
+
"transport",
|
|
15
|
+
"shared mobility",
|
|
16
|
+
"micromobility",
|
|
17
|
+
"GHG emissions"
|
|
18
|
+
],
|
|
19
|
+
"related_identifiers": [
|
|
20
|
+
{
|
|
21
|
+
"identifier": "https://www.itf-oecd.org/good-go-assessing-environmental-performance-new-mobility",
|
|
22
|
+
"relation": "isDerivedFrom",
|
|
23
|
+
"scheme": "url"
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0 (2026-09-09)
|
|
4
|
+
|
|
5
|
+
NEW
|
|
6
|
+
|
|
7
|
+
- Initial release: the ITF "Good to go?" (Cazzola & Crist 2020) urban
|
|
8
|
+
transport LCA model as a Python library.
|
|
9
|
+
- 56 transport modes with typed, immutable scenario parameters
|
|
10
|
+
(`cafein.lca.mode()` + `.replace()`, or keyword overrides on
|
|
11
|
+
`TransportLCA.calculate()`).
|
|
12
|
+
- Session-level electricity generation mix: packaged region presets from the
|
|
13
|
+
workbook plus fully custom mixes; propagates to use phase, hydrogen
|
|
14
|
+
electrolysis and shared-fleet servicing vehicles.
|
|
15
|
+
- Scenario bundles: `Scenario.load()` reads a TOML file of per-mode
|
|
16
|
+
parameter overrides plus an electricity mix, with `best`/`central`/`worst`
|
|
17
|
+
cases, and `TransportLCA(scenario=...)` applies it to every mode.
|
|
18
|
+
- Scenario values carry provenance (evidence type, geography, source,
|
|
19
|
+
confidence, note), validated on load and exposed by
|
|
20
|
+
`Scenario.provenance()`.
|
|
21
|
+
- Packaged scenarios `india_metropolitan` (Delhi/Mumbai operating
|
|
22
|
+
conditions, provenance per value) and `finland_2020` (electricity mix),
|
|
23
|
+
listed by `list_scenarios()`; tutorial page.
|
|
24
|
+
- Results per pkm/vkm/vehicle decomposed into the five life-cycle
|
|
25
|
+
components, as pandas objects; `TransportLCA.summary()` for all modes.
|
|
26
|
+
- Optional `cafein` extra (`pip install cafein.lca[cafein]`) pinning cafein
|
|
27
|
+
0.25.0, for use alongside the cafein routing package; the core install
|
|
28
|
+
stays dependency-light.
|
|
29
|
+
- Export helpers `TransportLCA.transit_factors()` and `.street_factors()`
|
|
30
|
+
return cafein-ready factor tables: the mode's four life-cycle components
|
|
31
|
+
(vehicle, fuel, infrastructure, operations), their `total`, an explicit
|
|
32
|
+
per-passenger-km or per-vehicle-km `basis`, and scenario provenance, for
|
|
33
|
+
use with the `cafein` routing package through `factors=`. `Scenario`
|
|
34
|
+
records its `name` and file `sha256` for that provenance. cafein 0.25.0
|
|
35
|
+
keeps the `total` column without warning and computes from the components.
|
|
36
|
+
- Named coefficient sets: the packaged tables are the set `itf-2020` under
|
|
37
|
+
`cafein/lca/data/itf-2020/` with a `manifest.toml`.
|
|
38
|
+
`TransportLCA(coefficients=...)` selects a set (default `itf-2020`, else a
|
|
39
|
+
scenario file's `coefficients` key), `available_coefficient_sets()` lists
|
|
40
|
+
the packaged sets, and `Result.coefficient_set` records which set produced
|
|
41
|
+
a result. 0.1.0 ships one set.
|
|
42
|
+
|
|
43
|
+
TESTS
|
|
44
|
+
|
|
45
|
+
- Golden-master suite: all 128 reproducible workbook columns match the
|
|
46
|
+
published workbook within 1e-9 relative tolerance (56 canonical modes,
|
|
47
|
+
48 sensitivity variants expressed as parameter overrides, 24 as column
|
|
48
|
+
fixtures).
|
|
49
|
+
- Acceptance test reproducing the repository's Finland-2020 GHG/pkm table.
|
|
50
|
+
- Formula-deviation audit of the workbook documented in
|
|
51
|
+
docs/workbook-audit.md.
|
|
52
|
+
- Integration job installing `cafein.lca[cafein]` and checking that
|
|
53
|
+
cafein.lca coexists with the cafein routing package under the shared
|
|
54
|
+
namespace; skipped when cafein is not installed.
|
|
55
|
+
- Export-helper tests: `tests/test_export.py` covers the schema, the 5->4
|
|
56
|
+
component mapping, the basis rule and provenance; the cafein round-trip in
|
|
57
|
+
`tests/test_integration.py` checks cafein loads the exported tables and
|
|
58
|
+
honours their `basis`.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
cff-version: 1.2.0
|
|
2
|
+
message: >-
|
|
3
|
+
If you use this software, please cite both the software and the underlying
|
|
4
|
+
model (Cazzola & Crist 2020).
|
|
5
|
+
type: software
|
|
6
|
+
title: "cafein.lca: Life-cycle assessment of urban passenger transport in Python"
|
|
7
|
+
authors:
|
|
8
|
+
- family-names: Tenkanen
|
|
9
|
+
given-names: Henrikki
|
|
10
|
+
affiliation: Aalto University
|
|
11
|
+
license: MIT
|
|
12
|
+
repository-code: "https://github.com/cafein-py/cafein.lca"
|
|
13
|
+
keywords:
|
|
14
|
+
- life-cycle assessment
|
|
15
|
+
- transport
|
|
16
|
+
- shared mobility
|
|
17
|
+
- GHG emissions
|
|
18
|
+
references:
|
|
19
|
+
- type: report
|
|
20
|
+
title: "Good to Go? Assessing the Environmental Performance of New Mobility"
|
|
21
|
+
authors:
|
|
22
|
+
- family-names: Cazzola
|
|
23
|
+
given-names: Pierpaolo
|
|
24
|
+
- family-names: Crist
|
|
25
|
+
given-names: Philippe
|
|
26
|
+
year: 2020
|
|
27
|
+
institution:
|
|
28
|
+
name: International Transport Forum
|
|
29
|
+
url: "https://www.itf-oecd.org/good-go-assessing-environmental-performance-new-mobility"
|
cafein_lca-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Henrikki Tenkanen
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
The datasets packaged under cafein/lca/data/ are extracted from the
|
|
16
|
+
International Transport Forum workbook accompanying Cazzola, P. and P. Crist
|
|
17
|
+
(2020), "Good to Go? Assessing the Environmental Performance of New Mobility"
|
|
18
|
+
(© OECD/ITF 2020) and are used and adapted with citation under the OECD terms
|
|
19
|
+
and conditions; several coefficients derive from Argonne National
|
|
20
|
+
Laboratory's GREET model.
|
|
21
|
+
|
|
22
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
23
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
24
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
25
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
26
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
27
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
28
|
+
SOFTWARE.
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: cafein.lca
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Life-cycle assessment of urban passenger transport in Python
|
|
5
|
+
Project-URL: Homepage, https://github.com/cafein-py/cafein.lca
|
|
6
|
+
Project-URL: Documentation, https://cafein-lca.readthedocs.io
|
|
7
|
+
Project-URL: Repository, https://github.com/cafein-py/cafein.lca
|
|
8
|
+
Project-URL: Issue Tracker, https://github.com/cafein-py/cafein.lca/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/cafein-py/cafein.lca/blob/main/CHANGELOG.md
|
|
10
|
+
Author-email: Henrikki Tenkanen <henrikki.tenkanen@aalto.fi>
|
|
11
|
+
License: MIT
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Keywords: GHG emissions,LCA,cafein,life-cycle assessment,micromobility,mobility,ridesourcing,shared mobility,transport
|
|
14
|
+
Classifier: Development Status :: 3 - Alpha
|
|
15
|
+
Classifier: Intended Audience :: Science/Research
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
24
|
+
Classifier: Topic :: Scientific/Engineering
|
|
25
|
+
Requires-Python: >=3.10
|
|
26
|
+
Requires-Dist: numpy>=1.24
|
|
27
|
+
Requires-Dist: pandas>=1.5
|
|
28
|
+
Requires-Dist: tomli>=2.0; python_version < '3.11'
|
|
29
|
+
Provides-Extra: cafein
|
|
30
|
+
Requires-Dist: cafein==0.25.0; extra == 'cafein'
|
|
31
|
+
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: black; extra == 'dev'
|
|
33
|
+
Requires-Dist: flake8; extra == 'dev'
|
|
34
|
+
Requires-Dist: pytest; extra == 'dev'
|
|
35
|
+
Requires-Dist: pytest-cov; extra == 'dev'
|
|
36
|
+
Provides-Extra: docs
|
|
37
|
+
Requires-Dist: matplotlib; extra == 'docs'
|
|
38
|
+
Requires-Dist: myst-nb; extra == 'docs'
|
|
39
|
+
Requires-Dist: sphinx; extra == 'docs'
|
|
40
|
+
Requires-Dist: sphinx-book-theme==1.2.0; extra == 'docs'
|
|
41
|
+
Requires-Dist: sphinx-design; extra == 'docs'
|
|
42
|
+
Description-Content-Type: text/markdown
|
|
43
|
+
|
|
44
|
+
# cafein.lca
|
|
45
|
+
|
|
46
|
+
**Life-cycle assessment of urban passenger transport in Python.**
|
|
47
|
+
|
|
48
|
+
`cafein.lca` computes life-cycle energy use and greenhouse-gas emissions
|
|
49
|
+
of urban transport modes per passenger-km and per vehicle-km, decomposed
|
|
50
|
+
into vehicle and battery manufacturing, delivery, use, the servicing of
|
|
51
|
+
shared fleets, and infrastructure; the first four are also available per
|
|
52
|
+
vehicle. It covers 56 modes, from
|
|
53
|
+
private e-scooters to metro trains. The main assumptions behind a result
|
|
54
|
+
are named parameters (electricity mix, vehicle lifetime, mileage,
|
|
55
|
+
occupancy, battery size, servicing logistics), while the per-mode
|
|
56
|
+
technical data and the shared coefficient tables ship as packaged data.
|
|
57
|
+
|
|
58
|
+
The calculation is a standard attributional life-cycle assessment. The
|
|
59
|
+
default coefficient set, `itf-2020`, is extracted from the International
|
|
60
|
+
Transport Forum's *Good to Go?* study (see the attribution below) and is
|
|
61
|
+
held to that source by the test suite; `TransportLCA(coefficients=...)`
|
|
62
|
+
names the set, so later sets can coexist. Regional scenarios replace selected
|
|
63
|
+
assumptions, such as occupancy, lifetimes and mileage, with sourced local
|
|
64
|
+
values while the remaining defaults stay in force; each scenario carries
|
|
65
|
+
best, central and worst cases with the evidence and source behind every
|
|
66
|
+
value, and an Indian metropolitan scenario is packaged.
|
|
67
|
+
|
|
68
|
+
`cafein.lca` is part of the [cafein](https://github.com/cafein-py) family
|
|
69
|
+
of packages. It installs and runs on its own and does not require the
|
|
70
|
+
`cafein` core package.
|
|
71
|
+
|
|
72
|
+
## Installation
|
|
73
|
+
|
|
74
|
+
Until the first release is on PyPI, install from the repository:
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
pip install git+https://github.com/cafein-py/cafein.lca.git
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Once 0.1.0 is released, `pip install cafein.lca` will do the same.
|
|
81
|
+
|
|
82
|
+
## Example
|
|
83
|
+
|
|
84
|
+
A session holds the electricity mix, and a calculation returns the
|
|
85
|
+
result for one mode. This computes the life-cycle emissions of a
|
|
86
|
+
battery-electric car on the EU 28 grid, in g CO₂e per passenger-km:
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
from cafein.lca import TransportLCA
|
|
90
|
+
|
|
91
|
+
lca = TransportLCA(power_mix="EU 28")
|
|
92
|
+
car = lca.calculate("private_car_bev")
|
|
93
|
+
round(car.ghg_per_pkm, 1)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
That number is the whole-life total; `car.per_pkm` breaks it into the
|
|
97
|
+
five components, and `lca.summary()` runs every mode at once. Changing
|
|
98
|
+
assumptions, custom electricity mixes and scenarios are covered in the
|
|
99
|
+
documentation below.
|
|
100
|
+
|
|
101
|
+
## Documentation
|
|
102
|
+
|
|
103
|
+
The documentation at https://cafein-lca.readthedocs.io covers:
|
|
104
|
+
|
|
105
|
+
- [Getting started](https://cafein-lca.readthedocs.io/en/latest/getting_started/installation.html):
|
|
106
|
+
installation and a first calculation.
|
|
107
|
+
- [User guide](https://cafein-lca.readthedocs.io/en/latest/user_guide/reading_results.html):
|
|
108
|
+
reading results, modes and parameters, the electricity mix, sensitivity
|
|
109
|
+
analysis.
|
|
110
|
+
- [Scenarios](https://cafein-lca.readthedocs.io/en/latest/scenarios/scenarios.html):
|
|
111
|
+
reusable, evidence-tagged assumption bundles.
|
|
112
|
+
- [The model](https://cafein-lca.readthedocs.io/en/latest/model/model.html):
|
|
113
|
+
scope, stages, normalisation, provenance of the coefficients, and the
|
|
114
|
+
[audit](docs/model/workbook_audit.md) of the source model.
|
|
115
|
+
- [API reference](https://cafein-lca.readthedocs.io/en/latest/reference.html).
|
|
116
|
+
|
|
117
|
+
## Related tools
|
|
118
|
+
|
|
119
|
+
- **[carculator](https://github.com/romainsacchi/carculator)** (Paul Scherrer
|
|
120
|
+
Institut) and its siblings provide fully parameterised vehicle LCA over
|
|
121
|
+
ecoinvent-style background databases, with many midpoint indicators,
|
|
122
|
+
prospective scenarios and Monte Carlo. `cafein.lca` instead packages its
|
|
123
|
+
own coefficients for greenhouse gas and energy only, and covers the
|
|
124
|
+
mobility-services layer that vehicle LCAs usually omit: servicing of
|
|
125
|
+
shared fleets, deadheading of taxis and ridesourcing, and infrastructure
|
|
126
|
+
for every mode.
|
|
127
|
+
- **Brightway2 / lca_algebraic** — general LCA frameworks; this library
|
|
128
|
+
deliberately stays a lightweight domain model on plain pandas.
|
|
129
|
+
|
|
130
|
+
## Data attribution and license
|
|
131
|
+
|
|
132
|
+
The Python code is MIT-licensed. The packaged datasets are extracted from the
|
|
133
|
+
ITF workbook © OECD/ITF 2020, used and adapted with citation under the OECD
|
|
134
|
+
terms and conditions; several coefficients within it derive from Argonne
|
|
135
|
+
National Laboratory's [GREET](https://greet.anl.gov/) model. This is an
|
|
136
|
+
adaptation of OECD/ITF work and is not endorsed by the OECD. Cite the
|
|
137
|
+
original work when using the numbers:
|
|
138
|
+
|
|
139
|
+
> Cazzola, P. and P. Crist (2020), *Good to Go? Assessing the Environmental
|
|
140
|
+
> Performance of New Mobility*, International Transport Forum, Paris.
|
|
141
|
+
> https://www.itf-oecd.org/good-go-assessing-environmental-performance-new-mobility
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# cafein.lca
|
|
2
|
+
|
|
3
|
+
**Life-cycle assessment of urban passenger transport in Python.**
|
|
4
|
+
|
|
5
|
+
`cafein.lca` computes life-cycle energy use and greenhouse-gas emissions
|
|
6
|
+
of urban transport modes per passenger-km and per vehicle-km, decomposed
|
|
7
|
+
into vehicle and battery manufacturing, delivery, use, the servicing of
|
|
8
|
+
shared fleets, and infrastructure; the first four are also available per
|
|
9
|
+
vehicle. It covers 56 modes, from
|
|
10
|
+
private e-scooters to metro trains. The main assumptions behind a result
|
|
11
|
+
are named parameters (electricity mix, vehicle lifetime, mileage,
|
|
12
|
+
occupancy, battery size, servicing logistics), while the per-mode
|
|
13
|
+
technical data and the shared coefficient tables ship as packaged data.
|
|
14
|
+
|
|
15
|
+
The calculation is a standard attributional life-cycle assessment. The
|
|
16
|
+
default coefficient set, `itf-2020`, is extracted from the International
|
|
17
|
+
Transport Forum's *Good to Go?* study (see the attribution below) and is
|
|
18
|
+
held to that source by the test suite; `TransportLCA(coefficients=...)`
|
|
19
|
+
names the set, so later sets can coexist. Regional scenarios replace selected
|
|
20
|
+
assumptions, such as occupancy, lifetimes and mileage, with sourced local
|
|
21
|
+
values while the remaining defaults stay in force; each scenario carries
|
|
22
|
+
best, central and worst cases with the evidence and source behind every
|
|
23
|
+
value, and an Indian metropolitan scenario is packaged.
|
|
24
|
+
|
|
25
|
+
`cafein.lca` is part of the [cafein](https://github.com/cafein-py) family
|
|
26
|
+
of packages. It installs and runs on its own and does not require the
|
|
27
|
+
`cafein` core package.
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
Until the first release is on PyPI, install from the repository:
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
pip install git+https://github.com/cafein-py/cafein.lca.git
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Once 0.1.0 is released, `pip install cafein.lca` will do the same.
|
|
38
|
+
|
|
39
|
+
## Example
|
|
40
|
+
|
|
41
|
+
A session holds the electricity mix, and a calculation returns the
|
|
42
|
+
result for one mode. This computes the life-cycle emissions of a
|
|
43
|
+
battery-electric car on the EU 28 grid, in g CO₂e per passenger-km:
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
from cafein.lca import TransportLCA
|
|
47
|
+
|
|
48
|
+
lca = TransportLCA(power_mix="EU 28")
|
|
49
|
+
car = lca.calculate("private_car_bev")
|
|
50
|
+
round(car.ghg_per_pkm, 1)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
That number is the whole-life total; `car.per_pkm` breaks it into the
|
|
54
|
+
five components, and `lca.summary()` runs every mode at once. Changing
|
|
55
|
+
assumptions, custom electricity mixes and scenarios are covered in the
|
|
56
|
+
documentation below.
|
|
57
|
+
|
|
58
|
+
## Documentation
|
|
59
|
+
|
|
60
|
+
The documentation at https://cafein-lca.readthedocs.io covers:
|
|
61
|
+
|
|
62
|
+
- [Getting started](https://cafein-lca.readthedocs.io/en/latest/getting_started/installation.html):
|
|
63
|
+
installation and a first calculation.
|
|
64
|
+
- [User guide](https://cafein-lca.readthedocs.io/en/latest/user_guide/reading_results.html):
|
|
65
|
+
reading results, modes and parameters, the electricity mix, sensitivity
|
|
66
|
+
analysis.
|
|
67
|
+
- [Scenarios](https://cafein-lca.readthedocs.io/en/latest/scenarios/scenarios.html):
|
|
68
|
+
reusable, evidence-tagged assumption bundles.
|
|
69
|
+
- [The model](https://cafein-lca.readthedocs.io/en/latest/model/model.html):
|
|
70
|
+
scope, stages, normalisation, provenance of the coefficients, and the
|
|
71
|
+
[audit](docs/model/workbook_audit.md) of the source model.
|
|
72
|
+
- [API reference](https://cafein-lca.readthedocs.io/en/latest/reference.html).
|
|
73
|
+
|
|
74
|
+
## Related tools
|
|
75
|
+
|
|
76
|
+
- **[carculator](https://github.com/romainsacchi/carculator)** (Paul Scherrer
|
|
77
|
+
Institut) and its siblings provide fully parameterised vehicle LCA over
|
|
78
|
+
ecoinvent-style background databases, with many midpoint indicators,
|
|
79
|
+
prospective scenarios and Monte Carlo. `cafein.lca` instead packages its
|
|
80
|
+
own coefficients for greenhouse gas and energy only, and covers the
|
|
81
|
+
mobility-services layer that vehicle LCAs usually omit: servicing of
|
|
82
|
+
shared fleets, deadheading of taxis and ridesourcing, and infrastructure
|
|
83
|
+
for every mode.
|
|
84
|
+
- **Brightway2 / lca_algebraic** — general LCA frameworks; this library
|
|
85
|
+
deliberately stays a lightweight domain model on plain pandas.
|
|
86
|
+
|
|
87
|
+
## Data attribution and license
|
|
88
|
+
|
|
89
|
+
The Python code is MIT-licensed. The packaged datasets are extracted from the
|
|
90
|
+
ITF workbook © OECD/ITF 2020, used and adapted with citation under the OECD
|
|
91
|
+
terms and conditions; several coefficients within it derive from Argonne
|
|
92
|
+
National Laboratory's [GREET](https://greet.anl.gov/) model. This is an
|
|
93
|
+
adaptation of OECD/ITF work and is not endorsed by the OECD. Cite the
|
|
94
|
+
original work when using the numbers:
|
|
95
|
+
|
|
96
|
+
> Cazzola, P. and P. Crist (2020), *Good to Go? Assessing the Environmental
|
|
97
|
+
> Performance of New Mobility*, International Transport Forum, Paris.
|
|
98
|
+
> https://www.itf-oecd.org/good-go-assessing-environmental-performance-new-mobility
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Releasing
|
|
2
|
+
|
|
3
|
+
1. Update `CHANGELOG.md`: move the unreleased items under the new version
|
|
4
|
+
heading with the release date.
|
|
5
|
+
2. Set the version in `pyproject.toml` and `cafein/lca/__init__.py`
|
|
6
|
+
(`__version__`) — they must match the tag.
|
|
7
|
+
3. Commit, push, and wait for CI to go green on the full matrix.
|
|
8
|
+
4. Dry run: trigger the *Release* workflow manually (`workflow_dispatch`) —
|
|
9
|
+
it runs tests and builds the sdist/wheel without publishing.
|
|
10
|
+
5. Tag and push: `git tag v0.1.0 && git push origin v0.1.0`. The tag-driven
|
|
11
|
+
run tests, checks the tag against the package version, builds, publishes
|
|
12
|
+
to PyPI via Trusted Publishing (OIDC; the `pypi` environment must be
|
|
13
|
+
configured in the repo settings and the project registered as a trusted
|
|
14
|
+
publisher on PyPI), and creates the GitHub release.
|
|
15
|
+
6. Post-release: bump to the next `.dev0` version; check the Zenodo record
|
|
16
|
+
(if the GitHub–Zenodo integration is enabled, the release is archived
|
|
17
|
+
automatically using `.zenodo.json`).
|
|
18
|
+
7. conda-forge: after the first PyPI release, submit a feedstock to
|
|
19
|
+
conda-forge/staged-recipes.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""cafein.lca: Life-cycle assessment of urban passenger transport in Python.
|
|
2
|
+
|
|
3
|
+
Adapted from the life-cycle assessment model published with Cazzola & Crist
|
|
4
|
+
(2020), "Good to Go? Assessing the Environmental Performance of New
|
|
5
|
+
Mobility", International Transport Forum, Paris.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .config import available_coefficient_sets
|
|
9
|
+
from .lca import TransportLCA
|
|
10
|
+
from .modes import list_modes, mode
|
|
11
|
+
from .scenarios import Scenario, list_scenarios
|
|
12
|
+
|
|
13
|
+
__version__ = "0.1.0"
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"TransportLCA",
|
|
17
|
+
"Scenario",
|
|
18
|
+
"list_scenarios",
|
|
19
|
+
"available_coefficient_sets",
|
|
20
|
+
"mode",
|
|
21
|
+
"list_modes",
|
|
22
|
+
"__version__",
|
|
23
|
+
]
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"""Packaged assumption datasets (the environment layer of the model).
|
|
2
|
+
|
|
3
|
+
Loads the CSVs of a named coefficient set under ``cafein/lca/data/<set>/``
|
|
4
|
+
(extracted from the ITF workbook by ``scripts/extract_workbook.py``) into
|
|
5
|
+
plain dictionaries, lazily and once. The default set is ``itf-2020``.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import csv
|
|
9
|
+
import functools
|
|
10
|
+
import pathlib
|
|
11
|
+
|
|
12
|
+
DATA_DIR = pathlib.Path(__file__).parent / "data"
|
|
13
|
+
|
|
14
|
+
#: The default coefficient set; its directory under ``DATA_DIR`` holds the
|
|
15
|
+
#: environment tables and ``modes.csv`` plus a ``manifest.toml``.
|
|
16
|
+
DEFAULT_COEFFICIENTS = "itf-2020"
|
|
17
|
+
|
|
18
|
+
#: Directory of the coefficient set the module-level ``conf`` reads from.
|
|
19
|
+
COEFFICIENTS_DIR = DATA_DIR / DEFAULT_COEFFICIENTS
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def available_coefficient_sets():
|
|
23
|
+
"""Names of the coefficient sets packaged under ``DATA_DIR``.
|
|
24
|
+
|
|
25
|
+
A set is a directory holding a ``manifest.toml`` (so ``scenarios/`` is not
|
|
26
|
+
one).
|
|
27
|
+
"""
|
|
28
|
+
return sorted(d.name for d in DATA_DIR.iterdir() if (d / "manifest.toml").is_file())
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
POWER_SOURCES = ["oil", "natural_gas", "coal", "nuclear", "biomass", "other_renewables"]
|
|
32
|
+
MATERIALS = [
|
|
33
|
+
"steel",
|
|
34
|
+
"stainless_steel",
|
|
35
|
+
"cast_iron",
|
|
36
|
+
"wrought_aluminum",
|
|
37
|
+
"cast_aluminum",
|
|
38
|
+
"copper_brass",
|
|
39
|
+
"glass",
|
|
40
|
+
"plastics",
|
|
41
|
+
"carbon_fiber",
|
|
42
|
+
"rubber",
|
|
43
|
+
"others",
|
|
44
|
+
]
|
|
45
|
+
DELIVERY_LEGS = [
|
|
46
|
+
"air",
|
|
47
|
+
"ship",
|
|
48
|
+
"train_a",
|
|
49
|
+
"train_b",
|
|
50
|
+
"heavy_truck_a",
|
|
51
|
+
"heavy_truck_b",
|
|
52
|
+
"medium_truck_a",
|
|
53
|
+
"medium_truck_b",
|
|
54
|
+
"van_a",
|
|
55
|
+
"van_b",
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
# 0_Total R12 / 1_Manufacturing helper row 37 region labels -> CSV suffix.
|
|
59
|
+
PRODUCTION_REGIONS = {
|
|
60
|
+
"With Al smelting mostly form coal": "coal_al",
|
|
61
|
+
"Default": "default",
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _read(name):
|
|
66
|
+
with open(COEFFICIENTS_DIR / name, newline="") as f:
|
|
67
|
+
return list(csv.DictReader(f))
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _num(value):
|
|
71
|
+
if value in (None, ""):
|
|
72
|
+
return 0.0
|
|
73
|
+
return float(value)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class _Conf:
|
|
77
|
+
"""Namespace of packaged datasets, loaded on first attribute access."""
|
|
78
|
+
|
|
79
|
+
@functools.cached_property
|
|
80
|
+
def power_mix_catalog(self):
|
|
81
|
+
return {
|
|
82
|
+
r["region"]: [_num(r[s]) for s in POWER_SOURCES]
|
|
83
|
+
for r in _read("power_gen_mix.csv")
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
@functools.cached_property
|
|
87
|
+
def electricity_pathways(self):
|
|
88
|
+
rows = _read("electricity_pathways.csv")
|
|
89
|
+
return {
|
|
90
|
+
"wtt_energy_factor": [_num(r["wtt_energy_factor"]) for r in rows],
|
|
91
|
+
"ghg_g_per_kwh": [_num(r["ghg_g_per_kwh"]) for r in rows],
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
@functools.cached_property
|
|
95
|
+
def fuel_wtt_energy(self):
|
|
96
|
+
return {
|
|
97
|
+
r["fuel"]: _num(r["wtt_energy_factor"])
|
|
98
|
+
for r in _read("fuel_wtt_energy.csv")
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
@functools.cached_property
|
|
102
|
+
def fuel_ghg(self):
|
|
103
|
+
return {r["fuel"]: _num(r["ghg_wtw_g_per_l"]) for r in _read("fuel_ghg.csv")}
|
|
104
|
+
|
|
105
|
+
@functools.cached_property
|
|
106
|
+
def hydrogen_pathways(self):
|
|
107
|
+
rows = _read("hydrogen_pathways.csv")
|
|
108
|
+
named = {
|
|
109
|
+
r["pathway"]: (_num(r["wtt_energy_factor"]), _num(r["ghg_g_per_mj"]))
|
|
110
|
+
for r in rows
|
|
111
|
+
}
|
|
112
|
+
# Rows 2-7 of the workbook table are the per-power-source electrolysis
|
|
113
|
+
# factors, in POWER_SOURCES order (3_Use R29/R33 SUMPRODUCT).
|
|
114
|
+
per_source = rows[1:7]
|
|
115
|
+
return {
|
|
116
|
+
"named": named,
|
|
117
|
+
"electrolysis_energy": [_num(r["wtt_energy_factor"]) for r in per_source],
|
|
118
|
+
"electrolysis_ghg": [_num(r["ghg_g_per_mj"]) for r in per_source],
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
@functools.cached_property
|
|
122
|
+
def material_intensities(self):
|
|
123
|
+
return {r["material"]: r for r in _read("material_intensities.csv")}
|
|
124
|
+
|
|
125
|
+
@functools.cached_property
|
|
126
|
+
def assembly_disposal(self):
|
|
127
|
+
return {r["step"]: r for r in _read("assembly_disposal_intensities.csv")}
|
|
128
|
+
|
|
129
|
+
@functools.cached_property
|
|
130
|
+
def battery_chemistries(self):
|
|
131
|
+
return {r["chemistry"]: r for r in _read("battery_chemistries.csv")}
|
|
132
|
+
|
|
133
|
+
@functools.cached_property
|
|
134
|
+
def delivery_legs(self):
|
|
135
|
+
rows = {r["leg"]: r for r in _read("delivery_legs.csv")}
|
|
136
|
+
return {
|
|
137
|
+
"energy_mj_per_tkm": [
|
|
138
|
+
_num(rows[leg]["energy_mj_per_tkm"]) for leg in DELIVERY_LEGS
|
|
139
|
+
],
|
|
140
|
+
"ghg_g_per_mj": [_num(rows[leg]["ghg_g_per_mj"]) for leg in DELIVERY_LEGS],
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
@functools.cached_property
|
|
144
|
+
def service_vehicles(self):
|
|
145
|
+
return {
|
|
146
|
+
r["vehicle"]: {
|
|
147
|
+
"fuel_mj_per_km": _num(r["fuel_mj_per_km"]),
|
|
148
|
+
"electricity_mj_per_km": _num(r["electricity_mj_per_km"]),
|
|
149
|
+
"fuel": r["fuel"],
|
|
150
|
+
"electric_share": _num(r["electric_share"]),
|
|
151
|
+
"low_carbon_electricity": r["low_carbon_electricity"] == "1",
|
|
152
|
+
"energy_mj_per_km_world": _num(r["energy_mj_per_km_world"]),
|
|
153
|
+
"ghg_g_per_km_world": _num(r["ghg_g_per_km_world"]),
|
|
154
|
+
}
|
|
155
|
+
for r in _read("service_vehicles.csv")
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
@functools.cached_property
|
|
159
|
+
def fuel_ghg_ttw(self):
|
|
160
|
+
return {r["fuel"]: _num(r["ghg_ttw_g_per_l"]) for r in _read("fuel_ghg.csv")}
|
|
161
|
+
|
|
162
|
+
@functools.cached_property
|
|
163
|
+
def infrastructure_types(self):
|
|
164
|
+
return {r["infrastructure"]: r for r in _read("infrastructure_types.csv")}
|
|
165
|
+
|
|
166
|
+
@functools.cached_property
|
|
167
|
+
def constants(self):
|
|
168
|
+
return {r["name"]: r["value"] for r in _read("constants.csv")}
|
|
169
|
+
|
|
170
|
+
def constant(self, name):
|
|
171
|
+
return float(self.constants[name])
|
|
172
|
+
|
|
173
|
+
def material_intensity(self, material, production_region, metric):
|
|
174
|
+
"""Energy [MJ/kg] or GHG [g/kg] of material production (1_Mfg R38-51,
|
|
175
|
+
R86-99)."""
|
|
176
|
+
suffix = PRODUCTION_REGIONS[production_region]
|
|
177
|
+
unit = "mj_per_kg" if metric == "energy" else "g_per_kg"
|
|
178
|
+
return _num(self.material_intensities[material][f"{metric}_{unit}_{suffix}"])
|
|
179
|
+
|
|
180
|
+
def assembly_disposal_intensity(self, step, production_region, metric):
|
|
181
|
+
"""Vehicle assembly/disposal intensity per kg (1_Mfg R55-56/R103-104)."""
|
|
182
|
+
suffix = PRODUCTION_REGIONS[production_region]
|
|
183
|
+
unit = "mj_per_kg" if metric == "energy" else "g_per_kg"
|
|
184
|
+
return _num(self.assembly_disposal[step][f"{metric}_{unit}_{suffix}"])
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
conf = _Conf()
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
chemistry,specific_energy_kwh_per_kg,mfg_energy_mmbtu_coal_al,mfg_energy_mmbtu_default,mfg_ghg_g_coal_al,mfg_ghg_g_default
|
|
2
|
+
LMO,0.12123932196229183,17.550913251476587,14.53187694051551,1473442.404116315,982470.2863481897
|
|
3
|
+
NMC111,0.14262303541417382,26.534854451874825,23.515818140913748,2102479.095876358,1611506.9781082333
|
|
4
|
+
LFP: hydrothermal,0.1159133798267138,18.38705948608153,15.368023175120452,1498720.0654745686,1007747.9477064431
|
|
5
|
+
LFP: solid state,0.1159133798267138,17.23866559275126,14.219629281790183,1426779.775985455,935807.6582173294
|
|
6
|
+
NMC622,0.15504780647427463,26.682490257730674,23.663453946769597,2109288.564353485,1618316.4465853602
|
|
7
|
+
NMC811,0.14922041422599697,26.986728378511966,23.96769206755089,2108573.3561928202,1617601.2384246956
|
|
8
|
+
LMR-NMC:Gr,0.175,20.70821432697536,17.689178016014278,1715155.7425372568,1224183.6247691314
|
|
9
|
+
LMR-NMC:Gr-SI,0.2,20.70821432697536,17.689178016014278,1715155.7425372568,1224183.6247691314
|
|
10
|
+
NCA,0.15857163037285157,27.979802847890866,24.96076653692979,2174508.7671961817,1683536.649428057
|