Пример #1
0
 def _process_nbfix_line(self, line, equivalents=None):
     try:
         a1, a2, rmin1, eps1, rmin2, eps2 = line.split()[:6]
     except ValueError:
         raise ParameterError('Could not understand LJEDIT line [%s]' % line)
     try:
         rmin1 = float(rmin1)
         eps1 = float(eps1)
         rmin2 = float(rmin2)
         eps2 = float(eps2)
     except ValueError:
         raise ParameterError('Could not convert LJEDIT parameters '
                              'to floats.')
     self.nbfix_types[(min(a1, a2), max(a1, a2))] = \
             (math.sqrt(eps1*eps2), rmin1+rmin2)
     if equivalents is not None:
         # We need to add the same nbfixes to all atom types that are
         # equivalent to the atom types defined in the LJEDIT line
         for oa1 in equivalents[a1]:
             self.nbfix_types[(min(oa1, a2), max(oa1, a2))] = \
                     (math.sqrt(eps1*eps2), rmin1+rmin2)
         for oa2 in equivalents[a2]:
             self.nbfix_types[(min(a1, oa2), max(a1, oa2))] = \
                     (math.sqrt(eps1*eps2), rmin1+rmin2)
         # Now do the equivalenced of atom 1 with the equivalenced of atom 2
         for oa1 in equivalents[a1]:
             for oa2 in equivalents[a2]:
                 self.nbfix_types[(min(oa1, oa2), max(oa1, oa2))] = \
                         (math.sqrt(eps1*eps2), rmin1+rmin2)
Пример #2
0
 def _process_nonbond_line(self, line):
     try:
         atyp, rmin, eps = line.split()[:3]
     except ValueError:
         raise ParameterError('Could not understand nonbond parameter line '
                              '[%s]' % line)
     try:
         self.atom_types[atyp].set_lj_params(float(eps), float(rmin))
     except KeyError:
         raise ParameterError('Atom type %s not present in the database.' %
                              atyp)
     except ValueError:
         raise ParameterError('Could not convert nonbond parameters to '
                              'floats [%s, %s]' % (rmin, eps))
Пример #3
0
 def _process_mass_line(self, line):
     words = line.split()
     try:
         mass = float(words[1])
     except ValueError:
         raise ParameterError('Could not convert mass to float [%s]' % words[1])
     except IndexError:
         raise ParameterError('Error parsing MASS line. Not enough tokens')
     if words[0] in self.atom_types:
         self.atom_types[words[0]].mass = mass
     elif words[0] in ('EP', 'LP'):
         atype = AtomType(words[0], len(self.atom_types)+1, mass, 0)
         self.atom_types[words[0]] = atype
     else:
         atype = AtomType(words[0], len(self.atom_types)+1, mass,
                          AtomicNum[element_by_mass(mass)])
         self.atom_types[words[0]] = atype
Пример #4
0
 def _process_angle_line(self, line):
     rematch = _anglere.match(line)
     if not rematch:
         raise ParameterError('Could not understand ANGLE line [%s]' % line)
     a1, a2, a3, k, eq = rematch.groups()
     a1 = a1.strip(); a2 = a2.strip(); a3 = a3.strip()
     typ = AngleType(float(k), float(eq))
     self.angle_types[(a1, a2, a3)] = typ
     self.angle_types[(a3, a2, a1)] = typ
Пример #5
0
 def _process_bond_line(self, line):
     rematch = _bondre.match(line)
     if not rematch:
         raise ParameterError('Could not understand BOND line [%s]' % line)
     a1, a2, k, eq = rematch.groups()
     a1 = a1.strip(); a2 = a2.strip()
     typ = BondType(float(k), float(eq))
     self.bond_types[(a1, a2)] = typ
     self.bond_types[(a2, a1)] = typ
Пример #6
0
 def _process_improper_line(self, line):
     rematch = _impropre.match(line)
     if not rematch:
         raise ParameterError('Could not understand IMPROPER line '
                                 '[%s]' % line)
     a1, a2, a3, a4, k, phi, per = rematch.groups()
     a1 = a1.strip(); a2 = a2.strip();
     a3 = a3.strip(); a4 = a4.strip()
     # Pre-sort the improper types, assuming atom3 is the central atom (which
     # it must be in Amber parameter files!!!!)
     a1, a2, a4 = sorted([a1, a2, a4])
     key = (a1, a2, a3, a4)
     self.improper_periodic_types[key] = \
             DihedralType(float(k), float(per), float(phi))
