Пример #1
0
    def test_modify_omm(self, ethane_system_topology):
        periodic_torsion = openmm.PeriodicTorsionForce()
        periodic_torsion.addTorsion(0, 1, 2, 3, 10, 20, 30)
        my_ommp = Ommperator(ethane_system_topology[0],
                             ethane_system_topology[1])
        my_dih_ommp = PeriodicTorsionForceOmmperator(my_ommp, periodic_torsion,
                                                     0)

        periodic_torsion.setTorsionParameters(0, 1, 2, 3, 4, 20, 30, 40)

        assert my_dih_ommp.particle1 == periodic_torsion.getTorsionParameters(
            0)[0]
        assert my_dih_ommp.particle1 == 1
        assert my_dih_ommp.particle2 == periodic_torsion.getTorsionParameters(
            0)[1]
        assert my_dih_ommp.particle2 == 2
        assert my_dih_ommp.particle3 == periodic_torsion.getTorsionParameters(
            0)[2]
        assert my_dih_ommp.particle3 == 3
        assert my_dih_ommp.particle4 == periodic_torsion.getTorsionParameters(
            0)[3]
        assert my_dih_ommp.particle4 == 4
        assert my_dih_ommp.n == periodic_torsion.getTorsionParameters(0)[4]
        assert my_dih_ommp.phase == periodic_torsion.getTorsionParameters(0)[5]
        assert my_dih_ommp.k == periodic_torsion.getTorsionParameters(0)[6]
Пример #2
0
def _process_improper_torsion_forces(openff_sys, openmm_sys):
    """Process the Impropers section of an OpenFF Interchange into corresponding
    forces within an openmm.PeriodicTorsionForce"""
    if "ImproperTorsions" not in openff_sys.handlers.keys():
        return

    for force in openmm_sys.getForces():
        if type(force) == openmm.PeriodicTorsionForce:
            torsion_force = force
            break
    else:
        torsion_force = openmm.PeriodicTorsionForce()

    improper_torsion_handler = openff_sys.handlers["ImproperTorsions"]

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

        k = params["k"].m_as(off_unit.kilojoule / off_unit.mol)
        periodicity = int(params["periodicity"])
        phase = params["phase"].m_as(off_unit.radian)
        idivf = int(params["idivf"])

        torsion_force.addTorsion(
            indices[0],
            indices[1],
            indices[2],
            indices[3],
            periodicity,
            phase,
            k / idivf,
        )
Пример #3
0
def _process_proper_torsion_forces(openff_sys, openmm_sys):
    """Process the Propers section of an OpenFF Interchange into corresponding
    forces within an openmm.PeriodicTorsionForce"""
    torsion_force = openmm.PeriodicTorsionForce()
    openmm_sys.addForce(torsion_force)

    proper_torsion_handler = openff_sys.handlers["ProperTorsions"]

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

        k = params["k"].m_as(off_unit.kilojoule / off_unit.mol)
        periodicity = int(params["periodicity"])
        phase = params["phase"].m_as(off_unit.radian)
        idivf = int(params["idivf"])
        torsion_force.addTorsion(
            indices[0],
            indices[1],
            indices[2],
            indices[3],
            periodicity,
            phase,
            k / idivf,
        )
Пример #4
0
    def _create_empty_system(cutoff):
        """Creates an empty system object with stub forces.

        Parameters
        ----------
        cutoff: simtk.unit
            The non-bonded cutoff.

        Returns
        -------
        simtk.openmm.System
            The created system object.
        """

        system = openmm.System()

        system.addForce(openmm.HarmonicBondForce())
        system.addForce(openmm.HarmonicAngleForce())
        system.addForce(openmm.PeriodicTorsionForce())

        nonbonded_force = openmm.NonbondedForce()
        nonbonded_force.setCutoffDistance(cutoff)
        nonbonded_force.setNonbondedMethod(openmm.NonbondedForce.PME)

        system.addForce(nonbonded_force)

        return system
Пример #5
0
def _process_proper_torsion_forces(openff_sys, openmm_sys):
    """Process the Propers section of an OpenFF System into corresponding
    forces within an openmm.PeriodicTorsionForce"""
    torsion_force = openmm.PeriodicTorsionForce()
    openmm_sys.addForce(torsion_force)

    proper_torsion_handler = openff_sys.handlers["ProperTorsions"]

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

        k = params["k"].to(off_unit.Unit(
            str(kcal_mol))).magnitude * kcal_mol / kj_mol
        periodicity = int(params["periodicity"])
        phase = params["phase"].to(off_unit.degree).magnitude
        phase = phase * unit.degree / unit.radian
        idivf = int(params["idivf"])
        torsion_force.addTorsion(
            indices[0],
            indices[1],
            indices[2],
            indices[3],
            periodicity,
            phase,
            k / idivf,
        )
Пример #6
0
    def _addPeriodicTorsionsToSystem(self, sys, OPLS):
        """Create the torsion terms
        """
        if OPLS:
            periodic = mm.CustomTorsionForce('f * cos(n * theta - phi0)')
            periodic.addPerTorsionParameter('n')
            periodic.addPerTorsionParameter('phi0')
            periodic.addPerTorsionParameter('f')
        else:
            periodic = mm.PeriodicTorsionForce()
        sys.addForce(periodic)

        q = """SELECT p0, p1, p2, p3, phi0, fc0, fc1, fc2, fc3, fc4, fc5, fc6
        FROM dihedral_trig_term INNER JOIN dihedral_trig_param
        ON dihedral_trig_term.param=dihedral_trig_param.id"""

        for (fcounter, conn, tables, offset) in self._localVars():
            for p0, p1, p2, p3, phi0, fc0, fc1, fc2, fc3, fc4, fc5, fc6 in conn.execute(
                    q):
                p0 += offset
                p1 += offset
                p2 += offset
                p3 += offset
                for order, fc in enumerate([fc0, fc1, fc2, fc3, fc4, fc5,
                                            fc6]):
                    if fc == 0:
                        continue
                    if OPLS:
                        periodic.addTorsion(
                            p0, p1, p2, p3,
                            [order, phi0 * degree, fc * kilocalorie_per_mole])
                    else:
                        periodic.addTorsion(p0, p1, p2, p3, order,
                                            phi0 * degree,
                                            fc * kilocalorie_per_mole)
Пример #7
0
def test_opls_torsion_kernel():
    kernel = OplsTorsionKernel(
        top.positions,
        [dihedral.id_atoms for dihedral in top.dihedrals],
        [[1 + 0.1 * i, 2 + 0.1 * i, 3 + 0.1 * i, 4 + 0.1 * i]
         for i in range(top.n_dihedral)],
    )
    phi, energy, forces = kernel.evaluate()
    print()
    for dihedral, val in zip(top.dihedrals, phi):
        print([i + 1 for i in dihedral.id_atoms], val * 180 / np.pi)
    print(energy)
    print(sum(energy))
    print(forces)

    force = mm.PeriodicTorsionForce()
    force.setForceGroup(3)
    for i, dihedral in enumerate(top.dihedrals):
        for k in range(1, 5):
            force.addTorsion(*dihedral.id_atoms, k, ((k - 1) % 2) * np.pi,
                             k + 0.1 * i)
    e, f = calc_energy_with_omm(force, top.positions)

    assert pytest.approx(sum(energy), rel=1E-6) == e
    assert pytest.approx(forces, rel=1E-6) == f
def _create_torsion_sim(periodicity: int = 2,
                        phase=0 * omm_angle_unit,
                        k=10.0 * omm_energy_unit) -> app.Simulation:
    """Create a 4-particle OpenMM Simulation containing only a PeriodicTorsionForce"""
    system = mm.System()

    # add 4 particles of unit mass
    for _ in range(4):
        system.addParticle(1)

    # add torsion force to system
    force = mm.PeriodicTorsionForce()
    force.addTorsion(0, 1, 2, 3, periodicity, phase, k)
    system.addForce(force)

    # create openmm Simulation, which requires a Topology and Integrator
    topology = app.Topology()
    chain = topology.addChain()
    residue = topology.addResidue("torsion", chain)
    for name in ["a", "b", "c", "d"]:
        topology.addAtom(name, "C", residue)
    integrator = mm.VerletIntegrator(1.0)
    sim = app.Simulation(topology, system, integrator)

    return sim
