Example #1
0
def add_cnb(system, indices, suffix='', lambda0=1.0):
    """
    takes in a system, turns off alchemical
    only adds a single lambda controlling parameter!

    suffix is for adding a modifier to change the name of the lambda parameter
    """

    # Retrieve the NonbondedForce
    forces = {force.__class__.__name__: force for force in system.getForces()}
    nbforce = forces['NonbondedForce']

    # Add a CustomNonbondedForce to handle only alchemically-modified interactions
    alchemical_particles = set(list(indices))
    chemical_particles = set(range(
        system.getNumParticles())) - alchemical_particles
    energy_function = 'lambda{}*4*epsilon*x*(x-1.0); x = (sigma/reff_sterics)^6;'.format(
        suffix)
    energy_function += 'reff_sterics = sigma*(0.5*(1.0-lambda{}) + (r/sigma)^6)^(1/6);'.format(
        suffix)
    energy_function += 'sigma = 0.5*(sigma1+sigma2); epsilon = sqrt(epsilon1*epsilon2);'
    custom_force = openmm.CustomNonbondedForce(energy_function)
    custom_force.addGlobalParameter('lambda{}'.format(suffix), lambda0)
    custom_force.addPerParticleParameter('sigma')
    custom_force.addPerParticleParameter('epsilon')
    for index in range(system.getNumParticles()):
        [charge, sigma, epsilon] = nbforce.getParticleParameters(index)
        custom_force.addParticle([sigma, epsilon])
        if index in alchemical_particles:
            nbforce.setParticleParameters(index, charge * 0, sigma,
                                          epsilon * 0)

    custom_force.addInteractionGroup(alchemical_particles, chemical_particles)
    system.addForce(custom_force)
Example #2
0
        def remove_qmmm_elec_force(self, system, qm_index, mm_index,
                                   qm_cutoff):
            forces = {
                force.__class__.__name__: force
                for force in system.getForces()
            }
            reference_force = forces['NonbondedForce']
            ONE_4PI_EPS0 = 138.935456

            if qm_cutoff is None:
                qm_cutoff = reference_force.getCutoffDistance()

            expression = '-1*ONE_4PI_EPS0*chargeprod/r;'
            expression += 'chargeprod = charge1*charge2;'
            expression += 'ONE_4PI_EPS0 = %.16e;' % (ONE_4PI_EPS0)

            force = openmm.CustomNonbondedForce(expression)
            force.addPerParticleParameter("charge")
            force.setUseSwitchingFunction(False)
            force.setCutoffDistance(qm_cutoff)
            force.setUseLongRangeCorrection(False)
            for index in range(reference_force.getNumParticles()):
                [charge, sigma,
                 epsilon] = reference_force.getParticleParameters(index)
                force.addParticle([charge])
            for index in range(reference_force.getNumExceptions()):
                [iatom, jatom, chargeprod, sigma,
                 epsilon] = reference_force.getExceptionParameters(index)
                force.addExclusion(iatom, jatom)
            force.setForceGroup(reference_force.getForceGroup())
            force.addInteractionGroup(qm_index, mm_index)
            system.addForce(force)

            return force
Example #3
0
def soft_lennard_jones_force(sim_object, epsilon=0.42, trunc=2, cutoff=2.5):
    """A softened version of lennard-Jones force.
    Now we're moving to polynomial forces, so go there instead.
    """

    nbCutOffDist = sim_object.conlen * cutoff

    repul_energy = (
        "step(REPcut2 - REPU) * REPU +"
        " step(REPU - REPcut2) * REPcut2 * (1 + tanh(REPU/REPcut2 - 1));"
        "REPU = 4 * REPe * ((REPsigma/r2)^12 - (REPsigma/r2)^6);"
        "r2 = (r^10. + (REPsigma03)^10.)^0.1")
    sim_object.force_dict["Nonbonded"] = openmm.CustomNonbondedForce(
        repul_energy)
    repulforceGr = sim_object.force_dict["Nonbonded"]
    repulforceGr.addGlobalParameter("REPe", sim_object.kT * epsilon)

    repulforceGr.addGlobalParameter("REPsigma", sim_object.conlen)
    repulforceGr.addGlobalParameter("REPsigma03", 0.3 * sim_object.conlen)
    repulforceGr.addGlobalParameter("REPcut", sim_object.kT * trunc)
    repulforceGr.addGlobalParameter("REPcut2", 0.5 * trunc * sim_object.kT)

    for _ in range(sim_object.N):
        repulforceGr.addParticle(())

    repulforceGr.setCutoffDistance(nbCutOffDist)
Example #4
0
def AddAlchemyForces(system, ligand_ind):
    """
    Input an OpenMM 'system' object and the indices of the ligand
    No output. Function adds alchemical nonbonded forces to the system
    """
    forces = {force.__class__.__name__: force for force in system.getForces()}
    nbforce = forces['NonbondedForce']

    ligand = ligand_ind
    protein = set(range(system.getNumParticles())) - ligand

    alchemical_energy = 'lambda*4*epsilon*x*(x-1.0); x = (sigma/reff_sterics)^6;'
    alchemical_energy += 'reff_sterics = sigma*(0.5*(1.0-lambda) + (r/sigma)^6)^(1/6);'
    alchemical_energy += 'sigma = 0.5*(sigma1+sigma2); epsilon = sqrt(epsilon1*epsilon2);'

    alchemical_force = mm.CustomNonbondedForce(alchemical_energy)
    alchemical_force.addGlobalParameter('lambda', 1.0)
    alchemical_force.addPerParticleParameter('sigma')
    alchemical_force.addPerParticleParameter('epsilon')
    for atm in range(system.getNumParticles()):
        # Get the atom's default nonbonded parameters
        [charge, sigma, epsilon] = nbforce.getParticleParameters(atm)
        alchemical_force.addParticle([sigma, epsilon])
        if atm in ligand:
            # TODO: Not actually sure which '*0' options are necessary
            nbforce.setParticleParameters(atm, charge * 0, sigma, epsilon)
    alchemical_force.addInteractionGroup(ligand, protein)
    system.addForce(alchemical_force)
Example #5
0
    def opls_lj(self):
        """
        This function changes the standard OpenMM combination rules to use OPLS, execp and normal pairs are only
        required if their are virtual sites in the molecule.
        """

        # Get the system information from the openmm system
        forces = {self.system.getForce(index).__class__.__name__: self.system.getForce(index) for index in
                  range(self.system.getNumForces())}
        # Use the nondonded_force to get the same rules
        nonbonded_force = forces['NonbondedForce']
        lorentz = mm.CustomNonbondedForce(
            'epsilon*((sigma/r)^12-(sigma/r)^6); sigma=sqrt(sigma1*sigma2); epsilon=sqrt(epsilon1*epsilon2)*4.0')
        lorentz.setNonbondedMethod(nonbonded_force.getNonbondedMethod())
        lorentz.addPerParticleParameter('sigma')
        lorentz.addPerParticleParameter('epsilon')
        lorentz.setCutoffDistance(nonbonded_force.getCutoffDistance())
        self.system.addForce(lorentz)

        l_j_set = {}
        # For each particle, calculate the combination list again
        for index in range(nonbonded_force.getNumParticles()):
            charge, sigma, epsilon = nonbonded_force.getParticleParameters(index)
            l_j_set[index] = (sigma, epsilon, charge)
            lorentz.addParticle([sigma, epsilon])
            nonbonded_force.setParticleParameters(index, charge, 0, 0)

        for i in range(nonbonded_force.getNumExceptions()):
            (p1, p2, q, sig, eps) = nonbonded_force.getExceptionParameters(i)
            # ALL THE 12,13 and 14 interactions are EXCLUDED FROM CUSTOM NONBONDED FORCE
            lorentz.addExclusion(p1, p2)
            if eps._value != 0.0:
                charge = 0.5 * (l_j_set[p1][2] * l_j_set[p2][2])
                sig14 = np.sqrt(l_j_set[p1][0] * l_j_set[p2][0])
                nonbonded_force.setExceptionParameters(i, p1, p2, charge, sig14, eps)
Example #6
0
    def opls_lj(self):
        # Get the system information from the OpenMM system.
        forces = {
            self.system.getForce(index).__class__.__name__:
            self.system.getForce(index)
            for index in range(self.system.getNumForces())
        }

        # Use the nonbonded_force to get the same rules.
        nonbonded_force = forces["NonbondedForce"]
        lorentz = openmm.CustomNonbondedForce(
            "epsilon*((sigma/r)^12-(sigma/r)^6); sigma=sqrt(sigma1*sigma2); epsilon=sqrt(epsilon1*epsilon2)*4.0"
        )

        lorentz.setNonbondedMethod(nonbonded_force.getNonbondedMethod())
        lorentz.setCutoffDistance(nonbonded_force.getCutoffDistance())
        lorentz.addPerParticleParameter("sigma")
        lorentz.addPerParticleParameter("epsilon")
        self.system.addForce(lorentz)
        l_j_set = {}
        # For each particle, calculate the combination list again.
        for index in range(nonbonded_force.getNumParticles()):
            charge, sigma, epsilon = nonbonded_force.getParticleParameters(
                index)
            l_j_set[index] = AtomParams(charge, sigma, epsilon)
            lorentz.addParticle([sigma, epsilon])
            nonbonded_force.setParticleParameters(index, charge, 0, 0)

        exclusions = {}
        for i in range(nonbonded_force.getNumExceptions()):
            p1, p2, q, _, eps = nonbonded_force.getExceptionParameters(i)
            # store the index of the exception by the sorted atom keys
            # ALL THE 12, 13 and 14 interactions are EXCLUDED FROM CUSTOM NONBONDED FORCE
            lorentz.addExclusion(p1, p2)
            exclusions[tuple(sorted((p1, p2)))] = i
            if eps._value != 0.0:
                sig14 = np.sqrt(l_j_set[p1].sigma * l_j_set[p2].sigma)
                nonbonded_force.setExceptionParameters(i, p1, p2, q, sig14,
                                                       eps)

        # If there is a virtual site in the molecule we have to change the exceptions and pairs lists
        if self.molecule.extra_sites is not None:
            # get the interaction lists
            excep_pairs, normal_pairs = self.get_vsite_interactions()

            for pair in excep_pairs:  # scale 14 interactions
                atom1 = l_j_set[pair[0]]
                atom2 = l_j_set[pair[1]]
                q = atom1.charge * atom2.charge * 0.5
                if pair not in exclusions:
                    lorentz.addExclusion(*pair)
                nonbonded_force.addException(*pair, q, 0, 0, True)

            for pair in normal_pairs:  # add the normal pairs here
                atom1 = l_j_set[pair[0]]
                atom2 = l_j_set[pair[1]]
                q = atom1.charge * atom2.charge
                if pair not in exclusions:
                    lorentz.addExclusion(*pair)
                nonbonded_force.addException(*pair, q, 0, 0, True)