Пример #7
0
    def _process_dihedral_line(self, line, finished_diheds, last_key):
        """ Processes a dihedral line, possibly part of a multi-term dihedral

        Parameters
        ----------
        line : str
            Line of the file that contains a dihedral term
        finished_diheds : dict
            Dictionary of dihedral parameters whose final term has been read in
            already (which means additional terms will overwrite, not add)
        last_key : str or None
            If not None, this is the key for the last dihedral type that should
            be implied if the atom types are missing. Atom types seem to only be
            required for the first term in a multi-term torsion definition

        Returns
        -------
        key or None
            If a negative periodicity indicates another term is coming, the
            current key is returned so it can be passed as key to the next
            _process_dihedral_call
        """
        rematch = _dihedre.match(line)
        if not rematch and last_key is None:
            raise ParameterError('Could not understand DIHEDRAL line '
                                 '[%s]' % line)
        elif not rematch:
            rematch = _dihed2re.match(line)
            if not rematch:
                raise ParameterError('Could not understand DIHEDRAL line '
                                     '[%s]' % line)
            div, k, phi, per = rematch.groups()
            key = last_key
            rkey = tuple(reversed(key))
            assert key in finished_diheds
            if finished_diheds[key]:
                raise AssertionError('Cannot have an implied torsion that '
                                     'has already finished!')
        else:
            a1, a2, a3, a4, div, k, phi, per = rematch.groups()
            a1, a2, a3, a4 =  a1.strip(), a2.strip(), a3.strip(), a4.strip()
            key = (a1, a2, a3, a4)
            rkey = (a4, a3, a2, a1)
            if last_key is not None and (last_key != key and last_key != rkey):
                warnings.warn('Expecting next term in dihedral %r, got '
                              'definition for dihedral %r' % (last_key, key),
                              ParameterWarning)
        scee = [float(x) for x in _sceere.findall(line)] or [1.2]
        scnb = [float(x) for x in _scnbre.findall(line)] or [2.0]
        per = float(per)
        typ = DihedralType(float(k)/float(div), abs(per), float(phi),
                           scee[0], scnb[0])
        if finished_diheds[key]:
            # This dihedral is already finished its definition, which means
            # we go ahead and add a new one to override it
            typs = DihedralTypeList()
            typs.append(typ)
            self.dihedral_types[key] = self.dihedral_types[rkey] = typs
        else:
            self.dihedral_types[key].append(typ)
        finished_diheds[key] = finished_diheds[rkey] = per >= 0
        if per < 0:
            return key
Пример #8
0
 def _parse_parm_dat(self, f, line):
     """ Internal parser for parm.dat files from open file handle """
     def fiter():
         yield line
         for l in f: yield l
         # Keep yielding empty string after file has ended
         yield ''
     fiter = fiter()
     rawline = next(fiter)
     finished_diheds = defaultdict(lambda: True)
     # Parse the masses
     while rawline:
         line = rawline.strip()
         if not line:
             break
         self._process_mass_line(line)
         rawline = next(fiter)
     next(fiter) # Skip the list of hydrophobic atom types
     # Process the bonds
     rawline = next(fiter)
     while rawline:
         line = rawline.strip()
         if not line:
             break
         self._process_bond_line(line)
         rawline = next(fiter)
     # Process the angles
     rawline = next(fiter)
     while rawline:
         line = rawline.strip()
         if not line:
             break
         self._process_angle_line(line)
         rawline = next(fiter)
     # Process the dihedrals
     rawline = next(fiter)
     key = None
     while rawline:
         line = rawline.strip()
         if not line:
             break
         key = self._process_dihedral_line(line, finished_diheds, key)
         rawline = next(fiter)
     # Process the impropers
     rawline = next(fiter)
     while rawline:
         line = rawline.strip()
         if not line:
             break
         self._process_improper_line(line)
         rawline = next(fiter)
     # Process the 10-12 terms
     rawline = next(fiter)
     while rawline:
         line = rawline.strip()
         if not line:
             break
         try:
             a1, a2, acoef, bcoef = line.split()[:4]
             acoef = float(acoef)
             bcoef = float(bcoef)
         except ValueError:
             raise ParameterError('Trouble parsing 10-12 terms')
         if acoef != 0 or bcoef != 0:
             raise ParameterError('10-12 potential not supported in '
                                  'AmberParameterSet currently')
         rawline = next(fiter)
     # Process 12-6 terms. Get Equivalencing first
     rawline = next(fiter)
     equivalent_ljtypes = dict()
     equivalent_types = defaultdict(list)
     while rawline:
         line = rawline.strip()
         if not line:
             break
         words = line.split()
         for typ in words[1:]:
             equivalent_ljtypes[typ] = words[0]
             equivalent_types[words[0]].append(typ)
         rawline = next(fiter)
     words = next(fiter).split()
     if len(words) < 2:
         raise ParameterError('Could not parse the kind of nonbonded '
                              'parameters in Amber parameter file')
     if words[1].upper() != 'RE':
         raise ParameterError('Only RE nonbonded parameters supported')
     rawline = next(fiter)
     while rawline:
         line = rawline.strip()
         if not line:
             break
         self._process_nonbond_line(line)
         rawline = next(fiter)
     # Now assign all of the equivalenced atoms
     for atyp, otyp in iteritems(equivalent_ljtypes):
         otyp = self.atom_types[otyp]
         if atyp in self.atom_types:
             if (self.atom_types[atyp].rmin is not None and
                     self.atom_types[atyp].epsilon is not None):
                 if (abs(otyp.epsilon-self.atom_types[atyp].epsilon) > TINY
                         or abs(otyp.rmin-self.atom_types[atyp].rmin) > TINY):
                     warnings.warn('Equivalency defined between %s and %s '
                                   'but parameters are not equal' %
                                   (otyp.name, atyp), AmberWarning)
                     # Remove from equivalent types
                     equivalent_types[otyp.name].remove(atyp)
                     continue
             self.atom_types[atyp].set_lj_params(otyp.epsilon, otyp.rmin)
     line = next(fiter).strip()
     if line == 'LJEDIT':
         rawline = next(fiter)
         while rawline:
             line = rawline.strip()
             if not line:
                 break
             self._process_nbfix_line(line, equivalent_types)
             rawline = next(fiter)