Пример #9
0
    def test_setting(self, ethane_system_topology):
        periodic_torsion = openmm.PeriodicTorsionForce()
        periodic_torsion.addTorsion(0, 1, 2, 3, 10, 20, 30)
        my_ommp = Ommperator(ethane_system_topology[0],
                             ethane_system_topology[1])
        my_dih_ommp = PeriodicTorsionForceOmmperator(my_ommp, periodic_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.n == 50
        my_dih_ommp.phase == 60
        my_dih_ommp.k == 70

        assert my_dih_ommp.particle1 == periodic_torsion.getTorsionParameters(
            0)[0]
        assert my_dih_ommp.particle1 == 10
        assert my_dih_ommp.particle2 == periodic_torsion.getTorsionParameters(
            0)[1]
        assert my_dih_ommp.particle2 == 20
        assert my_dih_ommp.particle3 == periodic_torsion.getTorsionParameters(
            0)[2]
        assert my_dih_ommp.particle3 == 30
        assert my_dih_ommp.particle4 == periodic_torsion.getTorsionParameters(
            0)[3]
        assert my_dih_ommp.particle4 == 40
        assert my_dih_ommp.n == periodic_torsion.getTorsionParameters(0)[4]
        assert my_dih_ommp.phase == periodic_torsion.getTorsionParameters(0)[5]
        assert my_dih_ommp.k == periodic_torsion.getTorsionParameters(0)[6]

        my_dih_ommp.set_params(p1=100,
                               p2=200,
                               p3=300,
                               p4=400,
                               n=500,
                               phase=600,
                               k=700)

        assert my_dih_ommp.particle1 == periodic_torsion.getTorsionParameters(
            0)[0]
        assert my_dih_ommp.particle1 == 100
        assert my_dih_ommp.particle2 == periodic_torsion.getTorsionParameters(
            0)[1]
        assert my_dih_ommp.particle2 == 200
        assert my_dih_ommp.particle3 == periodic_torsion.getTorsionParameters(
            0)[2]
        assert my_dih_ommp.particle3 == 300
        assert my_dih_ommp.particle4 == periodic_torsion.getTorsionParameters(
            0)[3]
        assert my_dih_ommp.particle4 == 400
        assert my_dih_ommp.n == periodic_torsion.getTorsionParameters(0)[4]
        assert my_dih_ommp.phase == periodic_torsion.getTorsionParameters(0)[5]
        assert my_dih_ommp.k == periodic_torsion.getTorsionParameters(0)[6]
Пример #10
0
    def create_force(self, system, topology, **kwargs):
        #force = super(ImproperTorsionHandler, self).create_force(system, topology, **kwargs)
        #force = super().create_force(system, topology, **kwargs)
        existing = [system.getForce(i) for i in range(system.getNumForces())]
        existing = [
            f for f in existing if type(f) == openmm.PeriodicTorsionForce
        ]
        if len(existing) == 0:
            force = openmm.PeriodicTorsionForce()
            system.addForce(force)
        else:
            force = existing[0]

        # Add all improper torsions to the system
        improper_matches = self.find_matches(topology)
        # The atom indices in the key of the dictionary match have been rearranged and shouldn't be trusted
        for (_, improper_match) in improper_matches.items():
            #for (atom_indices, improper_match) in improper_matches.items():
            # Ensure atoms are actually bonded correct pattern in Topology
            # For impropers, central atom is atom 1
            # for (i, j) in [(0, 1), (1, 2), (1, 3)]:
            #     topology.assert_bonded(atom_indices[i], atom_indices[j])
            self._assert_correct_connectivity(improper_match, [(0, 2), (1, 2),
                                                               (2, 3)])
            atom_indices = improper_match.environment_match.topology_atom_indices
            improper = improper_match.parameter_type

            # TODO: This is a lazy hack. idivf should be set according to the ParameterHandler's default_idivf attrib
            if improper.idivf is None:
                improper.idivf = [3 for item in improper.k]
            # Impropers are applied in three paths around the trefoil having the same handedness
            for (improper_periodicity, improper_phase, improper_k,
                 improper_idivf) in zip(improper.periodicity, improper.phase,
                                        improper.k, improper.idivf):
                # TODO: Implement correct "auto" behavior
                if improper_idivf == 'auto':
                    improper_idivf = 3
                    #logger.warning("The OpenForceField toolkit hasn't implemented "
                    #               "support for the torsion `idivf` value of 'auto'."
                    #               "Currently assuming a value of '3' for impropers.")
                # Permute non-central atoms
                #others = [atom_indices[0], atom_indices[2], atom_indices[3]]
                ## ((0, 1, 2), (1, 2, 0), and (2, 0, 1)) are the three paths around the trefoil
                #for p in [(others[i], others[j], others[k]) for (i, j, k) in [(0, 1, 2), (1, 2, 0), (2, 0, 1)]]:
                #    # The torsion force gets added three times, since the k is divided by three
                #    force.addTorsion(atom_indices[1], p[0], p[1], p[2],
                #                     improper_periodicity, improper_phase, improper_k/improper_idivf)
                force.addTorsion(atom_indices[0], atom_indices[1],
                                 atom_indices[2], atom_indices[3],
                                 improper_periodicity, improper_phase,
                                 improper_k / improper_idivf)
    def _addPeriodicTorsionsToSystem(self, sys):
        """Create the torsion terms
        """
        periodic = mm.PeriodicTorsionForce()
        sys.addForce(periodic)

        q = """SELECT p0, p1, p2, p3, phi0, fc0, fc1, fc2, fc3, fc4, fc5, fc6
        FROM dihedral_trig_term INNER JOIN dihedral_trig_param
        ON dihedral_trig_term.param=dihedral_trig_param.id"""
        for p0, p1, p2, p3, phi0, fc0, fc1, fc2, fc3, fc4, fc5, fc6 in self._conn.execute(q):
            for order, fc in enumerate([fc0, fc1, fc2, fc3, fc4, fc5, fc6]):
                if fc == 0:
                    continue
                periodic.addTorsion(p0, p1, p2, p3, order, phi0*degree, fc*kilocalorie_per_mole)
    def _addPeriodicTorsionsToSystem(self, sys, OPLS):
        """Create the torsion terms
        """

        go = []

        for (fcounter,conn,tables,offset) in self._localVars():
            if not self._hasTable('dihedral_trig_term', tables):
                go.append(False)
            else:
                go.append(True)

        if any(go):
            if OPLS:
                periodic = mm.CustomTorsionForce('f * cos(n * theta - phi0)')
                periodic.addPerTorsionParameter('n')
                periodic.addPerTorsionParameter('phi0')
                periodic.addPerTorsionParameter('f')
            else:
                periodic = mm.PeriodicTorsionForce()
            sys.addForce(periodic)
            periodic.setForceGroup(self._bonded_force_group)
        else:
            return
        
        q = """SELECT p0, p1, p2, p3, phi0, fc0, fc1, fc2, fc3, fc4, fc5, fc6
        FROM dihedral_trig_term INNER JOIN dihedral_trig_param
        ON dihedral_trig_term.param=dihedral_trig_param.id"""

        for (fcounter,conn,tables,offset) in self._localVars():
            if not go[fcounter]:
                continue
            for p0, p1, p2, p3, phi0, fc0, fc1, fc2, fc3, fc4, fc5, fc6 in conn.execute(q):
                p0 += offset
                p1 += offset
                p2 += offset
                p3 += offset
                for order, fc in enumerate([fc0, fc1, fc2, fc3, fc4, fc5, fc6]):
                    if fc == 0:
                        continue
                    if OPLS:
                        periodic.addTorsion(p0, p1, p2, p3, [order, phi0*degree, fc*kilocalorie_per_mole])
                    else:
                        periodic.addTorsion(p0, p1, p2, p3, order, phi0*degree, fc*kilocalorie_per_mole)
Пример #13
0
def _process_improper_torsion_forces(openff_sys, openmm_sys):
    """Process the Impropers section of an OpenFF System into corresponding
    forces within an openmm.PeriodicTorsionForce"""
    if "ImproperTorsions" not in openff_sys.handlers.keys():
        return

    for force in openmm_sys.getForces():
        if type(force) == openmm.PeriodicTorsionForce:
            torsion_force = force
            break
    else:
        torsion_force = openmm.PeriodicTorsionForce()

    improper_torsion_handler = openff_sys.handlers["ImproperTorsions"]

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

        k = params["k"].to(off_unit.Unit(
            str(kcal_mol))).magnitude * kcal_mol / kj_mol
        periodicity = int(params["periodicity"])
        phase = params["phase"].to(off_unit.degree).magnitude
        phase = phase * unit.degree / unit.radian
        idivf = int(params["idivf"])

        other_atoms = [indices[0], indices[2], indices[3]]
        for p in [(other_atoms[i], other_atoms[j], other_atoms[k])
                  for (i, j, k) in [(0, 1, 2), (1, 2, 0), (2, 0, 1)]]:
            torsion_force.addTorsion(
                indices[1],
                p[0],
                p[1],
                p[2],
                periodicity,
                phase,
                k / idivf,
            )
Пример #14
0
    def createPerturbedSystem(self, alchemical_state, mm=None, verbose=False):
        """
        Create a perturbed copy of the system given the specified alchemical state.

        ARGUMENTS

        alchemical_state (AlchemicalState) - the alchemical state to create from the reference system

        TODO

        * Start from a deep copy of the system, rather than building copy through Python interface.
        * isinstance(mm.NonbondedForce) and related expressions won't work if reference system was created with a different OpenMM implemnetation.

        EXAMPLES

        Create alchemical intermediates for 'denihilating' one water in a water box.
        
        >>> # Create a reference system.
        >>> import testsystems
        >>> [reference_system, coordinates] = testsystems.WaterBox()
        >>> # Create a factory to produce alchemical intermediates.
        >>> factory = AbsoluteAlchemicalFactory(reference_system, ligand_atoms=[0, 1, 2])
        >>> # Create an alchemically-perturbed state corresponding to fully-interacting.
        >>> alchemical_state = AlchemicalState(0.00, 1.00, 1.00, 1.)
        >>> # Create the perturbed system.
        >>> alchemical_system = factory.createPerturbedSystem(alchemical_state)
        >>> # Compare energies.
        >>> import simtk.openmm as openmm
        >>> import simtk.unit as units
        >>> timestep = 1.0 * units.femtosecond
        >>> reference_integrator = openmm.VerletIntegrator(timestep)
        >>> reference_context = openmm.Context(reference_system, reference_integrator)
        >>> reference_state = reference_context.getState(getEnergy=True)
        >>> reference_potential = reference_state.getPotentialEnergy()
        >>> alchemical_integrator = openmm.VerletIntegrator(timestep)
        >>> alchemical_context = openmm.Context(alchemical_system, alchemical_integrator)
        >>> alchemical_state = alchemical_context.getState(getEnergy=True)
        >>> alchemical_potential = alchemical_state.getPotentialEnergy()
        >>> delta = alchemical_potential - reference_potential 
        >>> print delta
        0.0 kJ/mol
        
        Create alchemical intermediates for 'denihilating' p-xylene in T4 lysozyme L99A in GBSA.
        
        >>> # Create a reference system.
        >>> import testsystems
        >>> [reference_system, coordinates] = testsystems.LysozymeImplicit()
        >>> # Compute reference potential.
        >>> timestep = 1.0 * units.femtosecond
        >>> reference_integrator = openmm.VerletIntegrator(timestep)
        >>> reference_context = openmm.Context(reference_system, reference_integrator)
        >>> reference_context.setPositions(coordinates)
        >>> reference_state = reference_context.getState(getEnergy=True)
        >>> reference_potential = reference_state.getPotentialEnergy()
        >>> # Create a factory to produce alchemical intermediates.
        >>> receptor_atoms = range(0,2603) # T4 lysozyme L99A
        >>> ligand_atoms = range(2603,2621) # p-xylene
        >>> factory = AbsoluteAlchemicalFactory(reference_system, ligand_atoms=ligand_atoms)
        >>> # Create an alchemically-perturbed state corresponding to fully-interacting.
        >>> alchemical_state = AlchemicalState(0.00, 1.00, 1.00, 1.)
        >>> # Create the perturbed systems using this protocol.
        >>> alchemical_system = factory.createPerturbedSystem(alchemical_state)
        >>> # Compare energies.        
        >>> alchemical_integrator = openmm.VerletIntegrator(timestep)
        >>> alchemical_context = openmm.Context(alchemical_system, alchemical_integrator)
        >>> alchemical_context.setPositions(coordinates)
        >>> alchemical_state = alchemical_context.getState(getEnergy=True)
        >>> alchemical_potential = alchemical_state.getPotentialEnergy()
        >>> delta = alchemical_potential - reference_potential 
        >>> print delta
        0.0 kJ/mol

        NOTES

        If lambda = 1.0 is specified for some force terms, they will not be replaced with modified forms.        

        """

        # Record timing statistics.
        initial_time = time.time()
        if verbose: print "Creating alchemically modified intermediate..."

        reference_system = self.reference_system

        # Create new deep copy reference system to modify.
        system = openmm.System()

        # Set periodic box vectors.
        [a, b, c] = reference_system.getDefaultPeriodicBoxVectors()
        system.setDefaultPeriodicBoxVectors(a, b, c)

        # Add atoms.
        for atom_index in range(reference_system.getNumParticles()):
            mass = reference_system.getParticleMass(atom_index)
            system.addParticle(mass)

        # Add constraints
        for constraint_index in range(reference_system.getNumConstraints()):
            [iatom, jatom,
             r0] = reference_system.getConstraintParameters(constraint_index)
            system.addConstraint(iatom, jatom, r0)

        # Modify forces as appropriate, copying other forces without modification.
        nforces = reference_system.getNumForces()
        for force_index in range(nforces):
            reference_force = reference_system.getForce(force_index)

            if isinstance(reference_force, openmm.PeriodicTorsionForce):
                # PeriodicTorsionForce
                force = openmm.PeriodicTorsionForce()
                for torsion_index in range(reference_force.getNumTorsions()):
                    # Retrieve parmaeters.
                    [
                        particle1, particle2, particle3, particle4,
                        periodicity, phase, k
                    ] = reference_force.getTorsionParameters(torsion_index)
                    # Scale torsion barrier of alchemically-modified system.
                    if set([particle1, particle2, particle3,
                            particle4]).issubset(self.ligand_atomset):
                        k *= alchemical_state.ligandTorsions
                    force.addTorsion(particle1, particle2, particle3,
                                     particle4, periodicity, phase, k)
                system.addForce(force)

            elif isinstance(reference_force, openmm.NonbondedForce):

                # Copy NonbondedForce.
                force = copy.deepcopy(reference_force)
                system.addForce(force)

                # Modify electrostatics.
                if alchemical_state.ligandElectrostatics != 1.0:
                    for particle_index in range(force.getNumParticles()):
                        # Retrieve parameters.
                        [charge, sigma,
                         epsilon] = force.getParticleParameters(particle_index)
                        # Alchemically modify charges.
                        if particle_index in self.ligand_atomset:
                            charge *= alchemical_state.ligandElectrostatics
                        # Set modified particle parameters.
                        force.setParticleParameters(particle_index, charge,
                                                    sigma, epsilon)
                    for exception_index in range(force.getNumExceptions()):
                        # Retrieve parameters.
                        [iatom, jatom, chargeprod, sigma, epsilon
                         ] = force.getExceptionParameters(exception_index)
                        # Alchemically modify chargeprod.
                        if (iatom in self.ligand_atomset) and (
                                jatom in self.ligand_atomset):
                            if alchemical_state.annihilateElectrostatics:
                                chargeprod *= alchemical_state.ligandElectrostatics**2
                        # Set modified exception parameters.
                        force.setExceptionParameters(exception_index, iatom,
                                                     jatom, chargeprod, sigma,
                                                     epsilon)

                # Modify Lennard-Jones if required.
                if alchemical_state.ligandLennardJones != 1.0:
                    # Create softcore Lennard-Jones interactions by modifying NonbondedForce and adding CustomNonbondedForce.
                    #self._alchemicallyModifyLennardJones(system, force, self.ligand_atoms, alchemical_state)
                    self._alchemicallyModifyLennardJonesGroup(
                        system, force, self.ligand_atoms, alchemical_state)

            elif isinstance(reference_force, openmm.GBSAOBCForce) and (
                    alchemical_state.ligandElectrostatics != 1.0):

                # Create a CustomNonbondedForce to implement softcore interactions.
                particle_lambdas = numpy.ones([system.getNumParticles()],
                                              numpy.float32)
                particle_lambdas[
                    self.ligand_atoms] = alchemical_state.ligandElectrostatics
                custom_force = AbsoluteAlchemicalFactory._createCustomSoftcoreGBOBC(
                    reference_force, particle_lambdas)
                system.addForce(custom_force)

            elif isinstance(reference_force, openmm.CustomExternalForce):

                force = openmm.CustomExternalForce(
                    reference_force.getEnergyFunction())
                for parameter_index in range(
                        reference_force.getNumGlobalParameters()):
                    name = reference_force.getGlobalParameterName(
                        parameter_index)
                    default_value = reference_force.getGlobalParameterDefaultValue(
                        parameter_index)
                    force.addGlobalParameter(name, default_value)
                for parameter_index in range(
                        reference_force.getNumPerParticleParameters()):
                    name = reference_force.getPerParticleParameterName(
                        parameter_index)
                    force.addPerParticleParameter(name)
                for index in range(reference_force.getNumParticles()):
                    [particle_index,
                     parameters] = reference_force.getParticleParameters(index)
                    force.addParticle(particle_index, parameters)
                system.addForce(force)

            elif isinstance(reference_force, openmm.CustomBondForce):

                force = openmm.CustomBondForce(
                    reference_force.getEnergyFunction())
                for parameter_index in range(
                        reference_force.getNumGlobalParameters()):
                    name = reference_force.getGlobalParameterName(
                        parameter_index)
                    default_value = reference_force.getGlobalParameterDefaultValue(
                        parameter_index)
                    force.addGlobalParameter(name, default_value)
                for parameter_index in range(
                        reference_force.getNumPerBondParameters()):
                    name = reference_force.getPerBondParameterName(
                        parameter_index)
                    force.addPerBondParameter(name)
                for index in range(reference_force.getNumBonds()):
                    [particle1, particle2,
                     parameters] = reference_force.getBondParameters(index)
                    force.addBond(particle1, particle2, parameters)
                system.addForce(force)

            else:

                # Copy force without modification.
                force = copy.deepcopy(reference_force)
                system.addForce(force)

        # Record timing statistics.
        final_time = time.time()
        elapsed_time = final_time - initial_time
        if verbose: print "Elapsed time %.3f s." % (elapsed_time)

        return system
Пример #15
0
def create_alchemical_intermediates(reference_system,
                                    bond_atoms,
                                    bond_lambda,
                                    kT,
                                    annihilate=False):
    """
    Build alchemically-modified system where ligand is decoupled or annihilated using Custom*Force classes.

    ARGUMENTS

    reference_system (simtk.openmm.System) - reference System object from which alchemical derivatives will be made (will not be modified)
    bond_atoms (list of int) - atoms spanning bond to be eliminated    
    bond_lambda (float) - lambda value for bond breaking (lambda = 1 is original system, lambda = 0 is broken-bond system)
    kT (simtk.unit.Quantity with units compatible with simtk.unit.kilocalories_per_mole) - thermal energy, used in constructing alchemical intermediates

    RETURNS

    system (simtk.openmm.System) - alchemical intermediate copy

    """

    # Create new system.
    system = openmm.System()

    # Set periodic box vectors.
    [a, b, c] = reference_system.getDefaultPeriodicBoxVectors()
    system.setDefaultPeriodicBoxVectors(a, b, c)

    # Add atoms.
    for atom_index in range(reference_system.getNumParticles()):
        mass = reference_system.getParticleMass(atom_index)
        system.addParticle(mass)

    # Add constraints
    for constraint_index in range(reference_system.getNumConstraints()):
        [iatom, jatom,
         r0] = reference_system.getConstraintParameters(constraint_index)
        # Raise an exception if the specified bond_atoms are part of a constrained bond; we can't handle that.
        if (iatom in bond_atoms) and (jatom in bond_atoms):
            raise Exception("Bond to be broken is part of a constraint.")
        system.addConstraint(iatom, jatom, r0)

    # Perturb force terms.
    for force_index in range(reference_system.getNumForces()):
        # Dispatch forces based on reference force type.
        reference_force = reference_system.getForce(force_index)

        if bond_lambda == 1.0:
            # Just make a copy of the force if lambda = 1.
            force = copy.deepcopy(reference_force)
            system.addForce(force)
            continue

        if isinstance(reference_force, openmm.HarmonicBondForce):
            force = openmm.HarmonicBondForce()
            for bond_index in range(reference_force.getNumBonds()):
                # Retrieve parameters.
                [iatom, jatom, r0,
                 K] = reference_force.getBondParameters(bond_index)
                if (iatom in bond_atoms) and (jatom in bond_atoms):
                    if bond_lambda == 0.0:
                        continue  # eliminate this bond if broken
                    # Replace this bond with a soft-core (Morse) bond.
                    softcore_bond_force = create_softcore_bond(
                        iatom, jatom, r0, K, kT, bond_lambda)
                    system.addForce(softcore_bond_force)
                else:
                    # Add bond parameters.
                    force.addBond(iatom, jatom, r0, K)

            # Add force to new system.
            system.addForce(force)

        elif isinstance(reference_force, openmm.HarmonicAngleForce):
            force = openmm.HarmonicAngleForce()
            for angle_index in range(reference_force.getNumAngles()):
                # Retrieve parameters.
                [iatom, jatom, katom, theta0,
                 Ktheta] = reference_force.getAngleParameters(angle_index)
                # Turn off angle terms that span bond.
                if ((iatom in bond_atoms) and
                    (jatom in bond_atoms)) or ((jatom in bond_atoms) and
                                               (katom in bond_atoms)):
                    if bond_lambda == 0.0:
                        continue  # eliminate this angle if bond broken
                    Ktheta *= bond_lambda
                # Add parameters.
                force.addAngle(iatom, jatom, katom, theta0, Ktheta)
            # Add force to system.
            system.addForce(force)

        elif isinstance(reference_force, openmm.PeriodicTorsionForce):
            force = openmm.PeriodicTorsionForce()
            for torsion_index in range(reference_force.getNumTorsions()):
                # Retrieve parmaeters.
                [
                    particle1, particle2, particle3, particle4, periodicity,
                    phase, k
                ] = reference_force.getTorsionParameters(torsion_index)
                # Annihilate if torsion spans bond.
                if ((particle1 in bond_atoms) and
                    (particle2 in bond_atoms)) or (
                        (particle2 in bond_atoms) and
                        (particle3 in bond_atoms)) or (
                            (particle3 in bond_atoms) and
                            (particle4 in bond_atoms)):
                    if bond_lambda == 0.0:
                        continue  # eliminate this torsion if bond broken
                    k *= bond_lambda
                # Add parameters.
                force.addTorsion(particle1, particle2, particle3, particle4,
                                 periodicity, phase, k)
            # Add force to system.
            system.addForce(force)

        elif isinstance(reference_force, openmm.NonbondedForce):
            # NonbondedForce will handle charges and exception interactions.
            force = openmm.NonbondedForce()
            for particle_index in range(reference_force.getNumParticles()):
                # Retrieve parameters.
                [charge, sigma, epsilon
                 ] = reference_force.getParticleParameters(particle_index)
                # Lennard-Jones and electrostatic interactions involving atoms in bond will be handled by CustomNonbondedForce except at lambda = 0 or 1.
                if ((bond_lambda > 0) and
                    (bond_lambda < 1)) and (particle_index in bond_atoms):
                    # TODO: We have to also add softcore electrostatics.
                    epsilon *= 0.0
                # Add modified particle parameters.
                force.addParticle(charge, sigma, epsilon)
            for exception_index in range(reference_force.getNumExceptions()):
                # Retrieve parameters.
                [iatom, jatom, chargeprod, sigma, epsilon
                 ] = reference_force.getExceptionParameters(exception_index)
                # Modify exception for bond atoms.
                if ((iatom in bond_atoms) and (jatom in bond_atoms)):
                    if (bond_lambda == 0.0):
                        continue  # Omit exception if bond has been turned off.
                    # Alchemically modify epsilon and chargeprod.
                    if (iatom in bond_atoms) and (jatom in bond_atoms):
                        # Attenuate exception interaction (since it will be covered by CustomNonbondedForce interactions).
                        epsilon *= bond_lambda
                        chargeprod *= bond_lambda
                    # TODO: Compute restored (1,3) and (1,4) interactions across modified bond.
                # Add modified exception parameters.
                force.addException(iatom, jatom, chargeprod, sigma, epsilon)
            # Set parameters.
            force.setNonbondedMethod(reference_force.getNonbondedMethod())
            force.setCutoffDistance(reference_force.getCutoffDistance())
            force.setReactionFieldDielectric(
                reference_force.getReactionFieldDielectric())
            force.setEwaldErrorTolerance(
                reference_force.getEwaldErrorTolerance())
            # Add force to new system.
            system.addForce(force)

            if (bond_lambda == 0.0) or (bond_lambda == 1.0):
                continue  # don't need softcore if bond is turned off

            # CustomNonbondedForce will handle the softcore interactions with and among alchemically-modified atoms.
            # Softcore potential.
            # TODO: Add coulomb interaction.
            energy_expression = "4*epsilon*compute*x*(x-1.0);"
            energy_expression += "x = 1.0/(alpha*(bond_lambda*(1.0-bond_lambda)/0.25) + (r/sigma)^6);"
            energy_expression += "epsilon = sqrt(epsilon1*epsilon2);"
            energy_expression += "sigma = 0.5*(sigma1 + sigma2);"
            energy_expression += "compute = (1-bond_lambda)*alchemical1*alchemical2 + (alchemical1*(1-alchemical2) + (1-alchemical1)*alchemical2);"  # only compute interactions with or between alchemically-modified atoms

            force = openmm.CustomNonbondedForce(energy_expression)
            alpha = 0.5  # softcore parameter
            force.addGlobalParameter("alpha", alpha)
            force.addGlobalParameter("bond_lambda", bond_lambda)
            force.addPerParticleParameter("charge")
            force.addPerParticleParameter("sigma")
            force.addPerParticleParameter("epsilon")
            force.addPerParticleParameter("alchemical")
            for particle_index in range(reference_force.getNumParticles()):
                # Retrieve parameters.
                [charge, sigma, epsilon
                 ] = reference_force.getParticleParameters(particle_index)
                # Alchemically modify parameters.
                if particle_index in bond_atoms:
                    force.addParticle([charge, sigma, epsilon, 1])
                else:
                    force.addParticle([charge, sigma, epsilon, 0])
            for exception_index in range(reference_force.getNumExceptions()):
                # Retrieve parameters.
                [iatom, jatom, chargeprod, sigma, epsilon
                 ] = reference_force.getExceptionParameters(exception_index)
                # Exclude exception for bonded atoms.
                if (iatom in bond_atoms) and (jatom in bond_atoms): continue
                # All exceptions are handled by NonbondedForce, so we exclude all these here.
                force.addExclusion(iatom, jatom)
            if reference_force.getNonbondedMethod() in [
                    openmm.NonbondedForce.Ewald, openmm.NonbondedForce.PME
            ]:
                force.setNonbondedMethod(
                    openmm.CustomNonbondedForce.CutoffPeriodic)
            else:
                force.setNonbondedMethod(reference_force.getNonbondedMethod())
            force.setCutoffDistance(reference_force.getCutoffDistance())
            system.addForce(force)

        else:
            # Add copy of force term.
            force = copy.deepcopy(reference_force)
            system.addForce(force)

    return system
Пример #16
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
Пример #17
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])
Пример #18
0
    def create_system(self, topology, **kwargs):
        """
        Create a System object with simple parameters from the provided Topology

        Any kwargs are ignored.

        Parameters
        ----------
        topology : openforcefield.topology.Topology
            The Topology to be parameterized

        Returns
        -------
        system : simtk.openmm.System
            The System object

        """
        # TODO: Allow periodicity to be determined from topology

        from openmmtools.constants import kB
        kT = kB * 300 * unit.kelvin  # hard-coded temperature for setting energy scales

        # Create a System
        system = openmm.System()

        # Add particles
        mass = 12.0 * unit.amu
        for atom in topology.atoms:
            system.addParticle(mass)

        # Add simple repulsive interactions
        # TODO: Use softcore repulsive interaction; Gaussian times switch?
        nonbonded = openmm.CustomNonbondedForce('100/(r/0.1)^4')
        nonbonded.setNonbondedMethod(
            openmm.CustomNonbondedForce.CutoffNonPeriodic)
        nonbonded.setCutoffDistance(1 * unit.nanometer)
        system.addForce(nonbonded)
        for atom in topology.atoms:
            nonbonded.addParticle([])

        # Build a list of which atom indices are bonded to each atom
        bondedToAtom = []
        for atom in topology.atoms():
            bondedToAtom.append(set())
        for (atom1, atom2) in topology.bonds():
            bondedToAtom[atom1.index].add(atom2.index)
            bondedToAtom[atom2.index].add(atom1.index)
        return bondedToAtom

        # Add bonds
        bond_force = openmm.HarmonicBondForce()
        r0 = 1.0 * unit.angstroms
        sigma_r = 0.1 * unit.angstroms
        Kr = kT / sigma_r**2
        for atom1, atom2 in topology.bonds():
            bond_force.addBond(atom1.index, atom2.index, r0, Kr)
        system.addForce(bond_force)

        # Add angles
        uniqueAngles = set()
        for bond in topology.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))
        angles = sorted(list(uniqueAngles))
        theta0 = 109.5 * unit.degrees  # TODO: Adapt based on number of bonds to each atom?
        sigma_theta = 10 * unit.degrees
        Ktheta = kT / sigma_theta**2
        angle_force = openmm.HarmonicAngleForce()
        for (atom1, atom2, atom3) in angles:
            angles.addAngle(atom1.index, atom2.index, atom3.index, theta0,
                            Ktheta)
        system.addForce(angle_force)

        # Make a list of all unique proper torsions
        uniquePropers = set()
        for angle in 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]))
        propers = sorted(list(uniquePropers))
        torsion_force = openmm.PeriodicTorsionForce()
        periodicity = 3
        phase = 0.0 * unit.degrees
        Kphi = 0.0 * kT
        for (atom1, atom2, atom3, atom4) in propers:
            torsion_force.add_torsion(atom1.index, atom2.index, atom3.index,
                                      atom4.index, periodicity, phase, Kphi)
        system.addForce(torsion_force)

        return system
