Source code for gmso.core.forcefield

from collections import ChainMap

from lxml import etree

from gmso.utils.ff_utils import (validate,
                                 parse_ff_metadata,
                                 parse_ff_atomtypes,
                                 parse_ff_connection_types)


[docs]class ForceField(object): """A generic implementation of the forcefield class. The ForceField class is one of the core data structures in gmso, which is used to hold a collection of gmso.core.Potential subclass objects along with some metadata to represent a forcefield. The forcefield object can be applied to any gmso.Topology which has effects on its Sites, Bonds, Angles and Dihedrals. Parameters ---------- name : str Name of the forcefield, default 'ForceField' version : str a cannonical semantic version of the forcefield, default 1.0.0 Attributes ---------- name : str Name of the forcefield version : str Version of the forcefield atom_types : dict A collection of atom types in the forcefield bond_types : dict A collection of bond types in the forcefield angle_types : dict A collection of angle types in the forcefield dihedral_types : dict A collection of dihedral types in the forcefield units : dict A collection of unyt.Unit objects used in the forcefield scaling_factors : dict A collection of scaling factors used in the forcefield See Also -------- gmso.ForceField.from_xml : A class method to create forcefield object from XML files """ def __init__(self, xml_loc=None): if xml_loc is not None: ff = ForceField.from_xml(xml_loc) self.name = ff.name self.version = ff.version self.atom_types = ff.atom_types self.bond_types = ff.bond_types self.angle_types = ff.angle_types self.dihedral_types = ff.dihedral_types self.improper_types = ff.improper_types self.potential_groups = ff.potential_groups self.scaling_factors = ff.scaling_factors self.units = ff.units else: self.name = 'ForceField' self.version = '1.0.0' self.atom_types = {} self.bond_types = {} self.angle_types = {} self.dihedral_types = {} self.improper_types = {} self.potential_groups = {} self.scaling_factors = {} self.units = {} def __repr__(self): descr = list('<Forcefield ') descr.append(self.name + ' ') descr.append('{:d} AtomTypes, '.format(len(self.atom_types))) descr.append('{:d} BondTypes, '.format(len(self.bond_types))) descr.append('{:d} AngleTypes, '.format(len(self.angle_types))) descr.append('{:d} DihedralTypes, '.format(len(self.dihedral_types))) descr.append('id: {}>'.format(id(self))) return ''.join(descr) @property def atom_class_groups(self): """Return a dictionary of atomClasses in the Forcefield""" atom_types = self.atom_types.values() atomclass_dict = {} for atom_type in atom_types: if atom_type.atomclass is not None: atomclass_group = atomclass_dict.get(atom_type.atomclass, []) atomclass_group.append(atom_type) atomclass_dict[atom_type.atomclass] = atomclass_group return atomclass_dict
[docs] @classmethod def from_xml(cls, xml_locs): """Create a gmso.Forcefield object from XML File(s) This class method creates a ForceFiled object from the reference XML file. This method takes in a single or collection of XML files with information about gmso.AtomTypes, gmso.BondTypes, gmso.AngleTypes and gmso.DihedralTypes to create the ForceField object. Parameters ---------- xml_locs : str or iterable of str string or iterable of strings containing the forcefield XML locations Returns -------- forcefield : gmso.ForceField A gmso.Forcefield object with a collection of Potential objects created using the information in the XML file """ if not hasattr(xml_locs, '__iter__'): xml_locs = [].append(xml_locs) if isinstance(xml_locs, str): xml_locs = [xml_locs] versions = [] names = [] ff_atomtypes_list = [] ff_bondtypes_list = [] ff_angletypes_list = [] ff_dihedraltypes_list = [] atom_types_dict = ChainMap() bond_types_dict = {} angle_types_dict = {} dihedral_types_dict = {} improper_types_dict = {} potential_groups = {} for loc in xml_locs: validate(loc) ff_tree = etree.parse(loc) ff_el = ff_tree.getroot() versions.append(ff_el.attrib['version']) names.append(ff_el.attrib['name']) ff_meta_tree = ff_tree.find('FFMetaData') if ff_meta_tree is not None: ff_meta_map = parse_ff_metadata(ff_meta_tree) ff_atomtypes_list.extend(ff_tree.findall('AtomTypes')) ff_bondtypes_list.extend(ff_tree.findall('BondTypes')) ff_angletypes_list.extend(ff_tree.findall('AngleTypes')) ff_dihedraltypes_list.extend(ff_tree.findall('DihedralTypes')) # Consolidate AtomTypes for atom_types in ff_atomtypes_list: this_atom_types_group = parse_ff_atomtypes(atom_types, ff_meta_map) this_atom_group_name = atom_types.attrib.get('name', None) if this_atom_group_name: potential_groups[this_atom_group_name] = this_atom_types_group atom_types_dict.update(this_atom_types_group) # Consolidate BondTypes for bond_types in ff_bondtypes_list: this_bond_types_group = parse_ff_connection_types( bond_types, atom_types_dict, child_tag='BondType' ) this_bond_types_group_name = bond_types.attrib.get('name', None) if this_bond_types_group_name: potential_groups[this_bond_types_group_name] = this_bond_types_group bond_types_dict.update(this_bond_types_group) # Consolidate AngleTypes for angle_types in ff_angletypes_list: this_angle_types_group = parse_ff_connection_types( angle_types, atom_types_dict, child_tag='AngleType' ) this_angle_types_group_name = angle_types.attrib.get('name', None) if this_angle_types_group_name: potential_groups[this_angle_types_group_name] = this_angle_types_group angle_types_dict.update(this_angle_types_group) # Consolidate DihedralTypes for dihedral_types in ff_dihedraltypes_list: this_dihedral_types_group = parse_ff_connection_types( dihedral_types, atom_types_dict, child_tag='DihedralType' ) this_improper_types_group = parse_ff_connection_types( dihedral_types, atom_types_dict, child_tag='ImproperType' ) this_group_name = dihedral_types.attrib.get('name', None) dihedral_types_dict.update(this_dihedral_types_group) improper_types_dict.update(this_improper_types_group) if this_group_name: this_dihedral_types_group.update(this_improper_types_group) potential_groups[this_group_name] = this_dihedral_types_group ff = cls() ff.name = names[0] ff.version = versions[0] ff.scaling_factors = ff_meta_map['scaling_factors'] ff.units = ff_meta_map['Units'] ff.atom_types = atom_types_dict.maps[0] ff.bond_types = bond_types_dict ff.angle_types = angle_types_dict ff.dihedral_types = dihedral_types_dict ff.improper_types = improper_types_dict ff.potential_groups = potential_groups return ff