From 1818e46665c135a918eaebcb83bb6536b12a6b65 Mon Sep 17 00:00:00 2001 From: Umesh Timalsina Date: Thu, 28 Apr 2022 14:04:19 -0500 Subject: [PATCH] Feature: Add non_element_types support for atomtyping (#648) * Feature: Add non_element_types support for atomtyping * Minor fixes to imports/ comments * Address review comments, couple of more asserts * WIP- few more asserts * WIP- Empty branch trigger for GHA * Fix test case --- gmso/core/forcefield.py | 16 ++++ gmso/tests/files/non-element-type-ff.xml | 104 +++++++++++++++++++++++ gmso/tests/test_forcefield.py | 36 ++++++++ 3 files changed, 156 insertions(+) create mode 100644 gmso/tests/files/non-element-type-ff.xml diff --git a/gmso/core/forcefield.py b/gmso/core/forcefield.py index 774ebe239..9cae979ff 100644 --- a/gmso/core/forcefield.py +++ b/gmso/core/forcefield.py @@ -5,6 +5,7 @@ from lxml import etree +from gmso.core.element import element_by_symbol from gmso.exceptions import MissingPotentialError from gmso.utils._constants import FF_TOKENS_SEPARATOR from gmso.utils.ff_utils import ( @@ -106,6 +107,21 @@ def __init__(self, xml_loc=None, strict=True, greedy=True): self.combining_rule = "geometric" self.units = {} + @property + def non_element_types(self): + """Get the non-element types in the ForceField.""" + non_element_types = set() + + for name, atom_type in self.atom_types.items(): + element_symbol = atom_type.get_tag( + "element" + ) # FixMe: Should we make this a first class citizen? + if element_symbol: + element = element_by_symbol(element_symbol) + non_element_types.add(element_symbol) if not element else None + + return non_element_types + @property def atom_class_groups(self): """Return a dictionary of atomClasses in the Forcefield.""" diff --git a/gmso/tests/files/non-element-type-ff.xml b/gmso/tests/files/non-element-type-ff.xml new file mode 100644 index 000000000..f1037d34e --- /dev/null +++ b/gmso/tests/files/non-element-type-ff.xml @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gmso/tests/test_forcefield.py b/gmso/tests/test_forcefield.py index f9e3c8e40..fb96b07e6 100644 --- a/gmso/tests/test_forcefield.py +++ b/gmso/tests/test_forcefield.py @@ -29,6 +29,10 @@ def opls_ethane_foyer(self): get_path(filename=get_path("oplsaa-ethane_foyer.xml")) ) + @pytest.fixture(scope="session") + def non_element_ff(self): + return ForceField(get_path(filename="non-element-type-ff.xml")) + def test_ff_name_version_from_xml(self, ff): assert ff.name == "ForceFieldOne" assert ff.version == "0.4.1" @@ -560,3 +564,35 @@ def test_get_improper_type_missing(self, opls_ethane_foyer): opls_ethane_foyer._get_improper_type( ["opls_359", "opls_600", "opls_700", "opls_800"], warn=True ) + + def test_non_element_types(self, non_element_ff, opls_ethane_foyer): + assert "_CH3" in non_element_ff.non_element_types + assert "_CH2" in non_element_ff.non_element_types + assert opls_ethane_foyer.non_element_types == set() + assert len(opls_ethane_foyer.atom_types) > 0 + + assert ( + non_element_ff.get_potential( + group="atom_type", key="CH2_sp3" + ).charge + == 0 + ) + assert ( + non_element_ff.get_potential( + group="atom_type", key="CH3_sp3" + ).charge + == 0 + ) + + assert ( + non_element_ff.get_potential( + group="atom_type", key="CH3_sp3" + ).definition + == "[_CH3;X1][_CH3,_CH2]" + ) + assert ( + non_element_ff.get_potential( + group="atom_type", key="CH2_sp3" + ).definition + == "[_CH2;X2]([_CH3,_CH2])[_CH3,_CH2]" + )