Пример #19
0
def add_force(cgmodel, force_type=None):
    """

        Given a 'cgmodel' and 'force_type' as input, this function adds
        the OpenMM force corresponding to 'force_type' to 'cgmodel.system'.

        :param cgmodel: CGModel() class object.
        :param type: class

        :param force_type: Designates the kind of 'force' provided. (Valid options include: "Bond", "Nonbonded", "Angle", and "Torsion")
        :type force_type: str

        :returns: 
             - cgmodel (class) - 'foldamers' CGModel() class object
             - force (class) - An OpenMM `Force() <https://simtk.org/api_docs/openmm/api4_1/python/classsimtk_1_1openmm_1_1openmm_1_1Force.html>`_ object.

        :Example:

        >>> from foldamers.cg_model.cgmodel import CGModel
        >>> cgmodel = CGModel()
        >>> force_type = "Bond"
        >>> cgmodel,force = add_force(cgmodel,force_type=force_type)

        """
    if force_type == "Bond":

        bond_force = mm.HarmonicBondForce()
        bond_list = []

        for bond_indices in cgmodel.get_bond_list():
            bond_list.append([bond_indices[0], bond_indices[1]])
            bond_force_constant = cgmodel.get_bond_force_constant(
                bond_indices[0], bond_indices[1])
            bond_length = cgmodel.get_bond_length(bond_indices[0],
                                                  bond_indices[1])
            if cgmodel.constrain_bonds:
                cgmodel.system.addConstraint(bond_indices[0], bond_indices[1],
                                             bond_length)
            bond_length = bond_length.in_units_of(unit.nanometer)._value
            bond_force.addBond(bond_indices[0], bond_indices[1], bond_length,
                               bond_force_constant)

        if len(bond_list) != bond_force.getNumBonds():
            print(
                "ERROR: The number of bonds in the coarse grained model is different\n"
            )
            print("from the number of bonds in its OpenMM System object\n")
            print("There are " + str(len(bond_list)) +
                  " bonds in the coarse grained model\n")
            print("and " + str(bond_force.getNumBonds()) +
                  " bonds in the OpenMM system object.")
            exit()

        cgmodel.system.addForce(bond_force)
        force = bond_force

    if force_type == "Nonbonded":

        nonbonded_force = mm.NonbondedForce()
        nonbonded_force.setNonbondedMethod(mm.NonbondedForce.NoCutoff)

        for particle in range(cgmodel.num_beads):
            charge = cgmodel.get_particle_charge(particle)
            sigma = cgmodel.get_sigma(particle)
            epsilon = cgmodel.get_epsilon(particle)
            nonbonded_force.addParticle(charge, sigma, epsilon)

        if len(cgmodel.bond_list) >= 1:
            nonbonded_force.createExceptionsFromBonds(cgmodel.bond_list, 1.0,
                                                      1.0)
        cgmodel.system.addForce(nonbonded_force)
        force = nonbonded_force
        #for particle in range(cgmodel.num_beads):
        #print(force.getParticleParameters(particle))

    if force_type == "Angle":
        angle_force = mm.HarmonicAngleForce()
        for angle in cgmodel.bond_angle_list:
            bond_angle_force_constant = cgmodel.get_bond_angle_force_constant(
                angle[0], angle[1], angle[2])
            equil_bond_angle = cgmodel.get_equil_bond_angle(
                angle[0], angle[1], angle[2])
            angle_force.addAngle(angle[0], angle[1], angle[2],
                                 equil_bond_angle, bond_angle_force_constant)
        cgmodel.system.addForce(angle_force)
        force = angle_force

    if force_type == "Torsion":
        torsion_force = mm.PeriodicTorsionForce()
        for torsion in cgmodel.torsion_list:
            torsion_force_constant = cgmodel.get_torsion_force_constant(
                torsion)
            equil_torsion_angle = cgmodel.get_equil_torsion_angle(torsion)
            periodicity = 0
            #print(torsion)
            #print(equil_torsion_angle)
            #print(torsion_force_constant)
            torsion_force.addTorsion(torsion[0], torsion[1], torsion[2],
                                     torsion[3], periodicity,
                                     equil_torsion_angle,
                                     torsion_force_constant)
            #print(torsion_force.getNumTorsions())
        cgmodel.system.addForce(torsion_force)
        force = torsion_force

    return (cgmodel, force)
