Skip to content

Commit

Permalink
Merge branch 'master' into write_gmx_top
Browse files Browse the repository at this point in the history
  • Loading branch information
mattwthompson committed Feb 5, 2020
2 parents ad06e11 + 512e14d commit 0606846
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 7 deletions.
74 changes: 74 additions & 0 deletions topology/core/element.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,80 @@ def element_by_mass(mass, exact=True):
return matched_element


def element_by_smarts_string(smarts_string):
"""Search for an element by a given SMARTS string
Parameters
----------
smarts_string : str
SMARTS string representation of an atom type or its local chemical
context. The Foyer SMARTS parser will be used to find the central atom
and look up an Element. Note that this means some SMARTS grammar may
not be parsed properly. For details, see
https://github.com/mosdef-hub/foyer/issues/63
Returns
-------
matched_element : element.Element or None
Return an element from the periodict table if we find a match,
otherwise return None
"""
from foyer.smarts import SMARTS

PARSER = SMARTS()

symbol = next(PARSER.parse(smarts_string).find_data('atom_symbol')).children[0]
print(symbol)
matched_element = element_by_symbol(symbol)

if matched_element is None:
raise TopologyError(f''
'Failed to find an element from SMARTS string {smarts_string). The'
'parser detected a central node with name {symbol}'
)

return matched_element


def element_by_atom_type(atom_type):
"""Search for an element by a given a topology AtomType object
Parameters
----------
atom_type : topology.core.atom_type.AtomType
AtomType object to be parsed for element information. Attributes are
looked up in the order of mass, name, and finally definition (the
SMARTS string). Because of the loose structure of this class, a
successful lookup is not guaranteed.
Returns
-------
matched_element : element.Element or None
Return an element from the periodict table if we find a match,
otherwise return None
"""
matched_element = None

if matched_element is None and atom_type.mass:
matched_element = element_by_mass(atom_type.mass, exact=False)
if matched_element is None and atom_type.name:
matched_element = element_by_symbol(atom_type.name)
if matched_element is None and atom_type.definition:
matched_element = element_by_smarts_string(atom_type.definition)

if matched_element is None:
import pdb; pdb.set_trace()
raise TopologyError(f'Failed to find an element from atom type'
'{atom_type} with ' 'properties mass: {atom_type.mass}, name:'
'{atom_type.name}, and ' 'definition: {atom_type.definition}'
)

return matched_element
return elem


Hydrogen = Element(atomic_number=1, name='hydrogen', symbol='H', mass=1.0079 * u.amu)
Helium = Element(atomic_number=2, name='helium', symbol='He', mass=4.0026 * u.amu)
Lithium = Element(atomic_number=3, name='lithium', symbol='Li', mass=6.941 * u.amu)
Expand Down
40 changes: 33 additions & 7 deletions topology/tests/test_element.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from topology.core import element
from topology.core.element import Carbon
from topology.core.atom_type import AtomType
from topology.tests.base_test import BaseTest
from topology.exceptions import TopologyError

Expand All @@ -16,39 +17,39 @@ def test_element(self):
assert carbon.symbol == 'C'
assert carbon.mass == element.Carbon.mass

def test_element_by(self):
#Test element_by_name
def test_element_by_name(self):
for name in ['Carbon', 'carbon', ' CarBon 12 ']:
carbon = element.element_by_name(name)

assert carbon.name == element.Carbon.name
assert carbon.symbol == element.Carbon.symbol
assert carbon.mass == element.Carbon.mass

#Test element_by_symbol
def test_element_by_symbol(self):
for symbol in ['N', 'n', ' N7']:
nitrogen = element.element_by_symbol(symbol)

assert nitrogen.name == element.Nitrogen.name
assert nitrogen.symbol == element.Nitrogen.symbol
assert nitrogen.mass == element.Nitrogen.mass

#Test element_by_atomic_number
def test_element_by_atomic_number(self):
for number in [8, '8', '08', 'Oxygen-08']:
oxygen = element.element_by_atomic_number(number)

assert oxygen.name == element.Oxygen.name
assert oxygen.symbol == element.Oxygen.symbol
assert oxygen.mass == element.Oxygen.mass

#Test element_by_mass
def test_element_by_mass(self):
for mass in ['Fluorine-19', 19, 19 * u.amu]:
fluorine = element.element_by_mass(mass)

assert fluorine.name == element.Fluorine.name
assert fluorine.symbol == element.Fluorine.symbol
assert fluorine.mass == element.Fluorine.mass
#Additional element_by_mass test

def test_element_by_mass_exactness(self):
cobalt = element.element_by_mass(58.9)
nickel = element.element_by_mass(58.7)
chlorine = element.element_by_mass(35, exact=False)
Expand All @@ -57,6 +58,31 @@ def test_element_by(self):
assert nickel == element.Nickel
assert chlorine == element.Chlorine

def test_element_by_smarts_string(self):
SMARTS = ['C', '[C;X3](C)(O)C', '[C;X4](C)(C)(C)H',
'[C;X4;r3]1(H)(H)[C;X4;r3][C;X4;r3]1', '[C;X3]([O;X1])[N;X3]',
'[C;X3;r6]1(OH)[C;X3;r6][C;X3;r6][C;X3;r6][C;X3;r6][C;X3;r6]1']

for smarts in SMARTS:
carbon = element.element_by_smarts_string(smarts)

assert carbon.name == element.Carbon.name
assert carbon.symbol == element.Carbon.symbol
assert carbon.mass == element.Carbon.mass

def test_element_by_atom_type(self):
carbon_type = AtomType(mass=12.011, definition='C', name='C')
mass_only = AtomType(mass=12.011)
def_only = AtomType(definition='C')
name_only = AtomType(name='C')

for atom_type in [carbon_type, mass_only, def_only, name_only]:
carbon = element.element_by_atom_type(atom_type)

assert carbon.name == element.Carbon.name
assert carbon.symbol == element.Carbon.symbol
assert carbon.mass == element.Carbon.mass

def test_all_elements(self):
for num in range(1, 119):
elem = element.element_by_atomic_number(num)
Expand All @@ -65,4 +91,4 @@ def test_all_elements(self):
@pytest.mark.parametrize('atomic_number', [0, -1, 1000, 17.02])
def test_bad_atomic_number(self, atomic_number):
with pytest.raises(TopologyError):
element.element_by_atomic_number(atomic_number)
element.element_by_atomic_number(atomic_number)

0 comments on commit 0606846

Please sign in to comment.