Source code for qrules.settings

"""Default configuration for `qrules`.

It is possible to change some settings from the outside, for instance:

>>> import qrules as q
>>> q.settings.MAX_ANGULAR_MOMENTUM = 4
>>> q.settings.MAX_SPIN_MAGNITUDE = 3
"""

from copy import deepcopy
from enum import Enum, auto
from os.path import dirname, join, realpath
from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple, Union

from qrules.conservation_rules import (
    BaryonNumberConservation,
    BottomnessConservation,
    ChargeConservation,
    CharmConservation,
    ConservationRule,
    EdgeQNConservationRule,
    ElectronLNConservation,
    GraphElementRule,
    MassConservation,
    MuonLNConservation,
    StrangenessConservation,
    TauLNConservation,
    c_parity_conservation,
    clebsch_gordan_helicity_to_canonical,
    g_parity_conservation,
    gellmann_nishijima,
    helicity_conservation,
    identical_particle_symmetrization,
    isospin_conservation,
    isospin_validity,
    ls_spin_validity,
    parity_conservation,
    parity_conservation_helicity,
    spin_conservation,
    spin_magnitude_conservation,
    spin_validity,
)
from qrules.particle import Particle, ParticleCollection
from qrules.quantum_numbers import EdgeQuantumNumbers as EdgeQN
from qrules.quantum_numbers import NodeQuantumNumbers as NodeQN
from qrules.quantum_numbers import arange
from qrules.solving import EdgeSettings, NodeSettings

__QRULES_PATH = dirname(realpath(__file__))
ADDITIONAL_PARTICLES_DEFINITIONS_PATH: str = join(
    __QRULES_PATH, "additional_definitions.yml"
)

CONSERVATION_LAW_PRIORITIES: Dict[
    Union[GraphElementRule, EdgeQNConservationRule, ConservationRule], int
] = {
    MassConservation: 10,
    ElectronLNConservation: 45,
    MuonLNConservation: 44,
    TauLNConservation: 43,
    BaryonNumberConservation: 90,
    StrangenessConservation: 69,
    CharmConservation: 70,
    BottomnessConservation: 68,
    ChargeConservation: 100,
    spin_conservation: 8,
    spin_magnitude_conservation: 8,
    parity_conservation: 6,
    c_parity_conservation: 5,
    g_parity_conservation: 3,
    isospin_conservation: 60,
    ls_spin_validity: 89,
    helicity_conservation: 7,
    parity_conservation_helicity: 4,
    identical_particle_symmetrization: 2,
}
"""Determines the order with which to verify conservation rules."""


EDGE_RULE_PRIORITIES: Dict[GraphElementRule, int] = {
    gellmann_nishijima: 50,
    isospin_validity: 61,
    spin_validity: 62,
}
"""Determines the order with which to verify `.Edge` conservation rules."""


