Skip to content

Commit

Permalink
First commit
Browse files Browse the repository at this point in the history
  • Loading branch information
romainsacchi committed Apr 5, 2021
1 parent 2297988 commit 76e54a8
Show file tree
Hide file tree
Showing 154 changed files with 42,210 additions and 61,421 deletions.
9 changes: 7 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,17 @@ before_install:
https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh;
bash miniconda.sh -b -p $HOME/miniconda; export PATH="$HOME/miniconda/bin:$PATH";
hash -r; conda config --set always_yes yes --set changeps1 no; conda config --append
channels conda-forge; conda config --append channels cmutel; conda update conda;
channels conda-forge;
conda config --append channels cmutel;
conda config --append channels haasad;
conda config --append channels konstantinstadler;
conda update conda;
conda config --set channel_priority strict;
conda info -a; echo $(python --version);
fi
install:
- if [ "$TRAVIS_OS_NAME" == "linux" ]; then conda create -n tests-environment python=$TRAVIS_PYTHON_VERSION
pandas xarray xlrd numpy pytest pytest-cov coveralls klausen numexpr bw2io prettytable;
pandas xarray xlrd numpy pytest pytest-cov coveralls klausen numexpr bw2io prettytable wurst pycountry;
source activate tests-environment;
pip3 install -e .;
else pip3 install -r ci/macos-travis.txt; pip3 install -e .;
Expand Down
61 changes: 58 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,76 @@
</p>

<p align="center">
<a href="https://badge.fury.io/py/carculator-bus" target="_blank"><img src="https://badge.fury.io/py/carculator-bus.svg"></a>
<a href="https://badge.fury.io/py/carculator-truck" target="_blank"><img src="https://badge.fury.io/py/carculator-truck.svg"></a>
<a href="https://travis-ci.org/romainsacchi/carculator_bus" target="_blank"><img src="https://travis-ci.org/romainsacchi/carculator_bus.svg?branch=master"></a>
<a href="https://ci.appveyor.com/project/romainsacchi/carculator_bus" target="_blank"><img src="https://ci.appveyor.com/api/projects/status/github/romainsacchi/carculator_bus?svg=true"></a>
<a href="https://coveralls.io/github/romainsacchi/carculator_bus" target="_blank"><img src="https://coveralls.io/repos/github/romainsacchi/carculator_bus/badge.svg"></a>
<a href="https://carculator_bus.readthedocs.io/en/latest/" target="_blank"><img src="https://readthedocs.org/projects/carculator_bus/badge/?version=latest"></a>
</p>

Prospective environmental and economic life cycle assessment of buses and coaches.
Prospective environmental and economic life cycle assessment of medium and heavy duty vehicles.

