Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue 1182 verbose outputs and tagging PyBaMM citations #2862

Merged
merged 11 commits into from
Apr 13, 2023
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

# Features

- Added verbose logging to `pybamm.print_citations()` and citation tags for the `pybamm.Citations` class so that users can now see where the citations were registered when running simulations ([#2862](https://github.com/pybamm-team/PyBaMM/pull/2862))
- PyBaMM is now natively supported on Apple silicon chips (`M1/M2`) ([#2435](https://github.com/pybamm-team/PyBaMM/pull/2435))
- PyBaMM is now supported on Python `3.10` and `3.11` ([#2435](https://github.com/pybamm-team/PyBaMM/pull/2435))
- Updated to casadi 3.6, which required some changes to the casadi integrator. ([#2859](https://github.com/pybamm-team/PyBaMM/pull/2859))
Expand Down
91 changes: 85 additions & 6 deletions pybamm/citations.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import os
import warnings
import pybtex
from inspect import stack
from pybtex.database import parse_file, parse_string, Entry
from pybtex.scanner import PybtexError

Expand Down Expand Up @@ -35,6 +36,9 @@ def __init__(self):
# Dict mapping citations keys to BibTex entries
self._all_citations: dict[str, str] = dict()

# Dict mapping citation tags for use when registering citations
self._citation_tags = dict()

# store citation error
self._citation_err_msg = None

Expand All @@ -48,10 +52,21 @@ def _reset(self):
"""Reset citations to default only (only for testing purposes)"""
# Initialize empty papers to cite
self._papers_to_cite = set()
# Initialize empty citation tags
self._citation_tags = dict()
# Register the PyBaMM paper and the numpy paper
self.register("Sulzer2021")
self.register("Harris2020")

def _caller_name():
"""
Returns the qualified name of classes that call :meth:`register` internally.
This is used for tagging citations but only for verbose output
"""
# Attributed to https://stackoverflow.com/a/17065634
caller_name = stack()[2][0].f_locals["self"].__class__.__qualname__
return caller_name

def read_citations(self):
"""Reads the citations in `pybamm.CITATIONS.txt`. Other works can be cited
by passing a BibTeX citation to :meth:`register`.
Expand All @@ -78,6 +93,14 @@ def _add_citation(self, key, entry):
# Add to database
self._all_citations[key] = new_citation

def _add_citation_tag(self, key, entry):
"""Adds a tag for a citation key which represents the name of the class that
called :meth:`register`"""

# Add a citation tag to the citation_tags ordered dictionary with
# the key being the citation itself and the value being the name of the class
self._citation_tags[key] = entry

@property
def _cited(self):
"""Return a list of the BibTeX entries that have been cited"""
Expand All @@ -101,6 +124,14 @@ def register(self, key):
# Check if citation is a known key
if key in self._all_citations:
self._papers_to_cite.add(key)
# Add citation tags for the key
# This is used for verbose output
try:
caller = Citations._caller_name()
self._add_citation_tag(key, entry=caller)
# Don't add citation tags if the citation is registered manually
except KeyError: # pragma: no cover
pass
return

# Try to parse the citation using pybtex
Expand All @@ -119,14 +150,54 @@ def register(self, key):
# Unable to parse / unknown key
raise KeyError(f"Not a bibtex citation or known citation: {key}")

def print(self, filename=None, output_format="text"):
"""Print all citations that were used for running simulations.
def tag_citations(self): # pragma: no cover
"""Prints the citations tags for the citations that have been registered
(non-manually). This is used for verbose output when printing citations
such that it can be seen which citations were registered by PyBaMM classes.

To use, either call :meth:`tag_citations` after calling :meth:`register`
for all citations, or enable verbose output with :meth:`print_citations`
or :meth:`print`.

.. note::
If a citation is registered manually, it will not be tagged.

Examples
--------
.. code-block:: python
:linenos:

pybamm.citations.register("Doyle1993")
pybamm.citations.print() or pybamm.print_citations()

will print the following:

.. code-block::

Citations registered:
Sulzer2021 was cited due to the use of
pybamm.models.full_battery_models.lithium_ion.dfn

"""
if self._citation_tags:
print("\n Citations registered: \n")
for key, entry in self._citation_tags.items():
print(f"{key} was cited due to the use of {entry}")

def print(self, filename=None, output_format="text", verbose=False):
"""Print all citations that were used for running simulations. The verbose
option is provided to print the citation tags for the citations that have
been registered non-manually. This is available only upon printing
to the terminal.

Parameters
----------
filename : str, optional
Filename to which to print citations. If None, citations are printed to the
terminal.
Filename to which to print citations. If None, citations are printed
to the terminal.
verbose: bool, optional
If True, prints the citation tags for the citations that have been
registered
"""
if output_format == "text":
citations = pybtex.format_from_strings(
Expand All @@ -142,12 +213,14 @@ def print(self, filename=None, output_format="text"):

if filename is None:
print(citations)
if verbose:
self.tag_citations() # pragma: no cover
else:
with open(filename, "w") as f:
f.write(citations)


def print_citations(filename=None, output_format="text"):
def print_citations(filename=None, output_format="text", verbose=False):
"""See :meth:`Citations.print`"""
if citations._citation_err_msg is not None:
raise ImportError(
Expand All @@ -159,7 +232,13 @@ def print_citations(filename=None, output_format="text"):
f"{citations._citation_err_msg}"
)
else:
pybamm.citations.print(filename, output_format)
if verbose: # pragma: no cover
warnings.warn(
"Verbose output is not available for printing to files, only to the terminal" # noqa: E501
)
pybamm.citations.print(filename, output_format, verbose=True)
else:
pybamm.citations.print(filename, output_format)


citations = Citations()
Loading