Exemple #1
0
def _process_rb_torsion_forces(openff_sys, openmm_sys):
    """Process Ryckaert-Bellemans torsions"""
    rb_force = openmm.RBTorsionForce()
    openmm_sys.addForce(rb_force)

    rb_torsion_handler = openff_sys.handlers["RBTorsions"]

    for top_key, pot_key in rb_torsion_handler.slot_map.items():
        indices = top_key.atom_indices
        params = rb_torsion_handler.potentials[pot_key].parameters

        c0 = params["c0"].to(off_unit.Unit(
            str(kcal_mol))).magnitude * kcal_mol / kj_mol
        c1 = params["c1"].to(off_unit.Unit(
            str(kcal_mol))).magnitude * kcal_mol / kj_mol
        c2 = params["c2"].to(off_unit.Unit(
            str(kcal_mol))).magnitude * kcal_mol / kj_mol
        c3 = params["c3"].to(off_unit.Unit(
            str(kcal_mol))).magnitude * kcal_mol / kj_mol
        c4 = params["c4"].to(off_unit.Unit(
            str(kcal_mol))).magnitude * kcal_mol / kj_mol
        c5 = params["c5"].to(off_unit.Unit(
            str(kcal_mol))).magnitude * kcal_mol / kj_mol
        rb_force.addTorsion(
            indices[0],
            indices[1],
            indices[2],
            indices[3],
            c0,
            c1,
            c2,
            c3,
            c4,
            c5,
        )
Exemple #2
0
def _process_rb_torsion_forces(openff_sys, openmm_sys):
    """Process Ryckaert-Bellemans torsions"""
    rb_force = openmm.RBTorsionForce()
    openmm_sys.addForce(rb_force)

    rb_torsion_handler = openff_sys.handlers["RBTorsions"]

    for top_key, pot_key in rb_torsion_handler.slot_map.items():
        indices = top_key.atom_indices
        params = rb_torsion_handler.potentials[pot_key].parameters

        c0 = params["C0"].m_as(off_unit.kilojoule / off_unit.mol)
        c1 = params["C1"].m_as(off_unit.kilojoule / off_unit.mol)
        c2 = params["C2"].m_as(off_unit.kilojoule / off_unit.mol)
        c3 = params["C3"].m_as(off_unit.kilojoule / off_unit.mol)
        c4 = params["C4"].m_as(off_unit.kilojoule / off_unit.mol)
        c5 = params["C5"].m_as(off_unit.kilojoule / off_unit.mol)

        rb_force.addTorsion(
            indices[0],
            indices[1],
            indices[2],
            indices[3],
            c0,
            c1,
            c2,
            c3,
            c4,
            c5,
        )
