Example #1
0
    def createSystem(self, nonbondedMethod=ff.NoCutoff, nonbondedCutoff=1.0*unit.nanometer,
                     constraints=None, rigidWater=True, implicitSolvent=None, soluteDielectric=1.0, solventDielectric=78.5, ewaldErrorTolerance=0.0005, removeCMMotion=True, hydrogenMass=None):
        """Construct an OpenMM System representing the topology described by this
        prmtop file.

        Parameters
        ----------
        nonbondedMethod : object=NoCutoff
            The method to use for nonbonded interactions.  Allowed values are
            NoCutoff, CutoffNonPeriodic, CutoffPeriodic, Ewald, PME, or LJPME.
        nonbondedCutoff : distance=1*nanometer
            The cutoff distance to use for nonbonded interactions
        constraints : object=None
            Specifies which bonds and angles should be implemented with
            constraints. Allowed values are None, HBonds, AllBonds, or HAngles.
        rigidWater : boolean=True
            If true, water molecules will be fully rigid regardless of the value
            passed for the constraints argument
        implicitSolvent : object=None
            If not None, the implicit solvent model to use.  The only allowed
            value is OBC2.
        soluteDielectric : float=1.0
            The solute dielectric constant to use in the implicit solvent model.
        solventDielectric : float=78.5
            The solvent dielectric constant to use in the implicit solvent
            model.
        ewaldErrorTolerance : float=0.0005
            The error tolerance to use if nonbondedMethod is Ewald, PME, or LJPME.
        removeCMMotion : boolean=True
            If true, a CMMotionRemover will be added to the System
        hydrogenMass : mass=None
            The mass to use for hydrogen atoms bound to heavy atoms.  Any mass
            added to a hydrogen is subtracted from the heavy atom to keep their
            total mass the same.

        Returns
        -------
        System
             the newly created System
        """
        # Create the System.

        sys = mm.System()
        boxVectors = self.topology.getPeriodicBoxVectors()
        if boxVectors is not None:
            sys.setDefaultPeriodicBoxVectors(*boxVectors)
        elif nonbondedMethod in (ff.CutoffPeriodic, ff.Ewald, ff.PME, ff.LJPME):
            raise ValueError('Illegal nonbonded method for a non-periodic system')
        nb = mm.NonbondedForce()
        sys.addForce(nb)
        if implicitSolvent is OBC2:
            gb = mm.GBSAOBCForce()
            gb.setSoluteDielectric(soluteDielectric)
            gb.setSolventDielectric(solventDielectric)
            sys.addForce(gb)
            nb.setReactionFieldDielectric(1.0)
        elif implicitSolvent is not None:
            raise ValueError('Illegal value for implicitSolvent')
        bonds = None
        angles = None
        periodic = None
        rb = None
        harmonicTorsion = None
        cmap = None
        mapIndices = {}
        bondIndices = []
        topologyAtoms = list(self.topology.atoms())
        exceptions = []
        fudgeQQ = float(self._defaults[4])

        # Build a lookup table to let us process dihedrals more quickly.

        dihedralTypeTable = {}
        for key in self._dihedralTypes:
            if key[1] != 'X' and key[2] != 'X':
                if (key[1], key[2]) not in dihedralTypeTable:
                    dihedralTypeTable[(key[1], key[2])] = []
                dihedralTypeTable[(key[1], key[2])].append(key)
                if (key[2], key[1]) not in dihedralTypeTable:
                    dihedralTypeTable[(key[2], key[1])] = []
                dihedralTypeTable[(key[2], key[1])].append(key)
        wildcardDihedralTypes = []
        for key in self._dihedralTypes:
            if key[1] == 'X' or key[2] == 'X':
                wildcardDihedralTypes.append(key)
                for types in dihedralTypeTable.values():
                    types.append(key)

        # Loop over molecules and create the specified number of each type.

        for moleculeName, moleculeCount in self._molecules:
            moleculeType = self._moleculeTypes[moleculeName]
            for i in range(moleculeCount):

                # Record the types of all atoms.

                baseAtomIndex = sys.getNumParticles()
                atomTypes = [atom[1] for atom in moleculeType.atoms]
                try:
                    bondedTypes = [self._atomTypes[t][1] for t in atomTypes]
                except KeyError as e:
                    raise ValueError('Unknown atom type: ' + e.message)
                bondedTypes = [b if b is not None else a for a, b in zip(atomTypes, bondedTypes)]

                # Add atoms.

                for fields in moleculeType.atoms:
                    if len(fields) >= 8:
                        mass = float(fields[7])
                    else:
                        mass = float(self._atomTypes[fields[1]][3])
                    sys.addParticle(mass)

                # Add bonds.

                atomBonds = [{} for x in range(len(moleculeType.atoms))]
                for fields in moleculeType.bonds:
                    atoms = [int(x)-1 for x in fields[:2]]
                    types = tuple(bondedTypes[i] for i in atoms)
                    if len(fields) >= 5:
                        params = fields[3:5]
                    elif types in self._bondTypes:
                        params = self._bondTypes[types][3:5]
                    elif types[::-1] in self._bondTypes:
                        params = self._bondTypes[types[::-1]][3:5]
                    else:
                        raise ValueError('No parameters specified for bond: '+fields[0]+', '+fields[1])
                    # Decide whether to use a constraint or a bond.
                    useConstraint = False
                    if rigidWater and topologyAtoms[baseAtomIndex+atoms[0]].residue.name == 'HOH':
                        useConstraint = True
                    if constraints in (AllBonds, HAngles):
                        useConstraint = True
                    elif constraints is HBonds:
                        elements = [topologyAtoms[baseAtomIndex+i].element for i in atoms]
                        if elem.hydrogen in elements:
                            useConstraint = True
                    # Add the bond or constraint.
                    length = float(params[0])
                    if useConstraint:
                        sys.addConstraint(baseAtomIndex+atoms[0], baseAtomIndex+atoms[1], length)
                    else:
                        if bonds is None:
                            bonds = mm.HarmonicBondForce()
                            sys.addForce(bonds)
                        bonds.addBond(baseAtomIndex+atoms[0], baseAtomIndex+atoms[1], length, float(params[1]))
                    # Record information that will be needed for constraining angles.
                    atomBonds[atoms[0]][atoms[1]] = length
                    atomBonds[atoms[1]][atoms[0]] = length

                # Add angles.

                degToRad = math.pi/180
                for fields in moleculeType.angles:
                    atoms = [int(x)-1 for x in fields[:3]]
                    types = tuple(bondedTypes[i] for i in atoms)
                    if len(fields) >= 6:
                        params = fields[4:]
                    elif types in self._angleTypes:
                        params = self._angleTypes[types][4:]
                    elif types[::-1] in self._angleTypes:
                        params = self._angleTypes[types[::-1]][4:]
                    else:
                        raise ValueError('No parameters specified for angle: '+fields[0]+', '+fields[1]+', '+fields[2])
                    # Decide whether to use a constraint or a bond.
                    useConstraint = False
                    if rigidWater and topologyAtoms[baseAtomIndex+atoms[0]].residue.name == 'HOH':
                        useConstraint = True
                    if constraints is HAngles:
                        elements = [topologyAtoms[baseAtomIndex+i].element for i in atoms]
                        if elements[0] == elem.hydrogen and elements[2] == elem.hydrogen:
                            useConstraint = True
                        elif elements[1] == elem.oxygen and (elements[0] == elem.hydrogen or elements[2] == elem.hydrogen):
                            useConstraint = True
                    # Add the bond or constraint.
                    theta = float(params[0])*degToRad
                    if useConstraint:
                        # Compute the distance between atoms and add a constraint
                        if atoms[0] in atomBonds[atoms[1]] and atoms[2] in atomBonds[atoms[1]]:
                            l1 = atomBonds[atoms[1]][atoms[0]]
                            l2 = atomBonds[atoms[1]][atoms[2]]
                            length = math.sqrt(l1*l1 + l2*l2 - 2*l1*l2*math.cos(theta))
                            sys.addConstraint(baseAtomIndex+atoms[0], baseAtomIndex+atoms[2], length)
                    else:
                        if angles is None:
                            angles = mm.HarmonicAngleForce()
                            sys.addForce(angles)
                        angles.addAngle(baseAtomIndex+atoms[0], baseAtomIndex+atoms[1], baseAtomIndex+atoms[2], theta, float(params[1]))
                        if fields[3] == '5':
                            # This is a Urey-Bradley term, so add the bond.
                            if bonds is None:
                                bonds = mm.HarmonicBondForce()
                                sys.addForce(bonds)
                            k = float(params[3])
                            if k != 0:
                                bonds.addBond(baseAtomIndex+atoms[0], baseAtomIndex+atoms[2], float(params[2]), k)

                # Add torsions.

                for fields in moleculeType.dihedrals:
                    atoms = [int(x)-1 for x in fields[:4]]
                    types = tuple(bondedTypes[i] for i in atoms)
                    dihedralType = fields[4]
                    reversedTypes = types[::-1]+(dihedralType,)
                    types = types+(dihedralType,)
                    if (dihedralType in ('1', '2', '4', '9') and len(fields) > 7) or (dihedralType == '3' and len(fields) > 10):
                        paramsList = [fields]
                    else:
                        # Look for a matching dihedral type.
                        paramsList = None
                        if (types[1], types[2]) in dihedralTypeTable:
                            dihedralTypes = dihedralTypeTable[(types[1], types[2])]
                        else:
                            dihedralTypes = wildcardDihedralTypes
                        for key in dihedralTypes:
                            if all(a == b or a == 'X' for a, b in zip(key, types)) or all(a == b or a == 'X' for a, b in zip(key, reversedTypes)):
                                paramsList = self._dihedralTypes[key]
                                if 'X' not in key:
                                    break
                        if paramsList is None:
                            raise ValueError('No parameters specified for dihedral: '+fields[0]+', '+fields[1]+', '+fields[2]+', '+fields[3])
                    for params in paramsList:
                        if dihedralType in ('1', '4', '9'):
                            # Periodic torsion
                            k = float(params[6])
                            if k != 0:
                                if periodic is None:
                                    periodic = mm.PeriodicTorsionForce()
                                    sys.addForce(periodic)
                                periodic.addTorsion(baseAtomIndex+atoms[0], baseAtomIndex+atoms[1], baseAtomIndex+atoms[2], baseAtomIndex+atoms[3], int(float(params[7])), float(params[5])*degToRad, k)
                        elif dihedralType == '2':
                            # Harmonic torsion
                            k = float(params[6])
                            if k != 0:
                                if harmonicTorsion is None:
                                    harmonicTorsion = mm.CustomTorsionForce('0.5*k*(theta-theta0)^2')
                                    harmonicTorsion.addPerTorsionParameter('theta0')
                                    harmonicTorsion.addPerTorsionParameter('k')
                                    sys.addForce(harmonicTorsion)
                                harmonicTorsion.addTorsion(baseAtomIndex+atoms[0], baseAtomIndex+atoms[1], baseAtomIndex+atoms[2], baseAtomIndex+atoms[3], (float(params[5])*degToRad, k))
                        else:
                            # RB Torsion
                            c = [float(x) for x in params[5:11]]
                            if any(x != 0 for x in c):
                                if rb is None:
                                    rb = mm.RBTorsionForce()
                                    sys.addForce(rb)
                                rb.addTorsion(baseAtomIndex+atoms[0], baseAtomIndex+atoms[1], baseAtomIndex+atoms[2], baseAtomIndex+atoms[3], c[0], c[1], c[2], c[3], c[4], c[5])

                # Add CMAP terms.

                for fields in moleculeType.cmaps:
                    atoms = [int(x)-1 for x in fields[:5]]
                    types = tuple(bondedTypes[i] for i in atoms)
                    if len(fields) >= 8 and len(fields) >= 8+int(fields[6])*int(fields[7]):
                        params = fields
                    elif types in self._cmapTypes:
                        params = self._cmapTypes[types]
                    elif types[::-1] in self._cmapTypes:
                        params = self._cmapTypes[types[::-1]]
                    else:
                        raise ValueError('No parameters specified for cmap: '+fields[0]+', '+fields[1]+', '+fields[2]+', '+fields[3]+', '+fields[4])
                    if cmap is None:
                        cmap = mm.CMAPTorsionForce()
                        sys.addForce(cmap)
                    mapSize = int(params[6])
                    if mapSize != int(params[7]):
                        raise ValueError('Non-square CMAPs are not supported')
                    map = []
                    for i in range(mapSize):
                        for j in range(mapSize):
                            map.append(float(params[8+mapSize*((j+mapSize//2)%mapSize)+((i+mapSize//2)%mapSize)]))
                    map = tuple(map)
                    if map not in mapIndices:
                        mapIndices[map] = cmap.addMap(mapSize, map)
                    cmap.addTorsion(mapIndices[map], baseAtomIndex+atoms[0], baseAtomIndex+atoms[1], baseAtomIndex+atoms[2], baseAtomIndex+atoms[3],
                                 baseAtomIndex+atoms[1], baseAtomIndex+atoms[2], baseAtomIndex+atoms[3], baseAtomIndex+atoms[4])

                # Set nonbonded parameters for particles.

                for fields in moleculeType.atoms:
                    params = self._atomTypes[fields[1]]
                    if len(fields) > 6:
                        q = float(fields[6])
                    else:
                        q = float(params[4])
                    nb.addParticle(q, float(params[6]), float(params[7]))
                    if implicitSolvent is OBC2:
                        if fields[1] not in self._implicitTypes:
                            raise ValueError('No implicit solvent parameters specified for atom type: '+fields[1])
                        gbparams = self._implicitTypes[fields[1]]
                        gb.addParticle(q, float(gbparams[4]), float(gbparams[5]))
                for fields in moleculeType.bonds:
                    atoms = [int(x)-1 for x in fields[:2]]
                    bondIndices.append((baseAtomIndex+atoms[0], baseAtomIndex+atoms[1]))

                # Record nonbonded exceptions.

                for fields in moleculeType.pairs:
                    atoms = [int(x)-1 for x in fields[:2]]
                    types = tuple(atomTypes[i] for i in atoms)
                    if len(fields) >= 5:
                        params = fields[3:5]
                    elif types in self._pairTypes:
                        params = self._pairTypes[types][3:5]
                    elif types[::-1] in self._pairTypes:
                        params = self._pairTypes[types[::-1]][3:5]
                    elif not self._genpairs:
                        raise ValueError('No pair parameters defined for atom '
                                         'types %s and gen-pairs is "no"' % types)
                    else:
                        continue # We'll use the automatically generated parameters
                    atom1params = nb.getParticleParameters(baseAtomIndex+atoms[0])
                    atom2params = nb.getParticleParameters(baseAtomIndex+atoms[1])
                    exceptions.append((baseAtomIndex+atoms[0], baseAtomIndex+atoms[1], atom1params[0]*atom2params[0]*fudgeQQ, params[0], params[1]))
                for fields in moleculeType.exclusions:
                    atoms = [int(x)-1 for x in fields]
                    for atom in atoms[1:]:
                        if atom > atoms[0]:
                            exceptions.append((baseAtomIndex+atoms[0], baseAtomIndex+atom, 0, 0, 0))


        # Create nonbonded exceptions.

        nb.createExceptionsFromBonds(bondIndices, fudgeQQ, float(self._defaults[3]))
        for exception in exceptions:
            nb.addException(exception[0], exception[1], exception[2], float(exception[3]), float(exception[4]), True)

        # Finish configuring the NonbondedForce.

        methodMap = {ff.NoCutoff:mm.NonbondedForce.NoCutoff,
                     ff.CutoffNonPeriodic:mm.NonbondedForce.CutoffNonPeriodic,
                     ff.CutoffPeriodic:mm.NonbondedForce.CutoffPeriodic,
                     ff.Ewald:mm.NonbondedForce.Ewald,
                     ff.PME:mm.NonbondedForce.PME,
                     ff.LJPME:mm.NonbondedForce.LJPME}
        nb.setNonbondedMethod(methodMap[nonbondedMethod])
        nb.setCutoffDistance(nonbondedCutoff)
        nb.setEwaldErrorTolerance(ewaldErrorTolerance)

        # Adjust masses.

        if hydrogenMass is not None:
            for atom1, atom2 in self.topology.bonds():
                if atom1.element == elem.hydrogen:
                    (atom1, atom2) = (atom2, atom1)
                if atom2.element == elem.hydrogen and atom1.element not in (elem.hydrogen, None):
                    transferMass = hydrogenMass-sys.getParticleMass(atom2.index)
                    sys.setParticleMass(atom2.index, hydrogenMass)
                    sys.setParticleMass(atom1.index, sys.getParticleMass(atom1.index)-transferMass)

        # Add a CMMotionRemover.

        if removeCMMotion:
            sys.addForce(mm.CMMotionRemover())
        return sys
Example #2
0
    def createSystem(self, topology, nonbondedMethod=NoCutoff,
                     nonbondedCutoff=1.0*u.nanometer, constraints=None,
                     rigidWater=True, removeCMMotion=True, hydrogenMass=None,
                     **args):
        """Construct an OpenMM System representing a Topology with this force field.

        Parameters
        ----------
        topology : Topology
            The Topology for which to create a System
        nonbondedMethod : object=NoCutoff
            The method to use for nonbonded interactions.  Allowed values are
            NoCutoff, CutoffNonPeriodic, CutoffPeriodic, Ewald, or PME.
        nonbondedCutoff : distance=1*nanometer
            The cutoff distance to use for nonbonded interactions
        constraints : object=None
            Specifies which bonds and angles should be implemented with constraints.
            Allowed values are None, HBonds, AllBonds, or HAngles.
        rigidWater : boolean=True
            If true, water molecules will be fully rigid regardless of the value
            passed for the constraints argument
        removeCMMotion : boolean=True
            If true, a CMMotionRemover will be added to the System
        hydrogenMass : mass=None
            The mass to use for hydrogen atoms bound to heavy atoms.  Any mass
            added to a hydrogen is subtracted from the heavy atom to keep
            their total mass the same.
        args
             Arbitrary additional keyword arguments may also be specified.
             This allows extra parameters to be specified that are specific to
             particular force fields.

        Returns
        -------
        system
            the newly created System
        """

        # Atomtype the system.
        G = nx.Graph()
        G.add_nodes_from(topology.atoms())
        G.add_edges_from(topology.bonds())
        cycles = nx.cycle_basis(G)

        for atom in topology.atoms():
            atom.cycles = set()

        for cycle in cycles:
            for atom in cycle:
                atom.cycles.add(tuple(cycle))

        find_atomtypes(atoms=list(topology.atoms()), forcefield=self)

        data = app.ForceField._SystemData()
        data.atoms = list(topology.atoms())
        for atom in data.atoms:
            data.excludeAtomWith.append([])

        # Make a list of all bonds
        for bond in topology.bonds():
            data.bonds.append(app.ForceField._BondData(bond[0].index, bond[1].index))

        # Record which atoms are bonded to each other atom
        bondedToAtom = []
        for i in range(len(data.atoms)):
            bondedToAtom.append(set())
            data.atomBonds.append([])
        for i in range(len(data.bonds)):
            bond = data.bonds[i]
            bondedToAtom[bond.atom1].add(bond.atom2)
            bondedToAtom[bond.atom2].add(bond.atom1)
            data.atomBonds[bond.atom1].append(i)
            data.atomBonds[bond.atom2].append(i)

        # TODO: Better way to lookup nonbonded parameters...?
        nonbonded_params = None
        for generator in self.getGenerators():
            if isinstance(generator, NonbondedGenerator):
                nonbonded_params = generator.params.paramsForType
                break

        for chain in topology.chains():
            for res in chain.residues():
                for atom in res.atoms():
                    data.atomType[atom] = atom.id
                    if nonbonded_params:
                        params = nonbonded_params[atom.id]
                        data.atomParameters[atom] = params

        # Create the System and add atoms
        sys = mm.System()
        for atom in topology.atoms():
            # Look up the atom type name, returning a helpful error message if it cannot be found.
            if atom not in data.atomType:
                raise Exception("Could not identify atom type for atom '%s'." % str(atom))
            typename = data.atomType[atom]

            # Look up the type name in the list of registered atom types, returning a helpful error message if it cannot be found.
            if typename not in self._atomTypes:
                msg  = "Could not find typename '%s' for atom '%s' in list of known atom types.\n" % (typename, str(atom))
                msg += "Known atom types are: %s" % str(self._atomTypes.keys())
                raise Exception(msg)

            # Add the particle to the OpenMM system.
            mass = self._atomTypes[typename].mass
            sys.addParticle(mass)

        # Adjust hydrogen masses if requested.
        if hydrogenMass is not None:
            if not u.is_quantity(hydrogenMass):
                hydrogenMass *= u.dalton
            for atom1, atom2 in topology.bonds():
                if atom1.element == elem.hydrogen:
                    (atom1, atom2) = (atom2, atom1)
                if atom2.element == elem.hydrogen and atom1.element not in (elem.hydrogen, None):
                    transferMass = hydrogenMass-sys.getParticleMass(atom2.index)
                    sys.setParticleMass(atom2.index, hydrogenMass)
                    sys.setParticleMass(atom1.index, sys.getParticleMass(atom1.index)-transferMass)

        # Set periodic boundary conditions.
        boxVectors = topology.getPeriodicBoxVectors()
        if boxVectors is not None:
            sys.setDefaultPeriodicBoxVectors(boxVectors[0], boxVectors[1], boxVectors[2])
        elif nonbondedMethod not in [NoCutoff, CutoffNonPeriodic]:
            raise ValueError('Requested periodic boundary conditions for a Topology that does not specify periodic box dimensions')

        # Make a list of all unique angles
        uniqueAngles = set()
        for bond in data.bonds:
            for atom in bondedToAtom[bond.atom1]:
                if atom != bond.atom2:
                    if atom < bond.atom2:
                        uniqueAngles.add((atom, bond.atom1, bond.atom2))
                    else:
                        uniqueAngles.add((bond.atom2, bond.atom1, atom))
            for atom in bondedToAtom[bond.atom2]:
                if atom != bond.atom1:
                    if atom > bond.atom1:
                        uniqueAngles.add((bond.atom1, bond.atom2, atom))
                    else:
                        uniqueAngles.add((atom, bond.atom2, bond.atom1))
        data.angles = sorted(list(uniqueAngles))

        # Make a list of all unique proper torsions
        uniquePropers = set()
        for angle in data.angles:
            for atom in bondedToAtom[angle[0]]:
                if atom not in angle:
                    if atom < angle[2]:
                        uniquePropers.add((atom, angle[0], angle[1], angle[2]))
                    else:
                        uniquePropers.add((angle[2], angle[1], angle[0], atom))
            for atom in bondedToAtom[angle[2]]:
                if atom not in angle:
                    if atom > angle[0]:
                        uniquePropers.add((angle[0], angle[1], angle[2], atom))
                    else:
                        uniquePropers.add((atom, angle[2], angle[1], angle[0]))
        data.propers = sorted(list(uniquePropers))

        # Make a list of all unique improper torsions
        for atom in range(len(bondedToAtom)):
            bondedTo = bondedToAtom[atom]
            if len(bondedTo) > 2:
                for subset in itertools.combinations(bondedTo, 3):
                    data.impropers.append((atom, subset[0], subset[1], subset[2]))

        # Identify bonds that should be implemented with constraints
        if constraints == AllBonds or constraints == HAngles:
            for bond in data.bonds:
                bond.isConstrained = True
        elif constraints == HBonds:
            for bond in data.bonds:
                atom1 = data.atoms[bond.atom1]
                atom2 = data.atoms[bond.atom2]
                bond.isConstrained = atom1.name.startswith('H') or atom2.name.startswith('H')
        if rigidWater:
            for bond in data.bonds:
                atom1 = data.atoms[bond.atom1]
                atom2 = data.atoms[bond.atom2]
                if atom1.residue.name == 'HOH' and atom2.residue.name == 'HOH':
                    bond.isConstrained = True

        # Identify angles that should be implemented with constraints
        if constraints == HAngles:
            for angle in data.angles:
                atom1 = data.atoms[angle[0]]
                atom2 = data.atoms[angle[1]]
                atom3 = data.atoms[angle[2]]
                numH = 0
                if atom1.name.startswith('H'):
                    numH += 1
                if atom3.name.startswith('H'):
                    numH += 1
                data.isAngleConstrained.append(numH == 2 or (numH == 1 and atom2.name.startswith('O')))
        else:
            data.isAngleConstrained = len(data.angles)*[False]
        if rigidWater:
            for i in range(len(data.angles)):
                angle = data.angles[i]
                atom1 = data.atoms[angle[0]]
                atom2 = data.atoms[angle[1]]
                atom3 = data.atoms[angle[2]]
                if atom1.residue.name == 'HOH' and atom2.residue.name == 'HOH' and atom3.residue.name == 'HOH':
                    data.isAngleConstrained[i] = True

        # Add virtual sites
        for atom in data.virtualSites:
            (site, atoms, excludeWith) = data.virtualSites[atom]
            index = atom.index
            data.excludeAtomWith[excludeWith].append(index)
            if site.type == 'average2':
                sys.setVirtualSite(index, mm.TwoParticleAverageSite(atoms[0], atoms[1], site.weights[0], site.weights[1]))
            elif site.type == 'average3':
                sys.setVirtualSite(index, mm.ThreeParticleAverageSite(atoms[0], atoms[1], atoms[2], site.weights[0], site.weights[1], site.weights[2]))
            elif site.type == 'outOfPlane':
                sys.setVirtualSite(index, mm.OutOfPlaneSite(atoms[0], atoms[1], atoms[2], site.weights[0], site.weights[1], site.weights[2]))
            elif site.type == 'localCoords':
                sys.setVirtualSite(index, mm.LocalCoordinatesSite(atoms[0], atoms[1], atoms[2],
                                                                  mm.Vec3(site.originWeights[0], site.originWeights[1], site.originWeights[2]),
                                                                  mm.Vec3(site.xWeights[0], site.xWeights[1], site.xWeights[2]),
                                                                  mm.Vec3(site.yWeights[0], site.yWeights[1], site.yWeights[2]),
                                                                  mm.Vec3(site.localPos[0], site.localPos[1], site.localPos[2])))

        # Add forces to the System
        for force in self._forces:
            force.createForce(sys, data, nonbondedMethod, nonbondedCutoff, args)
        if removeCMMotion:
            sys.addForce(mm.CMMotionRemover())

        # Let force generators do postprocessing
        for force in self._forces:
            if 'postprocessSystem' in dir(force):
                force.postprocessSystem(sys, data, args)

        # Execute scripts found in the XML files.
        for script in self._scripts:
            exec(script, locals())
        return sys
Example #3
0
    def createSystem(self,
                     nonbondedMethod=ff.NoCutoff,
                     nonbondedCutoff=1.0 * unit.nanometer,
                     constraints=None,
                     rigidWater=True,
                     implicitSolvent=None,
                     implicitSolventSaltConc=0.0 * (unit.moles / unit.liter),
                     implicitSolventKappa=None,
                     temperature=298.15 * unit.kelvin,
                     soluteDielectric=1.0,
                     solventDielectric=78.5,
                     removeCMMotion=True,
                     hydrogenMass=None,
                     ewaldErrorTolerance=0.0005):
        """Construct an OpenMM System representing the topology described by this prmtop file.

        Parameters:
         - nonbondedMethod (object=NoCutoff) The method to use for nonbonded interactions.  Allowed values are
           NoCutoff, CutoffNonPeriodic, CutoffPeriodic, Ewald, or PME.
         - nonbondedCutoff (distance=1*nanometer) The cutoff distance to use for nonbonded interactions
         - constraints (object=None) Specifies which bonds angles should be implemented with constraints.
           Allowed values are None, HBonds, AllBonds, or HAngles.
         - rigidWater (boolean=True) If true, water molecules will be fully rigid regardless of the value passed for the constraints argument
         - implicitSolvent (object=None) If not None, the implicit solvent model to use.  Allowed values are HCT, OBC1, OBC2, GBn, or GBn2.
         - implicitSolventSaltConc (float=0.0*unit.moles/unit.liter) The salt concentration for GB 
                    calculations (modelled as a debye screening parameter). It is converted to the debye length (kappa)
                    using the provided temperature and solventDielectric
         - temperature (float=300*kelvin) Temperature of the system. Only used to compute the Debye length from
                    implicitSolventSoltConc
         - implicitSolventKappa (float units of 1/length) If this value is set, implicitSolventSaltConc will be ignored.
         - soluteDielectric (float=1.0) The solute dielectric constant to use in the implicit solvent model.
         - solventDielectric (float=78.5) The solvent dielectric constant to use in the implicit solvent model.
         - removeCMMotion (boolean=True) If true, a CMMotionRemover will be added to the System
         - hydrogenMass (mass=None) The mass to use for hydrogen atoms bound to heavy atoms.  Any mass added to a hydrogen is
           subtracted from the heavy atom to keep their total mass the same.
         - ewaldErrorTolerance (float=0.0005) The error tolerance to use if nonbondedMethod is Ewald or PME.
        Returns: the newly created System
        """
        methodMap = {
            ff.NoCutoff: 'NoCutoff',
            ff.CutoffNonPeriodic: 'CutoffNonPeriodic',
            ff.CutoffPeriodic: 'CutoffPeriodic',
            ff.Ewald: 'Ewald',
            ff.PME: 'PME'
        }
        if nonbondedMethod not in methodMap:
            raise ValueError('Illegal value for nonbonded method')
        if not self._prmtop.getIfBox() and nonbondedMethod in (
                ff.CutoffPeriodic, ff.Ewald, ff.PME):
            raise ValueError(
                'Illegal nonbonded method for a non-periodic system')
        constraintMap = {
            None: None,
            ff.HBonds: 'h-bonds',
            ff.AllBonds: 'all-bonds',
            ff.HAngles: 'h-angles'
        }
        if constraints is None:
            constraintString = None
        elif constraints in constraintMap:
            constraintString = constraintMap[constraints]
        else:
            raise ValueError('Illegal value for constraints')
        if implicitSolvent is None:
            implicitString = None
        elif implicitSolvent is HCT:
            implicitString = 'HCT'
        elif implicitSolvent is OBC1:
            implicitString = 'OBC1'
        elif implicitSolvent is OBC2:
            implicitString = 'OBC2'
        elif implicitSolvent is GBn:
            implicitString = 'GBn'
        elif implicitSolvent is GBn2:
            implicitString = 'GBn2'
        else:
            raise ValueError('Illegal value for implicit solvent model')
        # If implicitSolventKappa is None, compute it from the salt concentration
        if implicitSolvent is not None and implicitSolventKappa is None:
            if unit.is_quantity(implicitSolventSaltConc):
                implicitSolventSaltConc = implicitSolventSaltConc.value_in_unit(
                    unit.moles / unit.liter)
            if unit.is_quantity(temperature):
                temperature = temperature.value_in_unit(unit.kelvin)
            # The constant is 1 / sqrt( epsilon_0 * kB / (2 * NA * q^2 * 1000) )
            # where NA is avogadro's number, epsilon_0 is the permittivity of
            # free space, q is the elementary charge (this number matches
            # Amber's kappa conversion factor)
            implicitSolventKappa = 50.33355 * sqrt(
                implicitSolventSaltConc / solventDielectric / temperature)
            # Multiply by 0.73 to account for ion exclusions, and multiply by 10
            # to convert to 1/nm from 1/angstroms
            implicitSolventKappa *= 7.3
        elif implicitSolvent is None:
            implicitSolventKappa = 0.0

        sys = amber_file_parser.readAmberSystem(
            prmtop_loader=self._prmtop,
            shake=constraintString,
            nonbondedCutoff=nonbondedCutoff,
            nonbondedMethod=methodMap[nonbondedMethod],
            flexibleConstraints=False,
            gbmodel=implicitString,
            soluteDielectric=soluteDielectric,
            solventDielectric=solventDielectric,
            implicitSolventKappa=implicitSolventKappa,
            rigidWater=rigidWater,
            elements=self.elements)

        if hydrogenMass is not None:
            for atom1, atom2 in self.topology.bonds():
                if atom1.element == elem.hydrogen:
                    (atom1, atom2) = (atom2, atom1)
                if atom2.element == elem.hydrogen and atom1.element not in (
                        elem.hydrogen, None):
                    transferMass = hydrogenMass - sys.getParticleMass(
                        atom2.index)
                    sys.setParticleMass(atom2.index, hydrogenMass)
                    sys.setParticleMass(
                        atom1.index,
                        sys.getParticleMass(atom1.index) - transferMass)
        for force in sys.getForces():
            if isinstance(force, mm.NonbondedForce):
                force.setEwaldErrorTolerance(ewaldErrorTolerance)
        if removeCMMotion:
            sys.addForce(mm.CMMotionRemover())
        return sys
    def createSystem(self, nonbondedMethod=ff.NoCutoff, nonbondedCutoff=1.0*nanometer,
                     ewaldErrorTolerance=0.0005, removeCMMotion=True, hydrogenMass=None):
        """Construct an OpenMM System representing the topology described by this
        DMS file

        Parameters
        ----------
        nonbondedMethod : object=NoCutoff
            The method to use for nonbonded interactions.  Allowed values are
            NoCutoff, CutoffNonPeriodic, CutoffPeriodic, Ewald, or PME.
        nonbondedCutoff : distance=1*nanometer
            The cutoff distance to use for nonbonded interactions
        ewaldErrorTolerance : float=0.0005
            The error tolerance to use if nonbondedMethod is Ewald or PME.
        removeCMMotion : boolean=True
            If true, a CMMotionRemover will be added to the System
        hydrogenMass : mass=None
            The mass to use for hydrogen atoms bound to heavy atoms.  Any mass
            added to a hydrogen is subtracted from the heavy atom to keep their
            total mass the same.
        """
        self._checkForUnsupportedTerms()
        sys = mm.System()

        # Buld the box dimensions
        sys = mm.System()
        boxSize = self.topology.getUnitCellDimensions()
        if boxSize is not None:
            sys.setDefaultPeriodicBoxVectors((boxSize[0], 0, 0), (0, boxSize[1], 0), (0, 0, boxSize[2]))
        elif nonbondedMethod in (ff.CutoffPeriodic, ff.Ewald, ff.PME):
            raise ValueError('Illegal nonbonded method for a non-periodic system')

        # Create all of the particles
        for mass in self._conn.execute('SELECT mass from particle'):
            sys.addParticle(mass[0]*dalton)

        # Add all of the forces
        self._addBondsToSystem(sys)
        self._addAnglesToSystem(sys)
        self._addConstraintsToSystem(sys)
        self._addPeriodicTorsionsToSystem(sys)
        self._addImproperHarmonicTorsionsToSystem(sys)
        self._addCMAPToSystem(sys)
        self._addVirtualSitesToSystem(sys)
        nb = self._addNonbondedForceToSystem(sys)

        # Finish configuring the NonbondedForce.
        methodMap = {ff.NoCutoff:mm.NonbondedForce.NoCutoff,
                     ff.CutoffNonPeriodic:mm.NonbondedForce.CutoffNonPeriodic,
                     ff.CutoffPeriodic:mm.NonbondedForce.CutoffPeriodic,
                     ff.Ewald:mm.NonbondedForce.Ewald,
                     ff.PME:mm.NonbondedForce.PME}
        nb.setNonbondedMethod(methodMap[nonbondedMethod])
        nb.setCutoffDistance(nonbondedCutoff)
        nb.setEwaldErrorTolerance(ewaldErrorTolerance)

        # Adjust masses.
        if hydrogenMass is not None:
            for atom1, atom2 in self.topology.bonds():
                if atom1.element == hydrogen:
                    (atom1, atom2) = (atom2, atom1)
                if atom2.element == hydrogen and atom1.element not in (hydrogen, None):
                    transferMass = hydrogenMass-sys.getParticleMass(atom2.index)
                    sys.setParticleMass(atom2.index, hydrogenMass)
                    sys.setParticleMass(atom1.index, sys.getParticleMass(atom1.index)-transferMass)

        # Add a CMMotionRemover.
        if removeCMMotion:
            sys.addForce(mm.CMMotionRemover())

        return sys
    def createSystem(self,
                     nonbondedMethod=ff.NoCutoff,
                     nonbondedCutoff=1.0 * nanometer,
                     ewaldErrorTolerance=0.0005,
                     removeCMMotion=True,
                     hydrogenMass=None,
                     OPLS=False,
                     implicitSolvent=None,
                     AGBNPVersion=1):
        """Construct an OpenMM System representing the topology described by this
        DMS file

        Parameters
        ----------
        nonbondedMethod : object=NoCutoff
            The method to use for nonbonded interactions.  Allowed values are
            NoCutoff, CutoffNonPeriodic, CutoffPeriodic, Ewald, PME, or LJPME.
        nonbondedCutoff : distance=1*nanometer
            The cutoff distance to use for nonbonded interactions
        ewaldErrorTolerance : float=0.0005
            The error tolerance to use if nonbondedMethod is Ewald, PME, or LJPME.
        removeCMMotion : boolean=True
            If true, a CMMotionRemover will be added to the System
        hydrogenMass : mass=None
            The mass to use for hydrogen atoms bound to heavy atoms.  Any mass
            added to a hydrogen is subtracted from the heavy atom to keep their
            total mass the same.
        OPLS : boolean=False
            If True, forces OPLS combining rules
        implicitSolvent: string=None
            If not None, creates implicit solvent force of the given name
            Allowed values are: HCT and 'AGBNP'
            (the corresponding tables must be present in the DMS file)
        AGBNPVersion: int=1
            AGBNP implicit solvent version
        """
        self._checkForUnsupportedTerms()
        sys = mm.System()

        # Build the box dimensions
        boxSize = self.topology.getUnitCellDimensions()
        if boxSize is not None:
            sys.setDefaultPeriodicBoxVectors(
                (boxSize[0], 0, 0), (0, boxSize[1], 0), (0, 0, boxSize[2]))
        elif nonbondedMethod in (ff.CutoffPeriodic, ff.Ewald, ff.PME,
                                 ff.LJPME):
            raise ValueError(
                'Illegal nonbonded method for a non-periodic system')

        # Create all of the particles
        for (fcounter, conn, tables, offset) in self._localVars():
            for mass in conn.execute('SELECT mass FROM particle ORDER BY id'):
                sys.addParticle(mass[0] * dalton)

        # Add all of the forces
        self._addBondsToSystem(sys)
        self._addAnglesToSystem(sys)
        self._addConstraintsToSystem(sys)
        self._addPeriodicTorsionsToSystem(sys, OPLS)
        self._addImproperHarmonicTorsionsToSystem(sys)
        self._addCMAPToSystem(sys)
        self._addVirtualSitesToSystem(sys)
        self._addPositionalHarmonicRestraints(sys)
        nb, cnb = self._addNonbondedForceToSystem(sys, OPLS)

        # Finish configuring the NonbondedForce.
        methodMap = {
            ff.NoCutoff: mm.NonbondedForce.NoCutoff,
            ff.CutoffNonPeriodic: mm.NonbondedForce.CutoffNonPeriodic,
            ff.CutoffPeriodic: mm.NonbondedForce.CutoffPeriodic,
            ff.Ewald: mm.NonbondedForce.Ewald,
            ff.PME: mm.NonbondedForce.PME,
            ff.LJPME: mm.NonbondedForce.LJPME
        }
        nb.setNonbondedMethod(methodMap[nonbondedMethod])
        nb.setCutoffDistance(nonbondedCutoff)
        nb.setEwaldErrorTolerance(ewaldErrorTolerance)
        if cnb is not None:
            nb.setUseDispersionCorrection(False)
            if nonbondedMethod in (ff.CutoffPeriodic, ff.Ewald, ff.PME,
                                   ff.LJPME):
                cnb.setNonbondedMethod(methodMap[ff.CutoffPeriodic])
                cnb.setCutoffDistance(nonbondedCutoff)
            elif nonbondedMethod == ff.CutoffNonPeriodic:
                cnb.setNonbondedMethod(methodMap[ff.CutoffNonPeriodic])
                cnb.setCutoffDistance(nonbondedCutoff)
            else:
                cnb.setNonbondedMethod(methodMap[ff.NoCutoff])
            cnb.setUseSwitchingFunction(False)
            cnb.setUseLongRangeCorrection(False)

        #add implicit solvent model.
        if implicitSolvent is not None:

            if not (implicitSolvent in (HCT, 'AGBNP', 'GVolSA', 'AGBNP3')):
                raise ValueError('Illegal implicit solvent method')

            if self._verbose:
                print('Adding implicit solvent ...')

            #with implicit solvent turn off native reaction field
            #However note that this does not affect the shifted Coulomb potential of the Nonbonded force
            #(it affects the only the energy, not the forces and equation of motion)
            nb.setReactionFieldDielectric(1.0)

            if implicitSolvent is HCT:
                gb_parms = self._get_gb_params()

                if gb_parms:
                    if self._verbose:
                        print('Adding HCT GB force ...')
                    gb = GBSAHCTForce(SA='ACE')
                    for i in range(len(gb_parms)):
                        gb.addParticle(list(gb_parms[i]))
                    gb.finalize()
                    sys.addForce(gb)
                else:
                    raise IOError("No HCT parameters found in DMS file")

            if implicitSolvent is 'AGBNP3':
                #load AGBNP3 plugin if available
                try:
                    from AGBNP3plugin import AGBNP3Force
                except ImportError:
                    raise NotImplementedError(
                        'AGBNP3 is not supported in this version')
                #sets up AGBNP3
                gb_parms = self._get_agbnp2_params()
                if gb_parms:
                    if self._verbose:
                        print('Adding AGBNP3 force ...')
                    gb = AGBNP3Force()
                    # add particles
                    for i in range(len(gb_parms)):
                        p = gb_parms[i]
                        gb.addParticle(p[0], p[1], p[2], p[3], p[4], p[5],
                                       p[6])
                    # connection table (from bonds)
                    self._add_agbnp2_ct(gb)
                    sys.addForce(gb)
                else:
                    raise IOError("No AGBNP parameters found in DMS file")

            if implicitSolvent is 'GVolSA':
                #implemented as AGBNP version 0
                implicitSolvent = 'AGBNP'
                AGBNPVersion = 0
                if self._verbose:
                    print('Using GVolSA')

            if implicitSolvent is 'AGBNP':
                #load AGBNP plugin if available
                try:
                    from AGBNPplugin import AGBNPForce
                except ImportError:
                    raise NotImplementedError(
                        'AGBNP is not supported in this version')
                #sets up AGBNP
                gb_parms = self._get_agbnp2_params()
                if gb_parms:
                    gb = AGBNPForce()
                    gb.setNonbondedMethod(methodMap[nonbondedMethod])
                    gb.setCutoffDistance(nonbondedCutoff)
                    gb.setVersion(AGBNPVersion)
                    if self._verbose:
                        print('Using AGBNP force version %d ...' %
                              AGBNPVersion)
                    # add particles
                    for i in range(len(gb_parms)):
                        [
                            radiusN, chargeN, gammaN, alphaN, hbtype, hbwN,
                            ishydrogenN
                        ] = gb_parms[i]
                        h_flag = ishydrogenN > 0
                        gb.addParticle(radiusN, gammaN, alphaN, chargeN,
                                       h_flag)
                    sys.addForce(gb)
                    self.gb_parms = gb_parms
                    self.agbnp = gb
                else:
                    raise IOError("No AGBNP parameters found in DMS file")

        # Adjust masses.
        if hydrogenMass is not None:
            for atom1, atom2 in self.topology.bonds():

                if atom1.element == hydrogen:
                    (atom1, atom2) = (atom2, atom1)
                if atom2.element == hydrogen and atom1.element not in (
                        hydrogen, None):
                    transferMass = hydrogenMass - sys.getParticleMass(
                        atom2.index)
                    sys.setParticleMass(atom2.index, hydrogenMass)
                    sys.setParticleMass(
                        atom1.index,
                        sys.getParticleMass(atom1.index) - transferMass)

        # Add a CMMotionRemover.
        if removeCMMotion:
            sys.addForce(mm.CMMotionRemover())

        return sys
Example #6
0
    def createSystem(self,
                     nonbondedMethod=ff.NoCutoff,
                     nonbondedCutoff=1.0 * u.nanometer,
                     constraints=None,
                     rigidWater=True,
                     implicitSolvent=None,
                     implicitSolventSaltConc=0.0 * (u.moles / u.liter),
                     implicitSolventKappa=None,
                     temperature=298.15 * u.kelvin,
                     soluteDielectric=1.0,
                     solventDielectric=78.5,
                     removeCMMotion=True,
                     hydrogenMass=None,
                     ewaldErrorTolerance=0.0005,
                     switchDistance=0.0 * u.nanometer,
                     gbsaModel='ACE'):
        """Construct an OpenMM System representing the topology described by this
        prmtop file.

        Parameters
        ----------
        nonbondedMethod : object=NoCutoff
            The method to use for nonbonded interactions.  Allowed values are
            NoCutoff, CutoffNonPeriodic, CutoffPeriodic, Ewald, PME, or LJPME.
        nonbondedCutoff : distance=1*nanometer
            The cutoff distance to use for nonbonded interactions
        constraints : object=None
            Specifies which bonds angles should be implemented with constraints.
            Allowed values are None, HBonds, AllBonds, or HAngles.
        rigidWater : boolean=True
            If true, water molecules will be fully rigid regardless of the value
            passed for the constraints argument
        implicitSolvent : object=None
            If not None, the implicit solvent model to use.  Allowed values are
            HCT, OBC1, OBC2, GBn, or GBn2.
        implicitSolventSaltConc : float=0.0*unit.moles/unit.liter
            The salt concentration for GB calculations (modelled as a debye
            screening parameter). It is converted to the debye length (kappa)
            using the provided temperature and solventDielectric
        temperature : float=300*kelvin
            Temperature of the system. Only used to compute the Debye length
            from implicitSolventSoltConc
        implicitSolventKappa : float units of 1/length
            If this value is set, implicitSolventSaltConc will be ignored.
        soluteDielectric : float=1.0
            The solute dielectric constant to use in the implicit solvent model.
        solventDielectric : float=78.5
            The solvent dielectric constant to use in the implicit solvent
            model.
        removeCMMotion : boolean=True
            If true, a CMMotionRemover will be added to the System
        hydrogenMass : mass=None
            The mass to use for hydrogen atoms bound to heavy atoms.  Any mass
            added to a hydrogen is subtracted from the heavy atom to keep their
            total mass the same.  If rigidWater is used to make water molecules
            rigid, then water hydrogens are not altered.
        ewaldErrorTolerance : float=0.0005
            The error tolerance to use if nonbondedMethod is Ewald, PME, or LJPME.
        switchDistance : float=0*nanometers
            The distance at which the potential energy switching function is
            turned on for Lennard-Jones interactions. If the switchDistance is 0
            or evaluates to boolean False, no switching function will be used.
            Values greater than nonbondedCutoff or less than 0 raise ValueError
        gbsaModel : str='ACE'
            The SA model used to model the nonpolar solvation component of GB
            implicit solvent models. If GB is active, this must be 'ACE' or None
            (the latter indicates no SA model will be used). Other values will
            result in a ValueError

        Returns
        -------
        System
            the newly created System
        """
        if self._prmtop.chamber:
            raise ValueError("CHAMBER-style topology file detected. CHAMBER "
                             "topologies are not supported -- use the native "
                             "CHARMM files directly.")
        methodMap = {
            ff.NoCutoff: 'NoCutoff',
            ff.CutoffNonPeriodic: 'CutoffNonPeriodic',
            ff.CutoffPeriodic: 'CutoffPeriodic',
            ff.Ewald: 'Ewald',
            ff.PME: 'PME',
            ff.LJPME: 'LJPME'
        }
        if nonbondedMethod not in methodMap:
            raise ValueError('Illegal value for nonbonded method')
        if not self._prmtop.getIfBox() and nonbondedMethod in (
                ff.CutoffPeriodic, ff.Ewald, ff.PME, ff.LJPME):
            raise ValueError(
                'Illegal nonbonded method for a non-periodic system')
        constraintMap = {
            None: None,
            ff.HBonds: 'h-bonds',
            ff.AllBonds: 'all-bonds',
            ff.HAngles: 'h-angles'
        }
        if constraints is None:
            constraintString = None
        elif constraints in constraintMap:
            constraintString = constraintMap[constraints]
        else:
            raise ValueError('Illegal value for constraints')
        if implicitSolvent is None:
            implicitString = None
        elif implicitSolvent is HCT:
            implicitString = 'HCT'
        elif implicitSolvent is OBC1:
            implicitString = 'OBC1'
        elif implicitSolvent is OBC2:
            implicitString = 'OBC2'
        elif implicitSolvent is GBn:
            implicitString = 'GBn'
        elif implicitSolvent is GBn2:
            implicitString = 'GBn2'
        else:
            raise ValueError('Illegal value for implicit solvent model')
        # If implicitSolventKappa is None, compute it from the salt concentration
        if implicitSolvent is not None and implicitSolventKappa is None:
            if u.is_quantity(implicitSolventSaltConc):
                implicitSolventSaltConc = implicitSolventSaltConc.value_in_unit(
                    u.moles / u.liter)
            if u.is_quantity(temperature):
                temperature = temperature.value_in_unit(u.kelvin)
            # The constant is 1 / sqrt( epsilon_0 * kB / (2 * NA * q^2 * 1000) )
            # where NA is avogadro's number, epsilon_0 is the permittivity of
            # free space, q is the elementary charge (this number matches
            # Amber's kappa conversion factor)
            implicitSolventKappa = 50.33355 * sqrt(
                implicitSolventSaltConc / solventDielectric / temperature)
            # Multiply by 0.73 to account for ion exclusions, and multiply by 10
            # to convert to 1/nm from 1/angstroms
            implicitSolventKappa *= 7.3
        elif implicitSolvent is None:
            implicitSolventKappa = 0.0

        sys = amber_file_parser.readAmberSystem(
            self.topology,
            prmtop_loader=self._prmtop,
            shake=constraintString,
            nonbondedCutoff=nonbondedCutoff,
            nonbondedMethod=methodMap[nonbondedMethod],
            flexibleConstraints=False,
            gbmodel=implicitString,
            soluteDielectric=soluteDielectric,
            solventDielectric=solventDielectric,
            implicitSolventKappa=implicitSolventKappa,
            rigidWater=rigidWater,
            elements=self.elements,
            gbsaModel=gbsaModel)

        if hydrogenMass is not None:
            for atom1, atom2 in self.topology.bonds():
                if atom1.element == elem.hydrogen:
                    (atom1, atom2) = (atom2, atom1)
                if rigidWater and atom2.residue.name == 'HOH':
                    continue
                if atom2.element == elem.hydrogen and atom1.element not in (
                        elem.hydrogen, None):
                    transferMass = hydrogenMass - sys.getParticleMass(
                        atom2.index)
                    sys.setParticleMass(atom2.index, hydrogenMass)
                    sys.setParticleMass(
                        atom1.index,
                        sys.getParticleMass(atom1.index) - transferMass)
        for force in sys.getForces():
            if isinstance(force, mm.NonbondedForce):
                force.setEwaldErrorTolerance(ewaldErrorTolerance)
        if removeCMMotion:
            sys.addForce(mm.CMMotionRemover())

        if switchDistance and nonbondedMethod is not ff.NoCutoff:
            # make sure it's legal
            if (_strip_optunit(switchDistance, u.nanometer) >= _strip_optunit(
                    nonbondedCutoff, u.nanometer)):
                raise ValueError('switchDistance is too large compared '
                                 'to the cutoff!')
            if _strip_optunit(switchDistance, u.nanometer) < 0:
                # Detects negatives for both Quantity and float
                raise ValueError('switchDistance must be non-negative!')
            force.setUseSwitchingFunction(True)
            force.setSwitchingDistance(switchDistance)

        return sys
    def createSystem(self, nonbondedMethod=ff.NoCutoff, nonbondedCutoff=1.0*nanometer,
                     ewaldErrorTolerance=0.0005, removeCMMotion=True, hydrogenMass=None, OPLS=False, implicitSolvent=None):
        """Construct an OpenMM System representing the topology described by this dms file

        Parameters:
         - nonbondedMethod (object=NoCutoff) The method to use for nonbonded interactions.  Allowed values are
           NoCutoff, CutoffNonPeriodic, CutoffPeriodic, Ewald, or PME.
         - nonbondedCutoff (distance=1*nanometer) The cutoff distance to use for nonbonded interactions
         - ewaldErrorTolerance (float=0.0005) The error tolerance to use if nonbondedMethod is Ewald or PME.
         - removeCMMotion (boolean=True) If true, a CMMotionRemover will be added to the System
         - hydrogenMass (mass=None) The mass to use for hydrogen atoms bound to heavy atoms.  Any mass added to a hydrogen is
           subtracted from the heavy atom to keep their total mass the same.
         - OPLS (boolean=False) If True, force field parameters are interpreted as OPLS parameters; OPLS variants of 
           torsional and non-bonded forces are constructed.
	 - implicitSolvent (object=None) if not None, the implicit solvent model to use, the only allowed value is HCT
        """
        self._checkForUnsupportedTerms()
        sys = mm.System()

        # Buld the box dimensions
        sys = mm.System()
        boxSize = self.topology.getUnitCellDimensions()
        if boxSize is not None:
            sys.setDefaultPeriodicBoxVectors((boxSize[0], 0, 0), (0, boxSize[1], 0), (0, 0, boxSize[2]))
        elif nonbondedMethod in (ff.CutoffPeriodic, ff.Ewald, ff.PME):
            raise ValueError('Illegal nonbonded method for a non-periodic system')

        # Create all of the particles
        for mass in self._conn.execute('SELECT mass FROM particle ORDER BY id'):
            sys.addParticle(mass[0]*dalton)

        # Add all of the forces
        self._addBondsToSystem(sys)
        self._addAnglesToSystem(sys)
        self._addConstraintsToSystem(sys)
        self._addPeriodicTorsionsToSystem(sys, OPLS)
        self._addImproperHarmonicTorsionsToSystem(sys)
        self._addCMAPToSystem(sys)
        self._addVirtualSitesToSystem(sys)
        nb, cnb = self._addNonbondedForceToSystem(sys, OPLS)
        
        # Finish configuring the NonbondedForce.
        methodMap = {ff.NoCutoff:mm.NonbondedForce.NoCutoff,
                     ff.CutoffNonPeriodic:mm.NonbondedForce.CutoffNonPeriodic,
                     ff.CutoffPeriodic:mm.NonbondedForce.CutoffPeriodic,
                     ff.Ewald:mm.NonbondedForce.Ewald,
                     ff.PME:mm.NonbondedForce.PME}
        nb.setNonbondedMethod(methodMap[nonbondedMethod])
        nb.setCutoffDistance(nonbondedCutoff)
        nb.setEwaldErrorTolerance(ewaldErrorTolerance)
        if cnb is not None:
            cnb.setNonbondedMethod(methodMap[nonbondedMethod])
            cnb.setCutoffDistance(nonbondedCutoff)
        
	#add implicit solvent model. Right now, only HCT model is considered.
	if implicitSolvent is not None:

            print('Adding implicit solvent ...')

            if implicitSolvent is HCT:
                gb_parms = self._get_gb_params()
                if gb_parms:
                    print('Adding HCT GB force ...')
                    gb = GBSAHCTForce(SA='ACE')
                    for i in range(len(gb_parms)):	      
                        gb.addParticle(list(gb_parms[i]))
                    sys.addForce(gb)
            
            if implicitSolvent is 'AGBNP':
                #load AGBNP3 plugin if available
                try:
                    from AGBNP3plugin import AGBNP3Force
                    AGBNP3enabled = True
                except ImportError:
                    AGBNP3enabled = False
                #sets up AGBNP3
                if AGBNP3enabled:
                    gb_parms = self._get_agbnp2_params()
                    if gb_parms:
                        print('Adding AGBNP3 force ...')
                        gb = AGBNP3Force()
                        # add particles
                        for i in range(len(gb_parms)):
                            p = gb_parms[i]
                            gb.addParticle(p[0],p[1],p[2],p[3],p[4],p[5],p[6])
                        # connection table (from bonds)
                        self._add_agbnp2_ct(gb)
                        sys.addForce(gb)
                else:
                    print('Warning: AGBNP is not supported in this version')
                
            if implicitSolvent is 'GVolSA':
                #load Gaussvol plugin if available
                try:
                    from GVolplugin import GVolForce
                    GVolEnabled = True
                except ImportError:
                    GVolEnabled = False
                #sets up GVol
                if GVolEnabled:
                    gb_parms = self._get_agbnp2_params()
                    if gb_parms:
                        print('Adding GVol force ...')
                        gb = GVolForce()
                        gb.setNonbondedMethod(methodMap[nonbondedMethod])
                        gb.setCutoffDistance(nonbondedCutoff)
                        # add particles
                        for i in range(len(gb_parms)):
                            [radiusN,chargeN,gammaN,alphaN,hbtype,hbwN,ishydrogenN] = gb_parms[i]
                            h_flag = ishydrogenN > 0
                            Roffset = 0.05;
                            radiusN += Roffset;
                            gb.addParticle(radiusN, gammaN, h_flag)
                            #print "Adding", radiusN, gammaN, h_flag
                        print "Adding GVolforce ..."
                        sys.addForce(gb)
                        print "Done"
                else:
                    print('Warning: GVol is not supported in this version')

        # Adjust masses.
        if hydrogenMass is not None:
            for atom1, atom2 in self.topology.bonds():
                
                if atom1.element == hydrogen:
                    (atom1, atom2) = (atom2, atom1)
                if atom2.element == hydrogen and atom1.element not in (hydrogen, None):
                    transferMass = hydrogenMass-sys.getParticleMass(atom2.index)
                    sys.setParticleMass(atom2.index, hydrogenMass)
                    sys.setParticleMass(atom1.index, sys.getParticleMass(atom1.index)-transferMass)
		
        # Add a CMMotionRemover.
        if removeCMMotion:
            sys.addForce(mm.CMMotionRemover())

        return sys
Example #8
0
    def createSystem(self,
                     nonbondedMethod=None,
                     nonbondedCutoff=None,
                     removeCMMotion=True,
                     constraints=None):
        """Construct an OpenMM System representing the topology described by
        this prmtop infile.

        Parameters:
         - nonbondedMethod (object=NoCutoff) The method to use for nonbonded
         interactions.  Allowed values are
           NoCutoff, CutoffNonPeriodic, CutoffPeriodic, Ewald, or PME.
         - nonbondedCutoff (distance=1*nanometer) The cutoff distance to use for
         nonbonded interactions
         - constraints (object=None) Specifies which bonds and angles should be
         implemented with constraints. Allowed values are None, HBonds, AllBonds
         or HAngles.
         - rigidWater (boolean=True) If true, water molecules will be fully
         rigid regardless of the value passed for the constraints argument
         - implicitSolvent (object=None) If not None, the implicit solvent model
         to use.  The only allowed value is OBC2.
         - soluteDielectric (float=1.0) The solute dielectric constant to use in
         the implicit solvent model.
         - solventDielectric (float=78.5) The solvent dielectric constant to use
         in the implicit solvent model.
         - ewaldErrorTolerance (float=0.0005) The error tolerance to use if
         nonbondedMethod is Ewald or PME.
         - removeCMMotion (boolean=True) If true, a CMMotionRemover will be
         added to the System
         - hydrogenMass (mass=None) The mass to use for hydrogen atoms bound to
         heavy atoms.  Any mass added to a hydrogen is
           subtracted from the heavy atom to keep their total mass the same.
        Returns: the newly created System
        """
        # Create the System.

        syst = mm.System()
        nb = mm.NonbondedForce()
        nb.setNonbondedMethod(nonbondedMethod)
        nb.setCutoffDistance(nonbondedCutoff)
        syst.addForce(nb)

        boxSize = self.topology.getUnitCellDimensions()
        if boxSize is not None:
            syst.setDefaultPeriodicBoxVectors(
                (boxSize[0], 0, 0), (0, boxSize[1], 0), (0, 0, boxSize[2]))

        # Build a lookup table to let us process dihedrals more quickly.
        dihedralTypeTable, wildcardDihedralTypes = self._buildDihLookupTable()

        # Loop over molecules and create the specified number of each type.
        allAtomTypes = []
        allcharges = []
        allExceptions = []

        for moleculeName, moleculeCount in self.molecules:
            moleculeType = self.moleculeTypes[moleculeName]
            for i in range(moleculeCount):
                # Record the types of all atoms.
                baseAtomIndex = syst.getNumParticles()
                atomTypes = [atom[1] for atom in moleculeType.atoms]
                charges = [atom[6] for atom in moleculeType.atoms]
                for charge in charges:
                    allcharges.append(charge)
                for atomType in atomTypes:
                    allAtomTypes.append(atomType)

                try:
                    bondedTypes = [self.atomTypes[t][1] for t in atomTypes]
                except KeyError as e:
                    raise ValueError('Unknown atom type: ' + e.message)
                bondedTypes = [
                    b if b is not None else a
                    for a, b in zip(atomTypes, bondedTypes)
                ]
                # Add atoms.
                self._addAtomsToSystem(syst, moleculeType)

                # Add bonds.
                atomBonds = self._addBondsToSystem(syst, moleculeType,
                                                   bondedTypes, constraints,
                                                   baseAtomIndex)
                # Add constraints
                self._addConstraintsToSystem(syst, moleculeType, bondedTypes,
                                             constraints, baseAtomIndex)

                # Add angles.
                self._addAngleToSystem(syst, moleculeType, bondedTypes,
                                       atomBonds, baseAtomIndex)

                # Add torsions.
                self._addTorsionToSystem(syst, moleculeType, bondedTypes,
                                         dihedralTypeTable,
                                         wildcardDihedralTypes, baseAtomIndex)

                # Set nonbonded parameters for particles.
                exceptions = self._setnonbondedParams(nb, moleculeType,
                                                      baseAtomIndex, atomTypes)
                for exception in exceptions:
                    allExceptions.append(exception)

        # Add pairInteractions first as exceptions, followed by the rest
        # This way other exceptions can override pairInteractions
        for i in range(syst.getNumParticles() - 1):
            atomType1 = allAtomTypes[i]
            for j in range(i + 1, syst.getNumParticles()):
                atomType2 = allAtomTypes[j]

                try:
                    sig, eps = self.nonbondParams[(atomType1, atomType2)]
                except KeyError:
                    try:
                        sig, eps = self.nonbondParams[(atomType2, atomType1)]
                    except KeyError():
                        msg = "%s,%s pair interactions not found" % (atomType2,
                                                                     atomType1)
                        raise KeyError(msg)

                chargeProd = float(allcharges[i]) * float(allcharges[j])
                nb.addException(i, j, chargeProd, sig, eps, True)

        for exception in allExceptions:
            nb.addException(exception[0], exception[1], exception[2],
                            float(exception[3]), float(exception[4]), True)

        # Add a CMMotionRemover.
        if removeCMMotion:
            syst.addForce(mm.CMMotionRemover())

        return syst
Example #9
0
    def export(system):
        '''
        Generate OpenMM system from a system

        Parameters
        ----------
        system : System

        Returns
        -------
        omm_system : simtk.openmm.System
        '''
        try:
            import simtk.openmm as mm
        except ImportError:
            raise ImportError('Can not import OpenMM')

        supported_terms = {
            LJ126Term, MieTerm, HarmonicBondTerm, HarmonicAngleTerm,
            SDKAngleTerm, PeriodicDihedralTerm, OplsImproperTerm,
            HarmonicImproperTerm, DrudeTerm
        }
        unsupported = system.ff_classes - supported_terms
        if unsupported != set():
            raise Exception(
                'Unsupported FF terms: %s' %
                (', '.join(map(lambda x: x.__name__, unsupported))))

        if system.vsite_types - {TIP4PSite} != set():
            raise Exception(
                'Virtual sites other than TIP4PSite haven\'t been implemented')

        top = system.topology
        ff = system.ff

        omm_system = mm.System()
        if system.use_pbc:
            omm_system.setDefaultPeriodicBoxVectors(*top.cell.vectors)
        for atom in top.atoms:
            omm_system.addParticle(atom.mass)

        ### Set up bonds #######################################################################
        for bond_class in system.bond_classes:
            if bond_class == HarmonicBondTerm:
                logger.info('Setting up harmonic bonds...')
                bforce = mm.HarmonicBondForce()
                for bond in top.bonds:
                    if bond.is_drude:
                        # DrudeForce will handle the bond between Drude pair
                        continue
                    bterm = system.bond_terms[id(bond)]
                    if type(bterm) != HarmonicBondTerm:
                        continue
                    bforce.addBond(bond.atom1.id, bond.atom2.id, bterm.length,
                                   bterm.k * 2)
            else:
                raise Exception('Bond terms other that HarmonicBondTerm '
                                'haven\'t been implemented')
            bforce.setUsesPeriodicBoundaryConditions(system.use_pbc)
            bforce.setForceGroup(ForceGroup.BOND)
            omm_system.addForce(bforce)

        ### Set up angles #######################################################################
        for angle_class in system.angle_classes:
            if angle_class == HarmonicAngleTerm:
                logger.info('Setting up harmonic angles...')
                aforce = mm.HarmonicAngleForce()
                for angle in top.angles:
                    aterm = system.angle_terms[id(angle)]
                    if type(aterm) == HarmonicAngleTerm:
                        aforce.addAngle(angle.atom1.id, angle.atom2.id,
                                        angle.atom3.id, aterm.theta * PI / 180,
                                        aterm.k * 2)
            elif angle_class == SDKAngleTerm:
                logger.info('Setting up SDK angles...')
                aforce = mm.CustomCompoundBondForce(
                    3, 'k*(theta-theta0)^2+step(rmin-r)*LJ96;'
                    'LJ96=6.75*epsilon*((sigma/r)^9-(sigma/r)^6)+epsilon;'
                    'theta=angle(p1,p2,p3);'
                    'r=distance(p1,p3);'
                    'rmin=1.144714*sigma')
                aforce.addPerBondParameter('theta0')
                aforce.addPerBondParameter('k')
                aforce.addPerBondParameter('epsilon')
                aforce.addPerBondParameter('sigma')
                for angle in top.angles:
                    aterm = system.angle_terms[id(angle)]
                    if type(aterm) != SDKAngleTerm:
                        continue
                    vdw = ff.get_vdw_term(ff.atom_types[angle.atom1.type],
                                          ff.atom_types[angle.atom2.type])
                    if type(
                            vdw
                    ) != MieTerm or vdw.repulsion != 9 or vdw.attraction != 6:
                        raise Exception(
                            f'Corresponding 9-6 MieTerm for {aterm} not found in FF'
                        )
                    aforce.addBond(
                        [angle.atom1.id, angle.atom2.id, angle.atom3.id], [
                            aterm.theta * PI / 180, aterm.k, vdw.epsilon,
                            vdw.sigma
                        ])
            else:
                raise Exception(
                    'Angle terms other that HarmonicAngleTerm and SDKAngleTerm '
                    'haven\'t been implemented')
            aforce.setUsesPeriodicBoundaryConditions(system.use_pbc)
            aforce.setForceGroup(ForceGroup.ANGLE)
            omm_system.addForce(aforce)

        ### Set up constraints #################################################################
        logger.info(
            f'Setting up {len(system.constrain_bonds)} bond constraints...')
        for bond in top.bonds:
            if id(bond) in system.constrain_bonds:
                omm_system.addConstraint(bond.atom1.id, bond.atom2.id,
                                         system.constrain_bonds[id(bond)])
        logger.info(
            f'Setting up {len(system.constrain_angles)} angle constraints...')
        for angle in top.angles:
            if id(angle) in system.constrain_angles:
                omm_system.addConstraint(angle.atom1.id, angle.atom3.id,
                                         system.constrain_angles[id(angle)])

        ### Set up dihedrals ###################################################################
        for dihedral_class in system.dihedral_classes:
            if dihedral_class == PeriodicDihedralTerm:
                logger.info('Setting up periodic dihedrals...')
                dforce = mm.PeriodicTorsionForce()
                for dihedral in top.dihedrals:
                    dterm = system.dihedral_terms[id(dihedral)]
                    ia1, ia2, ia3, ia4 = dihedral.atom1.id, dihedral.atom2.id, dihedral.atom3.id, dihedral.atom4.id
                    if type(dterm) == PeriodicDihedralTerm:
                        for par in dterm.parameters:
                            dforce.addTorsion(ia1, ia2, ia3, ia4, par.n,
                                              par.phi * PI / 180, par.k)
                    else:
                        continue
            else:
                raise Exception(
                    'Dihedral terms other that PeriodicDihedralTerm '
                    'haven\'t been implemented')
            dforce.setUsesPeriodicBoundaryConditions(system.use_pbc)
            dforce.setForceGroup(ForceGroup.DIHEDRAL)
            omm_system.addForce(dforce)

        ### Set up impropers ####################################################################
        for improper_class in system.improper_classes:
            if improper_class == OplsImproperTerm:
                logger.info('Setting up periodic impropers...')
                iforce = mm.CustomTorsionForce('k*(1-cos(2*theta))')
                iforce.addPerTorsionParameter('k')
                for improper in top.impropers:
                    iterm = system.improper_terms[id(improper)]
                    if type(iterm) == OplsImproperTerm:
                        # in OPLS convention, the third atom is the central atom
                        iforce.addTorsion(improper.atom2.id, improper.atom3.id,
                                          improper.atom1.id, improper.atom4.id,
                                          [iterm.k])
            elif improper_class == HarmonicImproperTerm:
                logger.info('Setting up harmonic impropers...')
                iforce = mm.CustomTorsionForce(f'k*min(dtheta,2*pi-dtheta)^2;'
                                               f'dtheta=abs(theta-phi0);'
                                               f'pi={PI}')
                iforce.addPerTorsionParameter('phi0')
                iforce.addPerTorsionParameter('k')
                for improper in top.impropers:
                    iterm = system.improper_terms[id(improper)]
                    if type(iterm) == HarmonicImproperTerm:
                        iforce.addTorsion(improper.atom1.id, improper.atom2.id,
                                          improper.atom3.id, improper.atom4.id,
                                          [iterm.phi * PI / 180, iterm.k])
            else:
                raise Exception(
                    'Improper terms other that PeriodicImproperTerm and '
                    'HarmonicImproperTerm haven\'t been implemented')
            iforce.setUsesPeriodicBoundaryConditions(system.use_pbc)
            iforce.setForceGroup(ForceGroup.IMPROPER)
            omm_system.addForce(iforce)

        ### Set up non-bonded interactions #########################################################
        # NonbonedForce is not flexible enough. Use it only for Coulomb interactions (including 1-4 Coulomb exceptions)
        # CustomNonbondedForce handles vdW interactions (including 1-4 LJ exceptions)
        cutoff = ff.vdw_cutoff
        logger.info('Setting up Coulomb interactions...')
        nbforce = mm.NonbondedForce()
        if system.use_pbc:
            nbforce.setNonbondedMethod(mm.NonbondedForce.PME)
            nbforce.setEwaldErrorTolerance(5E-4)
            nbforce.setCutoffDistance(cutoff)
            # dispersion will be handled by CustomNonbondedForce
            nbforce.setUseDispersionCorrection(False)
            try:
                nbforce.setExceptionsUsePeriodicBoundaryConditions(True)
            except:
                logger.warning('Cannot apply PBC for Coulomb 1-4 exceptions')
        else:
            nbforce.setNonbondedMethod(mm.NonbondedForce.NoCutoff)
        nbforce.setForceGroup(ForceGroup.COULOMB)
        omm_system.addForce(nbforce)
        for atom in top.atoms:
            nbforce.addParticle(atom.charge, 1.0, 0.0)

        ### Set up vdW interactions #########################################################
        atom_types = list(ff.atom_types.values())
        type_names = list(ff.atom_types.keys())
        n_type = len(atom_types)
        for vdw_class in system.vdw_classes:
            if vdw_class == LJ126Term:
                logger.info('Setting up LJ-12-6 vdW interactions...')
                if system.use_pbc and ff.vdw_long_range == ForceField.VDW_LONGRANGE_SHIFT:
                    invRc6 = 1 / cutoff**6
                    cforce = mm.CustomNonbondedForce(
                        f'A(type1,type2)*(invR6*invR6-{invRc6 * invRc6})-'
                        f'B(type1,type2)*(invR6-{invRc6});'
                        f'invR6=1/r^6')
                else:
                    cforce = mm.CustomNonbondedForce(
                        'A(type1,type2)*invR6*invR6-B(type1,type2)*invR6;'
                        'invR6=1/r^6')
                cforce.addPerParticleParameter('type')
                A_list = [0.0] * n_type * n_type
                B_list = [0.0] * n_type * n_type
                for i, atype1 in enumerate(atom_types):
                    for j, atype2 in enumerate(atom_types):
                        vdw = ff.get_vdw_term(atype1, atype2)
                        if type(vdw) == LJ126Term:
                            A = 4 * vdw.epsilon * vdw.sigma**12
                            B = 4 * vdw.epsilon * vdw.sigma**6
                        else:
                            A = B = 0
                        A_list[i + n_type * j] = A
                        B_list[i + n_type * j] = B
                cforce.addTabulatedFunction(
                    'A', mm.Discrete2DFunction(n_type, n_type, A_list))
                cforce.addTabulatedFunction(
                    'B', mm.Discrete2DFunction(n_type, n_type, B_list))

                for atom in top.atoms:
                    id_type = type_names.index(atom.type)
                    cforce.addParticle([id_type])

            elif vdw_class == MieTerm:
                logger.info('Setting up Mie vdW interactions...')
                if system.use_pbc and ff.vdw_long_range == ForceField.VDW_LONGRANGE_SHIFT:
                    cforce = mm.CustomNonbondedForce(
                        'A(type1,type2)/r^REP(type1,type2)-'
                        'B(type1,type2)/r^ATT(type1,type2)-'
                        'SHIFT(type1,type2)')
                else:
                    cforce = mm.CustomNonbondedForce(
                        'A(type1,type2)/r^REP(type1,type2)-'
                        'B(type1,type2)/r^ATT(type1,type2)')
                cforce.addPerParticleParameter('type')
                A_list = [0.0] * n_type * n_type
                B_list = [0.0] * n_type * n_type
                REP_list = [0.0] * n_type * n_type
                ATT_list = [0.0] * n_type * n_type
                SHIFT_list = [0.0] * n_type * n_type
                for i, atype1 in enumerate(atom_types):
                    for j, atype2 in enumerate(atom_types):
                        vdw = ff.get_vdw_term(atype1, atype2)
                        if type(vdw) == MieTerm:
                            A = vdw.factor_energy(
                            ) * vdw.epsilon * vdw.sigma**vdw.repulsion
                            B = vdw.factor_energy(
                            ) * vdw.epsilon * vdw.sigma**vdw.attraction
                            REP = vdw.repulsion
                            ATT = vdw.attraction
                            SHIFT = A / cutoff**REP - B / cutoff**ATT
                        else:
                            A = B = REP = ATT = SHIFT = 0
                        A_list[i + n_type * j] = A
                        B_list[i + n_type * j] = B
                        REP_list[i + n_type * j] = REP
                        ATT_list[i + n_type * j] = ATT
                        SHIFT_list[i + n_type * j] = SHIFT
                cforce.addTabulatedFunction(
                    'A', mm.Discrete2DFunction(n_type, n_type, A_list))
                cforce.addTabulatedFunction(
                    'B', mm.Discrete2DFunction(n_type, n_type, B_list))
                cforce.addTabulatedFunction(
                    'REP', mm.Discrete2DFunction(n_type, n_type, REP_list))
                cforce.addTabulatedFunction(
                    'ATT', mm.Discrete2DFunction(n_type, n_type, ATT_list))
                if system.use_pbc and ff.vdw_long_range == ForceField.VDW_LONGRANGE_SHIFT:
                    cforce.addTabulatedFunction(
                        'SHIFT',
                        mm.Discrete2DFunction(n_type, n_type, SHIFT_list))

                for atom in top.atoms:
                    id_type = type_names.index(atom.type)
                    cforce.addParticle([id_type])

            else:
                raise Exception('vdW terms other than LJ126Term and MieTerm '
                                'haven\'t been implemented')
            if system.use_pbc:
                cforce.setNonbondedMethod(
                    mm.CustomNonbondedForce.CutoffPeriodic)
                cforce.setCutoffDistance(cutoff)
                if ff.vdw_long_range == ForceField.VDW_LONGRANGE_CORRECT:
                    cforce.setUseLongRangeCorrection(True)
            else:
                cforce.setNonbondedMethod(mm.CustomNonbondedForce.NoCutoff)
            cforce.setForceGroup(ForceGroup.VDW)
            omm_system.addForce(cforce)

        ### Set up 1-2, 1-3 and 1-4 exceptions ##################################################
        logger.info('Setting up 1-2, 1-3 and 1-4 exceptions...')
        custom_nb_forces = [
            f for f in omm_system.getForces()
            if type(f) == mm.CustomNonbondedForce
        ]
        pair12, pair13, pair14 = top.get_12_13_14_pairs()
        for atom1, atom2 in pair12 + pair13:
            nbforce.addException(atom1.id, atom2.id, 0.0, 1.0, 0.0)
            for f in custom_nb_forces:
                f.addExclusion(atom1.id, atom2.id)
        # As long as 1-4 LJ OR Coulomb need to be scaled, then this pair should be excluded from ALL non-bonded forces.
        # This is required by OpenMM's internal implementation.
        # Even though NonbondedForce can handle 1-4 vdW, we use it only for 1-4 Coulomb.
        # And use CustomBondForce to handle 1-4 vdW, which makes it more clear for energy decomposition.
        if ff.scale_14_vdw != 1 or ff.scale_14_coulomb != 1:
            pair14_forces = {}  # {VdwTerm: mm.NbForce}
            for atom1, atom2 in pair14:
                charge_prod = atom1.charge * atom2.charge * ff.scale_14_coulomb
                nbforce.addException(atom1.id, atom2.id, charge_prod, 1.0, 0.0)
                for f in custom_nb_forces:
                    f.addExclusion(atom1.id, atom2.id)
                if ff.scale_14_vdw == 0:
                    continue
                vdw = ff.get_vdw_term(ff.atom_types[atom1.type],
                                      ff.atom_types[atom2.type])
                # We generalize LJ126Term and MieTerm because of minimal computational cost for 1-4 vdW
                if type(vdw) in (LJ126Term, MieTerm):
                    cbforce = pair14_forces.get(MieTerm)
                    if cbforce is None:
                        cbforce = mm.CustomBondForce(
                            'C*epsilon*((sigma/r)^n-(sigma/r)^m);'
                            'C=n/(n-m)*(n/m)^(m/(n-m))')
                        cbforce.addPerBondParameter('epsilon')
                        cbforce.addPerBondParameter('sigma')
                        cbforce.addPerBondParameter('n')
                        cbforce.addPerBondParameter('m')
                        cbforce.setUsesPeriodicBoundaryConditions(
                            system.use_pbc)
                        cbforce.setForceGroup(ForceGroup.VDW)
                        omm_system.addForce(cbforce)
                        pair14_forces[MieTerm] = cbforce
                    epsilon = vdw.epsilon * ff.scale_14_vdw
                    if type(vdw) == LJ126Term:
                        cbforce.addBond(atom1.id, atom2.id,
                                        [epsilon, vdw.sigma, 12, 6])
                    elif type(vdw) == MieTerm:
                        cbforce.addBond(atom1.id, atom2.id, [
                            epsilon, vdw.sigma, vdw.repulsion, vdw.attraction
                        ])
                else:
                    raise Exception(
                        '1-4 scaling for vdW terms other than LJ126Term and MieTerm '
                        'haven\'t been implemented')

        ### Set up Drude particles ##############################################################
        for polar_class in system.polarizable_classes:
            if polar_class == DrudeTerm:
                logger.info('Setting up Drude polarizations...')
                pforce = mm.DrudeForce()
                pforce.setForceGroup(ForceGroup.DRUDE)
                omm_system.addForce(pforce)
                parent_idx_thole = {
                }  # {parent: (index in DrudeForce, thole)} for addScreenPair
                for parent, drude in system.drude_pairs.items():
                    pterm = system.polarizable_terms[parent]
                    n_H = len([
                        atom for atom in parent.bond_partners
                        if atom.symbol == 'H'
                    ])
                    alpha = pterm.alpha + n_H * pterm.merge_alpha_H
                    idx = pforce.addParticle(drude.id, parent.id, -1, -1, -1,
                                             drude.charge, alpha, 0, 0)
                    parent_idx_thole[parent] = (idx, pterm.thole)

                # exclude the non-boned interactions between Drude and parent
                # and those concerning Drude particles in 1-2 and 1-3 pairs
                # pairs formed by real atoms have already been handled above
                # also apply thole screening between 1-2 and 1-3 Drude dipole pairs
                drude_exclusions = list(system.drude_pairs.items())
                for atom1, atom2 in pair12 + pair13:
                    drude1 = system.drude_pairs.get(atom1)
                    drude2 = system.drude_pairs.get(atom2)
                    if drude1 is not None:
                        drude_exclusions.append((drude1, atom2))
                    if drude2 is not None:
                        drude_exclusions.append((atom1, drude2))
                    if drude1 is not None and drude2 is not None:
                        drude_exclusions.append((drude1, drude2))
                        idx1, thole1 = parent_idx_thole[atom1]
                        idx2, thole2 = parent_idx_thole[atom2]
                        pforce.addScreenedPair(idx1, idx2,
                                               (thole1 + thole2) / 2)
                for a1, a2 in drude_exclusions:
                    nbforce.addException(a1.id, a2.id, 0, 1.0, 0)
                    for f in custom_nb_forces:
                        f.addExclusion(a1.id, a2.id)

                # scale the non-boned interactions concerning Drude particles in 1-4 pairs
                # pairs formed by real atoms have already been handled above
                drude_exceptions14 = []
                for atom1, atom2 in pair14:
                    drude1 = system.drude_pairs.get(atom1)
                    drude2 = system.drude_pairs.get(atom2)
                    if drude1 is not None:
                        drude_exceptions14.append((drude1, atom2))
                    if drude2 is not None:
                        drude_exceptions14.append((atom1, drude2))
                    if drude1 is not None and drude2 is not None:
                        drude_exceptions14.append((drude1, drude2))
                for a1, a2 in drude_exceptions14:
                    charge_prod = a1.charge * a2.charge * ff.scale_14_coulomb
                    nbforce.addException(a1.id, a2.id, charge_prod, 1.0, 0.0)
                    for f in custom_nb_forces:
                        f.addExclusion(a1.id, a2.id)
            else:
                raise Exception(
                    'Polarizable terms other that DrudeTerm haven\'t been implemented'
                )

        ### Set up virtual sites ################################################################
        if top.has_virtual_site:
            logger.info('Setting up virtual sites...')
            for atom in top.atoms:
                vsite = atom.virtual_site
                if type(vsite) == TIP4PSite:
                    O, H1, H2 = vsite.parents
                    coeffs = system.get_TIP4P_linear_coeffs(atom)
                    omm_vsite = mm.ThreeParticleAverageSite(
                        O.id, H1.id, H2.id, *coeffs)
                    omm_system.setVirtualSite(atom.id, omm_vsite)
                elif vsite is not None:
                    raise Exception(
                        'Virtual sites other than TIP4PSite haven\'t been implemented'
                    )

            # exclude the non-boned interactions between virtual sites and parents
            # and particles (atoms, drude particles, virtual sites) in 1-2 and 1-3 pairs
            # TODO Assume no more than one virtual site is attached to each atom
            vsite_exclusions = list(system.vsite_pairs.items())
            for atom, vsite in system.vsite_pairs.items():
                drude = system.drude_pairs.get(atom)
                if drude is not None:
                    vsite_exclusions.append((vsite, drude))
            for atom1, atom2 in pair12 + pair13:
                vsite1 = system.vsite_pairs.get(atom1)
                vsite2 = system.vsite_pairs.get(atom2)
                drude1 = system.drude_pairs.get(atom1)
                drude2 = system.drude_pairs.get(atom2)
                if vsite1 is not None:
                    vsite_exclusions.append((vsite1, atom2))
                    if drude2 is not None:
                        vsite_exclusions.append((vsite1, drude2))
                if vsite2 is not None:
                    vsite_exclusions.append((vsite2, atom1))
                    if drude1 is not None:
                        vsite_exclusions.append((vsite2, drude1))
                if None not in [vsite1, vsite2]:
                    vsite_exclusions.append((vsite1, vsite2))
            for a1, a2 in vsite_exclusions:
                nbforce.addException(a1.id, a2.id, 0, 1.0, 0)
                for f in custom_nb_forces:
                    f.addExclusion(a1.id, a2.id)

            # scale the non-boned interactions between virtual sites and particles in 1-4 pairs
            # TODO Assume no 1-4 LJ interactions on virtual sites
            vsite_exceptions14 = []
            for atom1, atom2 in pair14:
                vsite1 = system.vsite_pairs.get(atom1)
                vsite2 = system.vsite_pairs.get(atom2)
                drude1 = system.drude_pairs.get(atom1)
                drude2 = system.drude_pairs.get(atom2)
                if vsite1 is not None:
                    vsite_exceptions14.append((vsite1, atom2))
                    if drude2 is not None:
                        vsite_exceptions14.append((vsite1, drude2))
                if vsite2 is not None:
                    vsite_exceptions14.append((vsite2, atom1))
                    if drude1 is not None:
                        vsite_exceptions14.append((vsite2, drude1))
                if None not in [vsite1, vsite2]:
                    vsite_exceptions14.append((vsite1, vsite2))
            for a1, a2 in vsite_exceptions14:
                charge_prod = a1.charge * a2.charge * ff.scale_14_coulomb
                nbforce.addException(a1.id, a2.id, charge_prod, 1.0, 0.0)
                for f in custom_nb_forces:
                    f.addExclusion(a1.id, a2.id)

        ### Remove COM motion ###################################################################
        logger.info('Setting up COM motion remover...')
        omm_system.addForce(mm.CMMotionRemover(10))

        return omm_system
Example #10
0
    def createSystem(self,
                     nonbondedMethod=ff.NoCutoff,
                     nonbondedCutoff=1.0 * unit.nanometer,
                     constraints=None,
                     rigidWater=True,
                     implicitSolvent=None,
                     soluteDielectric=1.0,
                     solventDielectric=78.5,
                     removeCMMotion=True,
                     hydrogenMass=None,
                     ewaldErrorTolerance=0.0005):
        """Construct an OpenMM System representing the topology described by this prmtop file.

        Parameters:
         - nonbondedMethod (object=NoCutoff) The method to use for nonbonded interactions.  Allowed values are
           NoCutoff, CutoffNonPeriodic, CutoffPeriodic, Ewald, or PME.
         - nonbondedCutoff (distance=1*nanometer) The cutoff distance to use for nonbonded interactions
         - constraints (object=None) Specifies which bonds angles should be implemented with constraints.
           Allowed values are None, HBonds, AllBonds, or HAngles.
         - rigidWater (boolean=True) If true, water molecules will be fully rigid regardless of the value passed for the constraints argument
         - implicitSolvent (object=None) If not None, the implicit solvent model to use.  Allowed values are HCT, OBC1, OBC2, GBn, or GBn2.
         - soluteDielectric (float=1.0) The solute dielectric constant to use in the implicit solvent model.
         - solventDielectric (float=78.5) The solvent dielectric constant to use in the implicit solvent model.
         - removeCMMotion (boolean=True) If true, a CMMotionRemover will be added to the System
         - hydrogenMass (mass=None) The mass to use for hydrogen atoms bound to heavy atoms.  Any mass added to a hydrogen is
           subtracted from the heavy atom to keep their total mass the same.
         - ewaldErrorTolerance (float=0.0005) The error tolerance to use if nonbondedMethod is Ewald or PME.
        Returns: the newly created System
        """
        methodMap = {
            ff.NoCutoff: 'NoCutoff',
            ff.CutoffNonPeriodic: 'CutoffNonPeriodic',
            ff.CutoffPeriodic: 'CutoffPeriodic',
            ff.Ewald: 'Ewald',
            ff.PME: 'PME'
        }
        if nonbondedMethod not in methodMap:
            raise ValueError('Illegal value for nonbonded method')
        if not self._prmtop.getIfBox() and nonbondedMethod in (
                ff.CutoffPeriodic, ff.Ewald, ff.PME):
            raise ValueError(
                'Illegal nonbonded method for a non-periodic system')
        constraintMap = {
            None: None,
            ff.HBonds: 'h-bonds',
            ff.AllBonds: 'all-bonds',
            ff.HAngles: 'h-angles'
        }
        if constraints is None:
            constraintString = None
        elif constraints in constraintMap:
            constraintString = constraintMap[constraints]
        else:
            raise ValueError('Illegal value for constraints')
        if implicitSolvent is None:
            implicitString = None
        elif implicitSolvent is HCT:
            implicitString = 'HCT'
        elif implicitSolvent is OBC1:
            implicitString = 'OBC1'
        elif implicitSolvent is OBC2:
            implicitString = 'OBC2'
        elif implicitSolvent is GBn:
            implicitString = 'GBn'
        elif implicitSolvent is GBn2:
            implicitString = 'GBn2'
        else:
            raise ValueError('Illegal value for implicit solvent model')
        sys = amber_file_parser.readAmberSystem(
            prmtop_loader=self._prmtop,
            shake=constraintString,
            nonbondedCutoff=nonbondedCutoff,
            nonbondedMethod=methodMap[nonbondedMethod],
            flexibleConstraints=False,
            gbmodel=implicitString,
            soluteDielectric=soluteDielectric,
            solventDielectric=solventDielectric,
            rigidWater=rigidWater,
            elements=self.elements)
        if hydrogenMass is not None:
            for atom1, atom2 in self.topology.bonds():
                if atom1.element == elem.hydrogen:
                    (atom1, atom2) = (atom2, atom1)
                if atom2.element == elem.hydrogen and atom1.element not in (
                        elem.hydrogen, None):
                    transferMass = hydrogenMass - sys.getParticleMass(
                        atom2.index)
                    sys.setParticleMass(atom2.index, hydrogenMass)
                    sys.setParticleMass(
                        atom1.index,
                        sys.getParticleMass(atom1.index) - transferMass)
        for force in sys.getForces():
            if isinstance(force, mm.NonbondedForce):
                force.setEwaldErrorTolerance(ewaldErrorTolerance)
        if removeCMMotion:
            sys.addForce(mm.CMMotionRemover())
        return sys