Пример #20
0
def build_alchemically_modified_system(reference_system,
                                       receptor_atoms,
                                       ligand_atoms,
                                       annihilate=True):
    """
    Build alchemically-modified system where ligand is decoupled or annihilated.
    
    """

    # Create new system.
    system = openmm.System()

    # Add atoms.
    for atom_index in range(reference_system.getNumParticles()):
        mass = reference_system.getParticleMass(atom_index)
        system.addParticle(mass)

    # Add constraints
    for constraint_index in range(reference_system.getNumConstraints()):
        [iatom, jatom,
         r0] = reference_system.getConstraintParameters(constraint_index)
        system.addConstraint(iatom, jatom, r0)

    # Perturb force terms.
    for force_index in range(reference_system.getNumForces()):
        reference_force = reference_system.getForce(force_index)
        # Dispatch forces
        if isinstance(reference_force, openmm.HarmonicBondForce):
            # HarmonicBondForce
            force = openmm.HarmonicBondForce()
            for bond_index in range(reference_force.getNumBonds()):
                # Retrieve parameters.
                [iatom, jatom, r0,
                 K] = reference_force.getBondParameters(bond_index)
                # Annihilate if directed.
                if annihilate and (iatom
                                   in ligand_atoms) and (jatom
                                                         in ligand_atoms):
                    K *= 0.0
                # Add bond parameters.
                force.addBond(iatom, jatom, r0, K)
            # Add force to new system.
            system.addForce(force)
        elif isinstance(reference_force, openmm.HarmonicAngleForce):
            # HarmonicAngleForce
            force = openmm.HarmonicAngleForce()
            for angle_index in range(reference_force.getNumAngles()):
                # Retrieve parameters.
                [iatom, jatom, katom, theta0,
                 Ktheta] = reference_force.getAngleParameters(angle_index)
                # Annihilate if directed:
                if annihilate and (iatom in ligand_atoms) and (
                        jatom in ligand_atoms) and (katom in ligand_atoms):
                    Ktheta *= 0.0
                # Add parameters.
                force.addAngle(iatom, jatom, katom, theta0, Ktheta)
            # Add force to system.
            system.addForce(force)
        elif isinstance(reference_force, openmm.PeriodicTorsionForce):
            # PeriodicTorsionForce
            force = openmm.PeriodicTorsionForce()
            for torsion_index in range(reference_force.getNumTorsions()):
                # Retrieve parmaeters.
                [
                    particle1, particle2, particle3, particle4, periodicity,
                    phase, k
                ] = reference_force.getTorsionParameters(torsion_index)
                # Annihilate if directed:
                if annihilate and (particle1 in ligand_atoms) and (
                        particle2 in ligand_atoms) and (
                            particle3 in ligand_atoms) and (particle4
                                                            in ligand_atoms):
                    k *= 0.0
                # Add parameters.
                force.addTorsion(particle1, particle2, particle3, particle4,
                                 periodicity, phase, k)
            # Add force to system.
            system.addForce(force)
        elif isinstance(reference_force, openmm.NonbondedForce):
            # NonbondedForce
            force = openmm.NonbondedSoftcoreForce()
            for particle_index in range(reference_force.getNumParticles()):
                # Retrieve parameters.
                [charge, sigma, epsilon
                 ] = reference_force.getParticleParameters(particle_index)
                # Alchemically modify parameters.
                alchemical_lambda = 1.0
                if particle_index in ligand_atoms:
                    alchemical_lambda = 0.0
                    charge *= 0.0
                    if annihilate:
                        epsilon *= 0.0
                # Add modified particle parameters.
                force.addParticle(charge, sigma, epsilon, alchemical_lambda)
            for exception_index in range(reference_force.getNumExceptions()):
                # Retrieve parameters.
                [iatom, jatom, chargeprod, sigma, epsilon
                 ] = reference_force.getExceptionParameters(exception_index)
                # TODO: Alchemically modify parameters.
                if (iatom in ligand_atoms) and (jatom in ligand_atoms):
                    chargeprod *= 0.0
                    if annihilate:
                        epsilon *= 0.0
                # Add modified exception parameters.
                force.addException(iatom, jatom, chargeprod, sigma, epsilon)
            # Set parameters.
            force.setNonbondedMethod(reference_force.getNonbondedMethod())
            force.setCutoffDistance(reference_force.getCutoffDistance())
            force.setReactionFieldDielectric(
                reference_force.getReactionFieldDielectric())
            force.setEwaldErrorTolerance(
                reference_force.getEwaldErrorTolerance())
            # Add force to new system.
            system.addForce(force)
        elif isinstance(reference_force, openmm.GBSAOBCForce):
            # GBSAOBCForce
            force = openmm.GBSAOBCSoftcoreForce()
            for particle_index in range(reference_force.getNumParticles()):
                # Retrieve parameters.
                [charge, radius, scaling_factor
                 ] = reference_force.getParticleParameters(particle_index)
                # Alchemically modify parameters.
                nonpolar_scaling_factor = 1.0
                if particle_index in ligand_atoms:
                    charge *= 0.0
                    #radius *= 0.0
                    #scaling_factor *= 0.0
                    nonpolar_scaling_factor = 0.0
                    pass
                # Add parameters.
                force.addParticle(charge, radius, scaling_factor,
                                  nonpolar_scaling_factor)
            force.setSolventDielectric(reference_force.getSolventDielectric())
            force.setSoluteDielectric(reference_force.getSoluteDielectric())
            #force.setCutoffDistance( reference_force.getCutoffDistance() )
            #force.setNonbondedMethod( reference_force.getNonbondedMethod() )
            # Add force to new system.
            system.addForce(force)
        else:
            # Don't add unrecognized forces.
            pass

    return system