Exemple #3
0
    def test_setting(self, ethane_system_topology):
        rb_torsion = openmm.RBTorsionForce()
        rb_torsion.addTorsion(0, 1, 2, 3, 10, 20, 30, 40, 50, 60)
        my_ommp = Ommperator(ethane_system_topology[0],
                             ethane_system_topology[1])
        my_dih_ommp = RBTorsionForceOmmperator(my_ommp, rb_torsion, 0)

        my_dih_ommp.particle1 = 10
        my_dih_ommp.particle2 = 20
        my_dih_ommp.particle3 = 30
        my_dih_ommp.particle4 = 40
        my_dih_ommp.c0 = 100
        my_dih_ommp.c1 = 200
        my_dih_ommp.c2 = 300
        my_dih_ommp.c3 = 400
        my_dih_ommp.c4 = 500
        my_dih_ommp.c5 = 600

        assert my_dih_ommp.particle1 == rb_torsion.getTorsionParameters(0)[0]
        assert my_dih_ommp.particle1 == 10
        assert my_dih_ommp.particle2 == rb_torsion.getTorsionParameters(0)[1]
        assert my_dih_ommp.particle2 == 20
        assert my_dih_ommp.particle3 == rb_torsion.getTorsionParameters(0)[2]
        assert my_dih_ommp.particle3 == 30
        assert my_dih_ommp.particle4 == rb_torsion.getTorsionParameters(0)[3]
        assert my_dih_ommp.particle4 == 40
        assert my_dih_ommp.c0 == rb_torsion.getTorsionParameters(0)[4]
        assert my_dih_ommp.c1 == rb_torsion.getTorsionParameters(0)[5]
        assert my_dih_ommp.c2 == rb_torsion.getTorsionParameters(0)[6]
        assert my_dih_ommp.c3 == rb_torsion.getTorsionParameters(0)[7]
        assert my_dih_ommp.c4 == rb_torsion.getTorsionParameters(0)[8]
        assert my_dih_ommp.c5 == rb_torsion.getTorsionParameters(0)[9]

        my_dih_ommp.set_params(p1=100,
                               p2=200,
                               p3=300,
                               p4=400,
                               c0=1000,
                               c1=2000,
                               c2=3000,
                               c3=4000,
                               c4=5000,
                               c5=6000)

        assert my_dih_ommp.particle1 == rb_torsion.getTorsionParameters(0)[0]
        assert my_dih_ommp.particle1 == 100
        assert my_dih_ommp.particle2 == rb_torsion.getTorsionParameters(0)[1]
        assert my_dih_ommp.particle2 == 200
        assert my_dih_ommp.particle3 == rb_torsion.getTorsionParameters(0)[2]
        assert my_dih_ommp.particle3 == 300
        assert my_dih_ommp.particle4 == rb_torsion.getTorsionParameters(0)[3]
        assert my_dih_ommp.particle4 == 400
        assert my_dih_ommp.c0 == rb_torsion.getTorsionParameters(0)[4]
        assert my_dih_ommp.c1 == rb_torsion.getTorsionParameters(0)[5]
        assert my_dih_ommp.c2 == rb_torsion.getTorsionParameters(0)[6]
        assert my_dih_ommp.c3 == rb_torsion.getTorsionParameters(0)[7]
        assert my_dih_ommp.c4 == rb_torsion.getTorsionParameters(0)[8]
        assert my_dih_ommp.c5 == rb_torsion.getTorsionParameters(0)[9]
Exemple #4
0
    def test_parsing(self, ethane_system_topology):
        rb_torsion = openmm.RBTorsionForce()
        rb_torsion.addTorsion(0, 1, 2, 3, 10, 20, 30, 40, 50, 60)
        my_ommp = Ommperator(ethane_system_topology[0],
                             ethane_system_topology[1])
        my_dih_ommp = RBTorsionForceOmmperator(my_ommp, rb_torsion, 0)

        assert my_dih_ommp.particle1 == rb_torsion.getTorsionParameters(0)[0]
        assert my_dih_ommp.particle2 == rb_torsion.getTorsionParameters(0)[1]
        assert my_dih_ommp.particle3 == rb_torsion.getTorsionParameters(0)[2]
        assert my_dih_ommp.particle4 == rb_torsion.getTorsionParameters(0)[3]
        assert my_dih_ommp.c0 == rb_torsion.getTorsionParameters(0)[4]
        assert my_dih_ommp.c1 == rb_torsion.getTorsionParameters(0)[5]
        assert my_dih_ommp.c2 == rb_torsion.getTorsionParameters(0)[6]
        assert my_dih_ommp.c3 == rb_torsion.getTorsionParameters(0)[7]
        assert my_dih_ommp.c4 == rb_torsion.getTorsionParameters(0)[8]
        assert my_dih_ommp.c5 == rb_torsion.getTorsionParameters(0)[9]
Exemple #5
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
Exemple #6
0
    def _addTorsionToSystem(self, syst, moleculeType, bondedTypes,
                            dihedralTypeTable, wildcardDihedralTypes,
                            baseAtomIndex):

        degToRad = math.pi / 180
        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) > 6) 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:
                        periodic = mm.PeriodicTorsionForce()
                        syst.addForce(periodic)
                        periodic.addTorsion(baseAtomIndex + atoms[0],
                                            baseAtomIndex + atoms[1],
                                            baseAtomIndex + atoms[2],
                                            baseAtomIndex + atoms[3],
                                            int(params[7]),
                                            float(params[5]) * degToRad, k)

                elif dihedralType == '2':
                    # Harmonic torsion
                    k = float(params[6])
                    if k != 0:
                        harmonicTorsion = mm.CustomTorsionForce(
                            '0.5*k*(theta-theta0)^2')
                        harmonicTorsion.addPerTorsionParameter('theta0')
                        harmonicTorsion.addPerTorsionParameter('k')
                        syst.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):
                        rb = mm.RBTorsionForce()
                        syst.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])