Example #7
0
def opls_lj(system):

    forces = {
        system.getForce(indx).__class__.__name__: system.getForce(indx)
        for indx in range(system.getNumForces())
    }
    nonbonded_force = forces['NonbondedForce']
    lorentz = mm.CustomNonbondedForce(
        'epsilon*((sigma/r)^12-(sigma/r)^6); sigma=sqrt(sigma1*sigma2); epsilon=sqrt(epsilon1*epsilon2)*4.0'
    )
    lorentz.setNonbondedMethod(nonbonded_force.getNonbondedMethod())
    lorentz.addPerParticleParameter('sigma')
    lorentz.addPerParticleParameter('epsilon')
    lorentz.setCutoffDistance(nonbonded_force.getCutoffDistance())
    system.addForce(lorentz)
    l_j_set = {}

    for index in range(nonbonded_force.getNumParticles()):
        charge, sigma, epsilon = nonbonded_force.getParticleParameters(index)
        l_j_set[index] = (sigma, epsilon)
        lorentz.addParticle([sigma, epsilon])
        nonbonded_force.setParticleParameters(index, charge, sigma,
                                              epsilon * 0)

    for i in range(nonbonded_force.getNumExceptions()):
        (p1, p2, q, sig, eps) = nonbonded_force.getExceptionParameters(i)
        lorentz.addExclusion(p1, p2)
        if eps._value != 0.0:
            sig14 = (l_j_set[p1][0] * l_j_set[p2][0])**0.5
            eps14 = (l_j_set[p1][1] * l_j_set[p2][1])**0.5
            nonbonded_force.setExceptionParameters(i, p1, p2, q, sig14, eps14)

    return system
Example #8
0
def grosberg_repulsive_force(
    sim_object, trunc=None,  radiusMult=1.0, name="grosberg_repulsive",trunc_function = "min(trunc1, trunc2)",
):
    """This is the fastest non-transparent repulsive force.
    (that preserves topology, doesn't allow chain passing)
    Done according to the paper:
    (Halverson, Jonathan D., et al. "Molecular dynamics simulation study of
     nonconcatenated ring polymers in a melt. I. Statics."
     The Journal of chemical physics 134 (2011): 204904.)
    Parameters
    ----------    
    
    trunc : None, float or N-array of floats    
        "transparency" values for each particular particle, 
        which correspond to the truncation values in kT for the grosberg repulsion energy between a pair of such particles.
        Value of 1.5 yields frequent passing,
        3 - average passing, 5 - rare passing.
    radiusMult : float (optional)
        Multiplier for the size of the force. To make scale the energy larger, set to be more than 1.         
    trunc_function : str (optional)
        a formula to calculate the truncation between a pair of particles with transparencies trunc1 and trunc2
        Default is min(trunc1, trunc2)
 

    """
    radius = sim_object.conlen * radiusMult
    nbCutOffDist = radius * 2.0 ** (1.0 / 6.0)
    if trunc is None:
        repul_energy = "4 * e * ((sigma/r)^12 - (sigma/r)^6) + e"
    else:
        trunc = _to_array_1d(trunc, sim_object.N)
        repul_energy = (
            "step(cut2*trunc_pair - U) * U"
            " + step(U - cut2*trunc_pair) * cut2 * trunc_pair * (1 + tanh(U/(cut2*trunc_pair) - 1));"
            f"trunc_pair={trunc_function};"
            "U = 4 * e * ((sigma/r2)^12 - (sigma/r2)^6) + e;"
            "r2 = (r^10. + (sigma03)^10.)^0.1"
        )
    force = openmm.CustomNonbondedForce(repul_energy)
    force.name = name

    force.addGlobalParameter("e", sim_object.kT)
    force.addGlobalParameter("sigma", radius)
    force.addGlobalParameter("sigma03", 0.3 * radius)
    
    if trunc is not None:
        force.addGlobalParameter("cut2", 0.5 * sim_object.kT)
        force.addPerParticleParameter("trunc")

        for i in range(sim_object.N):  # adding all the particles on which force acts
            force.addParticle([float(trunc[i])])
    else:
        for i in range(sim_object.N):  # adding all the particles on which force acts
            force.addParticle(())
        
    force.setCutoffDistance(nbCutOffDist)

    return force
Example #9
0
def CLPolCoulTT(system, donors, b=45.0):
    '''
    Apply Tang-Toennies damping for the Coulomb interactions between selected H-bond donors and Drude dipoles.

    Parameters
    ----------
    system : mm.System
    donors : list of int
        Indexes of particles served as H-bond donors
    b : float, optional
        b in unit of /nm

    Returns
    -------
    force : mm.CustomNonbondedForce

    '''
    nbforce: mm.NonbondedForce = next(f for f in system.getForces()
                                      if type(f) == mm.NonbondedForce)
    dforce: mm.DrudeForce = next(f for f in system.getForces()
                                 if type(f) == mm.DrudeForce)

    drude_pairs = {}  # {parent: drude}
    dipole_set = set()
    for i in range(dforce.getNumParticles()):
        drude, parent, p2, p3, p4, q, alpha, aniso12, aniso34 = dforce.getParticleParameters(
            i)
        drude_pairs[parent] = drude
        dipole_set.add(parent)
        dipole_set.add(drude)

    ttforce = mm.CustomNonbondedForce('-%.6f*q1*q2/r*beta*gamma;'
                                      'beta=exp(-br);'
                                      'gamma=1+br+br*br/2+br2*br/6+br2*br2/24;'
                                      'br2=br*br;'
                                      'br=%.6f*r' % (CONST.ONE_4PI_EPS0, b))
    ttforce.addPerParticleParameter('q')
    for i in range(system.getNumParticles()):
        if i in drude_pairs:
            q, _, _ = nbforce.getParticleParameters(drude_pairs[i])
            q = -q
        else:
            q, _, _ = nbforce.getParticleParameters(i)
        q = q.value_in_unit(qe)
        ttforce.addParticle([q])
    ttforce.setNonbondedMethod(mm.CustomNonbondedForce.CutoffPeriodic)
    ttforce.setCutoffDistance(1.2 * nm)
    ttforce.addInteractionGroup(set(donors), dipole_set)
    ttforce.setForceGroup(9)
    # map all the exclusions from NonbondedForce
    for i in range(nbforce.getNumExceptions()):
        ii, jj, _, _, _ = nbforce.getExceptionParameters(i)
        ttforce.addExclusion(ii, jj)
    system.addForce(ttforce)

    return ttforce
Example #10
0
def create_c6c12(cutoff: Quantity) -> mm.CustomNonbondedForce:
    potential = mm.CustomNonbondedForce('C12_ij/(r6^2) - C6_ij/r6; '
                                        'r6 = r^6; '
                                        'C6_ij = sqrt(C61*C62); '
                                        'C12_ij = sqrt(C121*C122)')
    potential.addPerParticleParameter('C6')
    potential.addPerParticleParameter('C12')
    potential.setCutoffDistance(cutoff)
    potential.setNonbondedMethod(potential.CutoffPeriodic)
    return potential
Example #11
0
def create_epsilon_sigma(cutoff: Quantity) -> mm.CustomNonbondedForce:
    potential = mm.CustomNonbondedForce(
        '4 * epsilon_ij * (frac6^2 - frac6); '
        'frac6 = (sigma_ij/r)^6; '
        'epsilon_ij = sqrt(epsilon1*epsilon2); '
        'sigma_ij = 0.5 * (sigma1 + sigma2)')
    potential.addPerParticleParameter('sigma')
    potential.addPerParticleParameter('epsilon')
    potential.setCutoffDistance(cutoff)
    potential.setNonbondedMethod(potential.CutoffPeriodic)
    return potential
Example #12
0
    def test_setting(self, geometric_ethane_system_topology):
        nb_force = openmm.CustomNonbondedForce('epsilon1*epsilon2*(sigr6^2-sigr6); sigr6=sigr2*sigr2*sigr2; '
                                        'sigr2=(sigc/r)^2; sigc=sigma1*sigma2')
        nb_force.addPerParticleParameter('epsilon')
        nb_force.addPerParticleParameter('sigma')
        nb_force.addParticle((1*unit.kilojoule_per_mole, 2*unit.nanometer))

        my_ommp = Ommperator(geometric_ethane_system_topology[0], 
                geometric_ethane_system_topology[1])
        my_nb_ommp = CustomNonbondedForceOmmperator(my_ommp, nb_force, 0)

        my_nb_ommp.set_params(10*unit.kilojoule_per_mole, -1)

        assert my_nb_ommp.parameters == nb_force.getParticleParameters(0)
        assert my_nb_ommp.parameters[0] == 10
        assert my_nb_ommp.parameters[1] == 2

        my_nb_ommp.set_params(-1, 20*unit.nanometer)

        assert my_nb_ommp.parameters == nb_force.getParticleParameters(0)
        assert my_nb_ommp.parameters[0] == 10
        assert my_nb_ommp.parameters[1] == 20


        my_nb_ommp.set_params(epsilon=100*unit.kilojoule_per_mole)

        assert my_nb_ommp.parameters == nb_force.getParticleParameters(0)
        assert my_nb_ommp.parameters[0] == 100
        assert my_nb_ommp.parameters[1] == 20

        my_nb_ommp.set_params(sigma=200*unit.nanometer)

        assert my_nb_ommp.parameters == nb_force.getParticleParameters(0)
        assert my_nb_ommp.parameters[0] == 100
        assert my_nb_ommp.parameters[1] == 200

        my_nb_ommp.set_params(1000*unit.kilojoule_per_mole, 2000*unit.nanometer)

        assert my_nb_ommp.parameters == nb_force.getParticleParameters(0)
        assert my_nb_ommp.parameters[0] == 1000
        assert my_nb_ommp.parameters[1] == 2000

        my_nb_ommp.set_params(epsilon=10000*unit.kilojoule_per_mole, 
                sigma=20000*unit.nanometer)

        assert my_nb_ommp.parameters == nb_force.getParticleParameters(0)
        assert my_nb_ommp.parameters[0] == 10000
        assert my_nb_ommp.parameters[1] == 20000



        with pytest.raises(ValueError):
            my_nb_ommp.set_params(1,2,epsilon=3, sigma=4)
Example #13
0
    def test_tabulatedFunction(self):
        f = mm.CustomNonbondedForce('g(r)')

        r = np.linspace(0, 10)
        g_of_r = np.sin(r)

        indx = f.addFunction('g', g_of_r, np.min(r), np.max(r))

        name, g_of_r_out, min_r_out, max_r_out = f.getFunctionParameters(indx)

        np.testing.assert_array_almost_equal(g_of_r, np.asarray(g_of_r_out))
        assert min_r_out == np.min(r)
        assert max_r_out == np.max(r)
Example #14
0
def add_excluded_volume(system: mm.System, args: ListOfArgs):
    print("      Adding excluded volume...")
    print(f"         epsilon = {args.EV_EPSILON}")
    print(f"         sigma = {args.EV_SIGMA}")
    ev_force = mm.CustomNonbondedForce('epsilon*((sigma1+sigma2)/r)^12')
    ev_force.addGlobalParameter('epsilon', defaultValue=args.EV_EPSILON)
    ev_force.addPerParticleParameter('sigma')
    system.addForce(ev_force)
    counter = 0
    for i in range(system.getNumParticles()):
        ev_force.addParticle([args.EV_SIGMA])
        counter += 1
    print(f"         {counter} ev interactions added.")
