Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update from comments for form and mult #549

Merged
merged 1 commit into from
Aug 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions automol/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@

- const
- util
- error
- mult
- error
- mult
- form
- inchi_key
- vmat
Expand Down
84 changes: 44 additions & 40 deletions automol/const.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
""" Handle Full Reaction-Class designations for reactions that
describe all meaningful attributes of the reaction required
for electronic structure and kinetic calculations.
"""Handle Full Reaction-Class designations for reactions that
describe all meaningful attributes of the reaction required
for electronic structure and kinetic calculations.
"""
import dataclasses
import enum


class ReactionClass(str, enum.Enum):
"""Reaction class names"""
"""Reaction class names."""

TRIVIAL = "trivial"
# Unimolecular reactions
Expand All @@ -24,17 +24,25 @@ class ReactionClass(str, enum.Enum):
SUBSTITUTION = "substitution"

def __str__(self):
"""Construct a string from Reaction Class.

:return: String from self
"""
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Edit:

    def __str__(self) -> str:
        """Convert a reaction class object to a string.
        
        :return: The reaction class string
        """

return self.value

def __repr__(self):
"""_summary_.

:return: _description_
:rtype: _type_
"""
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Edit:

    def __repr__(self) -> str:
        """Get a string literal describing the reaction class object.
        
        :return: The reaction class string literal
        """

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: __repr__() and __str__() are essentially the same, except that the first one is supposed to represent the object, whereas the second one is supposed to be the result of "converting the object to a string".

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The first one gets called when you call repr(object), whereas the second one gets called when you call str(object).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Example:

>>> print(repr("abc"))
'abc'
>>> print(str("abc"))
abc

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way we are using __repr__() above, simply returning the string literal, isn't really the intended usage, and I don't think it's really necessary. But I am hesitant to take it out because this class is tied in with some ugly higher-level code that is very bug-prone. Eventually, I would like to get rid of these classes entirely.

return repr(self.value)

@classmethod
def reverse(cls, value: str):
"""Get the class for the reverse of a reaction
"""Get the class for the reverse of a reaction.

:param value: A reaction class
:type value: str
:return: The class of the reverse reaction
:rtype: ReactionClass
"""
Expand All @@ -57,23 +65,19 @@ def reverse(cls, value: str):

@classmethod
def is_reversible(cls, value: str) -> bool:
"""Is this reaction class reversible?
"""Is this reaction class reversible?.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be consistent with the standard formatting that the linter wants, let's replace questions like these with condition al statements:

"""Whether this reaction class is reversible.


:param value: A reaction class
:type value: str
:return: `True` if it is, `False` if it isn't
:rtype: bool
"""
return cls.reverse(value) is not None

