diff --git a/decaylanguage/__init__.py b/decaylanguage/__init__.py index 4e193864..6b1df889 100644 --- a/decaylanguage/__init__.py +++ b/decaylanguage/__init__.py @@ -1,6 +1,11 @@ +from __future__ import absolute_import + __version__ = '0.2.0' version = __version__ version_info = __version__.split('.') + +# Direct access to decay file parsing tools +from .dec import DecFileParser diff --git a/decaylanguage/data/decfile.lark b/decaylanguage/data/decfile.lark index 618f378a..c191bdac 100644 --- a/decaylanguage/data/decfile.lark +++ b/decaylanguage/data/decfile.lark @@ -44,7 +44,7 @@ model_options : (value | LABEL)+ _NEWLINE: ( /\r?\n[\t ]*/ | COMMENT )+ // We must set priorities here to use lalr - match model name above label, and label above something else -MODEL_NAME.2 : "BaryonPCR"|"BTO3PI_CP"|"BTOSLLALI"|"BTOSLLBALL"|"BTOXSGAMMA"|"BTOXSLL"|"CB3PI-MPP"|"CB3PI-P00"|"D_DALITZ"|"ETA_DALITZ"|"GOITY_ROBERTS"|"HELAMP"|"HQET"|"ISGW2"|"OMEGA_DALITZ"|"PARTWAVE"|"PHSP"|"PI0_DALITZ"|"PYTHIA"|"SLN"|"STS"|"SVP_HELAMP"|"SVS"|"SVV_HELAMP"|"TAUHADNU"|"TAULNUNU"|"TAUSCALARNU"|"TAUVECTORNU"|"TSS"|"TVS_PWAVE"|"VLL"|"VSP_PWAVE"|"VSS"|"VSS_BMIX"|"VUB"|"VVP"|"VVPIPI"|"VVS_PWAVE"|"SSD_CP" +MODEL_NAME.2 : "BaryonPCR"|"BTO3PI_CP"|"BTOSLLALI"|"BTOSLLBALL"|"BTOXSGAMMA"|"BTOXSLL"|"CB3PI-MPP"|"CB3PI-P00"|"D_DALITZ"|"ETA_DALITZ"|"GOITY_ROBERTS"|"HELAMP"|"HQET"|"ISGW2"|"LbAmpGen"|"OMEGA_DALITZ"|"PARTWAVE"|"PHSP"|"PI0_DALITZ"|"PYTHIA"|"SLN"|"STS"|"SVP_HELAMP"|"SVS"|"SVV_HELAMP"|"TAUHADNU"|"TAULNUNU"|"TAUSCALARNU"|"TAUVECTORNU"|"TSS"|"TVS_PWAVE"|"VLL"|"VSP_PWAVE"|"VSS"|"VSS_BMIX"|"VUB"|"VVP"|"VVPIPI"|"VVS_PWAVE"|"SSD_CP" LABEL : /[a-zA-Z0-9\/\-+*_()']+/ COMMENT : /[;#][^\n]*/ diff --git a/decaylanguage/dec/__init__.py b/decaylanguage/dec/__init__.py index e69de29b..10e7de86 100644 --- a/decaylanguage/dec/__init__.py +++ b/decaylanguage/dec/__init__.py @@ -0,0 +1,3 @@ +from __future__ import absolute_import + +from .dec import DecFileParser diff --git a/decaylanguage/dec/dec.py b/decaylanguage/dec/dec.py index 1b72d88d..0d2861c2 100644 --- a/decaylanguage/dec/dec.py +++ b/decaylanguage/dec/dec.py @@ -1,8 +1,40 @@ +""" +Submodule with classes and utilities to deal with and parse .dec decay files. + +Basic assumptions +----------------- + +1) For standard (non-alias) particle (names): + - Decay modes defined via a 'Decay' statement. + - Related antiparticle decay modes either defined via a 'CDecay' statement + or via a 'Decay' statement. The latter option is often used if CP matters. +2) For particle names defined via aliases: + - Particle decay modes defined as above. + - Related antiparticle decay modes defined with either options above, + *but* there needs to be a 'ChargeConj' statement specifying the + particle-antiparticle match. Typically:: + + Alias MyP+ P+ + Alias MyP- P- + ChargeConj MyP+ MyP- + Decay MyP+ + ... + Enddecay + CDecay MyP- + +3) As a consequence, particles that are self-conjugate should not be used + in 'CDecay' statements, obviously. +""" + from __future__ import absolute_import, division, print_function import os import warnings -from lark import Lark, Transformer, Tree + +from lark import Lark +from lark import Tree, Transformer, Visitor + +from particle import Particle, ParticleNotFound from ..data import open_text from .. import data @@ -31,62 +63,103 @@ class DecFileParser(object): Example ------- - >>> parser = DecFileParser('my-decay-file.dec') - >>> parser.parse() + >>> parsed_file = DecFileParser.from_file('my-decay-file.dec') """ __slots__ = ("_grammar", "_grammar_info", - "_decay_file", + "_dec_file_name", "_parsed_dec_file", - "_parsed_decays") + "_parsed_decays", + "_include_ccdecays") - def __init__(self, decay_file): + def __init__(self, filename): """ - Parser constructor. + Default constructor. Parameters ---------- - decay_file: str - Input .dec decay file. + filename: str + Input .dec decay file name. """ - # Conversion to handle pathlib on Python < 3.6: - decay_file = str(decay_file) - - # Check input file - if not os.path.exists(decay_file): - raise FileNotFoundError("'{0}'!".format(decay_file)) - self._grammar = None # Loaded Lark grammar definition file self._grammar_info = None # Name of Lark grammar definition file - self._decay_file = decay_file # Name of the input decay file - self._parsed_dec_file = None # Parsed decay file + # Conversion to handle pathlib on Python < 3.6: + self._dec_file_name = str(filename) # Name of the input decay file + self._parsed_dec_file = None # Parsed decay file self._parsed_decays = None # Particle decays found in the decay file - def parse(self): + # By default, consider charge-conjugate decays when parsing + self._include_ccdecays = True + + @classmethod + def from_file(cls, filename): """ - Parse the given .dec decay file according to the default Lark options. + Parse a .dec decay file. - See method 'load_grammar' for how to explicitly set the Lark parser - and the parsing options. + Parameters + ---------- + filename: str + Input .dec decay file name. """ - # Has a fine been parsed already? + # Conversion to handle pathlib on Python < 3.6: + filename = str(filename) + + # Check input file + if not os.path.exists(filename): + raise FileNotFoundError("'{0}'!".format(filename)) + + return cls(filename) + + def parse(self, include_ccdecays=True): + """ + Parse the given .dec decay file according to default Lark parser + and specified options, i.e., + parser = 'lalr', + lexer = 'standard'. + + See method 'load_grammar' for how to explicitly define the grammar + and set the Lark parsing options. + + Parameters + ---------- + include_ccdecays: boolean, optional, default=True + Choose whether or not to consider charge-conjugate decays, + which are specified via "CDecay ". + Make sure you understand the consequences of ignoring + charge conjugate decays - you won't have a complete picture! + """ + # Has a file been parsed already? if self._parsed_decays is not None: warnings.warn("Input file being re-parsed ...") - decay_file = open(self._decay_file).read() - parser = Lark(self.grammar, parser='lalr', lexer='standard') + # Override the parsing settings for charge conjugate decays + self._include_ccdecays = include_ccdecays if include_ccdecays else False - self._parsed_dec_file = parser.parse(decay_file) + # Retrieve all info on the default Lark grammar and its default options, + # effectively loading it + opts = self.grammar_info() + extraopts = dict((k, v) for k, v in opts.items() + if k not in ('lark_file', 'parser','lexer')) + # Instantiate the Lark parser according to chosen settings + parser = Lark(self.grammar(), parser=opts['parser'], lexer=opts['lexer']) + + dec_file = open(self._dec_file_name).read() + self._parsed_dec_file = parser.parse(dec_file) + + # At last, find all particle decays defined in the .dec decay file ... self._find_parsed_decays() - @property + # ... and create on the fly the charge conjugate decays, if requested + if self._include_ccdecays: + self._add_charge_conjugate_decays() + def grammar(self): """ - This accesses the internal Lark grammar definition file, + Access the internal Lark grammar definition file, loading it from the default location if needed. Returns @@ -99,9 +172,25 @@ def grammar(self): return self._grammar + def grammar_info(self): + """ + Access the internal Lark grammar definition file name and + parser options, loading the grammar from the default location if needed. + + Returns + ------- + out: dict + The Lark grammar definition file name and parser options. + """ + if not self.grammar_loaded: + self.load_grammar() + + return self._grammar_info + def load_grammar(self, filename=None, parser='lalr', lexer='standard', **options): """ - Load a Lark grammar definition file. + Load a Lark grammar definition file, either the default one, + or a user-specified one, optionally setting Lark parsing options. Parameters ---------- @@ -113,6 +202,7 @@ def load_grammar(self, filename=None, parser='lalr', lexer='standard', **options The Lark parser lexer mode to use. options: keyword arguments, optional Extra options to pass on to the parsing algorithm. + See Lark's Lark class for a description of available options for parser, lexer and options. """ @@ -134,15 +224,174 @@ def grammar_loaded(self): """Check to see if the Lark grammar definition file is loaded.""" return self._grammar is not None + def dict_definitions(self): + """ + Return a dictionary of all definitions in the input parsed file, + of the form "Define ", + as {'NAME1': VALUE1, 'NAME2': VALUE2, ...}. + """ + return get_definitions(self._parsed_dec_file) + + def dict_aliases(self): + """ + Return a dictionary of all alias definitions in the input parsed file, + of the form "Alias ", + as {'NAME1': ALIAS1, 'NAME2': ALIAS2, ...}. + """ + return get_aliases(self._parsed_dec_file) + + def dict_charge_conjugates(self): + """ + Return a dictionary of all charge conjugate definitions + in the input parsed file, of the form + "ChargeConj ", as + {'PARTICLE1': CC_PARTICLE1, 'PARTICLE2': CC_PARTICLE2, ...}. + """ + return get_charge_conjugate_defs(self._parsed_dec_file) + + def dict_pythia_definitions(self): + """ + Return a dictionary of all Pythia definitions in the input parsed file, + of the form + "PythiaBothParam =