Пример #21
0
def build_custom_system(reference_system,
                        receptor_atoms,
                        ligand_atoms,
                        valence_lambda,
                        coulomb_lambda,
                        vdw_lambda,
                        annihilate=False):
    """
    Build alchemically-modified system where ligand is decoupled or annihilated using Custom*Force classes.
    
    """

    # Create new system.
    system = openmm.System()

    # Set periodic box vectors.
    [a, b, c] = reference_system.getDefaultPeriodicBoxVectors()
    system.setDefaultPeriodicBoxVectors(a, b, c)

    # Add atoms.
    for atom_index in range(reference_system.getNumParticles()):
        mass = reference_system.getParticleMass(atom_index)
        system.addParticle(mass)

    # Add constraints
    for constraint_index in range(reference_system.getNumConstraints()):
        [iatom, jatom,
         r0] = reference_system.getConstraintParameters(constraint_index)
        system.addConstraint(iatom, jatom, r0)

    # Perturb force terms.
    for force_index in range(reference_system.getNumForces()):
        reference_force = reference_system.getForce(force_index)
        # Dispatch forces
        if isinstance(reference_force, openmm.HarmonicBondForce):
            # HarmonicBondForce
            force = openmm.HarmonicBondForce()
            for bond_index in range(reference_force.getNumBonds()):
                # Retrieve parameters.
                [iatom, jatom, r0,
                 K] = reference_force.getBondParameters(bond_index)
                # Annihilate if directed.
                if annihilate and (iatom
                                   in ligand_atoms) and (jatom
                                                         in ligand_atoms):
                    K *= valence_lambda
                # Add bond parameters.
                force.addBond(iatom, jatom, r0, K)
            # Add force to new system.
            system.addForce(force)
        elif isinstance(reference_force, openmm.HarmonicAngleForce):
            # HarmonicAngleForce
            force = openmm.HarmonicAngleForce()
            for angle_index in range(reference_force.getNumAngles()):
                # Retrieve parameters.
                [iatom, jatom, katom, theta0,
                 Ktheta] = reference_force.getAngleParameters(angle_index)
                # Annihilate if directed:
                if annihilate and (iatom in ligand_atoms) and (
                        jatom in ligand_atoms) and (katom in ligand_atoms):
                    Ktheta *= valence_lambda
                # Add parameters.
                force.addAngle(iatom, jatom, katom, theta0, Ktheta)
            # Add force to system.
            system.addForce(force)
        elif isinstance(reference_force, openmm.PeriodicTorsionForce):
            # PeriodicTorsionForce
            force = openmm.PeriodicTorsionForce()
            for torsion_index in range(reference_force.getNumTorsions()):
                # Retrieve parmaeters.
                [
                    particle1, particle2, particle3, particle4, periodicity,
                    phase, k
                ] = reference_force.getTorsionParameters(torsion_index)
                # Annihilate if directed:
                if annihilate and (particle1 in ligand_atoms) and (
                        particle2 in ligand_atoms) and (
                            particle3 in ligand_atoms) and (particle4
                                                            in ligand_atoms):
                    k *= valence_lambda
                # Add parameters.
                force.addTorsion(particle1, particle2, particle3, particle4,
                                 periodicity, phase, k)
            # Add force to system.
            system.addForce(force)
        elif isinstance(reference_force, openmm.NonbondedForce):
            # NonbondedForce will handle charges and exception interactions.
            force = openmm.NonbondedForce()
            for particle_index in range(reference_force.getNumParticles()):
                # Retrieve parameters.
                [charge, sigma, epsilon
                 ] = reference_force.getParticleParameters(particle_index)
                # Remove Lennard-Jones interactions, which will be handled by CustomNonbondedForce.
                epsilon *= 0.0
                # Alchemically modify charges.
                if particle_index in ligand_atoms:
                    charge *= coulomb_lambda
                # Add modified particle parameters.
                force.addParticle(charge, sigma, epsilon)
            for exception_index in range(reference_force.getNumExceptions()):
                # Retrieve parameters.
                [iatom, jatom, chargeprod, sigma, epsilon
                 ] = reference_force.getExceptionParameters(exception_index)
                # Alchemically modify epsilon and chargeprod.
                # Note that exceptions are handled by NonbondedForce and not CustomNonbondedForce.
                if (iatom in ligand_atoms) and (jatom in ligand_atoms):
                    if annihilate:
                        epsilon *= vdw_lambda
                        chargeprod *= coulomb_lambda
                # Add modified exception parameters.
                force.addException(iatom, jatom, chargeprod, sigma, epsilon)
            # Set parameters.
            force.setNonbondedMethod(reference_force.getNonbondedMethod())
            force.setCutoffDistance(reference_force.getCutoffDistance())
            force.setReactionFieldDielectric(
                reference_force.getReactionFieldDielectric())
            force.setEwaldErrorTolerance(
                reference_force.getEwaldErrorTolerance())
            # Add force to new system.
            system.addForce(force)

            # CustomNonbondedForce
            # Softcore potential.
            energy_expression = "4*epsilon*lambda*x*(x-1.0);"
            energy_expression += "x = 1.0/(alpha*(1.0-lambda) + (r/sigma)^6);"
            energy_expression += "epsilon = sqrt(epsilon1*epsilon2);"
            energy_expression += "sigma = 0.5*(sigma1 + sigma2);"
            energy_expression += "lambda = lambda1*lambda2;"

            force = openmm.CustomNonbondedForce(energy_expression)
            alpha = 0.5  # softcore parameter
            force.addGlobalParameter("alpha", alpha)
            force.addPerParticleParameter("sigma")
            force.addPerParticleParameter("epsilon")
            force.addPerParticleParameter("lambda")
            for particle_index in range(reference_force.getNumParticles()):
                # Retrieve parameters.
                [charge, sigma, epsilon
                 ] = reference_force.getParticleParameters(particle_index)
                # Alchemically modify parameters.
                if particle_index in ligand_atoms:
                    force.addParticle([sigma, epsilon, vdw_lambda])
                else:
                    force.addParticle([sigma, epsilon, 1.0])
            for exception_index in range(reference_force.getNumExceptions()):
                # Retrieve parameters.
                [iatom, jatom, chargeprod, sigma, epsilon
                 ] = reference_force.getExceptionParameters(exception_index)
                # All exceptions are handled by NonbondedForce, so we exclude all these here.
                force.addExclusion(iatom, jatom)
            if reference_force.getNonbondedMethod() in [
                    openmm.NonbondedForce.Ewald, openmm.NonbondedForce.PME
            ]:
                force.setNonbondedMethod(
                    openmm.CustomNonbondedForce.CutoffPeriodic)
            else:
                force.setNonbondedMethod(reference_force.getNonbondedMethod())
            force.setCutoffDistance(reference_force.getCutoffDistance())
            system.addForce(force)

        elif isinstance(reference_force, openmm.GBSAOBCForce):
            # GBSAOBCForce
            solvent_dielectric = reference_force.getSolventDielectric()
            solute_dielectric = reference_force.getSoluteDielectric()
            force = createCustomSoftcoreGBOBC(solvent_dielectric,
                                              solute_dielectric,
                                              igb=5)
            for particle_index in range(reference_force.getNumParticles()):
                # Retrieve parameters.
                [charge, radius, scaling_factor
                 ] = reference_force.getParticleParameters(particle_index)
                # Alchemically modify parameters.
                if particle_index in ligand_atoms:
                    # Scale charge and contribution to GB integrals.
                    force.addParticle([
                        charge * coulomb_lambda, radius, scaling_factor,
                        coulomb_lambda
                    ])
                else:
                    # Don't modulate GB.
                    force.addParticle([charge, radius, scaling_factor, 1.0])

            # Add force to new system.
            system.addForce(force)
        else:
            # Don't add unrecognized forces.
            pass

    return system