@classmethod
def is_bimolecular(cls, value: str) -> bool:
"""Is this reaction class bimolecular?
"""Is this reaction class bimolecular?.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"""Whether this reaction class is bimolecular.

(See above)


:param value: The reaction class
:type value: str
:return: `True` if it is, `False` if it isn't
:rtype: bool
"""
bimol_classes = (
cls.HYDROGEN_ABSTRACTION,
Expand All @@ -86,12 +90,10 @@ def is_bimolecular(cls, value: str) -> bool:

@classmethod
def requires_spin_designation(cls, value: str) -> bool:
"""Is this a reaction class that requires a spin designation?
"""Is this a reaction class that requires a spin designation?.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"""Whether this reaction class requires a spin designation.


:param value: The reaction class
:type value: str
:return: `True` if it is, `False` if it isn't
:rtype: bool
"""
need_spin_classes = (
cls.HYDROGEN_ABSTRACTION, # AVC: Why is this in here??
Expand All @@ -101,53 +103,55 @@ def requires_spin_designation(cls, value: str) -> bool:

@classmethod
def is_defined(cls, value: str) -> bool:
"""Is this reaction class defined?
"""Is this reaction class defined?.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"""Whether this reaction class is defined.


:param value: The reaction class
:type value: str
:return: `True` if it is, `False` if it isn't
:rtype: bool
"""
return str(value) in list(cls)


class ReactionSpin(str, enum.Enum):
"""reaction spin types"""
"""reaction spin types."""
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's capitalize the start of these docstrings: Reaction spin types.


LOW = "low-spin"
HIGH = "high-spin"
NONE = "unspecified spin"

def __str__(self):
"""_summary_.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

def __str__(self) -> str:
    """Convert a reaction spin object to a string.

    :return: The reaction spin string
    """


:return: _description_
:rtype: _type_
"""
return self.value

def __repr__(self):
"""_summary_.

:return: _description_
:rtype: _type_
"""
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

    def __repr__(self) -> str:
        """Get a string literal describing the reaction spin object.
        
        :return: The reaction spin string literal
        """

return repr(self.value)

@classmethod
def is_defined(cls, value: str) -> bool:
"""Check whether a reaction spin type is defined
"""Check whether a reaction spin type is defined.

:param value: The value to check for
:type value: str
:return: `True` if it does, `False` if it doesn't
:rtype: bool
"""
return value in list(cls)


@dataclasses.dataclass
class ReactionInfo:
"""General information about a reaction
"""General information about a reaction.

:param class_: The class name of the reaction
:type class_: ReactionClass
:param spin_: The spin-type of the reaction (low or high)
:type spin_: ReactionSpin
:param is_rad_rad: Whether this is a radical-radical reaction
:type is_rad_rad: bool
:param is_isc: Whether this is an intersystem crossing
:type is_isc: bool
"""

class_: ReactionClass
Expand All @@ -156,7 +160,7 @@ class ReactionInfo:
is_isc: bool = False

def __str__(self) -> str:
"""Generate a string representation of the reaction information"""
"""Generate a string representation of the reaction information."""
parts = []

if self.is_radical_radical():
Expand All @@ -172,47 +176,47 @@ def __str__(self) -> str:
return " ".join(map(str, parts))

def __repr__(self) -> str:
"""Generate a string representation of the reaction information"""
"""Generate a string representation of the reaction information."""
return str(self)

def string(self) -> str:
"""Get a string representation of the reaction"""
"""Get a string representation of the reaction."""
return str(self)

def reaction_class(self) -> ReactionClass:
"""Get the reaction class name"""
"""Get the reaction class name."""
return ReactionClass(self.class_)

def reaction_spin(self) -> ReactionSpin:
"""Get the reaction spin type"""
"""Get the reaction spin type."""
return ReactionSpin(self.spin)

def is_radical_radical(self) -> bool:
"""Is this a radical radical reaction?"""
"""Is this a radical radical reaction?."""
return self.is_rad_rad

def is_intersystem_crossing(self) -> bool:
"""Is this an intersystem crossing?"""
"""Is this an intersystem crossing?."""
return self.is_isc

def is_barrierless(self) -> bool:
"""Is this a barrierless reaction?"""
"""Is this a barrierless reaction?."""
return self.is_radical_radical() and not self.is_high_spin()

def is_low_spin(self) -> bool:
"""Is this a low-spin reaction?"""
"""Is this a low-spin reaction?."""
return self.reaction_spin() == ReactionSpin.LOW

def is_high_spin(self) -> bool:
"""Is this a high-spin reaction?"""
"""Is this a high-spin reaction?."""
return self.reaction_spin() == ReactionSpin.HIGH

def has_no_spin_designation(self) -> bool:
"""Is this the only possible spin-state?"""
"""Is this the only possible spin-state?."""
return self.reaction_spin() == ReactionSpin.NONE

def requires_spin_designation(self) -> bool:
"""Is a spin designation required for this reaction?"""
"""Is a spin designation required for this reaction?."""
spin_req_classes = (
ReactionClass.HYDROGEN_ABSTRACTION, # AVC: Why is this in here??
ReactionClass.ADDITION,
Expand All @@ -225,7 +229,7 @@ def requires_spin_designation(self) -> bool:

def requires_well_description(self) -> bool:
"""Determine if a reaction is appropriately described by the presence of
entrance- or exit-channel van der Waals wells
entrance- or exit-channel van der Waals wells.
"""
well_classes = (
ReactionClass.HYDROGEN_ABSTRACTION,
Expand Down
21 changes: 11 additions & 10 deletions automol/form/_form.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@
STOICH = pp.Opt(pp.Literal("*") | ppc.integer).setParseAction(lambda x: x if x else [1])
ATOM_COUNT = pp.Group(SYMBOL + STOICH)
FORMULA = pp.OneOrMore(ATOM_COUNT)
Formul = dict[str, int]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be Formula (throughout). You can do this using right-click and "Rename symbol" to update all instances.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Otherwise, the edits below look good.



def electron_count(fml: dict[str:int]) -> int:
def electron_count(fml: Formul) -> int:
"""Count the number of electrons for the atoms in a molecular formula.

:param fml: Stochiometric chemical formula
Expand All @@ -36,7 +37,7 @@ def electron_count(fml: dict[str:int]) -> int:
return elec_count


def atom_count(fml: dict[str:int]) -> int:
def atom_count(fml: Formul) -> int:
"""Count the number of atoms in this molecular formula.

:param fml: Stochiometric chemical formula
Expand All @@ -47,7 +48,7 @@ def atom_count(fml: dict[str:int]) -> int:
return sum(fml.values())


def heavy_atom_count(fml: dict[str:int]) -> int:
def heavy_atom_count(fml: Formul) -> int:
"""Count the number of heavy atoms in this molecular formula.

:param fml: Stochiometric chemical formula
Expand All @@ -58,7 +59,7 @@ def heavy_atom_count(fml: dict[str:int]) -> int:
return sum(fml.values())


def element_count(fml: dict[str:int], symb: str) -> int:
def element_count(fml: Formul, symb: str) -> int:
"""Count the number of a given element in this molecular formula.

:param fml: Stochiometric chemical formula
Expand Down Expand Up @@ -98,7 +99,7 @@ def match(fml1: dict[str, int], fml2: dict[str, int]) -> bool:
return fml1 == fml2


def add_element(fml: dict[str:int], symb: str, num: int = 1) -> dict[str:int]:
def add_element(fml: Formul, symb: str, num: int = 1) -> Formul:
"""Add or subtract (if num < 0) this element from the molecular formula.

:param fml: Stochiometric chemical formula
Expand All @@ -121,7 +122,7 @@ def add_element(fml: dict[str:int], symb: str, num: int = 1) -> dict[str:int]:
return fml


def join(fml1: dict[str:int], fml2: dict[str:int]) -> int:
def join(fml1: Formul, fml2: Formul) -> int:
"""Join two formulas together.

:param fml1: Stochiometric chemical formula 1
Expand All @@ -135,7 +136,7 @@ def join(fml1: dict[str:int], fml2: dict[str:int]) -> int:
return fml


def join_sequence(fmls: dict[str:int]) -> int:
def join_sequence(fmls: Formul) -> int:
"""Join a sequence of formulas together.

:param fml: Stochiometric chemical formula
Expand All @@ -154,7 +155,7 @@ def sorted_symbols_in_sequence(fmls: list[dict]) -> list[dict]:


# Str<->Dict Converters
def string(fml: dict[str:int], hyd: bool = True) -> str:
def string(fml: Formul, hyd: bool = True) -> str:
"""Convert formula dictionary to formula string in the Hill convention.
Resultant string is identical to InChI formula string.

Expand All @@ -173,7 +174,7 @@ def string(fml: dict[str:int], hyd: bool = True) -> str:
return fml_str


def string2(fml: dict[str:int]) -> str:
def string2(fml: Formul) -> str:
"""Convert formula dictionary to formula string that includes 1s in when
there is only one atom.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good up to this point. A few more edits for the functions below this point:

def from_string(...) -> Formula:
...
def sorted_symbols(...) -> tuple[str, ...]:
...
def sort_vector(fml: Formula, symbs: Sequence[str] | None = None) -> tuple[int, ...]:

Expand Down Expand Up @@ -281,7 +282,7 @@ def sort_vector(fml: str, symbs: list[str] | None = None):
return vec


def _is_standard(fml: dict[str:int]) -> bool:
def _is_standard(fml: Formul) -> bool:
"""Assess if the formula conforms to the standard form.

:param fml: stochiometric chemical formula
Expand Down
14 changes: 9 additions & 5 deletions automol/form/reac.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
"""reaction formulae."""
import itertools

from ._form import add_element, join_sequence
from _collections_abc import Sequence

from ._form import Formul, add_element, join_sequence

def is_valid_reaction(rct_fmls: list[str], prd_fmls: list[str]) -> bool:

def is_valid_reaction(rct_fmls: Sequence[Formul], prd_fmls: Sequence[Formul]) -> bool:
"""Use the formula to see if a reaction preserves stoichiometry.

:param rct_fmls: stoichiometries of the reactants
Expand All @@ -14,14 +16,16 @@ def is_valid_reaction(rct_fmls: list[str], prd_fmls: list[str]) -> bool:
return join_sequence(rct_fmls) == join_sequence(prd_fmls)


def argsort_hydrogen_abstraction(rct_fmls: list[str], prd_fmls: list[str]) -> bool:
"""Generates the indices which allows the reactants and products of
def argsort_hydrogen_abstraction(
rct_fmls: Sequence[Formul], prd_fmls: Sequence[Formul]
) -> tuple[tuple[int, int], tuple[int, int]] | None:
"""Generate the indices which allows the reactants and products of
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good.

a hydrogen abstraction reaction can be sorted as RH + Q => R + QH.

:param rct_fmls: stoichiometries of the reactants
:param prd_fmls: stoichiometries of the products
:return: Indices to sort reaction to R + QH
""" # noqa: D401
"""
rxn_idxs = None
if len(rct_fmls) == len(prd_fmls) == 2:
for idxs1, idxs2 in itertools.product(
Expand Down
Loading