Example #15
0
    def __init__(self, system, atoms, group=0):
        self.this = copy.deepcopy(system).this
        nonbonded = self.getForce(atomsmm.findNonbondedForce(self))

        potential = '4*lambda_vdw*epsilon*(1 - x)/x^2'
        potential += '; x=(r/sigma)^6 + 0.5*(1 - lambda_vdw)'
        potential += '; sigma = 0.5*(sigma1 + sigma2)'
        potential += '; epsilon = sqrt(epsilon1*epsilon2)'
        softcore = openmm.CustomNonbondedForce(potential)
        if nonbonded.getNonbondedMethod() == openmm.NonbondedForce.NoCutoff:
            softcore.setNonbondedMethod(openmm.CustomNonbondedForce.NoCutoff)
        else:
            softcore.setNonbondedMethod(
                openmm.CustomNonbondedForce.CutoffPeriodic)
        softcore.setCutoffDistance(nonbonded.getCutoffDistance())
        softcore.setUseSwitchingFunction(nonbonded.getUseSwitchingFunction())
        softcore.setSwitchingDistance(nonbonded.getSwitchingDistance())
        # softcore.setUseLongRangeCorrection(nonbonded.getUseDispersionCorrection())
        softcore.setUseLongRangeCorrection(False)
        softcore.addGlobalParameter('lambda_vdw', 1.0)
        softcore.addPerParticleParameter('sigma')
        softcore.addPerParticleParameter('epsilon')
        all = range(nonbonded.getNumParticles())
        for index in all:
            _, sigma, epsilon = nonbonded.getParticleParameters(index)
            softcore.addParticle([sigma, epsilon])
        softcore.addInteractionGroup(atoms, set(all) - set(atoms))
        softcore.setForceGroup(group)
        softcore.addEnergyParameterDerivative('lambda_vdw')
        self.addForce(softcore)

        parameters = []
        for index in atoms:
            parameters.append(nonbonded.getParticleParameters(index))
            nonbonded.setParticleParameters(index, 0.0, 1.0, 0.0)

        exception_pairs = []
        for index in range(nonbonded.getNumExceptions()):
            i, j, _, _, _ = nonbonded.getExceptionParameters(index)
            if set([i, j]).issubset(atoms):
                exception_pairs.append(set([i, j]))
        for i, j in itertools.combinations(atoms, 2):
            if set([i, j]) not in exception_pairs:
                q1, sig1, eps1 = parameters[i]
                q2, sig2, eps2 = parameters[j]
                nonbonded.addException(i, j, q1 * q2, (sig1 + sig2) / 2,
                                       np.sqrt(eps1 * eps2))
                softcore.addExclusion(
                    i, j)  # Needed for matching exception number
Example #16
0
    def test_parsing(self, geometric_ethane_system_topology):
        nb_force = openmm.CustomNonbondedForce('epsilon1*epsilon2*(sigr6^2-sigr6); sigr6=sigr2*sigr2*sigr2; '
                                        'sigr2=(sigc/r)^2; sigc=sigma1*sigma2')
        nb_force.addPerParticleParameter('epsilon')
        nb_force.addPerParticleParameter('sigma')
        nb_force.addParticle((1*unit.kilojoule_per_mole, 2*unit.nanometer))

        my_ommp = Ommperator(geometric_ethane_system_topology[0], 
                geometric_ethane_system_topology[1])
        my_nb_ommp = CustomNonbondedForceOmmperator(my_ommp, nb_force, 0)

        assert my_nb_ommp.parameters == nb_force.getParticleParameters(0)
        assert my_nb_ommp.energy_function == nb_force.getEnergyFunction()
        assert my_nb_ommp.parameter_index['epsilon'] == 0
        assert my_nb_ommp.parameter_index['sigma'] == 1
Example #17
0
    def test_modify_omm(self, geometric_ethane_system_topology):
        nb_force = openmm.CustomNonbondedForce('epsilon1*epsilon2*(sigr6^2-sigr6); sigr6=sigr2*sigr2*sigr2; '
                                        'sigr2=(sigc/r)^2; sigc=sigma1*sigma2')
        nb_force.addPerParticleParameter('epsilon')
        nb_force.addPerParticleParameter('sigma')
        nb_force.addParticle((1*unit.kilojoule_per_mole, 2*unit.nanometer))

        nb_force.setParticleParameters(0, (10*unit.kilojoule_per_mole, 
                20*unit.nanometer))

        my_ommp = Ommperator(geometric_ethane_system_topology[0], 
                geometric_ethane_system_topology[1])
        my_nb_ommp = CustomNonbondedForceOmmperator(my_ommp, nb_force, 0)

        assert my_nb_ommp.parameters == nb_force.getParticleParameters(0)
        assert my_nb_ommp.parameters[0] == 10
        assert my_nb_ommp.parameters[1] == 20
Example #18
0
def polynomial_repulsive(
    sim_object, trunc=3.0, radiusMult=1.0, name="polynomial_repulsive"
):
    """This is a simple polynomial repulsive potential. It has the value
    of `trunc` at zero, stays flat until 0.6-0.7 and then drops to zero
    together with its first derivative at r=1.0.
    
    See the gist below with an example of the potential. 
    https://gist.github.com/mimakaev/0327bf6ffe7057ee0e0625092ec8e318

    Parameters
    ----------

    trunc : float
        the energy value around r=0

    """
    radius = sim_object.conlen * radiusMult
    nbCutOffDist = radius
    repul_energy = (
        "rsc12 * (rsc2 - 1.0) * REPe / emin12 + REPe;"
        "rsc12 = rsc4 * rsc4 * rsc4;"
        "rsc4 = rsc2 * rsc2;"
        "rsc2 = rsc * rsc;"
        "rsc = r / REPsigma * rmin12;"
    )

    force = openmm.CustomNonbondedForce(repul_energy)
    force.name = name

    force.addGlobalParameter("REPe", trunc * sim_object.kT)
    force.addGlobalParameter("REPsigma", radius)
    # Coefficients for x^8*(x*x-1)
    # force.addGlobalParameter('emin12', 256.0 / 3125.0)
    # force.addGlobalParameter('rmin12', 2.0 / np.sqrt(5.0))
    # Coefficients for x^12*(x*x-1)
    force.addGlobalParameter("emin12", 46656.0 / 823543.0)
    force.addGlobalParameter("rmin12", np.sqrt(6.0 / 7.0))

    for _ in range(sim_object.N):
        force.addParticle(())

    force.setCutoffDistance(nbCutOffDist)

    return force
Example #19
0
def grosberg_repulsive_force(sim_object,
                             trunc=None,
                             radiusMult=1.0,
                             name="grosberg_repulsive"):
    """This is the fastest non-transparent repulsive force.
    (that preserves topology, doesn't allow chain passing)
    Done according to the paper:
    (Halverson, Jonathan D., et al. "Molecular dynamics simulation study of
     nonconcatenated ring polymers in a melt. I. Statics."
     The Journal of chemical physics 134 (2011): 204904.)
    Parameters
    ----------

    trunc : None or float
         truncation energy in kT, used for chain crossing.
         Value of 1.5 yields frequent passing,
         3 - average passing, 5 - rare passing.

    """
    radius = sim_object.conlen * radiusMult
    nbCutOffDist = radius * 2.0**(1.0 / 6.0)
    if trunc is None:
        repul_energy = "4 * e * ((sigma/r)^12 - (sigma/r)^6) + e"
    else:
        repul_energy = ("step(cut2 - U) * U"
                        " + step(U - cut2) * cut2 * (1 + tanh(U/cut2 - 1));"
                        "U = 4 * e * ((sigma/r2)^12 - (sigma/r2)^6) + e;"
                        "r2 = (r^10. + (sigma03)^10.)^0.1")
    force = openmm.CustomNonbondedForce(repul_energy)
    force.name = name

    force.addGlobalParameter("e", sim_object.kT)
    force.addGlobalParameter("sigma", radius)
    force.addGlobalParameter("sigma03", 0.3 * radius)
    if trunc is not None:
        force.addGlobalParameter("cut", sim_object.kT * trunc)
        force.addGlobalParameter("cut2", 0.5 * trunc * sim_object.kT)
    for _ in range(sim_object.N):
        force.addParticle(())

    force.setCutoffDistance(nbCutOffDist)

    return force
Example #20
0
    def opls(system):
        """Apply the opls combination rule to the system."""

        from numpy import sqrt
        import simtk.openmm as mm

        # get system information from the openmm system
        forces = {
            system.getForce(index).__class__.__name__: system.getForce(index)
            for index in range(system.getNumForces())
        }
        # use the nondonded_force tp get the same rules
        nonbonded_force = forces['NonbondedForce']
        lorentz = mm.CustomNonbondedForce(
            'epsilon*((sigma/r)^12-(sigma/r)^6); sigma=sqrt(sigma1*sigma2); epsilon=sqrt(epsilon1*epsilon2)*4.0'
        )
        lorentz.setNonbondedMethod(mm.CustomNonbondedForce.NoCutoff)
        lorentz.addPerParticleParameter('sigma')
        lorentz.addPerParticleParameter('epsilon')
        lorentz.setCutoffDistance(nonbonded_force.getCutoffDistance())
        system.addForce(lorentz)
        ljset = {}
        # Now for each particle calculate the combination list again
        for index in range(nonbonded_force.getNumParticles()):
            charge, sigma, epsilon = nonbonded_force.getParticleParameters(
                index)
            # print(nonbonded_force.getParticleParameters(index))
            ljset[index] = (sigma, epsilon)
            lorentz.addParticle([sigma, epsilon])
            nonbonded_force.setParticleParameters(index, charge, 0, 0)
        for i in range(nonbonded_force.getNumExceptions()):
            (p1, p2, q, sig, eps) = nonbonded_force.getExceptionParameters(i)
            # ALL THE 12,13 interactions are EXCLUDED FROM CUSTOM NONBONDED FORCE
            # All 1,4 are scaled by the amount in the xml file
            lorentz.addExclusion(p1, p2)
            if eps._value != 0.0:
                # combine sigma using the geometric combination rule
                sig14 = sqrt(ljset[p1][0] * ljset[p2][0])
                nonbonded_force.setExceptionParameters(i, p1, p2, q, sig14,
                                                       eps)
        return system
Example #21
0
def minimizing_repulsive_Force(sim_object):
    """
    Adds a special force which could be use for very efficient resolution of crossings
    Use this force to perform (local) energy minimization if your monomers are all "on top of each other"
    E.g. if you start your simulations with fractional brownyan motion with h < 0.4
    Then switch to a normal force, and re-do energy minimization. 
    """
    radius = sim_object.conlen * 1.3

    nbCutOffDist = radius * 1.0
    repul_energy = "1000* REPe * (1-r/REPr)^2 "

    sim_object.force_dict[
        "Nonbonded_minimizing_Force"] = openmm.CustomNonbondedForce(
            repul_energy)
    repulforceGr = sim_object.force_dict["Nonbonded_minimizing_Force"]
    repulforceGr.addGlobalParameter("REPe", sim_object.kT)
    repulforceGr.addGlobalParameter("REPr", sim_object.kT)
    for _ in range(sim_object.N):
        repulforceGr.addParticle(())
    repulforceGr.setCutoffDistance(nbCutOffDist)