Пример #22
0
def build_softcore_system(reference_system,
                          receptor_atoms,
                          ligand_atoms,
                          valence_lambda,
                          coulomb_lambda,
                          vdw_lambda,
                          annihilate=False):
    """
    Build alchemically-modified system where ligand is decoupled or annihilated using *SoftcoreForce classes.
    
    """

    # Create new system.
    system = openmm.System()

    # Set periodic box vectors.
    [a, b, c] = reference_system.getDefaultPeriodicBoxVectors()
    system.setDefaultPeriodicBoxVectors(a, b, c)

    # Add atoms.
    for atom_index in range(reference_system.getNumParticles()):
        mass = reference_system.getParticleMass(atom_index)
        system.addParticle(mass)

    # Add constraints
    for constraint_index in range(reference_system.getNumConstraints()):
        [iatom, jatom,
         r0] = reference_system.getConstraintParameters(constraint_index)
        system.addConstraint(iatom, jatom, r0)

    # Perturb force terms.
    for force_index in range(reference_system.getNumForces()):
        reference_force = reference_system.getForce(force_index)
        # Dispatch forces
        if isinstance(reference_force, openmm.HarmonicBondForce):
            # HarmonicBondForce
            force = openmm.HarmonicBondForce()
            for bond_index in range(reference_force.getNumBonds()):
                # Retrieve parameters.
                [iatom, jatom, r0,
                 K] = reference_force.getBondParameters(bond_index)
                # Annihilate if directed.
                if annihilate and (iatom
                                   in ligand_atoms) and (jatom
                                                         in ligand_atoms):
                    K *= valence_lambda
                # Add bond parameters.
                force.addBond(iatom, jatom, r0, K)
            # Add force to new system.
            system.addForce(force)
        elif isinstance(reference_force, openmm.HarmonicAngleForce):
            # HarmonicAngleForce
            force = openmm.HarmonicAngleForce()
            for angle_index in range(reference_force.getNumAngles()):
                # Retrieve parameters.
                [iatom, jatom, katom, theta0,
                 Ktheta] = reference_force.getAngleParameters(angle_index)
                # Annihilate if directed:
                if annihilate and (iatom in ligand_atoms) and (
                        jatom in ligand_atoms) and (katom in ligand_atoms):
                    Ktheta *= valence_lambda
                # Add parameters.
                force.addAngle(iatom, jatom, katom, theta0, Ktheta)
            # Add force to system.
            system.addForce(force)
        elif isinstance(reference_force, openmm.PeriodicTorsionForce):
            # PeriodicTorsionForce
            force = openmm.PeriodicTorsionForce()
            for torsion_index in range(reference_force.getNumTorsions()):
                # Retrieve parmaeters.
                [
                    particle1, particle2, particle3, particle4, periodicity,
                    phase, k
                ] = reference_force.getTorsionParameters(torsion_index)
                # Annihilate if directed:
                if annihilate and (particle1 in ligand_atoms) and (
                        particle2 in ligand_atoms) and (
                            particle3 in ligand_atoms) and (particle4
                                                            in ligand_atoms):
                    k *= valence_lambda
                # Add parameters.
                force.addTorsion(particle1, particle2, particle3, particle4,
                                 periodicity, phase, k)
            # Add force to system.
            system.addForce(force)
        elif isinstance(reference_force, openmm.NonbondedForce):
            # NonbondedForce
            force = openmm.NonbondedSoftcoreForce()
            for particle_index in range(reference_force.getNumParticles()):
                # Retrieve parameters.
                [charge, sigma, epsilon
                 ] = reference_force.getParticleParameters(particle_index)
                # Alchemically modify parameters.
                if particle_index in ligand_atoms:
                    charge *= coulomb_lambda
                    epsilon *= vdw_lambda
                    # Add modified particle parameters.
                    force.addParticle(charge, sigma, epsilon, vdw_lambda)
                else:
                    # Add unmodified particle parameters.
                    force.addParticle(charge, sigma, epsilon, 1.0)
            for exception_index in range(reference_force.getNumExceptions()):
                # Retrieve parameters.
                [iatom, jatom, chargeprod, sigma, epsilon
                 ] = reference_force.getExceptionParameters(exception_index)
                # Alchemically modify epsilon and chargeprod.
                if (iatom in ligand_atoms) and (jatom in ligand_atoms):
                    if annihilate:
                        epsilon *= vdw_lambda
                        chargeprod *= coulomb_lambda
                    # Add modified exception parameters.
                    force.addException(iatom, jatom, chargeprod, sigma,
                                       epsilon, vdw_lambda)
                else:
                    # Add unmodified exception parameters.
                    force.addException(iatom, jatom, chargeprod, sigma,
                                       epsilon, 1.0)
            # Set parameters.
            force.setNonbondedMethod(reference_force.getNonbondedMethod())
            force.setCutoffDistance(reference_force.getCutoffDistance())
            force.setReactionFieldDielectric(
                reference_force.getReactionFieldDielectric())
            force.setEwaldErrorTolerance(
                reference_force.getEwaldErrorTolerance())
            # Add force to new system.
            #system.addForce(force)

        elif isinstance(reference_force, openmm.GBSAOBCForce):
            # GBSAOBCForce
            force = openmm.GBSAOBCSoftcoreForce()

            force.setSolventDielectric(reference_force.getSolventDielectric())
            force.setSoluteDielectric(reference_force.getSoluteDielectric())

            for particle_index in range(reference_force.getNumParticles()):
                # Retrieve parameters.
                [charge, radius, scaling_factor
                 ] = reference_force.getParticleParameters(particle_index)
                # Alchemically modify parameters.
                if particle_index in ligand_atoms:
                    # Scale charge and contribution to GB integrals.
                    force.addParticle(charge * coulomb_lambda, radius,
                                      scaling_factor, coulomb_lambda)
                else:
                    # Don't modulate GB.
                    force.addParticle(charge, radius, scaling_factor, 1.0)

            # Add force to new system.
            #system.addForce(force)
        else:
            # Don't add unrecognized forces.
            pass

    return system