A fully parameterized Python model developed by the [Technology Assessment group](https://www.psi.ch/en/ta) of the
[Paul Scherrer Institut](https://www.psi.ch/en) to perform life cycle assessments (LCA) of buses and coaches.
[Paul Scherrer Institut](https://www.psi.ch/en) to perform life cycle assessments (LCA) of medium and heavy duty trucks.
Based on the Life Cycle Assessment tool for passenger vehicles [carculator](https://github.com/romainsacchi/carculator).

See [the documentation](https://carculator_bus.readthedocs.io/en/latest/index.html) for more detail, validation, etc.

The model has been the subject of a submission to the journal <i>Environmental Science and Technology</i>.
You may find a preprint version <a href="https://www.psi.ch/en/ta/preprint" target="_blank">here<a/>.


## How to install?

For the latest version, using conda::

conda install -c romainsacchi carculator_bus

or for a stable release, from Pypi::

pip install carculator_bus


## What does it do?

<i>carculator_bus</i> allows to model vehicles across:
<ul>
<li>different conventional and alternative powertrains: diesel, compressed natural gas, hybrid-diesel, plugin hybrid, electric, fuel cell</li>
<li>different gross weight cateogries: 3.5t, 7.5t, 18t, 26t, 32t, 40t and 60t</li>
<li>different fuel pathways: conventional fuels, bio-based fuels (biodiesel, biomethane), synthetic fuels
(Fischer-Tropsch-based synthetic diesel, synhtetic methane)</li>
<li>different years: from 2000 to 2050. Technological progress at the vehicle level but also in the rest of the world energy
system (e.g., power generation) is accounted for, using energy scenario-specific IAM-coupled ecoinvent databases produced by
<a href="https://github.com/romainsacchi/premise" target="_blank">premise</a>.</li>
<li>Inventories can be imported into <a href="https://brightway.dev/" target="_blank">Brightway2</a> and
<a href="https://www.simapro.com/" target="_blank">SimaPro 9.x.</a>.</li>
</ul>

<p align="center">
The energy model of <i>carculator_bus</i> considers the vehicle aerodynamics, the road gradient and other factors.
It also considers varying efficiencies of the transmission and engine at various load points for each second
of the driving cycle.
<img style="height:50px;" src="https://github.com/romainsacchi/carculator_bus/raw/master/docs/energy_model.png">
</p>

<p align="center">
The energy model and the calculated tank-to-wheel energy consumption is validated against the simulation software
<a href="https://ec.europa.eu/clima/policies/transport/vehicles/vecto_en" target="_blank">VECTO</a>.
<img style="height:50px;" src="https://github.com/romainsacchi/carculator_bus/raw/master/docs/vecto_validation.png">
</p>

<p align="center">
Benefits of hybrid powertrains are fully conidered: the possibility to recuperate braking energy as well as efficiency gains from engine
downsizing is accounted for.
<img style="height:50px;" src="https://github.com/romainsacchi/carculator_bus/raw/master/docs/hybrid_efficiency.png">
</p>

<p align="center">
Global warming potential impacts per ton-km for a 40-t truck, across different powertrain technologies,
using an urban driving cycle.
<img style="height:50px;" src="https://github.com/romainsacchi/carculator_bus/raw/master/docs/urban_gwp.png">
</p>

## How to use it?

See the notebook with [examples](https://github.com/romainsacchi/carculator_bus/blob/master/examples/Examples.ipynb).
Expand Down
17 changes: 11 additions & 6 deletions carculator_bus/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,34 +10,39 @@
"""

_all_ = (
"TruckInputParameters",
"BusInputParameters",
"fill_xarray_from_input_parameters",
"modify_xarray_from_custom_parameters",
"TruckModel",
"BusModel",
"get_standard_driving_cycle",
"EnergyConsumptionModel",
"get_gradients",
"HotEmissionsModel"
"HotEmissionsModel",
"create_fleet_composition_from_IAM_file",
"extract_electricity_mix_from_IAM_file",
"extract_biofuel_shares_from_IAM"
)

# library version
__version__ = (0, 0, 4)
__version__ = (0, 0, 1)

from pathlib import Path

DATA_DIR = Path(__file__).resolve().parent / "data"

from .truck_input_parameters import TruckInputParameters
from .bus_input_parameters import BusInputParameters
from .array import (
fill_xarray_from_input_parameters,
modify_xarray_from_custom_parameters,
)
from .driving_cycles import get_standard_driving_cycle
from .gradients import get_gradients
from .energy_consumption import EnergyConsumptionModel
from .model import TruckModel
from .model import BusModel
from .hot_emissions import HotEmissionsModel
from .inventory import InventoryCalculation
from .utils import (create_fleet_composition_from_IAM_file, extract_electricity_mix_from_IAM_file,
extract_biofuel_shares_from_IAM)



168 changes: 121 additions & 47 deletions carculator_bus/array.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
from .truck_input_parameters import TruckInputParameters as t_i_p
from .bus_input_parameters import BusInputParameters as b_i_p
import numpy as np
import pandas as pd
import stats_arrays as sa
import xarray as xr


def fill_xarray_from_input_parameters(tip, sensitivity=False):
def fill_xarray_from_input_parameters(bip, sensitivity=False, scope=None):
"""Create an `xarray` labeled array from the sampled input parameters.
Expand All @@ -16,102 +16,176 @@ def fill_xarray_from_input_parameters(tip, sensitivity=False):
:param sensitivity:
:param tip: Instance of the :class:`TruckInputParameters` class in :mod:`truck_input_parameters`.
:param bip: Instance of the :class:`BusInputParameters` class in :mod:`truck_input_parameters`.
:returns: `tuple`, `xarray.DataArray`
- tuple (`size_dict`, `powertrain_dict`, `parameter_dict`, `year_dict`)
- array
Dimensions of `array`:
0. Vehicle size, e.g. "3.5t", "7.5t", etc. str.
1. Powertrain, e.g. "ICE-d", "BEV". str.
0. Vehicle size, e.g. "9m", "13m-city", "18m", etc. str.
1. Powertrain, e.g. "ICE-d", "BEV-opp". str.
2. Year. int.
3. Samples.
"""

# Check whether the argument passed is an instance of :class:`TruckInputParameters`
if not isinstance(tip, t_i_p):
# Check whether the argument passed is an instance of :class:`BusInputParameters`
if not isinstance(bip, b_i_p):
raise TypeError(
"The argument passed is not an object of the TruckInputParameter class"
)

if scope is None:
scope = {
"size": bip.sizes,
"powertrain": bip.powertrains,
"year": bip.years
}
else:
if "size" not in scope:
scope["size"] = bip.sizes
if "powertrain" not in scope:
scope["powertrain"] = bip.powertrains
if "year" not in scope:
scope["year"] = bip.years

# Make sure to include PHEV-e and PHEV-c-d if
# PHEV-d is listed

if "PHEV-d" in scope["powertrain"]:
for pt in ["PHEV-e", "PHEV-c-d"]:
if pt not in scope["powertrain"]:
scope["powertrain"].append(pt)


if any(s for s in scope["size"] if s not in bip.sizes):
raise ValueError(
"One of the size types is not valid."
)

if any(y for y in scope["year"] if y not in bip.years):
raise ValueError(
"One of the years defined is not valid."
)

if any(pt for pt in scope["powertrain"] if pt not in bip.powertrains):
raise ValueError(
"One of the powertrain types is not valid."
)

# if the purpose is not to do a sensitivity analysis
# the dimension `value` of the array is as large as the number of iterations to perform
# that is, 1 in `static` mode, or several in `stochastic` mode.

d = {"9m": 1,
"13m-city": 2,
"13m-coach": 3,
"13m-city-double": 4,
"13m-coach-double": 5,
"18m": 6,
}

if not sensitivity:
array = xr.DataArray(
np.zeros(
(
len(tip.sizes),
len(tip.powertrains),
len(tip.parameters),
len(tip.years),
tip.iterations or 1,
len(scope["size"]),
len(scope["powertrain"]),
len(bip.parameters),
len(scope["year"]),
bip.iterations or 1,
)
),
coords=[
tip.sizes,
tip.powertrains,
tip.parameters,
tip.years,
np.arange(tip.iterations or 1),
sorted(scope["size"], key=lambda x: d[x]),
scope["powertrain"],
bip.parameters,
scope["year"],
np.arange(bip.iterations or 1),
],
dims=["size", "powertrain", "parameter", "year", "value"],
).astype("float32")
# if the purpose is ot do a sensitivity analysis
# then the length of the dimensions `value` equals the number of parameters
else:
params = ["reference"]
params.extend([a for a in tip.input_parameters])
params.extend([a for a in bip.input_parameters])
array = xr.DataArray(
np.zeros(
(
len(tip.sizes),
len(tip.powertrains),
len(tip.parameters),
len(tip.years),
len(scope["size"]),
len(scope["powertrain"]),
len(bip.parameters),
len(scope["year"]),
len(params),
)
),
coords=[tip.sizes, tip.powertrains, tip.parameters, tip.years, params, ],
coords=[bip.sizes, bip.powertrains, bip.parameters, bip.years, params, ],
dims=["size", "powertrain", "parameter", "year", "value"],
).astype("float32")

size_dict = {k: i for i, k in enumerate(tip.sizes)}
powertrain_dict = {k: i for i, k in enumerate(tip.powertrains)}
year_dict = {k: i for i, k in enumerate(tip.years)}
parameter_dict = {k: i for i, k in enumerate(tip.parameters)}
size_dict = {k: i for i, k in enumerate(scope["size"])}
powertrain_dict = {k: i for i, k in enumerate(scope["powertrain"])}
year_dict = {k: i for i, k in enumerate(scope["year"])}
parameter_dict = {k: i for i, k in enumerate(bip.parameters)}

if not sensitivity:
for param in tip:
# try:
array.loc[
dict(
powertrain=tip.metadata[param]["powertrain"],
size=tip.metadata[param]["sizes"],
year=tip.metadata[param]["year"],
parameter=tip.metadata[param]["name"],
)
] = tip.values[param]

for param in bip:

pwt = set(bip.metadata[param]["powertrain"]) if isinstance(bip.metadata[param]["powertrain"], list) \
else set([bip.metadata[param]["powertrain"]])

size = set(bip.metadata[param]["sizes"]) if isinstance(bip.metadata[param]["sizes"], list) \
else set([bip.metadata[param]["sizes"]])

year = set(bip.metadata[param]["year"]) if isinstance(bip.metadata[param]["year"], list) \
else set([bip.metadata[param]["year"]])

if pwt.intersection(scope["powertrain"]) \
and size.intersection(scope["size"]) \
and year.intersection(scope["year"]):
array.loc[
dict(
powertrain=[p for p in pwt
if p in scope["powertrain"]],
size=[s for s in size
if s in scope["size"]],
year=[y for y in year
if y in scope["year"]],
parameter=bip.metadata[param]["name"],
)
] = bip.values[param]

else:
# if `sensitivity` == True, the values of each parameter is
# incremented by 10% when `value` == `parameter`
for param in tip.input_parameters:
names = [n for n in tip.metadata if tip.metadata[n]["name"] == param]
for param in bip.input_parameters:
names = [n for n in bip.metadata if bip.metadata[n]['name'] == param]

pwt = set(bip.metadata[param]["powertrain"]) if isinstance(bip.metadata[param]["powertrain"], list) \
else set([bip.metadata[param]["powertrain"]])

size = set(bip.metadata[param]["sizes"]) if isinstance(bip.metadata[param]["sizes"], list) \
else set([bip.metadata[param]["sizes"]])

year = set(bip.metadata[param]["year"]) if isinstance(bip.metadata[param]["year"], list) \
else set([bip.metadata[param]["year"]])

for name in names:
vals = [
tip.values[name] for _ in range(0, len(tip.input_parameters) + 1)
]
vals[tip.input_parameters.index(param) + 1] *= 1.1
vals = [bip.values[name] for _ in range(0, len(bip.input_parameters) + 1)]
vals[bip.input_parameters.index(param) + 1] *= 1.1

array.loc[
dict(
powertrain=tip.metadata[name]["powertrain"],
size=tip.metadata[name]["sizes"],
year=tip.metadata[name]["year"],
parameter=tip.metadata[name]["name"],
powertrain=[p for p in pwt
if p in scope["powertrain"]],
size=[s for s in size
if s in scope["size"]],
year=[y for y in year
if y in scope["year"]],
parameter=bip.metadata[name]["name"],
)
] = vals

Expand Down
Loading

0 comments on commit 76e54a8

Please sign in to comment.