Example #22
0
 def __init__(self, alchemical_system, grid):
     self._system = openmm.System()
     for i in range(alchemical_system.getNumParticles()):
         self._system.addParticle(alchemical_system.getParticleMass(i))
     self._system.setDefaultPeriodicBoxVectors(
         *alchemical_system.getDefaultPeriodicBoxVectors())
     self._context = None
     self._numForces = len(grid)
     original = alchemical_system._alchemical_vdw_force
     # nonbonded = openmm.NonbondedForce()
     # for i in range(original.getNumParticles()):
     #     nonbonded.addParticle(*original.getParticleParameters(i))
     # nonbonded.setForceGroup(31)
     # self._system.addForce(nonbonded)  # For keeping neighbor list
     for index, value in enumerate(grid):
         ljsoft = f'4*lambda*epsilon*x*(x - 1)'
         ljsoft += f'; x = 1/((r/sigma)^6 + 0.5*(1-lambda))'
         ljsoft += f'; lambda = {value}'
         ljsoft += f'; sigma = 0.5*(sigma1 + sigma2)'
         ljsoft += f'; epsilon = sqrt(epsilon1*epsilon2)'
         force = openmm.CustomNonbondedForce(ljsoft)
         force.setNonbondedMethod(original.getNonbondedMethod())
         for parameter in ['sigma', 'epsilon']:
             force.addPerParticleParameter(parameter)
         for i in range(original.getNumParticles()):
             _, sigma, epsilon = original.getParticleParameters(i)
             force.addParticle((sigma, epsilon))
         for i in range(original.getNumExclusions()):
             force.addExclusion(*original.getExclusionParticles(i))
         force.setCutoffDistance(original.getCutoffDistance())
         force.setUseSwitchingFunction(original.getUseSwitchingFunction())
         force.setSwitchingDistance(original.getSwitchingDistance())
         if value != 0.0:
             force.setUseLongRangeCorrection(
                 original.getUseLongRangeCorrection())
         for i in range(original.getNumInteractionGroups()):
             force.addInteractionGroup(
                 *original.getInteractionGroupParameters(i))
         force.setForceGroup(index)
         self._system.addForce(force)
Example #23
0
def opls_lj(system, switch_dist):

    forces = {
        system.getForce(indx).__class__.__name__: system.getForce(indx)
        for indx in range(system.getNumForces())
    }
    nonbonded_force = forces['NonbondedForce']

    # Custom combining rule
    lorentz = mm.CustomNonbondedForce(
        '4*epsilon*((sigma/r)^12-(sigma/r)^6); sigma=sqrt(sigma1*sigma2); epsilon=sqrt(epsilon1*epsilon2)'
    )

    lorentz.setNonbondedMethod(mm.CustomNonbondedForce.CutoffPeriodic)
    lorentz.addPerParticleParameter('sigma')
    lorentz.addPerParticleParameter('epsilon')
    lorentz.setCutoffDistance(nonbonded_force.getCutoffDistance())
    lorentz.setUseSwitchingFunction(True)
    lorentz.setSwitchingDistance(switch_dist * unit.nanometer)
    lorentz.setUseLongRangeCorrection(True)
    system.addForce(lorentz)

    l_j_set = {}
    for indx in range(nonbonded_force.getNumParticles()):
        charge, sigma, epsilon = nonbonded_force.getParticleParameters(indx)
        l_j_set[indx] = (sigma, epsilon)
        lorentz.addParticle([sigma, epsilon])
        nonbonded_force.setParticleParameters(indx, charge, 0, 0)

    for indx in range(nonbonded_force.getNumExceptions()):
        (p1, p2, q, sig, eps) = nonbonded_force.getExceptionParameters(indx)
        # ALL THE 12, 13 and 14 interactions are EXCLUDED FROM CUSTOM NONBONDED FORCE
        lorentz.addExclusion(p1, p2)
        if eps._value > 0:
            sig14 = (l_j_set[p1][0] * l_j_set[p2][0])**0.5
            eps14 = (l_j_set[p1][1] * l_j_set[p2][1])**0.5
            nonbonded_force.setExceptionParameters(indx, p1, p2, q, sig14,
                                                   eps14)

    return system
