diff --git a/lciafmt/__init__.py b/lciafmt/__init__.py index 382019a..3843c40 100644 --- a/lciafmt/__init__.py +++ b/lciafmt/__init__.py @@ -129,11 +129,13 @@ def clear_cache(): cache.clear() -def to_jsonld(df: pd.DataFrame, zip_file: str, write_flows=False): +def to_jsonld(df: pd.DataFrame, zip_file: str, write_flows=False, **kwargs): """Generate a JSONLD file of the methods passed as DataFrame.""" util.log.info(f"write JSON-LD package to {zip_file}") with jsonld.Writer(zip_file) as w: - w.write(df, write_flows) + w.write(df, write_flows, + preferred_only=kwargs.get('preferred_only', False), + ) def map_flows(df: pd.DataFrame, system=None, mapping=None, diff --git a/lciafmt/data/lcia.bib b/lciafmt/data/lcia.bib index 1fd71f2..5f96129 100644 --- a/lciafmt/data/lcia.bib +++ b/lciafmt/data/lcia.bib @@ -16,3 +16,32 @@ @article{huijbregts_recipe_2017 year = {2017}, DOI = {10.1007/s11367-016-1246-y} } + +@incollection{forster_changes_2007, + address = {Cambridge, UK and New York, NY}, + title = {Changes in {Atmospheric} {Constituents} and in {Radiative} {Forcing}}, + booktitle = {Climate {Change} 2007: {The} {Physical} {Science} {Basis}. {Contribution} of {Working} {Group} {I} to the {Fourth} {Assessment} {Report} of the {Intergovernmental} {Panel} on {Climate} {Change}}, + publisher = {Cambridge University Press}, + author = {Forster, Piers and Ramaswamy, Venkatachalam}, + year = {2007}, +} + +@incollection{myhre_anthropogenic_2013, + address = {Cambridge, UK and New York, NY}, + title = {Anthropogenic and {Natural} {Radiative} {Forcing}}, + url = {https://archive.ipcc.ch/pdf/assessment-report/ar5/wg1/WG1AR5_Chapter08_FINAL.pdf}, + booktitle = {Climate {Change} 2013: {The} {Physical} {Science} {Basis}. {Contribution} of {Working} {Group} {I} to the {Fifth} {Assessment} {Report} of the {Intergovernmental} {Panel} on {Climate} {Change}}, + publisher = {Cambridge University Press}, + author = {Myhre, Gunnar and Shindell, Drew}, + year = {2013}, +} + +@incollection{smith_earths_2021, + address = {Cambridge, UK and New York, NY}, + title = {The {Earth}'s {Energy} {Budget}, {Climate} {Feedbacks}, and {Climate} {Sensitivity} {Supplementary} {Material}}, + url = {https://www.ipcc.ch/report/ar6/wg1/downloads/report/IPCC_AR6_WGI_Chapter07_SM.pdf}, + booktitle = {Climate {Change} 2021: {The} {Physical} {Science} {Basis}. {Contribution} of {Working} {Group} {I} to the {Sixth} {Assessment} {Report} of the {Intergovernmental} {Panel} on {Climate} {Change}}, + publisher = {Cambridge University Press}, + author = {Smith, Chris and Nicholls, Zebedee R. J. and Armour, Kyle and Collins, William and Forster, Piers and Meinshausen, Malte and Palmer, Matthew D. and Watanabe, Masahiro}, + year = {2021}, +} diff --git a/lciafmt/data/methods.json b/lciafmt/data/methods.json index 92314dc..d971d5f 100644 --- a/lciafmt/data/methods.json +++ b/lciafmt/data/methods.json @@ -61,6 +61,10 @@ "path": "ipcc", "case_insensitivity": "False", "url": "https://github.com/USEPA/LCIAformatter/blob/master/lciafmt/data/IPCC_GWP_values.csv", + "bib_id": {"AR6": "smith_earths_2021", + "AR5": "myhre_anthropogenic_2013", + "AR4": "forster_changes_2007" + }, "citation": "Forster and Ramaswamy 2007 (AR4), Myhre and Shindell 2013 (AR5), Forster and Storelvmo 2021 (AR6)", "source_type": "csv" } diff --git a/lciafmt/jsonld.py b/lciafmt/jsonld.py index b2b0f0c..8eb10a9 100644 --- a/lciafmt/jsonld.py +++ b/lciafmt/jsonld.py @@ -17,6 +17,7 @@ from esupy.util import make_uuid from esupy.bibtex import generate_sources +import fedelemflowlist from .util import is_non_empty_str, generate_method_description,\ log, pkg_version_number, datapath, check_as_class @@ -30,6 +31,7 @@ def __init__(self, zip_file: str): self.__indicators = {} self.__flows = {} self.__sources = {} + self.__sources_to_write = {} self.__bibids = {} self.__bibpath = datapath / 'lcia.bib' @@ -39,7 +41,7 @@ def __enter__(self): def __exit__(self, exc_type, exc_val, exc_tb): self.__writer.close() - def write(self, df: pd.DataFrame, write_flows=False): + def write(self, df: pd.DataFrame, write_flows=False, preferred_only=False): if 'source_method' not in df: df['source_method'] = df['Method'] if 'source_indicator' not in df: @@ -47,11 +49,24 @@ def write(self, df: pd.DataFrame, write_flows=False): if 'category' not in df: df['category'] = df['Method'] - for method in df['source_method'].unique(): + methods = pd.unique( + df[['Method', 'source_method']].values.ravel('K')) + indicators = pd.unique( + df[['Indicator', 'source_indicator']].values.ravel('K')) + + # identify all relevant bib_ids and sources + for method in methods: m = check_as_class(method) + if isinstance(m, str): + # not a recognized method, so no bib_id + continue bib = m.get_metadata().get('bib_id') if bib: - self.__bibids[bib] = m.value + if isinstance(bib, str): + self.__bibids[bib] = m.value + elif isinstance(bib, dict): + for k,v in bib.items(): + self.__bibids[v] = f'{m.value} {k}' for i in generate_sources(self.__bibpath, self.__bibids): self.__sources[i.id] = i @@ -69,10 +84,19 @@ def write(self, df: pd.DataFrame, write_flows=False): dicts = [ self.__indicators, self.__methods, - self.__sources + self.__sources_to_write ] if write_flows: - dicts.append(self.__flows) + log.info("writing flows from the fedelemflowlist ...") + flowlist = fedelemflowlist.get_flows(preferred_only) + flow_dict = self.__flows + flows = flowlist.query('`Flow UUID` in @flow_dict.keys()') + if preferred_only: + log.info("writing only preferred flows ...") + elif len(flows) != len(flow_dict): + log.warning("not all flows written...") + fedelemflowlist.write_jsonld(flows, path=None, + zw = self.__writer) for d in dicts: for v in d.values(): self.__writer.write(v) @@ -100,9 +124,14 @@ def __indicator(self, row) -> o.ImpactCategory: row['source_indicator']) ind.impact_factors = [] ind.version = pkg_version_number - source = self._return_source(row['Method']) + source = (self._return_source(row['source_method']) or + self._return_source(row['Method'] + ' ' + + row['Indicator']) or + self._return_source(row['source_method'] + ' ' + + row['source_indicator'])) if source: ind.source = source.to_ref() + self.__sources_to_write[source.id] = source self.__indicators[uid] = ind method = self.__method(row) @@ -121,6 +150,10 @@ def __method(self, row) -> o.ImpactMethod: m.id = uid m.name = row['Method'] m.version = pkg_version_number + source = self._return_source(row['Method']) + if source: + m.source = source.to_ref() + self.__sources_to_write[source.id] = source m.impact_categories = [] m.description = generate_method_description(row['Method']) self.__methods[uid] = m @@ -160,6 +193,6 @@ def __flow(self, row): def _return_source(self, name): for uid, s in self.__sources.items(): - if s.name == name: + if s.name == name or name.startswith(s.name): return s return None diff --git a/lciafmt/util.py b/lciafmt/util.py index 6946131..6ef28e1 100644 --- a/lciafmt/util.py +++ b/lciafmt/util.py @@ -21,7 +21,7 @@ # set version number of package, needs to be updated with setup.py -pkg_version_number = '1.1.2' +pkg_version_number = '1.1.3' MODULEPATH = Path(__file__).resolve().parent datapath = MODULEPATH / 'data' @@ -272,7 +272,8 @@ def download_method(method_id): download_from_remote(meta, paths) -def save_json(method_id, mapped_data, method=None, name='', write_flows=False): +def save_json(method_id, mapped_data, method=None, name='', + write_flows=False, **kwargs): """Save a method as json file in the outputpath. :param method_id: class Method @@ -288,12 +289,13 @@ def save_json(method_id, mapped_data, method=None, name='', write_flows=False): filename = name if method is not None: filename = method.replace('/', '_') - mapped_data = mapped_data[mapped_data['Method'] == method] + mapped_data = mapped_data.query('Method == @method').reset_index(drop=True) path = OUTPUTPATH / meta.category mkdir_if_missing(OUTPUTPATH) json_pack = path / f'{filename}_json_v{meta.tool_version}.zip' json_pack.unlink(missing_ok=True) - lciafmt.to_jsonld(mapped_data, json_pack, write_flows=write_flows) + lciafmt.to_jsonld(mapped_data, json_pack, write_flows=write_flows, + **kwargs) def compare_to_remote(local_df, method_id): diff --git a/requirements.txt b/requirements.txt index cd15a9c..a40f8db 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -git+https://github.com/USEPA/Federal-LCA-Commons-Elementary-Flow-List.git#egg=fedelemflowlist +git+https://github.com/USEPA/fedelemflowlist.git#egg=fedelemflowlist git+https://github.com/USEPA/esupy.git#egg=esupy olca-schema>=0.0.11 pandas>=0.23 diff --git a/setup.py b/setup.py index bd516b9..d0e0e56 100644 --- a/setup.py +++ b/setup.py @@ -2,13 +2,13 @@ setup( name="lciafmt", - version="1.1.2", + version="1.1.3", packages=["lciafmt"], package_dir={'lciafmt': 'lciafmt'}, package_data={'lciafmt': ["data/*.*"]}, include_package_data=True, python_requires=">=3.9", - install_requires=["fedelemflowlist @ git+https://github.com/USEPA/Federal-LCA-Commons-Elementary-Flow-List.git#egg=fedelemflowlist", + install_requires=["fedelemflowlist @ git+https://github.com/USEPA/fedelemflowlist.git#egg=fedelemflowlist", "esupy @ git+https://github.com/USEPA/esupy.git#egg=esupy", "olca-schema>=0.0.11", "pandas>=0.22", diff --git a/tests/test_generate_methods.py b/tests/test_generate_methods.py index b25e065..4d516e5 100644 --- a/tests/test_generate_methods.py +++ b/tests/test_generate_methods.py @@ -41,7 +41,8 @@ def test_method_write_json(): lciafmt.util.save_json(method_id = method_id, name = 'test_TRACI', mapped_data = method, - write_flows=True) + write_flows=True, + preferred_only=True) # Test FEDEFL Inventory method_id = lciafmt.Method.FEDEFL_INV method = lciafmt.get_mapped_method(method_id = method_id,