77import os
88import warnings
99import pybtex
10+ from inspect import stack
1011from pybtex .database import parse_file , parse_string , Entry
1112from pybtex .scanner import PybtexError
1213
@@ -35,6 +36,9 @@ def __init__(self):
3536 # Dict mapping citations keys to BibTex entries
3637 self ._all_citations : dict [str , str ] = dict ()
3738
39+ # Dict mapping citation tags for use when registering citations
40+ self ._citation_tags = dict ()
41+
3842 # store citation error
3943 self ._citation_err_msg = None
4044
@@ -48,10 +52,21 @@ def _reset(self):
4852 """Reset citations to default only (only for testing purposes)"""
4953 # Initialize empty papers to cite
5054 self ._papers_to_cite = set ()
55+ # Initialize empty citation tags
56+ self ._citation_tags = dict ()
5157 # Register the PyBaMM paper and the numpy paper
5258 self .register ("Sulzer2021" )
5359 self .register ("Harris2020" )
5460
61+ def _caller_name ():
62+ """
63+ Returns the qualified name of classes that call :meth:`register` internally.
64+ This is used for tagging citations but only for verbose output
65+ """
66+ # Attributed to https://stackoverflow.com/a/17065634
67+ caller_name = stack ()[2 ][0 ].f_locals ["self" ].__class__ .__qualname__
68+ return caller_name
69+
5570 def read_citations (self ):
5671 """Reads the citations in `pybamm.CITATIONS.txt`. Other works can be cited
5772 by passing a BibTeX citation to :meth:`register`.
@@ -78,6 +93,14 @@ def _add_citation(self, key, entry):
7893 # Add to database
7994 self ._all_citations [key ] = new_citation
8095
96+ def _add_citation_tag (self , key , entry ):
97+ """Adds a tag for a citation key which represents the name of the class that
98+ called :meth:`register`"""
99+
100+ # Add a citation tag to the citation_tags ordered dictionary with
101+ # the key being the citation itself and the value being the name of the class
102+ self ._citation_tags [key ] = entry
103+
81104 @property
82105 def _cited (self ):
83106 """Return a list of the BibTeX entries that have been cited"""
@@ -101,6 +124,14 @@ def register(self, key):
101124 # Check if citation is a known key
102125 if key in self ._all_citations :
103126 self ._papers_to_cite .add (key )
127+ # Add citation tags for the key
128+ # This is used for verbose output
129+ try :
130+ caller = Citations ._caller_name ()
131+ self ._add_citation_tag (key , entry = caller )
132+ # Don't add citation tags if the citation is registered manually
133+ except KeyError : # pragma: no cover
134+ pass
104135 return
105136
106137 # Try to parse the citation using pybtex
@@ -119,14 +150,54 @@ def register(self, key):
119150 # Unable to parse / unknown key
120151 raise KeyError (f"Not a bibtex citation or known citation: { key } " )
121152
122- def print (self , filename = None , output_format = "text" ):
123- """Print all citations that were used for running simulations.
153+ def tag_citations (self ): # pragma: no cover
154+ """Prints the citations tags for the citations that have been registered
155+ (non-manually). This is used for verbose output when printing citations
156+ such that it can be seen which citations were registered by PyBaMM classes.
157+
158+ To use, either call :meth:`tag_citations` after calling :meth:`register`
159+ for all citations, or enable verbose output with :meth:`print_citations`
160+ or :meth:`print`.
161+
162+ .. note::
163+ If a citation is registered manually, it will not be tagged.
164+
165+ Examples
166+ --------
167+ .. code-block:: python
168+ :linenos:
169+
170+ pybamm.citations.register("Doyle1993")
171+ pybamm.citations.print() or pybamm.print_citations()
172+
173+ will print the following:
174+
175+ .. code-block::
176+
177+ Citations registered:
178+ Sulzer2021 was cited due to the use of
179+ pybamm.models.full_battery_models.lithium_ion.dfn
180+
181+ """
182+ if self ._citation_tags :
183+ print ("\n Citations registered: \n " )
184+ for key , entry in self ._citation_tags .items ():
185+ print (f"{ key } was cited due to the use of { entry } " )
186+
187+ def print (self , filename = None , output_format = "text" , verbose = False ):
188+ """Print all citations that were used for running simulations. The verbose
189+ option is provided to print the citation tags for the citations that have
190+ been registered non-manually. This is available only upon printing
191+ to the terminal.
124192
125193 Parameters
126194 ----------
127195 filename : str, optional
128- Filename to which to print citations. If None, citations are printed to the
129- terminal.
196+ Filename to which to print citations. If None, citations are printed
197+ to the terminal.
198+ verbose: bool, optional
199+ If True, prints the citation tags for the citations that have been
200+ registered
130201 """
131202 if output_format == "text" :
132203 citations = pybtex .format_from_strings (
@@ -142,12 +213,14 @@ def print(self, filename=None, output_format="text"):
142213
143214 if filename is None :
144215 print (citations )
216+ if verbose :
217+ self .tag_citations () # pragma: no cover
145218 else :
146219 with open (filename , "w" ) as f :
147220 f .write (citations )
148221
149222
150- def print_citations (filename = None , output_format = "text" ):
223+ def print_citations (filename = None , output_format = "text" , verbose = False ):
151224 """See :meth:`Citations.print`"""
152225 if citations ._citation_err_msg is not None :
153226 raise ImportError (
@@ -159,7 +232,13 @@ def print_citations(filename=None, output_format="text"):
159232 f"{ citations ._citation_err_msg } "
160233 )
161234 else :
162- pybamm .citations .print (filename , output_format )
235+ if verbose : # pragma: no cover
236+ warnings .warn (
237+ "Verbose output is not available for printing to files, only to the terminal" # noqa: E501
238+ )
239+ pybamm .citations .print (filename , output_format , verbose = True )
240+ else :
241+ pybamm .citations .print (filename , output_format )
163242
164243
165244citations = Citations ()
0 commit comments