Source code for scine_chemoton.reaction_rules.element_rules

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
__copyright__ = """ This code is licensed under the 3-clause BSD license.
Copyright ETH Zurich, Department of Chemistry and Applied Biosciences, Reiher Group.
See LICENSE.txt for details.
"""

# Standard library imports
from abc import abstractmethod
from typing import List, Union, Dict

# Local application imports
from . import valid_element, RuleSet, BaseRule


[docs]class ElementBaseRule(BaseRule): """ A rule that defines reactivity simply based on element combinations """ def __init__(self, base_element: str, other_elements: Union[List[str], str]) -> None: """ Parameters ---------- base_element : str The element that is the base of the rule other_elements : Union[List[str], str] The other elements that are allowed to be combined with the base element """ super().__init__() if not valid_element(base_element): raise ValueError(f"{base_element} is not a valid element") self.base_element = base_element if isinstance(other_elements, str): self._other_elements = [other_elements] else: self._other_elements = other_elements for element in self._other_elements: if not valid_element(element): raise ValueError(f"{element} is not a valid element") def __repr__(self) -> str: return f"{self.__class__.__name__}('{self.base_element}', {repr(self._other_elements)})"
[docs] @abstractmethod def rule_applies(self, element_1: str, element_2: str) -> bool: """ If the rule applies for the two given elements """ pass # pylint: disable=unnecessary-pass
[docs]class SimpleElementCombinationRule(ElementBaseRule): """ A rule that defines possible combinations of elements. """
[docs] def rule_applies(self, element_1: str, element_2: str) -> bool: if element_1 == self.base_element and element_2 in self._other_elements: return True return element_2 == self.base_element and element_1 in self._other_elements
[docs]class ElementRuleSet(RuleSet): """ A dictionary holding elements as keys and either multiple or a single element as values. All keys and values are checked for valid element types. """ def __init__(self, kwargs: Dict[str, Union[ElementBaseRule, str, List[str]]], rule_type: type = SimpleElementCombinationRule) -> None: super().__init__(kwargs) self._rule_type = rule_type for k, v in kwargs.items(): if not isinstance(k, str): raise TypeError(f"{self.__class__.__name__} expects strings as keys") if not valid_element(k): raise TypeError(f"{k} is not a valid element symbol") if not isinstance(v, ElementBaseRule): rule = rule_type(k, v) self.data[k] = rule assert all(isinstance(v, ElementBaseRule) for v in self.data.values()) if any(k != v.base_element for k, v in self.data.items()): raise ValueError("All keys must be the same as the base element of their rule") def __repr__(self) -> str: return f"{self.__class__.__name__}({repr(self.data)}, {self._rule_type.__name__})"