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.
Files changed (42) hide show
  1. cafein_lca-0.1.0/.coveragerc +6 -0
  2. cafein_lca-0.1.0/.flake8 +4 -0
  3. cafein_lca-0.1.0/.gitignore +15 -0
  4. cafein_lca-0.1.0/.zenodo.json +26 -0
  5. cafein_lca-0.1.0/CHANGELOG.md +58 -0
  6. cafein_lca-0.1.0/CITATION.cff +29 -0
  7. cafein_lca-0.1.0/LICENSE +28 -0
  8. cafein_lca-0.1.0/PKG-INFO +141 -0
  9. cafein_lca-0.1.0/README.md +98 -0
  10. cafein_lca-0.1.0/RELEASING.md +19 -0
  11. cafein_lca-0.1.0/cafein/lca/__init__.py +23 -0
  12. cafein_lca-0.1.0/cafein/lca/config.py +187 -0
  13. cafein_lca-0.1.0/cafein/lca/data/itf-2020/assembly_disposal_intensities.csv +3 -0
  14. cafein_lca-0.1.0/cafein/lca/data/itf-2020/battery_chemistries.csv +10 -0
  15. cafein_lca-0.1.0/cafein/lca/data/itf-2020/constants.csv +25 -0
  16. cafein_lca-0.1.0/cafein/lca/data/itf-2020/delivery_legs.csv +11 -0
  17. cafein_lca-0.1.0/cafein/lca/data/itf-2020/electricity_pathways.csv +7 -0
  18. cafein_lca-0.1.0/cafein/lca/data/itf-2020/fuel_ghg.csv +17 -0
  19. cafein_lca-0.1.0/cafein/lca/data/itf-2020/fuel_wtt_energy.csv +3 -0
  20. cafein_lca-0.1.0/cafein/lca/data/itf-2020/hydrogen_pathways.csv +8 -0
  21. cafein_lca-0.1.0/cafein/lca/data/itf-2020/infrastructure_categories.csv +8 -0
  22. cafein_lca-0.1.0/cafein/lca/data/itf-2020/infrastructure_types.csv +7 -0
  23. cafein_lca-0.1.0/cafein/lca/data/itf-2020/manifest.toml +7 -0
  24. cafein_lca-0.1.0/cafein/lca/data/itf-2020/material_intensities.csv +15 -0
  25. cafein_lca-0.1.0/cafein/lca/data/itf-2020/modes.csv +131 -0
  26. cafein_lca-0.1.0/cafein/lca/data/itf-2020/power_gen_mix.csv +19 -0
  27. cafein_lca-0.1.0/cafein/lca/data/itf-2020/service_vehicles.csv +15 -0
  28. cafein_lca-0.1.0/cafein/lca/data/scenarios/finland_2020.toml +14 -0
  29. cafein_lca-0.1.0/cafein/lca/data/scenarios/india_metropolitan.toml +324 -0
  30. cafein_lca-0.1.0/cafein/lca/engine/__init__.py +1 -0
  31. cafein_lca-0.1.0/cafein/lca/engine/delivery.py +30 -0
  32. cafein_lca-0.1.0/cafein/lca/engine/infrastructure.py +86 -0
  33. cafein_lca-0.1.0/cafein/lca/engine/manufacturing.py +101 -0
  34. cafein_lca-0.1.0/cafein/lca/engine/services.py +81 -0
  35. cafein_lca-0.1.0/cafein/lca/engine/use.py +65 -0
  36. cafein_lca-0.1.0/cafein/lca/export.py +238 -0
  37. cafein_lca-0.1.0/cafein/lca/lca.py +185 -0
  38. cafein_lca-0.1.0/cafein/lca/modes.py +226 -0
  39. cafein_lca-0.1.0/cafein/lca/parameters.py +72 -0
  40. cafein_lca-0.1.0/cafein/lca/results.py +88 -0
  41. cafein_lca-0.1.0/cafein/lca/scenarios.py +205 -0
  42. cafein_lca-0.1.0/pyproject.toml +57 -0
@@ -0,0 +1,6 @@
1
+ [run]
2
+ branch = true
3
+ source = cafein.lca
4
+
5
+ [report]
6
+ show_missing = true
@@ -0,0 +1,4 @@
1
+ [flake8]
2
+ max-line-length = 88
3
+ extend-ignore = E203
4
+ exclude = .git,__pycache__,build,dist,docs
@@ -0,0 +1,15 @@
1
+ plans/
2
+ __pycache__/
3
+ *.egg-info/
4
+ dist/
5
+ build/
6
+ cache/
7
+ docs/_build/
8
+ docs/generated/
9
+
10
+
11
+ CLAUDE.md
12
+ .DS_Store
13
+ # local working files
14
+ resource-docs/
15
+ scripts/extract_workbook.py
@@ -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"
@@ -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,3 @@
1
+ step,energy_mj_per_kg_coal_al,energy_mj_per_kg_default,ghg_g_per_kg_coal_al,ghg_g_per_kg_default
2
+ assembly,8.224625841912632,8.224625841912632,538.6328307374549,538.6328307374549
3
+ disposal,2.265756636805004,2.265756636805004,153.35318310942364,153.35318310942364
@@ -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