Пример #9
0
    def from_leaprc(cls, fname, search_oldff=False):
        """ Load a parameter set from a leaprc file

        Parameters
        ----------
        fname : str or file-like
            Name of the file or open file-object from which a leaprc-style file
            will be read

        search_oldff : bool, optional, default=False
            If True, search the oldff directories in the main Amber leap
            folders. Default is False

        Notes
        -----
        This does not read all parts of a leaprc file -- only those pertinent to
        defining force field information. For instance, the following sections
        and commands are processed:

        - addAtomTypes
        - loadAmberParams
        - loadOFF
        - loadMol2
        - loadMol3
        """
        params = cls()
        if isinstance(fname, string_types):
            f = genopen(fname, 'r')
            own_handle = True
        else:
            f = fname
            own_handle = False
        # To make parsing easier, and because leaprc files are usually quite
        # short, I'll read the whole file into memory
        def joinlines(lines):
            newlines = []
            composite = []
            for line in lines:
                if line.endswith('\\\n'):
                    composite.append(line[:-2])
                    continue
                else:
                    composite.append(line)
                newlines.append(''.join(composite))
                composite = []
            if composite:
                newlines.append(''.join(composite))
            return newlines
        lines = joinlines(map(lambda line:
                    line if '#' not in line else line[:line.index('#')], f))
        text = ''.join(lines)
        if own_handle: f.close()
        lowertext = text.lower() # commands are case-insensitive
        # Now process the parameter files
        def process_fname(fname):
            if fname[0] in ('"', "'"):
                fname = fname[1:-1]
            fname = fname.replace('_BSTOKEN_', r'\ ').replace(r'\ ', ' ')
            return fname
        for line in lines:
            line = line.replace(r'\ ', '_BSTOKEN_')
            if _loadparamsre.findall(line):
                fname = process_fname(_loadparamsre.findall(line)[0])
                params.load_parameters(_find_amber_file(fname, search_oldff))
            elif _loadoffre.findall(line):
                fname = process_fname(_loadoffre.findall(line)[0])
                params.residues.update(
                    AmberOFFLibrary.parse(_find_amber_file(fname, search_oldff))
                )
            elif _loadmol2re.findall(line):
                (resname, fname), = _loadmol2re.findall(line)
                residue = Mol2File.parse(_find_amber_file(fname, search_oldff))
                if isinstance(residue, ResidueTemplateContainer):
                    warnings.warn('Multi-residue mol2 files not supported by '
                                  'tleap. Loading anyway using names in mol2',
                                  AmberWarning)
                    for res in residue:
                        params.residues[res.name] = res
                else:
                    params.residues[resname] = residue
        # Now process the addAtomTypes
        try:
            idx = lowertext.index('addatomtypes')
        except ValueError:
            # Does not exist in this file
            atom_types_str = ''
        else:
            i = idx + len('addatomtypes')
            while i < len(text) and text[i] != '{':
                if text[i] not in '\r\n\t ':
                    raise ParameterError('Unsupported addAtomTypes syntax in '
                                         'leaprc file')
                i += 1
            if i == len(text):
                raise ParameterError('Unsupported addAtomTypes syntax in '
                                     'leaprc file')
            # We are at our first brace
            chars = []
            nopen = 1
            i += 1
            while i < len(text):
                char = text[i]
                if char == '{':
                    nopen += 1
                elif char == '}':
                    nopen -= 1
                    if nopen == 0: break
                elif char == '\n':
                    char = ' '
                chars.append(char)
                i += 1
            atom_types_str = ''.join(chars).strip()
        for _, name, symb, hyb in _atomtypere.findall(atom_types_str):
            if symb not in AtomicNum:
                raise ParameterError('%s is not a recognized element' % symb)
            if name in params.atom_types:
                params.atom_types[name].atomic_number = AtomicNum[symb]
        return params
