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

Add version attribute to force fields #298

Merged
merged 9 commits into from
Dec 7, 2019
42 changes: 42 additions & 0 deletions foyer/forcefield.py
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,8 @@ def __init__(self, forcefield_files=None, name=None, validation=True, debug=Fals
self.atomTypeElements = dict()
self._included_forcefields = dict()
self.non_element_types = dict()
self._version = None
self._name = None

all_files_to_load = []
if forcefield_files is not None:
Expand All @@ -368,9 +370,25 @@ def __init__(self, forcefield_files=None, name=None, validation=True, debug=Fals
finally:
for ff_file_name in preprocessed_files:
os.remove(ff_file_name)

if isinstance(forcefield_files, str):
self._version = self._parse_version_number(forcefield_files)
self._name = self._parse_name(forcefield_files)
elif isinstance(forcefield_files, list):
self._version = [self._parse_version_number(f) for f in forcefield_files]
self._name = [self._parse_name(f) for f in forcefield_files]

self.parser = smarts.SMARTS(self.non_element_types)
self._SystemData = self._SystemData()

@property
def version(self):
return self._version

@property
def name(self):
return self._name

@property
def included_forcefields(self):
if any(self._included_forcefields):
Expand All @@ -385,6 +403,30 @@ def included_forcefields(self):
self._included_forcefields[basename] = ff_filepath
return self._included_forcefields

def _parse_version_number(self, forcefield_file):
with open(forcefield_file, 'r') as f:
tree = ET.parse(f)
root = tree.getroot()
try:
return root.attrib['version']
except KeyError:
Copy link
Contributor

Choose a reason for hiding this comment

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

Going off of Mike's comment about raising a warning, this seems like the place where that would go.

Suggested change
except KeyError:
except KeyError:
warn.Warn("version warning message here")
return None

warnings.warn(
'No force field version number found in force field XML file.'
)
return None

def _parse_name(self, forcefield_file):
with open(forcefield_file, 'r') as f:
tree = ET.parse(f)
root = tree.getroot()
try:
return root.attrib['name']
except KeyError:
warnings.warn(
'No force field name found in force field XML file.'
)
return None

def _create_element(self, element, mass):
if not isinstance(element, elem.Element):
try:
Expand Down
2 changes: 2 additions & 0 deletions foyer/forcefields/ff.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,8 @@
<xs:element ref="CustomTorsionForce" minOccurs="0"/>
<xs:element ref="NonbondedForce" minOccurs="0"/>
</xs:all>
<xs:attribute type="xs:string" name="version" use="optional"/>
<xs:attribute type="xs:string" name="name" use="optional"/>
</xs:complexType>
<xs:key name="atomtype_name_key">
<xs:selector xpath="AtomTypes/Type" />
Expand Down
8 changes: 8 additions & 0 deletions foyer/tests/files/lj.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<ForceField version="0.4.1" name="LJ">
<AtomTypes>
<Type name="LJ" class="" element="_LJ" mass="1.0" def="[_LJ]" desc="Generic LJ particle"/>
</AtomTypes>
<NonbondedForce coulomb14scale="0.5" lj14scale="0.5">
<Atom type="LJ" charge="0" sigma="1.0" epsilon="1.0"/>
</NonbondedForce>
</ForceField>
8 changes: 8 additions & 0 deletions foyer/tests/files/lj2.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<ForceField version="4.8.2" name="JL">
<AtomTypes>
<Type name="JL" class="" element="_JL" mass="1.0" def="[_JL]" desc="Generic JL particle"/>
</AtomTypes>
<NonbondedForce coulomb14scale="0.5" lj14scale="0.5">
<Atom type="JL" charge="0" sigma="1.0" epsilon="1.0"/>
</NonbondedForce>
</ForceField>
9 changes: 9 additions & 0 deletions foyer/tests/test_forcefield.py
Original file line number Diff line number Diff line change
Expand Up @@ -504,3 +504,12 @@ def test_write_xml_overrides():
elif attributes['name'] == 'opls_146':
assert attributes['overrides'] == 'opls_144'
assert str(item.xpath('comment()')) == '[<!--Note: original overrides="opls_144"-->]'

def test_load_metadata():
lj_ff = Forcefield(get_fn('lj.xml'))
assert lj_ff.version == '0.4.1'
assert lj_ff.name == 'LJ'

lj_ff = Forcefield(forcefield_files=[get_fn('lj.xml'), get_fn('lj2.xml')])
assert lj_ff.version == ['0.4.1', '4.8.2']
assert lj_ff.name == ['LJ', 'JL']