Пример #23
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
Пример #24
0
    def run(self):

        prmtop = self._get_prmtop()
        system = prmtop.createSystem()
        groups = {force.getForceGroup() for force in system.getForces()}

        if self.optimize:
            if self.restrained_dihedrals is not None:
                restraint = openmm.PeriodicTorsionForce()
                restraint.setForceGroup(max(groups) + 1)

                for dihedral in self.restrained_dihedrals:
                    restraint.addTorsion(*tuple(map(int, dihedral)),
                                         periodicity=1,
                                         phase=0,
                                         k=-1000 * unit.kilocalorie_per_mole)

                system.addForce(restraint)

        simulation = app.Simulation(
            prmtop.topology, system,
            openmm.VerletIntegrator(1 * unit.femtosecond),
            openmm.Platform.getPlatformByName('CPU'))

        results = []
        molecule_copy = self.molecule.copy()
        for iframe in range(self.molecule.numFrames):
            self.molecule.frame = iframe
            molecule_copy.frame = iframe

            directory = os.path.join(self.directory, '%05d' % iframe)
            os.makedirs(directory, exist_ok=True)
            pickleFile = os.path.join(directory, 'data.pkl')

            if self._completed(directory):
                with open(pickleFile, 'rb') as fd:
                    results.append(pickle.load(fd))
                logger.info('Loading QM data from %s' % pickleFile)
                continue

            simulation.context.setPositions(
                self.molecule.coords[:, :, iframe] * unit.angstrom)
            if self.optimize:
                if self.restrained_dihedrals is not None:
                    for i, dihedral in enumerate(self.restrained_dihedrals):
                        ref_angle = dihedralAngle(
                            self.molecule.coords[dihedral, :, iframe])
                        parameters = restraint.getTorsionParameters(i)
                        parameters[5] = ref_angle * unit.degree
                        restraint.setTorsionParameters(i, *parameters)
                    restraint.updateParametersInContext(simulation.context)
                simulation.minimizeEnergy(tolerance=0.001 *
                                          unit.kilocalorie_per_mole)
            state = simulation.context.getState(getEnergy=True,
                                                getPositions=True,
                                                groups=groups)

            result = QMResult()
            result.errored = False
            result.energy = state.getPotentialEnergy().value_in_unit(
                unit.kilocalorie_per_mole)
            result.coords = state.getPositions(asNumpy=True).value_in_unit(
                unit.angstrom).reshape((-1, 3, 1))
            result.dipole = self.molecule.getDipole()

            if self.esp_points is not None:
                assert self.molecule.numFrames == 1
                result.esp_points = self.esp_points
                distances = cdist(result.esp_points,
                                  result.coords[:, :, 0])  # Angstrom
                distances *= const.physical_constants['Bohr radius'][
                    0] / const.angstrom  # Angstrom --> Bohr
                result.esp_values = np.dot(
                    np.reciprocal(distances),
                    self.molecule.charge)  # Hartree/Bohr

            results.append(result)

            with open(pickleFile, 'wb') as fd:
                pickle.dump(result, fd)

            self.molecule.write(os.path.join(
                directory, 'mol-init.mol2'))  # Write an optimiz
            molecule_copy.coords[:, :, iframe] = result.coords[:, :, 0]
            molecule_copy.write(os.path.join(directory,
                                             'mol.mol2'))  # Write an optimiz

        return results