Пример #10
0
    def load_parameters(self, parmset, copy_parameters=True):
        """
        Loads parameters from a parameter set that was loaded via CHARMM RTF,
        PAR, and STR files.

        Parameters
        ----------
        parmset : :class:`CharmmParameterSet`
            List of all parameters

        copy_parameters : bool, optional, default=True
            If False, parmset will not be copied.

            WARNING:
            -------
            Not copying parmset will cause ParameterSet and Structure to share
            references to types.  If you modify the original parameter set, the
            references in Structure list_types will be silently modified.
            However, if you change any reference in the parameter set, then that
            reference will no longer be shared with structure.

            Example where the reference in ParameterSet is changed. The
            following will NOT modify the parameters in the psf::

                psf.load_parameters(parmset, copy_parameters=False)
                parmset.angle_types[('a1', 'a2', a3')] = AngleType(1, 2)

            The following WILL change the parameter in the psf because the
            reference has not been changed in ``ParameterSet``::

                psf.load_parameters(parmset, copy_parameters=False)
                a = parmset.angle_types[('a1', 'a2', 'a3')]
                a.k = 10
                a.theteq = 100

            Extra care should be taken when trying this with dihedral_types.
            Since dihedral_type is a Fourier sequence, ParameterSet stores
            DihedralType for every term in DihedralTypeList. Therefore, the
            example below will STILL modify the type in the :class:`Structure`
            list_types::

                parmset.dihedral_types[('a', 'b', 'c', 'd')][0] = DihedralType(1, 2, 3)

            This assigns a new instance of DihedralType to an existing
            DihedralTypeList that ParameterSet and Structure are tracking and
            the shared reference is NOT changed.

            Use with caution!

        Notes
        -----
        - If any dihedral or improper parameters cannot be found, I will try
          inserting wildcards (at either end for dihedrals and as the two
          central atoms in impropers) and see if that matches.  Wild-cards will
          apply ONLY if specific parameters cannot be found.

        - This method will expand the dihedrals attribute by adding a separate
          Dihedral object for each term for types that have a multi-term
          expansion

        Raises
        ------
        ParameterError if any parameters cannot be found
        """
        if copy_parameters:
            parmset = _copy(parmset)
        self.combining_rule = parmset.combining_rule
        # First load the atom types
        for atom in self.atoms:
            try:
                if isinstance(atom.type, int):
                    atype = parmset.atom_types_int[atom.type]
                else:
                    atype = parmset.atom_types_str[atom.type]
            except KeyError:
                raise ParameterError('Could not find atom type for %s' %
                                     atom.type)
            atom.atom_type = atype
            # Change to string type to look up the rest of the parameters
            atom.type = str(atom.atom_type)
            atom.atomic_number = atype.atomic_number

        # Next load all of the bonds
        for bond in self.bonds:
            # Construct the key
            key = (min(bond.atom1.type,
                       bond.atom2.type), max(bond.atom1.type, bond.atom2.type))
            try:
                bond.type = parmset.bond_types[key]
            except KeyError:
                raise ParameterError('Missing bond type for %r' % bond)
            bond.type.used = False
        # Build the bond_types list
        del self.bond_types[:]
        for bond in self.bonds:
            if bond.type.used: continue
            bond.type.used = True
            self.bond_types.append(bond.type)
            bond.type.list = self.bond_types
        # Next load all of the angles. If a Urey-Bradley term is defined for
        # this angle, also build the urey_bradley and urey_bradley_type lists
        del self.urey_bradleys[:]
        for ang in self.angles:
            # Construct the key
            key = (min(ang.atom1.type, ang.atom3.type), ang.atom2.type,
                   max(ang.atom1.type, ang.atom3.type))
            try:
                ang.type = parmset.angle_types[key]
                ang.type.used = False
                ubt = parmset.urey_bradley_types[key]
                if ubt is not NoUreyBradley:
                    ub = UreyBradley(ang.atom1, ang.atom3, ubt)
                    self.urey_bradleys.append(ub)
                    ubt.used = False
            except KeyError:
                raise ParameterError('Missing angle type for %r' % ang)
        del self.urey_bradley_types[:]
        del self.angle_types[:]
        for ub in self.urey_bradleys:
            if ub.type.used: continue
            ub.type.used = True
            self.urey_bradley_types.append(ub.type)
            ub.type.list = self.urey_bradley_types
        for ang in self.angles:
            if ang.type.used: continue
            ang.type.used = True
            self.angle_types.append(ang.type)
            ang.type.list = self.angle_types
        # Next load all of the dihedrals.
        active_dih_list = set()
        for dih in self.dihedrals:
            # Store the atoms
            a1, a2, a3, a4 = dih.atom1, dih.atom2, dih.atom3, dih.atom4
            key = (a1.type, a2.type, a3.type, a4.type)
            # First see if the exact dihedral is specified
            if not key in parmset.dihedral_types:
                # Check for wild-cards
                key = ('X', a2.type, a3.type, 'X')
                if not key in parmset.dihedral_types:
                    raise ParameterError('No dihedral parameters found for '
                                         '%r' % dih)
            dih.type = parmset.dihedral_types[key]
            dih.type.used = False
            pair = (dih.atom1.idx, dih.atom4.idx)  # To determine exclusions
            if (dih.atom1 in dih.atom4.bond_partners
                    or dih.atom1 in dih.atom4.angle_partners):
                dih.ignore_end = True
            elif pair in active_dih_list:
                dih.ignore_end = True
            else:
                active_dih_list.add(pair)
                active_dih_list.add((dih.atom4.idx, dih.atom1.idx))
        del self.dihedral_types[:]
        for dihedral in self.dihedrals:
            if dihedral.type.used: continue
            dihedral.type.used = True
            self.dihedral_types.append(dihedral.type)
            dihedral.type.list = self.dihedral_types
        # Now do the impropers
        for imp in self.impropers:
            # Store the atoms
            a1, a2, a3, a4 = imp.atom1, imp.atom2, imp.atom3, imp.atom4
            at1, at2, at3, at4 = a1.type, a2.type, a3.type, a4.type
            key = tuple(sorted([at1, at2, at3, at4]))
            altkey1 = a1.type, a2.type, a3.type, a4.type
            altkey2 = a4.type, a3.type, a2.type, a1.type
            # Check for exact harmonic or exact periodic
            if key in parmset.improper_types:
                imp.type = parmset.improper_types[key]
            elif key in parmset.improper_periodic_types:
                imp.type = parmset.improper_periodic_types[key]
            elif altkey1 in parmset.improper_periodic_types:
                imp.type = parmset.improper_periodic_types[altkey1]
            elif altkey2 in parmset.improper_periodic_types:
                imp.type = parmset.improper_periodic_types[altkey2]
            else:
                # Check for wild-card harmonic
                for anchor in (at2, at3, at4):
                    key = tuple(sorted([at1, anchor, 'X', 'X']))
                    if key in parmset.improper_types:
                        imp.type = parmset.improper_types[key]
                        break
                # Check for wild-card periodic
                if key not in parmset.improper_types:
                    for anchor in (at2, at3, at4):
                        key = tuple(sorted([at1, anchor, 'X', 'X']))
                        if key in parmset.improper_periodic_types:
                            imp.type = parmset.improper_periodic_types[key]
                            break
                    # Not found anywhere
                    if key not in parmset.improper_periodic_types:
                        raise ParameterError(
                            'No improper parameters found for '
                            '%r' % imp)
            imp.type.used = False
        # prepare list of harmonic impropers present in system
        del self.improper_types[:]
        for improper in self.impropers:
            if improper.type.used: continue
            improper.type.used = True
            if isinstance(improper.type, ImproperType):
                self.improper_types.append(improper.type)
                improper.type.list = self.improper_types
            elif isinstance(improper.type, DihedralType):
                self.dihedral_types.append(improper.type)
                improper.type.list = self.dihedral_types
            else:
                assert False, 'Should not be here'
        # Look through the list of impropers -- if there are any periodic
        # impropers, move them over to the dihedrals list
        for i in reversed(range(len(self.impropers))):
            if isinstance(self.impropers[i].type, DihedralType):
                imp = self.impropers.pop(i)
                dih = Dihedral(imp.atom1,
                               imp.atom2,
                               imp.atom3,
                               imp.atom4,
                               improper=True,
                               ignore_end=True,
                               type=imp.type)
                imp.delete()
                self.dihedrals.append(dih)
        # Now do the cmaps. These will not have wild-cards
        for cmap in self.cmaps:
            key = (cmap.atom1.type, cmap.atom2.type, cmap.atom3.type,
                   cmap.atom4.type, cmap.atom2.type, cmap.atom3.type,
                   cmap.atom4.type, cmap.atom5.type)
            try:
                cmap.type = parmset.cmap_types[key]
            except KeyError:
                raise ParameterError('No CMAP parameters found for %r' % cmap)
            cmap.type.used = False
        del self.cmap_types[:]
        for cmap in self.cmaps:
            if cmap.type.used: continue
            cmap.type.used = True
            self.cmap_types.append(cmap.type)
            cmap.type.list = self.cmap_types