[docs]class InteractionType(Enum): """Types of interactions in the form of an enumerate.""" STRONG = auto() EM = auto() WEAK = auto()
[docs]def create_interaction_settings( # pylint: disable=too-many-locals,too-many-arguments formalism_type: str, particle_db: ParticleCollection, nbody_topology: bool = False, mass_conservation_factor: Optional[float] = 3.0, max_angular_momentum: int = 2, max_spin_magnitude: float = 2.0, ) -> Dict[InteractionType, Tuple[EdgeSettings, NodeSettings]]: """Create a container that holds the settings for `.InteractionType`.""" formalism_edge_settings = EdgeSettings( conservation_rules={ isospin_validity, gellmann_nishijima, spin_validity, }, rule_priorities=EDGE_RULE_PRIORITIES, qn_domains=_create_domains(particle_db), ) formalism_node_settings = NodeSettings( rule_priorities=CONSERVATION_LAW_PRIORITIES ) angular_momentum_domain = __get_ang_mom_magnitudes( nbody_topology, max_angular_momentum ) spin_magnitude_domain = __get_spin_magnitudes( nbody_topology, max_spin_magnitude ) if "helicity" in formalism_type: formalism_node_settings.conservation_rules = { spin_magnitude_conservation, helicity_conservation, } formalism_node_settings.qn_domains = { NodeQN.l_magnitude: angular_momentum_domain, NodeQN.s_magnitude: spin_magnitude_domain, } elif formalism_type == "canonical": formalism_node_settings.conservation_rules = { spin_magnitude_conservation } if nbody_topology: formalism_node_settings.conservation_rules = { spin_conservation, ls_spin_validity, } formalism_node_settings.qn_domains = { NodeQN.l_magnitude: angular_momentum_domain, NodeQN.l_projection: __extend_negative(angular_momentum_domain), NodeQN.s_magnitude: spin_magnitude_domain, NodeQN.s_projection: __extend_negative(spin_magnitude_domain), } if formalism_type == "canonical-helicity": formalism_node_settings.conservation_rules.update( { clebsch_gordan_helicity_to_canonical, ls_spin_validity, } ) formalism_node_settings.qn_domains.update( { NodeQN.l_projection: [0], NodeQN.s_projection: __extend_negative(spin_magnitude_domain), } ) if mass_conservation_factor is not None: formalism_node_settings.conservation_rules.add( MassConservation(mass_conservation_factor) ) interaction_type_settings = {} weak_node_settings = deepcopy(formalism_node_settings) weak_node_settings.conservation_rules.update( [ ChargeConservation(), ElectronLNConservation(), MuonLNConservation(), TauLNConservation(), BaryonNumberConservation(), identical_particle_symmetrization, ] ) weak_node_settings.interaction_strength = 10 ** (-4) weak_edge_settings = deepcopy(formalism_edge_settings) interaction_type_settings[InteractionType.WEAK] = ( weak_edge_settings, weak_node_settings, ) em_node_settings = deepcopy(weak_node_settings) em_node_settings.conservation_rules.update( { CharmConservation(), StrangenessConservation(), BottomnessConservation(), parity_conservation, c_parity_conservation, } ) if "helicity" in formalism_type: em_node_settings.conservation_rules.add(parity_conservation_helicity) em_node_settings.qn_domains.update({NodeQN.parity_prefactor: [-1, 1]}) em_node_settings.interaction_strength = 1 em_edge_settings = deepcopy(weak_edge_settings) interaction_type_settings[InteractionType.EM] = ( em_edge_settings, em_node_settings, ) strong_node_settings = deepcopy(em_node_settings) strong_node_settings.conservation_rules.update( {isospin_conservation, g_parity_conservation} ) strong_node_settings.interaction_strength = 60 strong_edge_settings = deepcopy(em_edge_settings) interaction_type_settings[InteractionType.STRONG] = ( strong_edge_settings, strong_node_settings, ) return interaction_type_settings
def __get_ang_mom_magnitudes( is_nbody: bool, max_angular_momentum: int ) -> List[float]: if is_nbody: return [0] return _int_domain(0, max_angular_momentum) # type: ignore def __get_spin_magnitudes( is_nbody: bool, max_spin_magnitude: float ) -> List[float]: if is_nbody: return [0] return _halves_domain(0, max_spin_magnitude) def _create_domains(particle_db: ParticleCollection) -> Dict[Any, list]: domains: Dict[Any, list] = { EdgeQN.electron_lepton_number: [-1, 0, +1], EdgeQN.muon_lepton_number: [-1, 0, +1], EdgeQN.tau_lepton_number: [-1, 0, +1], EdgeQN.parity: [-1, +1], EdgeQN.c_parity: [-1, +1, None], EdgeQN.g_parity: [-1, +1, None], } for edge_qn, getter in { EdgeQN.charge: lambda p: p.charge, EdgeQN.baryon_number: lambda p: p.baryon_number, EdgeQN.strangeness: lambda p: p.strangeness, EdgeQN.charmness: lambda p: p.charmness, EdgeQN.bottomness: lambda p: p.bottomness, }.items(): domains[edge_qn] = __extend_negative( __positive_int_domain(particle_db, getter) ) domains[EdgeQN.spin_magnitude] = __positive_halves_domain( particle_db, lambda p: p.spin ) domains[EdgeQN.spin_projection] = __extend_negative( domains[EdgeQN.spin_magnitude] ) domains[EdgeQN.isospin_magnitude] = __positive_halves_domain( particle_db, lambda p: 0 if p.isospin is None else p.isospin.magnitude, ) domains[EdgeQN.isospin_projection] = __extend_negative( domains[EdgeQN.isospin_magnitude] ) return domains def __positive_halves_domain( particle_db: ParticleCollection, attr_getter: Callable[[Particle], Any] ) -> List[float]: values = set(map(attr_getter, particle_db)) return _halves_domain(0, max(values)) def __positive_int_domain( particle_db: ParticleCollection, attr_getter: Callable[[Particle], Any] ) -> List[int]: values = set(map(attr_getter, particle_db)) return _int_domain(0, max(values)) def _halves_domain(start: float, stop: float) -> List[float]: if start % 0.5 != 0.0: raise ValueError(f"Start value {start} needs to be multiple of 0.5") if stop % 0.5 != 0.0: raise ValueError(f"Stop value {stop} needs to be multiple of 0.5") return [ int(v) if v.is_integer() else v for v in arange(start, stop + 0.25, delta=0.5) ] def _int_domain(start: int, stop: int) -> List[int]: return list(range(start, stop + 1)) def __extend_negative( magnitudes: Iterable[Union[int, float]] ) -> List[Union[int, float]]: return sorted(list(magnitudes) + [-x for x in magnitudes if x > 0])