Пример #25
0
def add_force(cgmodel, force_type=None, rosetta_functional_form=False):
    """

    Given a 'cgmodel' and 'force_type' as input, this function adds
    the OpenMM force corresponding to 'force_type' to 'cgmodel.system'.

    :param cgmodel: CGModel() class object.
    :param type: class

    :param force_type: Designates the kind of 'force' provided. (Valid options include: "Bond", "Nonbonded", "Angle", and "Torsion")
    :type force_type: str

    :returns:
         - cgmodel (class) - 'foldamers' CGModel() class object
         - force (class) - An OpenMM `Force() <https://simtk.org/api_docs/openmm/api4_1/python/classsimtk_1_1openmm_1_1openmm_1_1Force.html>`_ object.

    :Example:

    >>> from foldamers.cg_model.cgmodel import CGModel
    >>> cgmodel = CGModel()
    >>> force_type = "Bond"
    >>> cgmodel,force = add_force(cgmodel,force_type=force_type)

    """
    if force_type == "Bond":

        bond_force = mm.HarmonicBondForce()
        bond_list = []

        for bond_indices in cgmodel.get_bond_list():
            bond_list.append([bond_indices[0], bond_indices[1]])
            if cgmodel.include_bond_forces:
                bond_force_constant = cgmodel.get_bond_force_constant(
                    bond_indices)
                bond_length = cgmodel.get_bond_length(bond_indices)
                bond_force.addBond(
                    bond_indices[0],
                    bond_indices[1],
                    bond_length.value_in_unit(unit.nanometer),
                    bond_force_constant.value_in_unit(unit.kilojoule_per_mole /
                                                      unit.nanometer**2),
                )
            if cgmodel.constrain_bonds:
                bond_length = cgmodel.get_bond_length(bond_indices)
                if not cgmodel.include_bond_forces:
                    bond_force.addBond(
                        bond_indices[0],
                        bond_indices[1],
                        bond_length.value_in_unit(unit.nanometer),
                        0.0,
                    )
                cgmodel.system.addConstraint(bond_indices[0], bond_indices[1],
                                             bond_length)

        if len(bond_list) != bond_force.getNumBonds():
            print(
                "ERROR: The number of bonds in the coarse grained model is different\n"
            )
            print("from the number of bonds in its OpenMM System object\n")
            print("There are " + str(len(bond_list)) +
                  " bonds in the coarse grained model\n")
            print("and " + str(bond_force.getNumBonds()) +
                  " bonds in the OpenMM system object.")
            exit()

        cgmodel.system.addForce(bond_force)
        force = bond_force

    if force_type == "Nonbonded":

        if cgmodel.binary_interaction_parameters:
            # If not an empty dictionary, use the parameters within

            for key, value in cgmodel.binary_interaction_parameters.items():
                # TODO: make kappa work for systems with more than 2 bead types
                kappa = value

            # Use custom nonbonded force with binary interaction parameter
            nonbonded_force = mm.CustomNonbondedForce(
                f"4*epsilon*((sigma/r)^12-(sigma/r)^6); sigma=0.5*(sigma1+sigma2); epsilon=(1-kappa)*sqrt(epsilon1*epsilon2)"
            )
            nonbonded_force.addPerParticleParameter("sigma")
            nonbonded_force.addPerParticleParameter("epsilon")

            # We need to specify a default value of kappa when adding global parameter
            nonbonded_force.addGlobalParameter("kappa", kappa)

            # TODO: add the rosetta_function_form switching function
            nonbonded_force.setNonbondedMethod(mm.NonbondedForce.NoCutoff)

            for particle in range(cgmodel.num_beads):
                # We don't need to define charge here, though we should add it in the future
                # We also don't need to define kappa since it is a global parameter
                sigma = cgmodel.get_particle_sigma(particle)
                epsilon = cgmodel.get_particle_epsilon(particle)
                nonbonded_force.addParticle((sigma, epsilon))

            if len(cgmodel.bond_list) >= 1:
                #***Note: customnonbonded force uses 'Exclusion' rather than 'Exception'
                # Each of these also takes different arguments
                if not rosetta_functional_form:
                    # This should not be applied if there are no angle forces.
                    if cgmodel.include_bond_angle_forces:
                        bond_cut = 2  # Particles separated by this many bonds or fewer are excluded
                        # A value of 2 means that 1-2, 1-3 interactions are 0, 1-4 interactions are 1
                        nonbonded_force.createExclusionsFromBonds(
                            cgmodel.bond_list, bond_cut)
                    else:
                        # Just remove the 1-2 nonbonded interactions.
                        # For customNonbondedForce, don't need to set charge product and epsilon here
                        for bond in cgmodel.bond_list:
                            nonbonded_force.addExclusion(bond[0], bond[1])

        else:
            nonbonded_force = mm.NonbondedForce()

            if rosetta_functional_form:
                # rosetta has a 4.5-6 A vdw cutoff.  Note the OpenMM cutoff may not be quite the same
                # functional form as the Rosetta cutoff, but it should be somewhat close.
                nonbonded_force.setNonbondedMethod(
                    mm.NonbondedForce.CutoffNonPeriodic)
                nonbonded_force.setCutoffDistance(
                    0.6)  # rosetta cutoff distance in nm
                nonbonded_force.setUseSwitchingFunction(True)
                nonbonded_force.setSwitchingDistance(
                    0.45)  # start of rosetta switching distance in nm
            else:
                nonbonded_force.setNonbondedMethod(mm.NonbondedForce.NoCutoff)

            for particle in range(cgmodel.num_beads):
                charge = cgmodel.get_particle_charge(particle)
                sigma = cgmodel.get_particle_sigma(particle)
                epsilon = cgmodel.get_particle_epsilon(particle)
                nonbonded_force.addParticle(charge, sigma, epsilon)

            if len(cgmodel.bond_list) >= 1:
                if not rosetta_functional_form:
                    # This should not be applied if there are no angle forces.
                    if cgmodel.include_bond_angle_forces:
                        nonbonded_force.createExceptionsFromBonds(
                            cgmodel.bond_list, 1.0, 1.0)
                    else:
                        # Just remove the 1-2 nonbonded interactions.
                        # If charge product and epsilon are 0, the interaction is omitted.
                        for bond in cgmodel.bond_list:
                            nonbonded_force.addException(
                                bond[0], bond[1], 0.0, 1.0, 0.0)
                if rosetta_functional_form:
                    # Remove i+3 interactions
                    nonbonded_force.createExceptionsFromBonds(
                        cgmodel.bond_list, 0.0, 0.0)
                    # Reduce the strength of i+4 interactions
                    for torsion in cgmodel.torsion_list:
                        for bond in cgmodel.bond_list:
                            if bond[0] not in torsion:
                                if bond[1] == torsion[0]:
                                    nonbonded_force = add_rosetta_exception_parameters(
                                        cgmodel, nonbonded_force, bond[0],
                                        torsion[3])
                                if bond[1] == torsion[3]:
                                    nonbonded_force = add_rosetta_exception_parameters(
                                        cgmodel, nonbonded_force, bond[0],
                                        torsion[0])
                            if bond[1] not in torsion:
                                if bond[0] == torsion[0]:
                                    nonbonded_force = add_rosetta_exception_parameters(
                                        cgmodel, nonbonded_force, bond[1],
                                        torsion[3])
                                if bond[0] == torsion[3]:
                                    nonbonded_force = add_rosetta_exception_parameters(
                                        cgmodel, nonbonded_force, bond[1],
                                        torsion[0])
        cgmodel.system.addForce(nonbonded_force)
        force = nonbonded_force

    if force_type == "Angle":
        angle_force = mm.HarmonicAngleForce()
        for angle in cgmodel.bond_angle_list:
            bond_angle_force_constant = cgmodel.get_bond_angle_force_constant(
                angle)
            equil_bond_angle = cgmodel.get_equil_bond_angle(angle)
            angle_force.addAngle(
                angle[0],
                angle[1],
                angle[2],
                equil_bond_angle.value_in_unit(unit.radian),
                bond_angle_force_constant.value_in_unit(
                    unit.kilojoule_per_mole / unit.radian**2),
            )
        cgmodel.system.addForce(angle_force)
        force = angle_force

    if force_type == "Torsion":
        torsion_force = mm.PeriodicTorsionForce()
        for torsion in cgmodel.torsion_list:
            torsion_force_constant = cgmodel.get_torsion_force_constant(
                torsion)
            torsion_phase_angle = cgmodel.get_torsion_phase_angle(torsion)
            periodicity = cgmodel.get_torsion_periodicity(torsion)

            if type(periodicity) == list:
                # Check periodic torsion parameter lists:
                # These can be either a list of quantities, or a quantity with a list as its value

                # Check torsion_phase_angle parameters:
                if type(torsion_phase_angle) == unit.quantity.Quantity:
                    # This is either a single quantity, or quantity with a list value
                    if type(torsion_phase_angle.value_in_unit(
                            unit.radian)) == list:
                        # Check if there are either 1 or len(periodicity) elements
                        if len(torsion_phase_angle) != len(
                                periodicity) and len(torsion_phase_angle) != 1:
                            # Mismatch is list lengths
                            print(
                                'ERROR: incompatible periodic torsion parameter lists'
                            )
                            exit()
                        if len(torsion_phase_angle) == 1:
                            # This happens when input is '[value]*unit.radian'
                            torsion_phase_angle_list = []
                            for i in range(len(periodicity)):
                                torsion_phase_angle_list.append(
                                    torsion_phase_angle[0])
                            # This is a list of quantities
                            torsion_phase_angle = torsion_phase_angle_list
                    else:
                        # Single quantity - apply same angle to all periodic terms:
                        torsion_phase_angle_list = []
                        for i in range(len(periodicity)):
                            torsion_phase_angle_list.append(
                                torsion_phase_angle)
                        # This is a list of quantities
                        torsion_phase_angle = torsion_phase_angle_list
                else:
                    # This is a list of quantities or incorrect input
                    if len(torsion_phase_angle) == 1:
                        # This is a list containing a single quantity
                        torsion_phase_angle_list = []
                        for i in range(len(periodicity)):
                            torsion_phase_angle_list.append(
                                torsion_phase_angle[0])
                        # This is a list of quantities
                        torsion_phase_angle = torsion_phase_angle_list

                # Check torsion_force_constant parameters:
                if type(torsion_force_constant) == unit.quantity.Quantity:
                    # This is either a single quantity, or quantity with a list value
                    if type(
                            torsion_force_constant.value_in_unit(
                                unit.kilojoule_per_mole)) == list:
                        # Check if there are either 1 or len(periodicity) elements
                        if len(torsion_force_constant) != len(
                                periodicity) and len(
                                    torsion_force_constant) != 1:
                            # Mismatch is list lengths
                            print(
                                'ERROR: incompatible periodic torsion parameter lists'
                            )
                            exit()
                        if len(torsion_force_constant) == 1:
                            # This happens when input is '[value]*unit.kilojoule_per_mole'
                            torsion_force_constant_list = []
                            for i in range(len(periodicity)):
                                torsion_force_constant_list.append(
                                    torsion_force_constant[0])
                            # This is a list of quantities
                            torsion_force_constant = torsion_force_constant_list
                    else:
                        # Single quantity - apply same angle to all periodic terms:
                        torsion_force_constant_list = []
                        for i in range(len(periodicity)):
                            torsion_force_constant_list.append(
                                torsion_force_constant)
                        # This is a list of quantities
                        torsion_force_constant = torsion_force_constant_list
                else:
                    # This is a list of quantities or incorrect input
                    if len(torsion_force_constant) == 1:
                        # This is a list containing a single quantity
                        torsion_force_constant_list = []
                        for i in range(len(periodicity)):
                            torsion_force_constant_list.append(
                                torsion_force_constant[0])
                        # This is a list of quantities
                        torsion_force_constant = torsion_force_constant_list

                # Add torsion force:
                for i in range(len(periodicity)):
                    # print(f'Adding torsion term to particles [{torsion[0]} {torsion[1]} {torsion[2]} {torsion[3]}]')
                    # print(f'periodicity: {periodicity[i]}')
                    # print(f'torsion_phase_angle: {torsion_phase_angle[i]}')
                    # print(f'torsion_force_constant: {torsion_force_constant[i]}\n')
                    torsion_force.addTorsion(
                        torsion[0],
                        torsion[1],
                        torsion[2],
                        torsion[3],
                        periodicity[i],
                        torsion_phase_angle[i].value_in_unit(unit.radian),
                        torsion_force_constant[i].value_in_unit(
                            unit.kilojoule_per_mole),
                    )

            else:
                # Single periodic torsion term:
                torsion_force.addTorsion(
                    torsion[0],
                    torsion[1],
                    torsion[2],
                    torsion[3],
                    periodicity,
                    torsion_phase_angle.value_in_unit(unit.radian),
                    torsion_force_constant.value_in_unit(
                        unit.kilojoule_per_mole),
                )

        cgmodel.system.addForce(torsion_force)

        # print(f"Number of torsion forces: {cgmodel.system.getForces()[3].getNumTorsions()}")
        force = torsion_force

    return (cgmodel, force)