Пример #11
0
    def load_parameters(self, parmset):
        """
        Loads parameters from a parameter set that was loaded via CHARMM RTF,
        PAR, and STR files.

        Parameters
        ----------
        parmset : :class:`CharmmParameterSet`
            List of all parameters

        Notes
        -----
        - If any dihedral or improper parameters cannot be found, I will try
          inserting wildcards (at either end for dihedrals and as the two
          central atoms in impropers) and see if that matches.  Wild-cards will
          apply ONLY if specific parameters cannot be found.

        - This method will expand the dihedrals attribute by adding a separate
          Dihedral object for each term for types that have a multi-term
          expansion

        Raises
        ------
        ParameterError if any parameters cannot be found
        """
        parmset = _copy(parmset)
        self.combining_rule = parmset.combining_rule
        # First load the atom types
        for atom in self.atoms:
            try:
                if isinstance(atom.type, int):
                    atype = parmset.atom_types_int[atom.type]
                else:
                    atype = parmset.atom_types_str[atom.type]
            except KeyError:
                raise ParameterError('Could not find atom type for %s' %
                                     atom.type)
            atom.atom_type = atype
            # Change to string type to look up the rest of the parameters
            atom.type = str(atom.atom_type)
            atom.atomic_number = atype.atomic_number

        # Next load all of the bonds
        for bond in self.bonds:
            # Construct the key
            key = (min(bond.atom1.type,
                       bond.atom2.type), max(bond.atom1.type, bond.atom2.type))
            try:
                bond.type = parmset.bond_types[key]
            except KeyError:
                raise ParameterError('Missing bond type for %r' % bond)
            bond.type.used = False
        # Build the bond_types list
        del self.bond_types[:]
        for bond in self.bonds:
            if bond.type.used: continue
            bond.type.used = True
            self.bond_types.append(bond.type)
            bond.type.list = self.bond_types
        # Next load all of the angles. If a Urey-Bradley term is defined for
        # this angle, also build the urey_bradley and urey_bradley_type lists
        del self.urey_bradleys[:]
        for ang in self.angles:
            # Construct the key
            key = (min(ang.atom1.type, ang.atom3.type), ang.atom2.type,
                   max(ang.atom1.type, ang.atom3.type))
            try:
                ang.type = parmset.angle_types[key]
                ang.type.used = False
                ubt = parmset.urey_bradley_types[key]
                if ubt is not NoUreyBradley:
                    ub = UreyBradley(ang.atom1, ang.atom3, ubt)
                    self.urey_bradleys.append(ub)
                    ubt.used = False
            except KeyError:
                raise ParameterError('Missing angle type for %r' % ang)
        del self.urey_bradley_types[:]
        del self.angle_types[:]
        for ub in self.urey_bradleys:
            if ub.type.used: continue
            ub.type.used = True
            self.urey_bradley_types.append(ub.type)
            ub.type.list = self.urey_bradley_types
        for ang in self.angles:
            if ang.type.used: continue
            ang.type.used = True
            self.angle_types.append(ang.type)
            ang.type.list = self.angle_types
        # Next load all of the dihedrals.
        active_dih_list = set()
        for dih in self.dihedrals:
            # Store the atoms
            a1, a2, a3, a4 = dih.atom1, dih.atom2, dih.atom3, dih.atom4
            key = (a1.type, a2.type, a3.type, a4.type)
            # First see if the exact dihedral is specified
            if not key in parmset.dihedral_types:
                # Check for wild-cards
                key = ('X', a2.type, a3.type, 'X')
                if not key in parmset.dihedral_types:
                    raise ParameterError('No dihedral parameters found for '
                                         '%r' % dih)
            dih.type = parmset.dihedral_types[key]
            dih.type.used = False
            pair = (dih.atom1.idx, dih.atom4.idx)  # To determine exclusions
            if (dih.atom1 in dih.atom4.bond_partners
                    or dih.atom1 in dih.atom4.angle_partners):
                dih.ignore_end = True
            elif pair in active_dih_list:
                dih.ignore_end = True
            else:
                active_dih_list.add(pair)
                active_dih_list.add((dih.atom4.idx, dih.atom1.idx))
        del self.dihedral_types[:]
        for dihedral in self.dihedrals:
            if dihedral.type.used: continue
            dihedral.type.used = True
            self.dihedral_types.append(dihedral.type)
            dihedral.type.list = self.dihedral_types
        # Now do the impropers
        for imp in self.impropers:
            # Store the atoms
            a1, a2, a3, a4 = imp.atom1, imp.atom2, imp.atom3, imp.atom4
            at1, at2, at3, at4 = a1.type, a2.type, a3.type, a4.type
            key = tuple(sorted([at1, at2, at3, at4]))
            # Check for exact harmonic or exact periodic
            if key in parmset.improper_types:
                imp.type = parmset.improper_types[key]
            elif key in parmset.improper_periodic_types:
                imp.type = parmset.improper_periodic_types[key]
            else:
                # Check for wild-card harmonic
                for anchor in (at2, at3, at4):
                    key = tuple(sorted([at1, anchor, 'X', 'X']))
                    if key in parmset.improper_types:
                        imp.type = parmset.improper_types[key]
                        break
                # Check for wild-card periodic
                if key not in parmset.improper_types:
                    for anchor in (at2, at3, at4):
                        key = tuple(sorted([at1, anchor, 'X', 'X']))
                        if key in parmset.improper_periodic_types:
                            imp.type = parmset.improper_periodic_types[key]
                            break
                    # Not found anywhere
                    if key not in parmset.improper_periodic_types:
                        raise ParameterError('No improper parameters found for'
                                             '%r' % imp)
            imp.type.used = False
        # prepare list of harmonic impropers present in system
        del self.improper_types[:]
        for improper in self.impropers:
            if improper.type.used: continue
            improper.type.used = True
            if isinstance(improper.type, ImproperType):
                self.improper_types.append(improper.type)
                improper.type.list = self.improper_types
            elif isinstance(improper.type, DihedralType):
                self.dihedral_types.append(improper.type)
                improper.type.list = self.dihedral_types
            else:
                assert False, 'Should not be here'
        # Look through the list of impropers -- if there are any periodic
        # impropers, move them over to the dihedrals list
        for i in reversed(range(len(self.impropers))):
            if isinstance(self.impropers[i].type, DihedralType):
                imp = self.impropers.pop(i)
                dih = Dihedral(imp.atom1,
                               imp.atom2,
                               imp.atom3,
                               imp.atom4,
                               improper=True,
                               ignore_end=True,
                               type=imp.type)
                imp.delete()
                self.dihedrals.append(dih)
        # Now do the cmaps. These will not have wild-cards
        for cmap in self.cmaps:
            key = (cmap.atom1.type, cmap.atom2.type, cmap.atom3.type,
                   cmap.atom4.type, cmap.atom2.type, cmap.atom3.type,
                   cmap.atom4.type, cmap.atom5.type)
            try:
                cmap.type = parmset.cmap_types[key]
            except KeyError:
                raise ParameterError('No CMAP parameters found for %r' % cmap)
            cmap.type.used = False
        del self.cmap_types[:]
        for cmap in self.cmaps:
            if cmap.type.used: continue
            cmap.type.used = True
            self.cmap_types.append(cmap.type)
            cmap.type.list = self.cmap_types
