Skip to content

Commit

Permalink
add chgmult to Molecule repr (#340)
Browse files Browse the repository at this point in the history
* Update test_molecule.py

* Update molecule.py

* Update changelog.rst
  • Loading branch information
loriab authored Sep 18, 2024
1 parent faa897a commit b687499
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 4 deletions.
3 changes: 3 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ Changelog
.. Misc.
.. +++++
- (:pr:`340`, :issue:`330`) Add molecular charge and multiplicity to Molecule repr formula,
so neutral singlet unchanged but radical cation has '2^formula+'.


0.29.0 / 2024-MM-DD (Unreleased)
--------------------------------
Expand Down
50 changes: 46 additions & 4 deletions qcelemental/models/molecule.py
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ class Config(ProtoModel.Config):
serialize_skip_defaults = True
repr_style = lambda self: [
("name", self.name),
("formula", self.get_molecular_formula()),
("formula", self.get_molecular_formula(chgmult=True)),
("hash", self.get_hash()[:7]),
]
fields = {
Expand Down Expand Up @@ -801,14 +801,16 @@ def get_hash(self):
m.update(concat.encode("utf-8"))
return m.hexdigest()

def get_molecular_formula(self, order: str = "alphabetical") -> str:
def get_molecular_formula(self, order: str = "alphabetical", chgmult: bool = False) -> str:
r"""
Returns the molecular formula for a molecule.
Parameters
----------
order: str, optional
Sorting order of the formula. Valid choices are "alphabetical" and "hill".
chgmult
If not neutral singlet, return formula as {mult}^{formula}{chg}.
Returns
-------
Expand All @@ -835,11 +837,47 @@ def get_molecular_formula(self, order: str = "alphabetical") -> str:
>>> hcl.get_molecular_formula()
ClH
>>> two_pentanol_radcat = qcelemental.models.Molecule('''
... 1 2
... C -4.43914 1.67538 -0.14135
... C -2.91385 1.70652 -0.10603
... H -4.82523 2.67391 -0.43607
... H -4.84330 1.41950 0.86129
... H -4.79340 0.92520 -0.88015
... H -2.59305 2.48187 0.62264
... H -2.53750 1.98573 -1.11429
... C -2.34173 0.34025 0.29616
... H -2.72306 0.06156 1.30365
... C -0.80326 0.34498 0.31454
... H -2.68994 -0.42103 -0.43686
... O -0.32958 1.26295 1.26740
... H -0.42012 0.59993 -0.70288
... C -0.26341 -1.04173 0.66218
... H -0.61130 -1.35318 1.67053
... H 0.84725 -1.02539 0.65807
... H -0.60666 -1.78872 -0.08521
... H -0.13614 2.11102 0.78881
... ''')
>>> two_pentanol_radcat.get_molecular_formula(chgmult=True)
2^C5H12O+
"""

from ..molutil import molecular_formula_from_symbols

return molecular_formula_from_symbols(symbols=self.symbols, order=order)
formula = molecular_formula_from_symbols(symbols=self.symbols, order=order)

c, m = self.molecular_charge, self.molecular_multiplicity
if not chgmult or (c == 0.0 and m == 1):
return formula

if m > 1:
formula = f"{m}^{formula}"
if c < 0.0:
formula += abs(int(c)) * "-"
elif c > 0.0:
formula += int(c) * "+"
return formula

### Constructors

Expand Down Expand Up @@ -1065,7 +1103,11 @@ def _orient_molecule_internal(self):
return new_geometry

def __repr_args__(self) -> "ReprArgs":
return [("name", self.name), ("formula", self.get_molecular_formula()), ("hash", self.get_hash()[:7])]
return [
("name", self.name),
("formula", self.get_molecular_formula(chgmult=True)),
("hash", self.get_hash()[:7]),
]

def _ipython_display_(self, **kwargs) -> None:
try:
Expand Down
45 changes: 45 additions & 0 deletions qcelemental/tests/test_molecule.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,51 @@ def test_molecule_compare():
assert water_molecule != water_molecule3


def test_molecule_repr_chgmult():
wat1 = water_molecule.copy()
assert "formula='H2O'," in wat1.__repr__(), "charge/mult wrongly present in Molecule repr"

wat2 = water_dimer_minima.dict()
wat2["fragment_charges"] = [1, 0]
for field in ["molecular_charge", "molecular_multiplicity", "fragment_multiplicities", "validated"]:
wat2.pop(field)
wat2 = Molecule(**wat2)
assert "formula='2^H4O2+'," in wat2.__repr__(), "charge/mult missing from Molecule repr"

two_pentanol_radcat = Molecule.from_data(
"""
1 2
C -4.43914 1.67538 -0.14135
C -2.91385 1.70652 -0.10603
H -4.82523 2.67391 -0.43607
H -4.84330 1.41950 0.86129
H -4.79340 0.92520 -0.88015
H -2.59305 2.48187 0.62264
H -2.53750 1.98573 -1.11429
C -2.34173 0.34025 0.29616
H -2.72306 0.06156 1.30365
C -0.80326 0.34498 0.31454
H -2.68994 -0.42103 -0.43686
O -0.32958 1.26295 1.26740
H -0.42012 0.59993 -0.70288
C -0.26341 -1.04173 0.66218
H -0.61130 -1.35318 1.67053
H 0.84725 -1.02539 0.65807
H -0.60666 -1.78872 -0.08521
H -0.13614 2.11102 0.78881
"""
)
assert "formula='2^C5H12O+'," in two_pentanol_radcat.__repr__(), "charge/mult missing from Molecule repr"

Oanion = Molecule.from_data(
"""
-2 1
O 0 0 0
"""
)
assert "formula='O--'," in Oanion.__repr__(), "charge/mult missing from Molecule repr"


def test_water_minima_data():
# Give it a name
mol_dict = water_dimer_minima.dict()
Expand Down

0 comments on commit b687499

Please sign in to comment.