def main(args):
    #Get the structure and topology files from the command line
    #ParmEd accepts a wide range of file types (Amber, GROMACS, CHARMM, OpenMM... but not LAMMPS)
    try:
        topFile = args[0]
        strucFile = args[1]
    except IndexError:
        print("Specify topology and structure files from the command line.")
        sys.exit(2)

    print("Using topology file: %s" % topFile)
    print("Using structure file: %s" % strucFile)

    print("\nSetting up system...")

    #Load in the files for initial simulations
    top = pmd.load_file(topFile)
    struc = pmd.load_file(strucFile)

    #Transfer unit cell information to topology object
    top.box = struc.box[:]

    #Set up some global features to use in all simulations
    temperature = 298.15 * u.kelvin

    #Define the platform (i.e. hardware and drivers) to use for running the simulation
    #This can be CUDA, OpenCL, CPU, or Reference
    #CUDA is for NVIDIA GPUs
    #OpenCL is for CPUs or GPUs, but must be used for old CPUs (not SSE4.1 compatible)
    #CPU only allows single precision (CUDA and OpenCL allow single, mixed, or double)
    #Reference is a clear, stable reference for other code development and is very slow, using double precision by default
    platform = mm.Platform.getPlatformByName('CUDA')
    prop = {  #'Threads': '2', #number of threads for CPU - all definitions must be strings (I think)
        'Precision':
        'mixed',  #for CUDA or OpenCL, select the precision (single, mixed, or double)
        'DeviceIndex':
        '0',  #selects which GPUs to use - set this to zero if using CUDA_VISIBLE_DEVICES
        'DeterministicForces':
        'True'  #Makes sure forces with CUDA and PME are deterministic
    }

    #Create the OpenMM system that can be used as a reference
    systemRef = top.createSystem(
        nonbondedMethod=app.
        PME,  #Uses PME for long-range electrostatics, simple cut-off for LJ
        nonbondedCutoff=12.0 *
        u.angstroms,  #Defines cut-off for non-bonded interactions
        rigidWater=True,  #Use rigid water molecules
        constraints=app.HBonds,  #Constrains all bonds involving hydrogens
        flexibleConstraints=
        False,  #Whether to include energies for constrained DOFs
        removeCMMotion=
        False,  #Whether or not to remove COM motion (don't want to if part of system frozen)
    )

    #Set up the integrator to use as a reference
    integratorRef = mm.LangevinIntegrator(
        temperature,  #Temperature for Langevin
        1.0 / u.picoseconds,  #Friction coefficient
        2.0 * u.femtoseconds,  #Integration timestep
    )
    integratorRef.setConstraintTolerance(1.0E-08)

    #To freeze atoms, set mass to zero (does not apply to virtual sites, termed "extra particles" in OpenMM)
    #Here assume (correctly, I think) that the topology indices for atoms correspond to those in the system
    for i, atom in enumerate(top.atoms):
        if atom.type in ('SU'):  #, 'CU', 'CUO'):
            systemRef.setParticleMass(i, 0 * u.dalton)

    #Get solute atoms and solute heavy atoms separately
    #Do this for as many solutes as we have so get list for each
    soluteIndices = []
    heavyIndices = []
    for res in top.residues:
        if res.name not in ['OTM', 'CTM', 'STM', 'NTM', 'SOL']:
            thissolinds = []
            thisheavyinds = []
            for atom in res.atoms:
                thissolinds.append(atom.idx)
                if 'H' not in atom.name[0]:
                    thisheavyinds.append(atom.idx)
            soluteIndices.append(thissolinds)
            heavyIndices.append(thisheavyinds)

    #For convenience, also create flattened version of soluteIndices
    allSoluteIndices = []
    for inds in soluteIndices:
        allSoluteIndices += inds

    #Also get surface SU atoms and surface CU atoms at top and bottom of surface
    surfIndices = []
    for atom in top.atoms:
        if atom.type == 'SU':
            surfIndices.append(atom.idx)

    print("\nSolute indices: %s" % str(soluteIndices))
    print("(all together - %s)" % str(allSoluteIndices))
    print("Solute heavy atom indices: %s" % str(heavyIndices))
    print("Surface SU atom indices: %s" % str(surfIndices))

    #Will now add a custom bonded force between heavy atoms of each solute and surface SU atoms
    #Should be in units of kJ/mol*nm^2, but should check this
    #Also, note that here we are using a flat-bottom restraint to keep close to surface
    #AND to keep from penetrating into surface when it's in the decoupled state
    refZlo = 1.4 * u.nanometer  #in nm, the distance between the SU atoms and the solute centroid
    refZhi = 1.7 * u.nanometer
    restraintExpression = '0.5*k*step(refZlo - (z2 - z1))*(((z2 - z1) - refZlo)^2)'
    restraintExpression += '+ 0.5*k*step((z2 - z1) - refZhi)*(((z2 - z1) - refZhi)^2)'
    restraintForce = mm.CustomCentroidBondForce(2, restraintExpression)
    restraintForce.addPerBondParameter('k')
    restraintForce.addPerBondParameter('refZlo')
    restraintForce.addPerBondParameter('refZhi')
    restraintForce.addGroup(surfIndices, np.ones(
        len(surfIndices)))  #Don't weight with masses
    #To assign flat-bottom restraint correctly, need to know if each solute is above or below interface
    #Will need surface z-positions for this
    suZpos = np.average(struc.coordinates[surfIndices, 2])
    for i in range(len(heavyIndices)):
        restraintForce.addGroup(heavyIndices[i], np.ones(len(heavyIndices[i])))
        solZpos = np.average(struc.coordinates[heavyIndices[i], 2])
        if (solZpos - suZpos) > 0:
            restraintForce.addBond([0, i + 1], [10000.0, refZlo, refZhi])
        else:
            #A little confusing, but have to negate and switch for when z2-z1 is always going to be negative
            restraintForce.addBond([0, i + 1], [10000.0, -refZhi, -refZlo])
    systemRef.addForce(restraintForce)

    #We also will need to force the solute to sample the entire surface
    #We will do this with flat-bottom restraints in x and y
    #But NOT on the centroid, because this is hard with CustomExternalForce
    #Instead, just apply it to the first heavy atom of each solute
    #Won't worry about keeping each solute in the same region of the surface on both faces...
    #Actually better if define regions on both faces differently
    initRefX = 0.25 * top.box[
        0] * u.angstrom  #Used parmed to load, and that program uses angstroms
    initRefY = 0.25 * top.box[1] * u.angstrom
    refDistX = 0.25 * top.box[0] * u.angstrom
    refDistY = 0.25 * top.box[1] * u.angstrom
    print('Default X reference distance: %s' % str(initRefX))
    print('Default Y reference distance: %s' % str(initRefY))
    restraintExpressionXY = '0.5*k*step(periodicdistance(x,0,0,refX,0,0) - distX)*((periodicdistance(x,0,0,refX,0,0) - distX)^2)'
    restraintExpressionXY += '+ 0.5*k*step(periodicdistance(0,y,0,0,refY,0) - distY)*((periodicdistance(0,y,0,0,refY,0) - distY)^2)'
    restraintXYForce = mm.CustomExternalForce(restraintExpressionXY)
    restraintXYForce.addPerParticleParameter('k')
    restraintXYForce.addPerParticleParameter('distX')
    restraintXYForce.addPerParticleParameter('distY')
    restraintXYForce.addGlobalParameter('refX', initRefX)
    restraintXYForce.addGlobalParameter('refY', initRefY)
    for i in range(len(heavyIndices)):
        restraintXYForce.addParticle(
            heavyIndices[i][0],
            [1000.0, refDistX, refDistY])  #k in units kJ/mol*nm^2
    systemRef.addForce(restraintXYForce)

    #JUST for acetic acid, will also need to add lambda states in order to soften torsions around the C-OH bond
    #Will base this on the work of Paluch, et al. 2011
    #First soften the torsions in the coupled state, then turn them on in the decoupled state
    #Defining lambda states here
    #Below will define a custom torsion force so can globally modify dihedrals of interest alchemically
    lambdaVec = np.array(  #electrostatic lambda - 1.0 is fully interacting, 0.0 is non-interacting
        [
            [
                1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 0.75, 0.50, 0.25, 0.00,
                0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00,
                0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00
            ],
            #LJ lambdas - 1.0 is fully interacting, 0.0 is non-interacting
            [
                1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
                0.90, 0.80, 0.70, 0.60, 0.50, 0.40, 0.35, 0.30, 0.25, 0.20,
                0.15, 0.10, 0.05, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00
            ],
            #Torsion lambdas - 1.0 is fully on, softening from there
            [
                1.00, 0.90, 0.70, 0.30, 0.10, 0.01, 0.01, 0.01, 0.01, 0.01,
                0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01,
                0.01, 0.01, 0.01, 0.01, 0.10, 0.30, 0.70, 0.90, 1.00
            ]
        ])

    #We need to add a custom non-bonded force for the solute being alchemically changed
    #Will be helpful to have handle on non-bonded force handling LJ and coulombic interactions
    NBForce = None
    for frc in systemRef.getForces():
        if (isinstance(frc, mm.NonbondedForce)):
            NBForce = frc

    #Turn off dispersion correction since have interface
    NBForce.setUseDispersionCorrection(False)

    forceLabelsRef = getForceLabels(systemRef)

    decompEnergy(systemRef,
                 struc.positions,
                 labels=forceLabelsRef,
                 verbose=True)

    #JUST do the below for acetic acid!
    #Otherwise will modify all torsions involving hydroxyls
    ########################### Starting some torsion modifications ###########################
    TorsionForce = None
    for frc in systemRef.getForces():
        if (isinstance(frc, mm.PeriodicTorsionForce)):
            TorsionForce = frc

    #Create custom torsion for those we want to soften
    alchTorsionFunction = "lambdaTorsion*k*(1+cos(per*theta-theta0))"
    AlchTorsionForce = mm.CustomTorsionForce(alchTorsionFunction)
    AlchTorsionForce.addGlobalParameter('lambdaTorsion', 1.0)
    AlchTorsionForce.addPerTorsionParameter('per')
    AlchTorsionForce.addPerTorsionParameter('theta0')
    AlchTorsionForce.addPerTorsionParameter('k')

    #Now need to find specific dihedrals we want to modify
    for ind in range(TorsionForce.getNumTorsions()):
        p1, p2, p3, p4, periodicity, phase, kval = TorsionForce.getTorsionParameters(
            ind)
        thisAtomNames = [top.atoms[x].name for x in [p1, p2, p3, p4]]
        if ((set([p1, p2, p3, p4]).issubset(allSoluteIndices))
                and (set(['H', 'O']).issubset(thisAtomNames))):
            AlchTorsionForce.addTorsion(p1, p2, p3, p4,
                                        [periodicity, phase, kval])
            #Don't want to duplicate
            TorsionForce.setTorsionParameters(ind, p1, p2, p3, p4, periodicity,
                                              phase, kval * 0.0)

    #Add custom torsion force to our system
    systemRef.addForce(AlchTorsionForce)
    ########################### Done with torsion modification bit ###########################

    #Separate out alchemical and regular particles using set objects
    alchemicalParticles = set(allSoluteIndices)
    chemicalParticles = set(range(
        systemRef.getNumParticles())) - alchemicalParticles

    #Define the soft-core function for turning on/off LJ interactions
    #In energy expressions for CustomNonbondedForce, r is a special variable and refers to the distance between particles
    #All other variables must be defined somewhere in the function.
    #The exception are variables like sigma1 and sigma2.
    #It is understood that a parameter will be added called 'sigma' and that the '1' and '2' are to specify the combining rule.
    softCoreFunction = '4.0*lambdaLJ*epsilon*x*(x-1.0); x = (1.0/reff_sterics);'
    softCoreFunction += 'reff_sterics = (0.5*(1.0-lambdaLJ) + ((r/sigma)^6));'
    softCoreFunction += 'sigma=0.5*(sigma1+sigma2); epsilon = sqrt(epsilon1*epsilon2)'
    #Define the system force for this function and its parameters
    SoftCoreForce = mm.CustomNonbondedForce(softCoreFunction)
    SoftCoreForce.addGlobalParameter(
        'lambdaLJ', 1.0
    )  #Throughout, should follow convention that lambdaLJ=1.0 is fully-interacting state
    SoftCoreForce.addPerParticleParameter('sigma')
    SoftCoreForce.addPerParticleParameter('epsilon')

    #Will turn off electrostatics completely in the original non-bonded force
    #In the end-state, only want electrostatics inside the alchemical molecule
    #To do this, just turn ON a custom force as we turn OFF electrostatics in the original force
    ONE_4PI_EPS0 = 138.935456  #in kJ/mol nm/e^2
    soluteCoulFunction = '(1.0-(lambdaQ^2))*ONE_4PI_EPS0*charge/r;'
    soluteCoulFunction += 'ONE_4PI_EPS0 = %.16e;' % (ONE_4PI_EPS0)
    soluteCoulFunction += 'charge = charge1*charge2'
    SoluteCoulForce = mm.CustomNonbondedForce(soluteCoulFunction)
    #Note this lambdaQ will be different than for soft core (it's also named differently, which is CRITICAL)
    #This lambdaQ corresponds to the lambda that scales the charges to zero
    #To turn on this custom force at the same rate, need to multiply by (1.0-lambdaQ**2), which we do
    SoluteCoulForce.addGlobalParameter('lambdaQ', 1.0)
    SoluteCoulForce.addPerParticleParameter('charge')

    #Also create custom force for intramolecular alchemical LJ interactions
    #Could include with electrostatics, but nice to break up
    #We could also do this with a separate NonbondedForce object, but it would be a little more work, actually
    soluteLJFunction = '4.0*epsilon*x*(x-1.0); x = (sigma/r)^6;'
    soluteLJFunction += 'sigma=0.5*(sigma1+sigma2); epsilon=sqrt(epsilon1*epsilon2)'
    SoluteLJForce = mm.CustomNonbondedForce(soluteLJFunction)
    SoluteLJForce.addPerParticleParameter('sigma')
    SoluteLJForce.addPerParticleParameter('epsilon')

    #Loop over all particles and add to custom forces
    #As we go, will also collect full charges on the solute particles
    #AND we will set up the solute-solute interaction forces
    alchemicalCharges = [[0]] * len(allSoluteIndices)
    for ind in range(systemRef.getNumParticles()):
        #Get current parameters in non-bonded force
        [charge, sigma, epsilon] = NBForce.getParticleParameters(ind)
        #Make sure that sigma is not set to zero! Fine for some ways of writing LJ energy, but NOT OK for soft-core!
        if sigma / u.nanometer == 0.0:
            newsigma = 0.3 * u.nanometer  #This 0.3 is what's used by GROMACS as a default value for sc-sigma
        else:
            newsigma = sigma
        #Add the particle to the soft-core force (do for ALL particles)
        SoftCoreForce.addParticle([newsigma, epsilon])
        #Also add the particle to the solute only forces
        SoluteCoulForce.addParticle([charge])
        SoluteLJForce.addParticle([sigma, epsilon])
        #If the particle is in the alchemical molecule, need to set it's LJ interactions to zero in original force
        if ind in allSoluteIndices:
            NBForce.setParticleParameters(ind, charge, sigma, epsilon * 0.0)
            #And keep track of full charge so we can scale it right by lambda
            alchemicalCharges[allSoluteIndices.index(ind)] = charge

    #Now we need to handle exceptions carefully
    for ind in range(NBForce.getNumExceptions()):
        [p1, p2, excCharge, excSig,
         excEps] = NBForce.getExceptionParameters(ind)
        #For consistency, must add exclusions where we have exceptions for custom forces
        SoftCoreForce.addExclusion(p1, p2)
        SoluteCoulForce.addExclusion(p1, p2)
        SoluteLJForce.addExclusion(p1, p2)

    #Only compute interactions between the alchemical and other particles for the soft-core force
    SoftCoreForce.addInteractionGroup(alchemicalParticles, chemicalParticles)

    #And only compute alchemical/alchemical interactions for other custom forces
    SoluteCoulForce.addInteractionGroup(alchemicalParticles,
                                        alchemicalParticles)
    SoluteLJForce.addInteractionGroup(alchemicalParticles, alchemicalParticles)

    #Set other soft-core parameters as needed
    SoftCoreForce.setCutoffDistance(12.0 * u.angstroms)
    SoftCoreForce.setNonbondedMethod(mm.CustomNonbondedForce.CutoffPeriodic)
    SoftCoreForce.setUseLongRangeCorrection(False)
    systemRef.addForce(SoftCoreForce)

    #Set other parameters as needed - note that for the solute force would like to set no cutoff
    #However, OpenMM won't allow a bunch of potentials with cutoffs then one without...
    #So as long as the solute is smaller than the cut-off, won't have any problems!
    SoluteCoulForce.setCutoffDistance(12.0 * u.angstroms)
    SoluteCoulForce.setNonbondedMethod(mm.CustomNonbondedForce.CutoffPeriodic)
    SoluteCoulForce.setUseLongRangeCorrection(False)
    systemRef.addForce(SoluteCoulForce)

    SoluteLJForce.setCutoffDistance(12.0 * u.angstroms)
    SoluteLJForce.setNonbondedMethod(mm.CustomNonbondedForce.CutoffPeriodic)
    SoluteLJForce.setUseLongRangeCorrection(False)
    systemRef.addForce(SoluteLJForce)

    forceLabelsRef = getForceLabels(systemRef)

    decompEnergy(systemRef, struc.positions, labels=forceLabelsRef)

    #Run set of simulations for particle restrained in x and y to each quadrant of the surface
    #For best results, the configuration read in should have the solute right in the center of the surface
    #Will store the lambda biasing weights as we got to help speed convergence (hopefully)
    for xfrac in [0.25, 0.75]:
        for yfrac in [0.25, 0.75]:

            os.mkdir('Quad_%1.2fX_%1.2fY' % (xfrac, yfrac))
            os.chdir('Quad_%1.2fX_%1.2fY' % (xfrac, yfrac))

            restraintXYForce.setGlobalParameterDefaultValue(
                0, xfrac * top.box[0] * u.angstrom)
            restraintXYForce.setGlobalParameterDefaultValue(
                1, yfrac * top.box[1] * u.angstrom)

            decompEnergy(systemRef, struc.positions, labels=forceLabelsRef)

            #Do NVT simulation
            stateFileNVT, stateNVT = doSimNVT(top,
                                              systemRef,
                                              integratorRef,
                                              platform,
                                              prop,
                                              temperature,
                                              pos=struc.positions)

            #And do NPT simulation using state information from NVT
            stateFileNPT, stateNPT = doSimNPT(top,
                                              systemRef,
                                              integratorRef,
                                              platform,
                                              prop,
                                              temperature,
                                              state=stateFileNVT)

            #And do production run in expanded ensemble!
            stateFileProd, stateProd, weightsVec = doSimExpanded(
                top,
                systemRef,
                integratorRef,
                platform,
                prop,
                temperature,
                0,
                lambdaVec,
                allSoluteIndices,
                alchemicalCharges,
                state=stateFileNPT,
                nSteps=20000000,
                equilSteps=7500000)

            #Save final simulation configuration in .gro format (mainly for genetic algorithm)
            finalStruc = copy.deepcopy(struc)
            boxVecs = np.array(stateProd.getPeriodicBoxVectors().value_in_unit(
                u.angstrom))  # parmed works with angstroms
            finalStruc.box = np.array([
                boxVecs[0, 0], boxVecs[1, 1], boxVecs[2, 2], 90.0, 90.0, 90.0
            ])
            finalStruc.coordinates = np.array(
                stateProd.getPositions().value_in_unit(u.angstrom))
            finalStruc.save('prod.gro')

            os.chdir('../')
