Skip to content

Commit

Permalink
Merge pull request #123 from cmutel/bw25compat
Browse files Browse the repository at this point in the history
Support for both Brightway 2 and 2.5
  • Loading branch information
romainsacchi authored Oct 12, 2023
2 parents c444caf + 363a934 commit 98ed648
Show file tree
Hide file tree
Showing 9 changed files with 234 additions and 136 deletions.
11 changes: 2 additions & 9 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,7 @@ jobs:
run: |
pip install pathlib
pip install -r requirements.txt --upgrade pip
pip install -e .
pip install pytest
pip install pytest-cov
pip install coveralls
pip install -e .[test]
- name: Run tests
run: |
Expand Down Expand Up @@ -114,10 +111,6 @@ jobs:
run: >-
python -m
build
--sdist
--wheel
--outdir dist/
.
- name: Publish distribution 📦 to PyPI if Release
if: startsWith(github.ref, 'refs/tags')
Expand Down Expand Up @@ -229,4 +222,4 @@ jobs:
conda install bottleneck
pytest -m "not ecoinvent"
env:
IAM_FILES_KEY: ${{ secrets.IAM_FILES_KEY }}
IAM_FILES_KEY: ${{ secrets.IAM_FILES_KEY }}
32 changes: 32 additions & 0 deletions premise/brightway2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from bw2data import databases
from bw2io.importers.base_lci import LCIImporter
from wurst.linking import change_db_name, check_internal_linking, link_internal

from .utils import reset_all_codes


class BW2Importer(LCIImporter):
def __init__(self, db_name, data):
self.db_name = db_name
self.data = data
for act in self.data:
act["database"] = self.db_name

# we override `write_database`
# to allow existing databases
# to be overwritten
def write_database(self):
if self.db_name in databases:
print(f"Database {self.db_name} already exists: it will be overwritten.")
super().write_database()


def write_brightway_database(data, name, reset_codes=False):
# Restore parameters to Brightway2 format
# which allows for uncertainty and comments
change_db_name(data, name)
if reset_codes:
reset_all_codes(data)
link_internal(data)
check_internal_linking(data)
BW2Importer(name, data).write_database()
90 changes: 90 additions & 0 deletions premise/brightway25.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import itertools
from copy import copy

from bw2data import Database, databases
from bw2io.importers.base_lci import LCIImporter
from wurst.linking import change_db_name, check_internal_linking, link_internal

from .utils import reset_all_codes


class BW25Importer(LCIImporter):
def __init__(self, db_name, data):
self.db_name = db_name
self.data = data
for act in self.data:
act["database"] = self.db_name

# we override `write_database`
# to allow existing databases
# to be overwritten
def write_database(self):
def no_exchange_generator(data):
for ds in data:
cp = copy(ds)
cp["exchanges"] = []
yield cp

if self.db_name in databases:
print(f"Database {self.db_name} already exists: " "it will be overwritten.")
super().write_database(
list(no_exchange_generator(self.data)), backend="iotable"
)

dependents = {exc["input"][0] for ds in self.data for exc in ds["exchanges"]}
lookup = {
obj.key: obj.id
for obj in itertools.chain(*[Database(label) for label in dependents])
}

def technosphere_generator(data, lookup):
for ds in data:
target = lookup[(ds["database"], ds["code"])]
for exc in ds["exchanges"]:
if exc["type"] in (
"substitution",
"production",
"generic production",
):
yield {
"row": lookup[exc["input"]],
"col": target,
"amount": exc["amount"],
"flip": False,
}
elif exc["type"] == "technosphere":
yield {
"row": lookup[exc["input"]],
"col": target,
"amount": exc["amount"],
"flip": True,
}

def biosphere_generator(data, lookup):
for ds in data:
target = lookup[(ds["database"], ds["code"])]
for exc in ds["exchanges"]:
if exc["type"] == "biosphere":
yield {
"row": lookup[exc["input"]],
"col": target,
"amount": exc["amount"],
"flip": False,
}

Database(self.db_name).write_exchanges(
technosphere_generator(self.data, lookup),
biosphere_generator(self.data, lookup),
list(dependents),
)


def write_brightway_database(data, name, reset_codes=False):
# Restore parameters to Brightway2 format
# which allows for uncertainty and comments
change_db_name(data, name)
if reset_codes:
reset_all_codes(data)
link_internal(data)
check_internal_linking(data)
BW25Importer(name, data).write_database()
24 changes: 19 additions & 5 deletions premise/ecoinvent_modification.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"""

import copy
import logging
import multiprocessing
import os
import pickle
Expand Down Expand Up @@ -49,9 +50,22 @@
load_constants,
print_version,
warning_about_biogenic_co2,
write_brightway2_database,
)

logger = logging.getLogger("module")

try:
import bw_processing

from .brightway25 import write_brightway_database

logger.info("Using Brightway 2.5")
except ImportError:
from .brightway2 import write_brightway_database

logger.info("Using Brightway 2")


FILEPATH_OIL_GAS_INVENTORIES = INVENTORY_DIR / "lci-ESU-oil-and-gas.xlsx"
FILEPATH_CARMA_INVENTORIES = INVENTORY_DIR / "lci-Carma-CCS.xlsx"
FILEPATH_CO_FIRING_INVENTORIES = INVENTORY_DIR / "lci-co-firing-power-plants.xlsx"
Expand Down Expand Up @@ -1358,7 +1372,7 @@ def write_superstructure_db_to_brightway(
scenario_list=list_scenarios,
)

write_brightway2_database(
write_brightway_database(
data=self.database,
name=name,
reset_codes=True,
Expand All @@ -1371,7 +1385,7 @@ def write_superstructure_db_to_brightway(

def write_db_to_brightway(self, name: [str, List[str]] = None):
"""
Register the new database into an open brightway2 project.
Register the new database into an open brightway project.
:param name: to give a (list) of custom name(s) to the database.
Should either be a string if there's only one database to export.
Or a list of strings if there are several databases.
Expand Down Expand Up @@ -1405,7 +1419,7 @@ def write_db_to_brightway(self, name: [str, List[str]] = None):
"The number of databases does not match the number of `name` given."
)