Пример #12
0
    def from_structure(cls, struct, allow_unequal_duplicates=True):
        """ Extracts known parameters from a Structure instance

        Parameters
        ----------
        struct : :class:`parmed.structure.Structure`
            The parametrized ``Structure`` instance from which to extract
            parameters into a ParameterSet
        allow_unequal_duplicates : bool, optional
            If True, if two or more unequal parameter types are defined by the
            same atom types, the last one encountered will be assigned. If
            False, an exception will be raised. Default is True

        Returns
        -------
        params : :class:`ParameterSet`
            The parameter set with all parameters defined in the Structure

        Notes
        -----
        The parameters here are copies of the ones in the Structure, so
        modifying the generated ParameterSet will have no effect on ``struct``.
        Furthermore, the *first* occurrence of each parameter will be used. If
        future ones differ, they will be silently ignored, since this is
        expected behavior in some instances (like with Gromacs topologies in the
        ff99sb-ildn force field) unless ``allow_unequal_duplicates`` is set to
        ``False``

        Dihedrals are a little trickier. They can be multi-term, which can be
        represented either as a *single* entry in dihedrals with a type of
        DihedralTypeList or multiple entries in dihedrals with a DihedralType
        parameter type. In this case, the parameter is constructed from either
        the first DihedralTypeList found or the first DihedralType of each
        periodicity found if no matching DihedralTypeList is found.

        Raises
        ------
        :class:`parmed.exceptions.ParameterError` if allow_unequal_duplicates is
        False and 2+ unequal parameters are defined between the same atom types.

        `NotImplementedError` if any AMOEBA potential terms are defined in the
        input structure
        """
        params = cls()
        found_dihed_type_list = dict()
        for atom in struct.atoms:
            if atom.atom_type in (UnassignedAtomType, None):
                atom_type = AtomType(atom.type, None, atom.mass,
                                     atom.atomic_number)
                atom_type.set_lj_params(atom.epsilon, atom.rmin,
                                        atom.epsilon_14, atom.rmin_14)
                params.atom_types[atom.type] = atom_type
            else:
                atom_type = copy(atom.atom_type)
                params.atom_types[str(atom_type)] = atom_type
                if atom_type.number is not None:
                    params.atom_types_int[int(atom_type)] = atom_type
                    params.atom_types_tuple[(int(atom_type), str(atom_type))] =\
                            atom_type
        for bond in struct.bonds:
            if bond.type is None: continue
            key = (bond.atom1.type, bond.atom2.type)
            if key in params.bond_types:
                if (not allow_unequal_duplicates
                        and params.bond_types[key] != bond.type):
                    raise ParameterError('Unequal bond types defined between '
                                         '%s and %s' % key)
                continue  # pragma: no cover
            typ = copy(bond.type)
            key = (bond.atom1.type, bond.atom2.type)
            params.bond_types[key] = typ
            params.bond_types[tuple(reversed(key))] = typ
        for angle in struct.angles:
            if angle.type is None: continue
            key = (angle.atom1.type, angle.atom2.type, angle.atom3.type)
            if key in params.angle_types:
                if (not allow_unequal_duplicates
                        and params.angle_types[key] != angle.type):
                    raise ParameterError('Unequal angle types defined between '
                                         '%s, %s, and %s' % key)
                continue  # pragma: no cover
            typ = copy(angle.type)
            key = (angle.atom1.type, angle.atom2.type, angle.atom3.type)
            params.angle_types[key] = typ
            params.angle_types[tuple(reversed(key))] = typ
            if angle.funct == 5:
                key = (angle.atom1.type, angle.atom3.type)
                params.urey_bradley_types[key] = NoUreyBradley
                params.urey_bradley_types[tuple(reversed(key))] = NoUreyBradley
        for dihedral in struct.dihedrals:
            if dihedral.type is None: continue
            key = (dihedral.atom1.type, dihedral.atom2.type,
                   dihedral.atom3.type, dihedral.atom4.type)
            if dihedral.improper:
                key = cls._periodic_improper_key(
                    dihedral.atom1,
                    dihedral.atom2,
                    dihedral.atom3,
                    dihedral.atom4,
                )
                if key in params.improper_periodic_types:
                    if (not allow_unequal_duplicates
                            and params.improper_periodic_types[key] !=
                            dihedral.type):
                        raise ParameterError('Unequal dihedral types defined '
                                             'between %s, %s, %s, and %s' %
                                             key)
                    continue  # pragma: no cover
                typ = copy(dihedral.type)
                params.improper_periodic_types[key] = typ
            else:
                # Proper dihedral. Look out for multi-term forms
                if (key in params.dihedral_types
                        and found_dihed_type_list[key]):
                    # Already found a multi-term dihedral type list
                    if not allow_unequal_duplicates:
                        if isinstance(dihedral.type, DihedralTypeList):
                            if params.dihedral_types[key] != dihedral.type:
                                raise ParameterError(
                                    'Unequal dihedral types '
                                    'defined between %s, %s, %s, and %s' % key)
                        elif isinstance(dihedral.type, DihedralType):
                            for dt in params.dihedral_types[key]:
                                if dt == dihedral.type:
                                    break
                            else:
                                raise ParameterError(
                                    'Unequal dihedral types '
                                    'defined between %s, %s, %s, and %s' % key)
                    continue  # pragma: no cover
                elif key in params.dihedral_types:
                    # We have one term of a potentially multi-term dihedral.
                    if isinstance(dihedral.type, DihedralTypeList):
                        # This is a full Fourier series list
                        found_dihed_type_list[key] = True
                        found_dihed_type_list[tuple(reversed(key))] = True
                        typ = copy(dihedral.type)
                        params.dihedral_types[key] = typ
                        params.dihedral_types[tuple(reversed(key))] = typ
                    else:
                        # This *might* be another term. Make sure another term
                        # with its periodicity does not already exist
                        for t in params.dihedral_types[key]:
                            if t.per == dihedral.type.per:
                                if (not allow_unequal_duplicates
                                        and t != dihedral.type):
                                    raise ParameterError(
                                        'Unequal dihedral '
                                        'types defined bewteen %s, %s, %s, '
                                        'and %s' % key)
                                break
                        else:
                            # If we got here, we did NOT find this periodicity.
                            # And since this is mutating a list in-place, it
                            # automatically propagates to the reversed key
                            typ = copy(dihedral.type)
                            params.dihedral_types[key].append(typ)
                else:
                    # New parameter. If it's a DihedralTypeList, assign it and
                    # be done with it. If it's a DihedralType, start a
                    # DihedralTypeList to be added to later.
                    if isinstance(dihedral.type, DihedralTypeList):
                        found_dihed_type_list[key] = True
                        found_dihed_type_list[tuple(reversed(key))] = True
                        typ = copy(dihedral.type)
                        params.dihedral_types[key] = typ
                        params.dihedral_types[tuple(reversed(key))] = typ
                    else:
                        found_dihed_type_list[key] = False
                        found_dihed_type_list[tuple(reversed(key))] = False
                        typ = DihedralTypeList()
                        typ.append(copy(dihedral.type))
                        params.dihedral_types[key] = typ
                        params.dihedral_types[tuple(reversed(key))] = typ
        for improper in struct.impropers:
            if improper.type is None: continue
            key = (improper.atom1.type, improper.atom2.type,
                   improper.atom3.type, improper.atom4.type)
            if key in params.improper_types:
                if (not allow_unequal_duplicates
                        and params.improper_types[key] != improper.type):
                    raise ParameterError('Unequal improper types defined '
                                         'between %s, %s, %s, and %s' % key)
                continue  # pragma: no cover
            params.improper_types[key] = copy(improper.type)
        for cmap in struct.cmaps:
            if cmap.type is None: continue
            key = (cmap.atom1.type, cmap.atom2.type, cmap.atom3.type,
                   cmap.atom4.type, cmap.atom2.type, cmap.atom3.type,
                   cmap.atom4.type, cmap.atom5.type)
            if key in params.cmap_types:
                if (not allow_unequal_duplicates
                        and cmap.type != params.cmap_types[key]):
                    raise ParameterError(
                        'Unequal CMAP types defined between '
                        '%s, %s, %s, %s, and %s' %
                        (key[0], key[1], key[2], key[3], key[7]))
                continue  # pragma: no cover
            typ = copy(cmap.type)
            params.cmap_types[key] = typ
            params.cmap_types[tuple(reversed(key))] = typ
        for urey in struct.urey_bradleys:
            if urey.type is None or urey.type is NoUreyBradley: continue
            key = (urey.atom1.type, urey.atom2.type)
            if key not in params.urey_bradley_types:
                warnings.warn('Angle corresponding to Urey-Bradley type not '
                              'found')
            typ = copy(urey.type)
            params.urey_bradley_types[key] = typ
            params.urey_bradley_types[tuple(reversed(key))] = typ
        for adjust in struct.adjusts:
            if adjust.type is None: continue
            key = (adjust.atom1.type, adjust.atom2.type)
            if key in params.pair_types:
                if (not allow_unequal_duplicates
                        and params.pair_types[key] != adjust.type):
                    raise ParameterError('Unequal pair types defined between '
                                         '%s and %s' % key)
                continue  # pragma: no cover
            typ = copy(adjust.type)
            params.pair_types[key] = typ
            params.pair_types[tuple(reversed(key))] = typ
        # Trap for Amoeba potentials
        if (struct.trigonal_angles or struct.out_of_plane_bends
                or struct.torsion_torsions or struct.stretch_bends
                or struct.trigonal_angles or struct.pi_torsions):
            raise NotImplementedError('Cannot extract parameters from an '
                                      'Amoeba-parametrized system yet')
        return params