Example #25
0
def createUnfoldedSurrogate3(topology,
                             reference_system,
                             locality=5,
                             cutoff=6.0 * unit.angstrom,
                             switch_width=1.0 * unit.angstrom):
    # Create system deep copy.
    system = copy.deepcopy(reference_system)

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

        if force_name == 'NonbondedForce':
            forces_to_remove.append(force_index)

            # Create CustomNonbondedForce instead.
            energy_expression = "islocal * (E_LJ + E_Coulomb);"
            energy_expression += "E_LJ = 4*epsilon*((sigma/r)^12 - (sigma/r)^6);"
            energy_expression += "E_Coulomb = 138.935456*chargeprod/r;"
            energy_expression += "islocal = step(locality - abs(resid1 - resid2) + 0.001);"
            custom_force = openmm.CustomNonbondedForce(energy_expression)
            custom_force.setNonbondedMethod(
                reference_force.getNonbondedMethod())  # TODO: Fix me
            custom_force.setCutoffDistance(reference_force.getCutoffDistance())
            custom_force.setSwitchingDistance(
                reference_force.getSwitchingDistance())
            custom_force.setUseSwitchingFunction(
                reference_force.getUseSwitchingFunction())
            custom_force.addGlobalParameter("locality", locality)
            custom_force.addPerParticleParameter("charge")  # charge product
            custom_force.addPerParticleParameter(
                "sigma")  # Lennard-Jones sigma
            custom_force.addPerParticleParameter(
                "epsilon")  # Lennard-Jones epsilon
            custom_force.addPerParticleParameter("resid")  # residue index
            system.addForce(custom_force)

            custom_force.setCutoffDistance(cutoff)
            custom_force.setNonbondedMethod(custom_force.CutoffNonPeriodic)
            custom_force.setUseSwitchingFunction(True)
            custom_force.setSwitchingDistance(
                custom_force.getCutoffDistance() - switch_width)

            # Create CustomBondForce
            energy_expression = "E_LJ + E_Coulomb;"
            energy_expression += "E_LJ = 4*epsilon*((sigma/r)^12 - (sigma/r)^6);"
            energy_expression += "E_Coulomb = 138.935456*chargeprod/r;"
            custom_bond_force = openmm.CustomBondForce(energy_expression)
            custom_bond_force.addPerBondParameter(
                "chargeprod")  # charge product
            custom_bond_force.addPerBondParameter(
                "sigma")  # Lennard-Jones sigma
            custom_bond_force.addPerBondParameter(
                "epsilon")  # Lennard-Jones epsilon
            system.addForce(custom_bond_force)

            # Add exceptions
            for index in range(reference_force.getNumExceptions()):
                [atom1_index, atom2_index, chargeprod, sigma,
                 epsilon] = reference_force.getExceptionParameters(index)
                custom_force.addExclusion(atom1_index, atom2_index)
                custom_bond_force.addBond(atom1_index, atom2_index,
                                          [chargeprod, sigma, epsilon])

            # Add terms.
            for atom in topology.atoms():
                [charge, sigma,
                 epsilon] = reference_force.getParticleParameters(atom.index)
                custom_force.addParticle(
                    [charge, sigma, epsilon, atom.residue.index])

        elif force_name == 'GBSAOBCForce':
            #forces_to_remove.append(force_index)
            reference_force.setNonbondedMethod(
                reference_force.CutoffNonPeriodic)
            reference_force.setCutoffDistance(cutoff)

    # Remove forces scheduled for removal.
    print("Removing forces:")
    print(forces_to_remove)
    for force_index in forces_to_remove[::-1]:
        system.removeForce(force_index)

    return system
