"""
This module contains general purpose functions that are used throughout PBjam.
"""
from . import PACKAGEDIR
import os
import numpy as np
from scipy.special import erf
[docs]class references():
""" A class for managing references used when running PBjam.
This is inherited by session and star.
Attributes
----------
bibfile : str
The pathname to the pbjam references list.
_reflist : list
List of references that is updated when new functions are used.
bibdict : dict
Dictionary of bib items from the PBjam reference list.
"""
def __init__(self):
self.bibfile = os.path.join(*[PACKAGEDIR, 'data', 'pbjam_references.bib'])
self._reflist = []
self.bibdict = self._parseBibFile()
def _findBlockEnd(self, string, idx):
""" Find block of {}
Go through string starting at idx, and find the index corresponding to
the curly bracket that closes the opening curly bracket.
So { will be closed by } even if there are more curly brackets in
between.
Note
----
This also works in reverse, so opening with } will be closed by {.
Parameters
----------
string : str
The string to parse.
idx : int
The index in string to start at.
"""
a = 0
for i, char in enumerate(string[idx:]):
if char == '{':
a -= 1
elif char == '}':
a += 1
if (i >= len(string[idx:])-1) and (a != 0):
print('Warning: Reached end of bibtex file with no closing curly bracket. Your .bib file may be formatted incorrectly. The reference list may be garbled.')
if a ==0:
break
if string[idx+i] == '{':
print('Warning: Ended on an opening bracket. Your .bib file may be formatted incorrectly.')
return idx+i
def _parseBibFile(self):
""" Put contents of a bibtex file into a dictionary.
Takes the contents of the PBjam bib file and stores it as a dictionary
of bib items.
Article shorthand names (e.g., @Article{shorthand_name) become the
dictionary key, similar to the way LaTeX handles citations.
Returns
-------
bibdict : dict
Dictionary of bib items from the PBjam reference list.
"""
with open(self.bibfile, 'r') as bib:
bib = bib.read()
openers = ['@ARTICLE', '@article', '@Article'
'@MISC', '@misc',
'@BOOK', '@book',
'@SOFTWARE', '@software',
'@INPROCEEDINGS', '@inproceedings'] #Update this if other types of entries are added to the bib file.
bibitems = []
safety = 0
while any([x in bib for x in openers]):
for opener in openers:
try:
start = bib.index(opener)
end = self._findBlockEnd(bib, start+len(opener))
bibitems.append([bib[start:end+1]])
bib = bib[:start] + bib[end+1:]
except:
pass
safety += 1
if safety > 1000:
break
bibitems = np.unique(bibitems)
bibdict = {}
for i, item in enumerate(bibitems):
key = item[item.index('{')+1:item.index(',')]
bibdict[key] = item
return bibdict
def _addRef(self, ref):
""" Add reference from bibdict to active list
The reference must be listed in the PBjam bibfile.
Parameters
----------
ref : str
Bib entry to add to the list
"""
if isinstance(ref, list):
for r in ref:
self._reflist.append(self.bibdict[r])
else:
self._reflist.append(self.bibdict[ref])
def __call__(self, bibfile=None):
""" Print the list of references used.
Parameters
----------
bibfile : str
Filepath to print the list of bib items.
"""
out = '\n\n'.join(np.unique(self._reflist))
print('References used in this run.')
print(out)
if bibfile is not None:
with open(bibfile, mode='w') as file_object: #robustify the filepath so it goes to the right place all the time.
print(out, file=file_object)
[docs]def isvalid(number):
""" Checks if number is finite.
Parameters
----------
number : object
Returns
-------
x : bool
Whether number a real float or not.
"""
if (number is None) or isinstance(number, str) or not np.isfinite(number):
return False
else:
return True
[docs]def get_priorpath():
""" Get default prior path name
Returns
-------
prior_file : str
Default path to the prior in the package directory structure.
"""
return os.path.join(*[PACKAGEDIR, 'data', 'prior_data.csv'])
[docs]def get_percentiles(X, nsigma = 2, **kwargs):
""" Get percentiles of an distribution
Compute the percentiles corresponding to sigma=1,2,3.. including the
median (50th), of an array.
Parameters
----------
X : numpy.array()
Array to find percentiles of
sigma : int, optional.
Sigma values to compute the percentiles of, e.g. 68% 95% are 1 and 2
sigma, etc. Default is 2.
kwargs : dict
Arguments to be passed to numpy.percentile
returns
-------
percentiles : numpy.array()
Numpy array of percentile values of X.
"""
a = np.array([0.5*(1+erf(z/np.sqrt(2))) for z in range(nsigma+1)])
percs = np.append((1-a[::-1][:-1]),a)*100
return np.percentile(X, percs, **kwargs)
[docs]def to_log10(x, xerr):
""" Transform to value to log10
Takes a value and related uncertainty and converts them to logscale.
Approximate.
Parameters
----------
x : float
Value to transform to logscale
xerr : float
Value uncertainty
Returns
-------
logval : list
logscaled value and uncertainty
"""
if xerr > 0:
return [np.log10(x), xerr/x/np.log(10.0)]
return [x, xerr]
[docs]def normal(x, mu, sigma):
""" Evaluate logarithm of normal distribution (not normalized!!)
Evaluates the logarithm of a normal distribution at x.
Inputs
------
x : float
Values to evaluate the normal distribution at.
mu : float
Distribution mean.
sigma : float
Distribution standard deviation.
Returns
-------
y : float
Logarithm of the normal distribution at x
"""
if (sigma < 0):
return 0.0
return -0.5 * (x - mu)**2 / sigma**2