""" This module contains the code required to parse BOLSIG+-compatible files.
To make the code re-usabe in other projects it is independent from the rest of
the BOLOS code.
Most user would only use the method :func:`parse` in this module, which is
documented below.
"""
import sys
import re
import numpy as np
import logging
[docs]def parse(fp):
""" Parses a BOLSIG+ cross-sections file.
Parameters
----------
fp : file-like
A file object pointing to a Bolsig+-compatible cross-sections file.
Returns
-------
processes : list of dictionaries
A list with all processes, in dictionary form, included in the file.
Note
----
This function does not return :class:`process.Process` instances so that
the parser is independent of the rest of the code and can be re-used in
other projects. If you want to convert a process in dictionary form `d` to
a :class:`process.Process` instance, use
>>> process = process.Process(**d)
"""
processes = []
for line in fp:
try:
key = line.strip()
fread = KEYWORDS[key]
# If the key is not found, we do not reach this line.
logging.debug("New process of type '%s'" % key)
d = fread(fp)
d['kind'] = key
processes.append(d)
except KeyError:
pass
logging.info("Parsing complete. %d processes read." % len(processes))
return processes
# BOLSIG+'s user guide saye that the separators must consist of at least five dashes
RE_SEP = re.compile("-----+")
def _read_until_sep(fp):
""" Reads lines from fp until a we find a separator line. """
lines = []
for line in fp:
if RE_SEP.match(line.strip()):
break
lines.append(line.strip())
return lines
def _read_block(fp, has_arg=True):
""" Reads data of a process, contained in a block.
has_arg indicates wether we have to read an argument line"""
target = fp.next().strip()
if has_arg:
arg = fp.next().strip()
else:
arg = None
comment = "\n".join(_read_until_sep(fp))
logging.debug("Read process '%s'" % target)
data = np.loadtxt(_read_until_sep(fp)).tolist()
return target, arg, comment, data
#
# Specialized funcion for each keyword. They all return dictionaries with the
# relevant attibutes.
#
def _read_momentum(fp):
""" Reads a MOMENTUM or EFFECTIVE block. """
target, arg, comment, data = _read_block(fp, has_arg=True)
mass_ratio = float(arg.split()[0])
d = dict(target=target,
mass_ratio=mass_ratio,
comment=comment,
data=data)
return d
RE_ARROW = re.compile('<?->')
def _read_excitation(fp):
""" Reads an EXCITATION or IONIZATION block. """
target, arg, comment, data = _read_block(fp, has_arg=True)
lhs, rhs = [s.strip() for s in RE_ARROW.split(target)]
d = dict(target=lhs,
product=rhs,
comment=comment,
data=data)
if '<->' in target.split():
threshold, weight_ratio = float(arg.split()[0]), float(arg.split()[1])
d['weight_ratio'] = weight_ratio
else:
threshold = float(arg.split()[0])
d['threshold'] = threshold
return d
def _read_attachment(fp):
""" Reads an ATTACHMENT block. """
target, arg, comment, data = _read_block(fp, has_arg=False)
d = dict(comment=comment,
data=data,
threshold=0.0)
lr = [s.strip() for s in RE_ARROW.split(target)]
if len(lr) == 2:
d['target'] = lr[0]
d['product'] = lr[1]
else:
d['target'] = target
return d
KEYWORDS = {"MOMENTUM": _read_momentum,
"ELASTIC": _read_momentum,
"EFFECTIVE": _read_momentum,
"EXCITATION": _read_excitation,
"IONIZATION": _read_excitation,
"ATTACHMENT": _read_attachment}