Example #26
0
def main(args):
  #Get the structure and topology files from the command line
  #ParmEd accepts a wide range of file types (Amber, GROMACS, CHARMM, OpenMM... but not LAMMPS) 
  try:
    topFile = args[0]
    strucFile = args[1]
  except IndexError:
    print("Specify topology and structure files from the command line.")
    sys.exit(2)
  
  print("Using topology file: %s" % topFile)
  print("Using structure file: %s" % strucFile)
  
  print("\nSetting up system...")
  
  #Load in the files for initial simulations
  top = pmd.load_file(topFile)
  struc = pmd.load_file(strucFile)
  
  #Transfer unit cell information to topology object
  top.box = struc.box[:]
  
  #Set up some global features to use in all simulations
  temperature = 298.15*u.kelvin
  
  #Define the platform (i.e. hardware and drivers) to use for running the simulation
  #This can be CUDA, OpenCL, CPU, or Reference 
  #CUDA is for NVIDIA GPUs
  #OpenCL is for CPUs or GPUs, but must be used for old CPUs (not SSE4.1 compatible)
  #CPU only allows single precision (CUDA and OpenCL allow single, mixed, or double)
  #Reference is a clear, stable reference for other code development and is very slow, using double precision by default
  platform = mm.Platform.getPlatformByName('CUDA')
  prop = {#'Threads': '2', #number of threads for CPU - all definitions must be strings (I think)
          'Precision': 'mixed', #for CUDA or OpenCL, select the precision (single, mixed, or double)
          'DeviceIndex': '0', #selects which GPUs to use - set this to zero if using CUDA_VISIBLE_DEVICES
          'DeterministicForces': 'True' #Makes sure forces with CUDA and PME are deterministic
         }
  
  #Create the OpenMM system that can be used as a reference
  systemRef = top.createSystem(
                               nonbondedMethod=app.PME, #Uses PME for long-range electrostatics, simple cut-off for LJ
                               nonbondedCutoff=12.0*u.angstroms, #Defines cut-off for non-bonded interactions
                               rigidWater=True, #Use rigid water molecules
                               constraints=app.HBonds, #Constrains all bonds involving hydrogens
                               flexibleConstraints=False, #Whether to include energies for constrained DOFs
                               removeCMMotion=True, #Whether or not to remove COM motion (don't want to if part of system frozen)
  )

  #Set up the integrator to use as a reference
  integratorRef = mm.LangevinIntegrator(
                                        temperature, #Temperature for Langevin
                                        1.0/u.picoseconds, #Friction coefficient
                                        2.0*u.femtoseconds, #Integration timestep
  )
  integratorRef.setConstraintTolerance(1.0E-08)

  #Get solute atoms and solute heavy atoms separately
  soluteIndices = []
  for res in top.residues:
    if res.name not in ['OTM', 'CTM', 'STM', 'NTM', 'SOL']:
      for atom in res.atoms:
        soluteIndices.append(atom.idx)

  #And define lambda states of interest
  lambdaVec = np.array(#electrostatic lambda - 1.0 is fully interacting, 0.0 is non-interacting
                       [[1.00, 0.75, 0.50, 0.25, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00], 
                       #LJ lambdas - 1.0 is fully interacting, 0.0 is non-interacting
                        [1.00, 1.00, 1.00, 1.00, 1.00, 0.90, 0.80, 0.70, 0.60, 0.50, 0.40, 0.35, 0.30, 0.25, 0.20, 0.15, 0.10, 0.05, 0.00] 
                       ])

  #JUST for boric acid, add a custom bonded force
  #Couldn't find a nice, compatible force field, but did find A forcefield, so using it
  #But has no angle terms on O-B-O and instead a weird bond repulsion term
  #This term also prevents out of plane bending
  #Simple in our case because boric acid is symmetric, so only need one parameter
  #Parameters come from Otkidach and Pletnev, 2001
  #Here, Ad = (A^2) / (d^6) since Ai and Aj and di and dj are all the same
  #In the original paper, B-OH bond had A = 1.72 and d = 0.354
  #Note that d is dimensionless and A should have units of (Angstrom^3)*(kcal/mol)^(1/2)
  #These units are inferred just to make things work out with kcal/mol and the given distance dependence
  bondRepulsionFunction = 'Ad*(1.0/r)^6'
  BondRepulsionForce = mm.CustomBondForce(bondRepulsionFunction)
  BondRepulsionForce.addPerBondParameter('Ad') #Units are technically kJ/mol * nm^6
  baOxInds = []
  for aind in soluteIndices:
    if top.atoms[aind].type == 'oh':
      baOxInds.append(aind)
  for i in range(len(baOxInds)):
    for j in range(i+1, len(baOxInds)):
      BondRepulsionForce.addBond(baOxInds[i], baOxInds[j], [0.006289686]) 

  systemRef.addForce(BondRepulsionForce)

  #We need to add a custom non-bonded force for the solute being alchemically changed
  #Will be helpful to have handle on non-bonded force handling LJ and coulombic interactions
  NBForce = None
  for frc in systemRef.getForces():
    if (isinstance(frc, mm.NonbondedForce)):
      NBForce = frc

  #Turn off dispersion correction since have interface
  NBForce.setUseDispersionCorrection(False)

  forceLabelsRef = getForceLabels(systemRef)

  decompEnergy(systemRef, struc.positions, labels=forceLabelsRef)

  #Separate out alchemical and regular particles using set objects
  alchemicalParticles = set(soluteIndices)
  chemicalParticles = set(range(systemRef.getNumParticles())) - alchemicalParticles

  #Define the soft-core function for turning on/off LJ interactions
  #In energy expressions for CustomNonbondedForce, r is a special variable and refers to the distance between particles
  #All other variables must be defined somewhere in the function.
  #The exception are variables like sigma1 and sigma2.
  #It is understood that a parameter will be added called 'sigma' and that the '1' and '2' are to specify the combining rule.
  softCoreFunction = '4.0*lambdaLJ*epsilon*x*(x-1.0); x = (1.0/reff_sterics);'
  softCoreFunction += 'reff_sterics = (0.5*(1.0-lambdaLJ) + ((r/sigma)^6));'
  softCoreFunction += 'sigma=0.5*(sigma1+sigma2); epsilon = sqrt(epsilon1*epsilon2)'
  #Define the system force for this function and its parameters
  SoftCoreForce = mm.CustomNonbondedForce(softCoreFunction)
  SoftCoreForce.addGlobalParameter('lambdaLJ', 1.0) #Throughout, should follow convention that lambdaLJ=1.0 is fully-interacting state
  SoftCoreForce.addPerParticleParameter('sigma')
  SoftCoreForce.addPerParticleParameter('epsilon')

  #Will turn off electrostatics completely in the original non-bonded force
  #In the end-state, only want electrostatics inside the alchemical molecule
  #To do this, just turn ON a custom force as we turn OFF electrostatics in the original force
  ONE_4PI_EPS0 = 138.935456 #in kJ/mol nm/e^2
  soluteCoulFunction = '(1.0-(lambdaQ^2))*ONE_4PI_EPS0*charge/r;'
  soluteCoulFunction += 'ONE_4PI_EPS0 = %.16e;' % (ONE_4PI_EPS0)
  soluteCoulFunction += 'charge = charge1*charge2'
  SoluteCoulForce = mm.CustomNonbondedForce(soluteCoulFunction)
  #Note this lambdaQ will be different than for soft core (it's also named differently, which is CRITICAL)
  #This lambdaQ corresponds to the lambda that scales the charges to zero
  #To turn on this custom force at the same rate, need to multiply by (1.0-lambdaQ**2), which we do
  SoluteCoulForce.addGlobalParameter('lambdaQ', 1.0) 
  SoluteCoulForce.addPerParticleParameter('charge')

  #Also create custom force for intramolecular alchemical LJ interactions
  #Could include with electrostatics, but nice to break up
  #We could also do this with a separate NonbondedForce object, but it would be a little more work, actually
  soluteLJFunction = '4.0*epsilon*x*(x-1.0); x = (sigma/r)^6;'
  soluteLJFunction += 'sigma=0.5*(sigma1+sigma2); epsilon=sqrt(epsilon1*epsilon2)'
  SoluteLJForce = mm.CustomNonbondedForce(soluteLJFunction)
  SoluteLJForce.addPerParticleParameter('sigma')
  SoluteLJForce.addPerParticleParameter('epsilon')
  
  #Loop over all particles and add to custom forces
  #As we go, will also collect full charges on the solute particles
  #AND we will set up the solute-solute interaction forces
  alchemicalCharges = [[0]]*len(soluteIndices)
  for ind in range(systemRef.getNumParticles()):
    #Get current parameters in non-bonded force
    [charge, sigma, epsilon] = NBForce.getParticleParameters(ind)
    #Make sure that sigma is not set to zero! Fine for some ways of writing LJ energy, but NOT OK for soft-core!
    if sigma/u.nanometer == 0.0:
      newsigma = 0.3*u.nanometer #This 0.3 is what's used by GROMACS as a default value for sc-sigma
    else:
      newsigma = sigma
    #Add the particle to the soft-core force (do for ALL particles)
    SoftCoreForce.addParticle([newsigma, epsilon])
    #Also add the particle to the solute only forces
    SoluteCoulForce.addParticle([charge])
    SoluteLJForce.addParticle([sigma, epsilon])
    #If the particle is in the alchemical molecule, need to set it's LJ interactions to zero in original force
    if ind in soluteIndices:
      NBForce.setParticleParameters(ind, charge, sigma, epsilon*0.0)
      #And keep track of full charge so we can scale it right by lambda
      alchemicalCharges[soluteIndices.index(ind)] = charge

  #Now we need to handle exceptions carefully
  for ind in range(NBForce.getNumExceptions()):
    [p1, p2, excCharge, excSig, excEps] = NBForce.getExceptionParameters(ind)
    #For consistency, must add exclusions where we have exceptions for custom forces
    SoftCoreForce.addExclusion(p1, p2)
    SoluteCoulForce.addExclusion(p1, p2)
    SoluteLJForce.addExclusion(p1, p2)

  #Only compute interactions between the alchemical and other particles for the soft-core force
  SoftCoreForce.addInteractionGroup(alchemicalParticles, chemicalParticles)

  #And only compute alchemical/alchemical interactions for other custom forces
  SoluteCoulForce.addInteractionGroup(alchemicalParticles, alchemicalParticles)
  SoluteLJForce.addInteractionGroup(alchemicalParticles, alchemicalParticles)

  #Set other soft-core parameters as needed
  SoftCoreForce.setCutoffDistance(12.0*u.angstroms)
  SoftCoreForce.setNonbondedMethod(mm.CustomNonbondedForce.CutoffPeriodic)
  SoftCoreForce.setUseLongRangeCorrection(False) 
  systemRef.addForce(SoftCoreForce)

  #Set other parameters as needed - note that for the solute force would like to set no cutoff
  #However, OpenMM won't allow a bunch of potentials with cutoffs then one without...
  #So as long as the solute is smaller than the cut-off, won't have any problems!
  SoluteCoulForce.setCutoffDistance(12.0*u.angstroms)
  SoluteCoulForce.setNonbondedMethod(mm.CustomNonbondedForce.CutoffPeriodic)
  SoluteCoulForce.setUseLongRangeCorrection(False) 
  systemRef.addForce(SoluteCoulForce)

  SoluteLJForce.setCutoffDistance(12.0*u.angstroms)
  SoluteLJForce.setNonbondedMethod(mm.CustomNonbondedForce.CutoffPeriodic)
  SoluteLJForce.setUseLongRangeCorrection(False) 
  systemRef.addForce(SoluteLJForce)

  #For the TRAPPE model of octanol, need to constrain ALL bonds, not just hydrogens
  #So loop through bonds and if it's associated with an octonal atom that's not a hydrogen, add constraint
  octHeavyInds = []
  for res in top.residues:
    if res.name == 'SOL':
      for atom in res.atoms:
        if 'H' not in atom.name[0]:
          octHeavyInds.append(atom.idx)

  print("With only hydrogens constrained have %i constraints."%systemRef.getNumConstraints())

  bondForce = None
  for frc in systemRef.getForces():
    if (isinstance(frc, mm.HarmonicBondForce)):
      bondForce = frc
  for k in range(bondForce.getNumBonds()):
    p1, p2, dist, kspring = bondForce.getBondParameters(k)
    if (p1 in octHeavyInds) and (p2 in octHeavyInds):
      systemRef.addConstraint(p1, p2, dist)
      #Turn off the bond potential energy if adding the constraint
      bondForce.setBondParameters(k, p1, p2, dist, 0.0) 

  print("With ALL octanol bonds constrained have %i constraints."%systemRef.getNumConstraints())

  forceLabelsRef = getForceLabels(systemRef)

  decompEnergy(systemRef, struc.positions, labels=forceLabelsRef)

  #Do NVT simulation
  stateFileNVT, stateNVT = doSimNVT(top, systemRef, integratorRef, platform, prop, temperature, pos=struc.positions)

  #And do NPT simulation using state information from NVT
  stateFileNPT, stateNPT = doSimNPT(top, systemRef, integratorRef, platform, prop, temperature, inBulk=True, state=stateFileNVT)

  #And do production run in expanded ensemble!
  stateFileProd, stateProd, weightsVec = doSimExpanded(top, systemRef, integratorRef, platform, prop, temperature, 0, lambdaVec, soluteIndices, alchemicalCharges, inBulk=True, state=stateFileNPT, nSteps=6000000)
Example #27
0
def heteropolymer_SSW(
    sim_object,
    interactionMatrix,
    monomerTypes,
    extraHardParticlesIdxs,
    repulsionEnergy=3.0,  # base repulsion energy for **all** particles
    repulsionRadius=1.0,
    attractionEnergy=3.0,  # base attraction energy for **all** particles
    attractionRadius=1.5,
    selectiveRepulsionEnergy=20.0,  # **extra** repulsive energy for **extraHard** particles
    selectiveAttractionEnergy=1.0,  # **extra** attraction energy that is multiplied by interactionMatrix
    keepVanishingInteractions=False,
    name="heteropolymer_SSW",
):
    """
    A version of smooth square well potential that enables the simulation of
    heteropolymers. Every monomer is assigned a number determining its type,
    then one can specify additional attraction between the types with the
    interactionMatrix. Repulsion between all monomers is the same, except for 
    extraHardParticles, which, if specified, have higher repulsion energy. 
    
    The overall potential is the same as in :py:func:`polychrom.forces.smooth_square_well`
    
    Treatment of extraHard particles is the same as in :py:func:`polychrom.forces.selective_SSW`

    This is an extension of SSW (smooth square well) force in which:
    
    a) You can give monomerTypes (e.g. 0, 1, 2 for A, B, C)
       and interaction strengths between these types. The corresponding entry in
       interactionMatrix is multiplied by selectiveAttractionEnergy to give the actual
       **additional** depth of the potential well.        
    b) You can select a subset of particles and make them "extra hard". See selective_SSW force for descrition. 
    
    Force summary
    *************
    
    Potential is the same as smooth square well, with the following parameters for particles i and j: 
    
    * Attraction energy (i,j) = attractionEnergy + selectiveAttractionEnergy * interactionMatrix[i,j] 

    * Repulsion Energy (i,j) = repulsionEnergy + selectiveRepulsionEnergy;  if (i) or (j) are extraHard
    * Repulsion Energy (i,j) = repulsionEnergy;  otherwise 

    Parameters
    ----------

    interactionMatrix: np.array
        the **EXTRA** interaction strenghts between the different types.
        Only upper triangular values are used. See "Force summary" above 
    monomerTypes: list of int or np.array
        the type of each monomer, starting at 0
    extraHardParticlesIdxs : list of int
        the list of indices of the "extra hard" particles. The extra hard
        particles repel all other particles with extra
        `selectiveRepulsionEnergy`
    repulsionEnergy: float
        the heigth of the repulsive part of the potential.
        E(0) = `repulsionEnergy`
    repulsionRadius: float
        the radius of the repulsive part of the potential.
        E(`repulsionRadius`) = 0,
        E'(`repulsionRadius`) = 0
    attractionEnergy: float
        the depth of the attractive part of the potential.
        E(`repulsionRadius`/2 + `attractionRadius`/2) = `attractionEnergy`
    attractionRadius: float
        the maximal range of the attractive part of the potential.
    selectiveRepulsionEnergy: float
        the **EXTRA** repulsion energy applied to the "extra hard" particles
    selectiveAttractionEnergy: float
        the **EXTRA** attraction energy (prefactor for the interactionMatrix interactions)
    keepVanishingInteractions : bool
        a flag that determines whether the terms that have zero interaction are
        still added to the force. This can be useful when changing the force
        dynamically (i.e. switching interactions on at some point)
    """

    # Check type info for consistency
    Ntypes = max(monomerTypes) + 1  # IDs should be zero based
    if any(np.less(interactionMatrix.shape, [Ntypes, Ntypes])):
        raise ValueError("Need interactions for {0:d} types!".format(Ntypes))
    if not np.allclose(interactionMatrix.T, interactionMatrix):
        raise ValueError("Interaction matrix should be symmetric!")

    indexpairs = []
    for i in range(0, Ntypes):
        for j in range(0, Ntypes):
            if (not interactionMatrix[i, j] == 0) or keepVanishingInteractions:
                indexpairs.append((i, j))

    energy = (
        "step(REPsigma - r) * Erep + step(r - REPsigma) * Eattr;"
        ""
        "Erep = rsc12 * (rsc2 - 1.0) * REPeTot / emin12 + REPeTot;"  # + ESlide;"
        "REPeTot = REPe + (ExtraHard1 + ExtraHard2) * REPeAdd;"
        "rsc12 = rsc4 * rsc4 * rsc4;"
        "rsc4 = rsc2 * rsc2;"
        "rsc2 = rsc * rsc;"
        "rsc = r / REPsigma * rmin12;"
        ""
        "Eattr = - rshft12 * (rshft2 - 1.0) * ATTReTot / emin12 - ATTReTot;"
        "ATTReTot = ATTRe"
    )
    if len(indexpairs) > 0:
        energy += (
            " + ATTReAdd*(delta(type1-{0:d})*delta(type2-{1:d})" "*INT_{0:d}_{1:d}"
        ).format(indexpairs[0][0], indexpairs[0][1])
        for i, j in indexpairs[1:]:
            energy += "+delta(type1-{0:d})*delta(type2-{1:d})*INT_{0:d}_{1:d}".format(
                i, j
            )
        energy += ")"
    energy += (
        ";"
        "rshft12 = rshft4 * rshft4 * rshft4;"
        "rshft4 = rshft2 * rshft2;"
        "rshft2 = rshft * rshft;"
        "rshft = (r - REPsigma - ATTRdelta) / ATTRdelta * rmin12;"
        ""
    )

    if selectiveRepulsionEnergy == float("inf"):
        energy += "REPeAdd = 4 * ((REPsigma / (2.0^(1.0/6.0)) / r)^12 - (REPsigma / (2.0^(1.0/6.0)) / r)^6) + 1;"

    force = openmm.CustomNonbondedForce(energy)
    force.name = name

    force.setCutoffDistance(attractionRadius * sim_object.conlen)

    force.addGlobalParameter("REPe", repulsionEnergy * sim_object.kT)
    if selectiveRepulsionEnergy != float("inf"):
        force.addGlobalParameter("REPeAdd", selectiveRepulsionEnergy * sim_object.kT)
    force.addGlobalParameter("REPsigma", repulsionRadius * sim_object.conlen)

    force.addGlobalParameter("ATTRe", attractionEnergy * sim_object.kT)
    force.addGlobalParameter("ATTReAdd", selectiveAttractionEnergy * sim_object.kT)
    force.addGlobalParameter(
        "ATTRdelta", sim_object.conlen * (attractionRadius - repulsionRadius) / 2.0
    )

    # Coefficients for x^12*(x*x-1)
    force.addGlobalParameter("emin12", 46656.0 / 823543.0)
    force.addGlobalParameter("rmin12", np.sqrt(6.0 / 7.0))

    for i, j in indexpairs:
        force.addGlobalParameter(
            "INT_{0:d}_{1:d}".format(i, j), interactionMatrix[i, j]
        )

    force.addPerParticleParameter("type")
    force.addPerParticleParameter("ExtraHard")

    for i in range(sim_object.N):
        force.addParticle((float(monomerTypes[i]), float(i in extraHardParticlesIdxs)))

    return force