print("Write new database(s) to Brightway2.")
print("Write new database(s) to Brightway.")

cache = {}

Expand Down Expand Up @@ -1438,7 +1452,7 @@ def write_db_to_brightway(self, name: [str, List[str]] = None):
)

for scen, scenario in enumerate(self.scenarios):
write_brightway2_database(
write_brightway_database(
scenario["database"],
name[scen],
)
Expand Down
30 changes: 0 additions & 30 deletions premise/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,8 @@
import pandas as pd
import xarray as xr
import yaml
from bw2data import databases
from bw2io.importers.base_lci import LCIImporter
from country_converter import CountryConverter
from prettytable import ALL, PrettyTable
from wurst.linking import change_db_name, check_internal_linking, link_internal
from wurst.searching import equals, get_many

from . import __version__
Expand Down Expand Up @@ -265,22 +262,6 @@ def hide_messages():
print("NewDatabase(..., quiet=True)")


class PremiseImporter(LCIImporter):
def __init__(self, db_name, data):
self.db_name = db_name
self.data = data
for act in self.data:
act["database"] = self.db_name

# we override `write_database`
# to allow existing databases
# to be overwritten
def write_database(self):
if self.db_name in databases:
print(f"Database {self.db_name} already exists: " "it will be overwritten.")
super().write_database()


def reset_all_codes(data):
"""
Re-generate all codes in each dataset of a database
Expand All @@ -297,17 +278,6 @@ def reset_all_codes(data):
return data


def write_brightway2_database(data, name, reset_codes=False):
# Restore parameters to Brightway2 format
# which allows for uncertainty and comments
change_db_name(data, name)
if reset_codes:
reset_all_codes(data)
link_internal(data)
check_internal_linking(data)
PremiseImporter(name, data).write_database()


def delete_log():
"""
Delete log file.
Expand Down
87 changes: 87 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
[build-system]
requires = ["setuptools>=61.0"]
build-backend = "setuptools.build_meta"

[project]
name = "premise"
authors = [
{ name="Romain Sacchi", email="[email protected]" },
{ name="Alois Dirnaichner", email="[email protected]" },
{ name=" Chris Mutel", email="[email protected]" }
]
maintainers = [
{ name="Romain Sacchi", email="[email protected]" }
]
description = "Coupling IAM output to ecoinvent LCA database ecoinvent for prospective LCA"
readme = "README.md"
dynamic = ["dependencies", "version"]
classifiers = [
"Intended Audience :: Science/Research",
"License :: OSI Approved :: BSD License",
"Natural Language :: English",
"Programming Language :: Python :: 3",
"Operating System :: OS Independent",
"Topic :: Scientific/Engineering",
]
requires-python = ">=3.9,<3.12"

[project.urls]
source = "https://github.com/polca/premise"
homepage = "https://github.com/polca/premise"
tracker = "https://github.com/polca/premise/issues"

[project.optional-dependencies]
testing = [
"setuptools",
"pytest",
"pytest-cov",
"coveralls"
]

docs = [
"sphinx-rtd-theme"
]
bw25 = [
"bw2analyzer >=0.11.4",
"bw2calc >=2.0.dev13",
"bw2data >=4.0.dev31",
"bw2io >=0.9.dev23",
"bw_processing >=0.8.2",
"matrix_utils >=0.2.5"
]

[tool.setuptools]
license-files = ["LICENSE"]
include-package-data = true
packages = ["premise"]

[tool.setuptools.dynamic]
dependencies = {file = ["requirements.txt"]}
version = {attr = "premise.__version__"}

[tool.pytest.ini_options]
markers = [
# marks tests that require ecoinvent (to be disabled on Travis)
"ecoinvent",
"serial"
]
norecursedirs = [
"dist",
"build",
".tox"
]
testpaths = ["tests/*.py"]

[tool.flake8]
# Some sane defaults for the code style checker flake8
max_line_length = 88
extend_ignore = ["E203", "W503"]
# ^ Black-compatible
# E203 and W503 have edge cases handled by black
exclude = [
".tox",
"build",
"dist",
".eggs",
"docs/conf.py",
]
7 changes: 0 additions & 7 deletions pytest.ini

This file was deleted.

6 changes: 4 additions & 2 deletions readthedocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,7 @@ formats: all
# Optionally set the version of Python and requirements required to build your docs
python:
install:
- requirements: docs/requirements.txt
- requirements: requirements.txt
- method: pip
path: .
extra_requirements:
- docs
Loading

0 comments on commit 98ed648

Please sign in to comment.