Example #28
0
def selective_SSW(
    sim_object,
    stickyParticlesIdxs,
    extraHardParticlesIdxs,
    repulsionEnergy=3.0,  # base repulsion energy for **all** particles
    repulsionRadius=1.0,
    attractionEnergy=3.0,  # base attraction energy for **all** particles
    attractionRadius=1.5,
    selectiveRepulsionEnergy=20.0,  # **extra** repulsive energy for **extraHard** particles
    selectiveAttractionEnergy=1.0,  # **extra** attractive energy for **sticky** particles
    name="selective_SSW",
):
    """
    This is a simple and fast polynomial force that looks like a smoothed
    version of the square-well potential. The energy equals `repulsionEnergy`
    around r=0, stays flat until 0.6-0.7, then drops to zero together
    with its first derivative at r=1.0. After that it drop down to
    `attractionEnergy` and gets back to zero at r=`attractionRadius`.

    The energy function is based on polynomials of 12th power. Both the
    function and its first derivative is continuous everywhere within its
    domain and they both get to zero at the boundary.

    This is a tunable version of SSW:
    a) You can specify the set of "sticky" particles. The sticky particles
    are attracted only to other sticky particles.
    b) You can **smultaneously** select a subset of particles and make them "extra hard".
    

    This force was used two-ways. First was to make a small subset of particles very sticky. 
    In that case, it is advantageous to make the sticky particles and their neighbours
    "extra hard" and thus prevent the system from collapsing.

    Another useage is to induce phase separation by making all B monomers sticky. In that case, 
    extraHard particles may not be needed at all, because the system would not collapse on iteslf. 

    Parameters
    ----------

    stickyParticlesIdxs: list of int
        the list of indices of the "sticky" particles. The sticky particles
        are attracted to each other with extra `selectiveAttractionEnergy`
    extraHardParticlesIdxs : list of int
        the list of indices of the "extra hard" particles. The extra hard
        particles repel all other particles with extra
        `selectiveRepulsionEnergy`
    repulsionEnergy: float
        the heigth of the repulsive part of the potential.
        E(0) = `repulsionEnergy`
    repulsionRadius: float
        the radius of the repulsive part of the potential.
        E(`repulsionRadius`) = 0,
        E'(`repulsionRadius`) = 0
    attractionEnergy: float
        the depth of the attractive part of the potential.
        E(`repulsionRadius`/2 + `attractionRadius`/2) = `attractionEnergy`
    attractionRadius: float
        the maximal range of the attractive part of the potential.
    selectiveRepulsionEnergy: float
        the **EXTRA** repulsion energy applied to the **extra hard** particles
    selectiveAttractionEnergy: float
        the **EXTRA** attraction energy applied to the **sticky** particles
    """

    energy = (
        "step(REPsigma - r) * Erep + step(r - REPsigma) * Eattr;"
        ""
        "Erep = rsc12 * (rsc2 - 1.0) * REPeTot / emin12 + REPeTot;"  # + ESlide;"
        "REPeTot = REPe + (ExtraHard1 + ExtraHard2) * REPeAdd;"
        "rsc12 = rsc4 * rsc4 * rsc4;"
        "rsc4 = rsc2 * rsc2;"
        "rsc2 = rsc * rsc;"
        "rsc = r / REPsigma * rmin12;"
        ""
        "Eattr = - rshft12 * (rshft2 - 1.0) * ATTReTot / emin12 - ATTReTot;"
        "ATTReTot = ATTRe + min(Sticky1, Sticky2) * ATTReAdd;"
        "rshft12 = rshft4 * rshft4 * rshft4;"
        "rshft4 = rshft2 * rshft2;"
        "rshft2 = rshft * rshft;"
        "rshft = (r - REPsigma - ATTRdelta) / ATTRdelta * rmin12;"
        ""
    )

    if selectiveRepulsionEnergy == float("inf"):
        energy += "REPeAdd = 4 * ((REPsigma / (2.0^(1.0/6.0)) / r)^12 - (REPsigma / (2.0^(1.0/6.0)) / r)^6) + 1;"

    force = openmm.CustomNonbondedForce(energy)
    force.name = name

    force.setCutoffDistance(attractionRadius * sim_object.conlen)

    force.addGlobalParameter("REPe", repulsionEnergy * sim_object.kT)
    if selectiveRepulsionEnergy != float("inf"):
        force.addGlobalParameter("REPeAdd", selectiveRepulsionEnergy * sim_object.kT)
    force.addGlobalParameter("REPsigma", repulsionRadius * sim_object.conlen)

    force.addGlobalParameter("ATTRe", attractionEnergy * sim_object.kT)
    force.addGlobalParameter("ATTReAdd", selectiveAttractionEnergy * sim_object.kT)
    force.addGlobalParameter(
        "ATTRdelta", sim_object.conlen * (attractionRadius - repulsionRadius) / 2.0
    )

    # Coefficients for x^12*(x*x-1)
    force.addGlobalParameter("emin12", 46656.0 / 823543.0)
    force.addGlobalParameter("rmin12", np.sqrt(6.0 / 7.0))

    force.addPerParticleParameter("Sticky")
    force.addPerParticleParameter("ExtraHard")

    counts = np.bincount(stickyParticlesIdxs, minlength=sim_object.N)

    for i in range(sim_object.N):
        force.addParticle((float(counts[i]), float(i in extraHardParticlesIdxs)))

    return force
Example #29
0
def smooth_square_well(
    sim_object,
    repulsionEnergy=3.0,
    repulsionRadius=1.0,
    attractionEnergy=0.5,
    attractionRadius=2.0,
    name="smooth_square_well",
):
    """
    This is a simple and fast polynomial force that looks like a smoothed
    version of the square-well potential. The energy equals `repulsionEnergy`
    around r=0, stays flat until 0.6-0.7, then drops to zero together
    with its first derivative at r=1.0. After that it drop down to
    `attractionEnergy` and gets back to zero at r=`attractionRadius`.

    The energy function is based on polynomials of 12th power. Both the
    function and its first derivative is continuous everywhere within its
    domain and they both get to zero at the boundary.

    Parameters
    ----------

    repulsionEnergy: float
        the heigth of the repulsive part of the potential.
        E(0) = `repulsionEnergy`
    repulsionRadius: float
        the radius of the repulsive part of the potential.
        E(`repulsionRadius`) = 0,
        E'(`repulsionRadius`) = 0
    attractionEnergy: float
        the depth of the attractive part of the potential.
        E(`repulsionRadius`/2 + `attractionRadius`/2) = `attractionEnergy`
    attractionRadius: float
        the radius of the attractive part of the potential.
        E(`attractionRadius`) = 0,
        E'(`attractionRadius`) = 0
    """
    nbCutOffDist = sim_object.conlen * attractionRadius
    energy = (
        "step(REPsigma - r) * Erep + step(r - REPsigma) * Eattr;"
        ""
        "Erep = rsc12 * (rsc2 - 1.0) * REPe / emin12 + REPe;"
        "rsc12 = rsc4 * rsc4 * rsc4;"
        "rsc4 = rsc2 * rsc2;"
        "rsc2 = rsc * rsc;"
        "rsc = r / REPsigma * rmin12;"
        ""
        "Eattr = - rshft12 * (rshft2 - 1.0) * ATTRe / emin12 - ATTRe;"
        "rshft12 = rshft4 * rshft4 * rshft4;"
        "rshft4 = rshft2 * rshft2;"
        "rshft2 = rshft * rshft;"
        "rshft = (r - REPsigma - ATTRdelta) / ATTRdelta * rmin12"
    )

    force = openmm.CustomNonbondedForce(energy)
    force.name = name

    force.addGlobalParameter("REPe", repulsionEnergy * sim_object.kT)
    force.addGlobalParameter("REPsigma", repulsionRadius * sim_object.conlen)

    force.addGlobalParameter("ATTRe", attractionEnergy * sim_object.kT)
    force.addGlobalParameter(
        "ATTRdelta", sim_object.conlen * (attractionRadius - repulsionRadius) / 2.0
    )
    # Coefficients for the minimum of x^12*(x*x-1)
    force.addGlobalParameter("emin12", 46656.0 / 823543.0)
    force.addGlobalParameter("rmin12", np.sqrt(6.0 / 7.0))

    for _ in range(sim_object.N):
        force.addParticle(())

    force.setCutoffDistance(nbCutOffDist)

    return force
Example #30
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