예제 #1
0
def _separate_urey_bradleys(system, topology):
    """ Separate urey bradley bonds from harmonic bonds in OpenMM System

    Parameters
    ---------
    topology : openmm.app.Topology
        Molecular structure to find atom types of
    system : openmm System

    """
    atoms = [a for a in topology.atoms()]
    bonds = [b for b in topology.bonds()]
    ub_force = mm.HarmonicBondForce()
    harmonic_bond_force = mm.HarmonicBondForce()
    for force_idx, force in enumerate(system.getForces()):
        if isinstance(force, mm.HarmonicBondForce):
            for bond_idx in range(force.getNumBonds()):
                if ((atoms[force.getBondParameters(bond_idx)[0]],
                    atoms[force.getBondParameters(bond_idx)[1]]) not in bonds and
                    (atoms[force.getBondParameters(bond_idx)[1]],
                     atoms[force.getBondParameters(bond_idx)[0]]) not in bonds):
                        ub_force.addBond(*force.getBondParameters(bond_idx))
                else:
                    harmonic_bond_force.addBond(
                        *force.getBondParameters(bond_idx))
            system.removeForce(force_idx)

    system.addForce(harmonic_bond_force)
    system.addForce(ub_force)
예제 #2
0
def add_harmonic_restraints(system: mm.System, args: ListOfArgs):
    """Restraints format is different here than in SM webservice.
    Example record: :10 :151\n
    or
    :10 :151 0.1 30000\n
    :i :j distance energy
    """
    print("      Adding harmonic restraints")
    if args.HR_USE_FLAT_BOTTOM_FORCE:
        contact_force = mm.CustomBondForce('step(r-r0) * (k/2) * (r-r0)^2')
        contact_force.addPerBondParameter('r0')
        contact_force.addPerBondParameter('k')
    else:
        contact_force = mm.HarmonicBondForce()
    system.addForce(contact_force)

    with open(args.HR_RESTRAINTS_PATH) as input_file:
        counter = 0
        for line in input_file:
            columns = line.split()
            atom_index_i = int(columns[0][1:]) - 1
            atom_index_j = int(columns[1][1:]) - 1
            try:
                r0 = float(columns[2])
                k = float(columns[3]) * args.HR_K_SCALE
            except IndexError:
                r0 = args.HR_R0_PARAM
                k = args.HR_K_PARAM
            if args.HR_USE_FLAT_BOTTOM_FORCE:
                contact_force.addBond(atom_index_i, atom_index_j, [r0, k])
            else:
                contact_force.addBond(atom_index_i, atom_index_j, r0, k)
            counter += 1
    print(f"         {counter} restraints added.")
예제 #3
0
    def _addBondsToSystem(self, sys):
        """Create the harmonic bonds
        """
        bonds = mm.HarmonicBondForce()
        sys.addForce(bonds)

        q = """SELECT p0, p1, r0, fc, constrained
        FROM stretch_harm_term INNER JOIN stretch_harm_param
        ON stretch_harm_term.param=stretch_harm_param.id"""
        for (fcounter, conn, tables, offset) in self._localVars():
            for p0, p1, r0, fc, constrained in conn.execute(q):
                p0 += offset
                p1 += offset
                if constrained:
                    sys.addConstraint(p0, p1, r0 * angstrom)
                else:
                    # Desmond writes the harmonic bond force without 1/2
                    # so we need to to double the force constant
                    bonds.addBond(p0, p1, r0 * angstrom,
                                  2 * fc * kilocalorie_per_mole / angstrom**2)

                # Record information that will be needed for constraining angles.
                self._atomBonds[p0][p1] = r0 * angstrom
                self._atomBonds[p1][p0] = r0 * angstrom

        return bonds
예제 #4
0
    def test_setting(self, ethane_system_topology):
        harmonic_bond = openmm.HarmonicBondForce()
        harmonic_bond.addBond(0, 1, 10, 20)
        my_ommp = Ommperator(ethane_system_topology[0], ethane_system_topology[1])
        my_bond_ommp = HarmonicBondForceOmmperator(my_ommp, harmonic_bond, 0)
        my_bond_ommp.particle1 = 5
        my_bond_ommp.particle2 = 6
        my_bond_ommp.k = 100
        my_bond_ommp.length = 200

        assert my_bond_ommp.particle1 == harmonic_bond.getBondParameters(0)[0]
        assert my_bond_ommp.particle1 == 5
        assert my_bond_ommp.particle2 == harmonic_bond.getBondParameters(0)[1]
        assert my_bond_ommp.particle2 == 6
        assert my_bond_ommp.length == harmonic_bond.getBondParameters(0)[2]
        assert my_bond_ommp.k == harmonic_bond.getBondParameters(0)[3]

        my_bond_ommp.set_params(p1=100, p2=200,
                            k=300, length=400)

        assert my_bond_ommp.particle1 == harmonic_bond.getBondParameters(0)[0]
        assert my_bond_ommp.particle1 == 100
        assert my_bond_ommp.particle2 == harmonic_bond.getBondParameters(0)[1]
        assert my_bond_ommp.particle2 == 200
        assert my_bond_ommp.length == harmonic_bond.getBondParameters(0)[2]
        assert my_bond_ommp.k == harmonic_bond.getBondParameters(0)[3]
예제 #5
0
    def _create_empty_system(cutoff):
        """Creates an empty system object with stub forces.

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

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

        system = openmm.System()

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

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

        system.addForce(nonbonded_force)

        return system
예제 #6
0
    def distance_restraint_force(self, atoms, distances, strengths):
        """
        Parameters
        ----------
        atoms : tuple of tuple of int or str
            Pair of atom indices to be restrained, with shape (n, 2),
            like ((a1, a2), (a3, a4)). Items can be str compatible with MDTraj DSL.
        distances : tuple of float
            Equilibrium distances for each pair
        strengths : tuple of float
            Force constant for each pair
        """
        system = self.system
        force = mm.HarmonicBondForce()
        force.setUsesPeriodicBoundaryConditions(self.system.usesPeriodicBoundaryConditions())
        for pair, distance, strength in zip(atoms, distances, strengths):
            indices = []
            for atom in pair:
                if isinstance(atom, str):
                    index = self.subset(atom)
                    if len(index) != 1:
                        raise ValueError('Distance restraint for selection `{}` returns != 1 atom!: {}'
                                         .format(atom, index))
                    indices.append(int(index[0]))
                elif isinstance(atom, (int, float)):
                    indices.append(int(atom))
                else:
                    raise ValueError('Distance restraint atoms must be int or str DSL selections')
            if distance == 'current':
                pos = self.positions or system.positions
                distance = np.linalg.norm(pos[indices[0]] - pos[indices[1]])

            force.addBond(indices[0], indices[1], distance*u.nanometers,
                          strength*u.kilocalories_per_mole/u.angstroms**2)
        return force
예제 #7
0
def _process_bond_forces(openff_sys, openmm_sys):
    """Process the Bonds section of an OpenFF System into a corresponding openmm.HarmonicBondForce"""
    harmonic_bond_force = openmm.HarmonicBondForce()
    openmm_sys.addForce(harmonic_bond_force)

    try:
        bond_handler = openff_sys.handlers["Bonds"]
    except KeyError:
        return

    try:
        constraint_handler = openff_sys.handlers["Constraints"]
        has_constraint_handler = True
    except KeyError:
        has_constraint_handler = False

    for top_key, pot_key in bond_handler.slot_map.items():
        if has_constraint_handler:
            # If this bond show up in the constraints ...
            if top_key in constraint_handler.slot_map:
                # ... don't add it as an interacting bond
                continue
        indices = top_key.atom_indices
        params = bond_handler.potentials[pot_key].parameters
        k = params["k"].to(off_unit.Unit(
            str(kcal_ang))).magnitude * kcal_ang / kj_nm
        length = params["length"].to(off_unit.nanometer).magnitude

        harmonic_bond_force.addBond(
            particle1=indices[0],
            particle2=indices[1],
            length=length,
            k=k,
        )
def addDummyAtomToSystem(system, topology, positions, resname, dummies,
                         worker):
    protein_CAs = []
    for atom in topology.atoms():
        if atom.residue.name not in ("HOH", "Cl-", "Na+",
                                     resname) and atom.name == "CA":
            protein_CAs.append(atom.index)
    modul = len(protein_CAs) % 20
    step_to_use = int((len(protein_CAs) - modul) / 20)
    if modul == 0:
        modul = None
    else:
        modul = modul * -1
    protein_CAs = protein_CAs[:modul:step_to_use]
    if worker == 0:
        utilities.print_unbuffered(
            "Added bond between dummy atom and protein atoms", protein_CAs)
    for dummy in dummies:
        system.setParticleMass(dummy, 0.0)
        for protein_particle in protein_CAs:
            distance_constraint = np.linalg.norm(
                positions[dummy].value_in_unit(unit.nanometers) -
                positions[protein_particle].value_in_unit(unit.nanometers))
            force_dummy = mm.HarmonicBondForce()
            constraint_force = 10 * 4.184 * 2  # express the contraint_force in kJ/mol/nm^2
            force_dummy.addBond(dummy, protein_particle, distance_constraint,
                                constraint_force)
            system.addForce(force_dummy)
        for forces in system.getForces():
            if isinstance(forces, mm.NonbondedForce):
                forces.setParticleParameters(dummy, 0.0, 1.0, 0.0)
예제 #9
0
    def test_parsing(self, ethane_system_topology):
        harmonic_bond = openmm.HarmonicBondForce()
        harmonic_bond.addBond(0, 1, 10, 20)
        my_ommp = Ommperator(ethane_system_topology[0], ethane_system_topology[1])
        my_bond_ommp = HarmonicBondForceOmmperator(my_ommp, harmonic_bond, 0)

        assert my_bond_ommp.particle1 == harmonic_bond.getBondParameters(0)[0]
        assert my_bond_ommp.particle2 == harmonic_bond.getBondParameters(0)[1]
        assert my_bond_ommp.length == harmonic_bond.getBondParameters(0)[2]
        assert my_bond_ommp.k == harmonic_bond.getBondParameters(0)[3]
예제 #10
0
    def apply(self, configuration, seed_kind='all'):
        """Converts a yaff configuration into an OpenMM seed

        Begins with a call to check_compatibility, and an assertion error is
        raised if the configuration is not compatible.
        The system object in the yaff seed is used to create a corresponding
        openmm system object, after which apply_generators_to_system is called.

        Parameters
        ----------

        configuration : openyaff.Configuration
            configuration for which to check compatibility

        platform : str
            OpenMM platform for which to perform the conversion because
            some conversion options (such as the exclusion policy) are platform
            dependent.

        seed_kind : str
            specifies the kind of seed to be converted

        """
        # raise AssertionError if not compatible
        self.check_compatibility(configuration)
        policy, dispersion_scale_index = self.determine_exclusion_policy(
            configuration)
        logger.debug('exclusion policy: ' + policy)
        logger.debug(
            'dispersion scale index: {}'.format(dispersion_scale_index))
        yaff_seed = configuration.create_seed(kind=seed_kind)
        logger.debug('creating OpenMM System object')
        system_mm = create_openmm_system(yaff_seed.system)
        dummy = mm.HarmonicBondForce()
        periodic = configuration.box is not None
        dummy.setUsesPeriodicBoundaryConditions(periodic)
        system_mm.addForce(dummy)  # add empty periodic force
        kwargs = {}

        # if system is periodic and contains electrostatis; compute PME params
        if (configuration.ewald_alphascale is not None
                and seed_kind in ['all', 'electrostatic', 'nonbonded']):
            alpha = configuration.ewald_alphascale
            delta = np.exp(-(alpha)**2) / 2
            if delta > self.pme_error_thres:
                kwargs['delta'] = delta
            else:
                kwargs['delta'] = self.pme_error_thres
        # add exclusion policy to kwargs
        kwargs['exclusion_policy'] = policy
        kwargs['dispersion_scale_index'] = dispersion_scale_index

        apply_generators_to_system(yaff_seed, system_mm, **kwargs)
        openmm_seed = OpenMMSeed(system_mm)
        return openmm_seed
예제 #11
0
def add_harmonic_bond(system: mm.System, args: ListOfArgs):
    print("      Adding harmonic bonds...")
    print(f"         r0 = {args.POL_HARMONIC_BOND_R0}")
    print(f"         k = {args.POL_HARMONIC_BOND_K} kJ/mol/nm^2")
    bond_force = mm.HarmonicBondForce()
    system.addForce(bond_force)
    counter = 0
    for i in range(system.getNumParticles() - 1):
        bond_force.addBond(i, i + 1, args.POL_HARMONIC_BOND_R0, args.POL_HARMONIC_BOND_K)
        counter += 1
    print(f"         {counter} harmonic bonds added.")
예제 #12
0
def harmonic_bonds(
    sim_object,
    bonds,
    bondWiggleDistance=0.05,
    bondLength=1.0,
    name="harmonic_bonds",
    override_checks=False,
):
    """Adds harmonic bonds

    Parameters
    ----------
    
    bonds : iterable of (int, int)
        Pairs of particle indices to be connected with a bond.
    bondWiggleDistance : float or iterable of float
        Average displacement from the equilibrium bond distance.
        Can be provided per-particle.
        If 0 then set k=0.
    bondLength : float or iterable of float
        The length of the bond.
        Can be provided per-particle.
    override_checks: bool
        If True then do not check that no bonds are repeated.
        False by default.
    """

    # check for repeated bonds
    if not override_checks:
        _check_bonds(bonds, sim_object.N)

    force = openmm.HarmonicBondForce()
    force.name = name

    bondLength = _to_array_1d(bondLength, len(bonds)) * sim_object.length_scale
    bondWiggleDistance = (_to_array_1d(bondWiggleDistance, len(bonds)) *
                          sim_object.length_scale)

    # using kbondScalingFactor because force accepts parameters with units
    kbond = sim_object.kbondScalingFactor / (bondWiggleDistance**2)
    kbond[bondWiggleDistance == 0] = 0

    for bond_idx, (i, j) in enumerate(bonds):
        if (i >= sim_object.N) or (j >= sim_object.N):
            raise ValueError("\nCannot add bond with monomers %d,%d that"
                             "are beyound the polymer length %d" %
                             (i, j, sim_object.N))

        force.addBond(int(i), int(j), float(bondLength[bond_idx]),
                      float(kbond[bond_idx]))

    return force
예제 #13
0
def _process_bond_forces(openff_sys, openmm_sys):
    """Process the Bonds section of an OpenFF System into a corresponding openmm.HarmonicBondForce"""
    harmonic_bond_force = openmm.HarmonicBondForce()
    openmm_sys.addForce(harmonic_bond_force)

    bond_handler = openff_sys.handlers["Bonds"]
    for bond, key in bond_handler.slot_map.items():
        indices = eval(bond)
        params = bond_handler.potentials[key].parameters
        k = params["k"].to(off_unit.Unit(
            str(kcal_ang))).magnitude * kcal_ang / kj_nm
        length = params["length"].to(off_unit.nanometer).magnitude

        harmonic_bond_force.addBond(
            particle1=indices[0],
            particle2=indices[1],
            length=length,
            k=k,
        )
예제 #14
0
def test_harmonic_bond_kernel():
    kernel = HarmonicBondKernel(
        top.positions,
        [bond.id_atoms for bond in top.bonds],
        [[0.1 + 0.01 * i, 50 + 5 * i] for i in range(top.n_bond)],
    )
    r, energy, forces = kernel.evaluate()
    print()
    print(r)
    print(energy)
    print(sum(energy))
    print(forces)

    force = mm.HarmonicBondForce()
    force.setForceGroup(1)
    for i, bond in enumerate(top.bonds):
        force.addBond(*bond.id_atoms, 0.1 + 0.01 * i, 100 + 10 * i)
    e, f = calc_energy_with_omm(force, top.positions)

    assert pytest.approx(sum(energy), rel=1E-6) == e
    assert pytest.approx(forces, rel=1E-6) == f
예제 #15
0
    def _addBondsToSystem(self, syst, moleculeType, bondedTypes, constraints,
                          baseAtomIndex):

        atomBonds = [{} for x in range(len(moleculeType.atoms))]
        for fields in moleculeType.bonds:
            atoms = [int(x) - 1 for x in fields[:2]]
            types = tuple(bondedTypes[i] for i in atoms)
            if len(fields) >= 5:
                params = fields[3:5]
            elif types in self.bondTypes:
                params = self.bondTypes[types][3:5]
            elif types[::-1] in self.bondTypes:
                params = self.bondTypes[types[::-1]][3:5]
            else:
                raise ValueError('No parameters specified for bond: ' +
                                 fields[0] + ', ' + fields[1])
            # Decide whether to use a constraint or a bond.
            useConstraint = False
            if constraints is AllBonds:
                useConstraint = True
            # Add the bond or constraint.
            length = float(params[0])
            if useConstraint:
                syst.addConstraint(baseAtomIndex + atoms[0],
                                   baseAtomIndex + atoms[1], length)
            else:
                bonds = mm.HarmonicBondForce()
                syst.addForce(bonds)
                bonds.addBond(baseAtomIndex + atoms[0],
                              baseAtomIndex + atoms[1], length,
                              float(params[1]))

            # Record information that will be needed for constraining
            # angles.
            atomBonds[atoms[0]][atoms[1]] = length
            atomBonds[atoms[1]][atoms[0]] = length
        return atomBonds
예제 #16
0
    def export(system):
        '''
        Generate OpenMM system from a system

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

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

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

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

        top = system.topology
        ff = system.ff

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        return omm_system
예제 #17
0
    def __init__(self, nx=3, ny=3, nz=3,
                 K=100.0, b=2.0, mass=39.948 * unit.amu, well_radius=1.0,
                 grid_spacing=1.0 * unit.angstrom,
                 coupling_strength=100.0 * unit.kilocalories_per_mole / unit.angstrom,
                 **kwargs):
        """Initialize particles on a 3D grid of specified size.

        Parameters
        ----------
        nx, ny, nz : ints
            number of particles in x, y, z directions, respectively
        K : float
            well depth
        b : float
            exponent
        mass : simtk.unit.Quantity
            particle mass
        grid_spacing : simtk.unit.Quantity
            increment between grid points
        coupling_strength : simtk.unit.quantity
            strength of HarmonicBond force between each neighboring pair of particles

        Attributes
        ----------
        topology, positions, system
        """

        # 1. Initialize
        # 2. Set particles on a 3D grid, use a CustomExternalForce to place each particle in a well
        # 3. Add a HarmonicBondForce to each neighboring pair of particles.


        ### 1. Initialize

        TestSystem.__init__(self, **kwargs)
        # Set unit of well-depth appropriately
        #K *= unit.kilocalories_per_mole / unit.angstroms ** b

        # Determine total number of atoms.
        natoms = nx * ny * nz

        # Create an empty system object.
        system = openmm.System()

        ### 2. Put particles on a 3D grid

        positions = unit.Quantity(np.zeros([natoms, 3], np.float32), unit.angstrom)

        atom_index = 0

        # Store the atom indices in a 3-way array
        # so that we can conveniently determine nearest neighbors
        atom_indices = np.zeros((nx, ny, nz), dtype=int)
        energy_expression = "{K} * (sqrt((x - x0)^2 + (y - y0)^2 + (z - z0)^2) / {r})^{b};".format(
            b=b, K=K, r=well_radius)
        force = openmm.CustomExternalForce(energy_expression)
        force.addPerParticleParameter("x0")
        force.addPerParticleParameter("y0")
        force.addPerParticleParameter("z0")

        for i in range(nx):
            for j in range(ny):
                for k in range(nz):
                    system.addParticle(mass)

                    xyz = (grid_spacing.value_in_unit(unit.angstrom)) * np.array([i, j, k], dtype=float)
                    positions[atom_index] = (xyz + np.random.randn(3)*0.01) * unit.angstrom

                    atom_indices[i, j, k] = atom_index

                    # Add this particle's well
                    force.addParticle(atom_index, xyz)

                    atom_index += 1
        system.addForce(force)

        ### 3. Couple each pair of neighbors

        # Find each pair of neighbors in this grid.
        # Connect each particle (i,j,k), to its "forward" neighbors,
        #   (i+1, j, k), (i, j+1, k), (i, j, k+1),
        # (if they exist)
        bonds = []
        for i in range(nx):
            for j in range(ny):
                for k in range(nz):
                    if (i + 1) < nx:
                        bonds.append((atom_indices[i, j, k], atom_indices[i + 1, j, k]))
                    if (j + 1) < ny:
                        bonds.append((atom_indices[i, j, k], atom_indices[i, j + 1, k]))
                    if (k + 1) < nz:
                        bonds.append((atom_indices[i, j, k], atom_indices[i, j, k + 1]))

        # Add these HarmonicBondForces to the system
        force = openmm.HarmonicBondForce()
        for bond in bonds:
            force.addBond(int(bond[0]), int(bond[1]), grid_spacing, coupling_strength)
        system.addForce(force)

        # Create topology.
        topology = app.Topology()
        element = app.Element.getBySymbol('Ar')
        chain = topology.addChain()
        for particle in range(system.getNumParticles()):
            residue = topology.addResidue('Ar', chain)
            topology.addAtom('Ar', element, residue)

        # Set topology, positions, system as instance attributes
        self.topology = topology
        self.positions = positions
        self.system = system
예제 #18
0
    def create_system(self, topology, **kwargs):
        """
        Create a System object with simple parameters from the provided Topology

        Any kwargs are ignored.

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

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

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

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

        # Create a System
        system = openmm.System()

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

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

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

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

        # Add angles
        uniqueAngles = set()
        for bond in topology.bonds():
            for atom in bondedToAtom[bond.atom1]:
                if atom != bond.atom2:
                    if atom < bond.atom2:
                        uniqueAngles.add((atom, bond.atom1, bond.atom2))
                    else:
                        uniqueAngles.add((bond.atom2, bond.atom1, atom))
            for atom in bondedToAtom[bond.atom2]:
                if atom != bond.atom1:
                    if atom > bond.atom1:
                        uniqueAngles.add((bond.atom1, bond.atom2, atom))
                    else:
                        uniqueAngles.add((atom, bond.atom2, bond.atom1))
        angles = sorted(list(uniqueAngles))
        theta0 = 109.5 * unit.degrees  # TODO: Adapt based on number of bonds to each atom?
        sigma_theta = 10 * unit.degrees
        Ktheta = kT / sigma_theta**2
        angle_force = openmm.HarmonicAngleForce()
        for (atom1, atom2, atom3) in angles:
            angles.addAngle(atom1.index, atom2.index, atom3.index, theta0,
                            Ktheta)
        system.addForce(angle_force)

        # Make a list of all unique proper torsions
        uniquePropers = set()
        for angle in angles:
            for atom in bondedToAtom[angle[0]]:
                if atom not in angle:
                    if atom < angle[2]:
                        uniquePropers.add((atom, angle[0], angle[1], angle[2]))
                    else:
                        uniquePropers.add((angle[2], angle[1], angle[0], atom))
            for atom in bondedToAtom[angle[2]]:
                if atom not in angle:
                    if atom > angle[0]:
                        uniquePropers.add((angle[0], angle[1], angle[2], atom))
                    else:
                        uniquePropers.add((atom, angle[2], angle[1], angle[0]))
        propers = sorted(list(uniquePropers))
        torsion_force = openmm.PeriodicTorsionForce()
        periodicity = 3
        phase = 0.0 * unit.degrees
        Kphi = 0.0 * kT
        for (atom1, atom2, atom3, atom4) in propers:
            torsion_force.add_torsion(atom1.index, atom2.index, atom3.index,
                                      atom4.index, periodicity, phase, Kphi)
        system.addForce(torsion_force)

        return system
예제 #19
0
    system.addConstraint(i, i + 1, 0.1 * u.nanometer)

# Pinning ends with rubber
pin_force = mm.CustomExternalForce("k*((x-x0)^2+(y-y0)^2+(z-z0)^2)")
pin_force.addGlobalParameter("k",
                             50 * u.kilocalories_per_mole / u.angstroms**2)
pin_force.addPerParticleParameter("x0")
pin_force.addPerParticleParameter("y0")
pin_force.addPerParticleParameter("z0")
pin_force.addParticle(0, [15 * u.angstrom, 0 * u.angstrom, 0 * u.angstrom])
pin_force.addParticle(system.getNumParticles() - 1,
                      [-15 * u.angstrom, 0 * u.angstrom, 0 * u.angstrom])
system.addForce(pin_force)

# Loop extrusion force
le_force = mm.HarmonicBondForce()
le_force.addBond(
    48, 50, 1 * u.angstrom,
    LE_FORCE_MATRIX[2][0] * u.kilocalories_per_mole / u.angstroms**2)
for i in range(2, 35):
    p1, p2 = 49 - i, 49 + i
    le_force.addBond(
        p1, p2, 1 * u.angstrom,
        LE_FORCE_MATRIX[1][0] * u.kilocalories_per_mole / u.angstroms**2)
system.addForce(le_force)

simulation = Simulation(pdb.topology, system, integrator)
simulation.context.setPositions(pdb.positions)
simulation.minimizeEnergy()
simulation.reporters.append(DCDReporter('trj.dcd', 1))
simulation.reporters.append(
예제 #20
0
def build_custom_system(reference_system,
                        receptor_atoms,
                        ligand_atoms,
                        valence_lambda,
                        coulomb_lambda,
                        vdw_lambda,
                        annihilate=False):
    """
    Build alchemically-modified system where ligand is decoupled or annihilated using Custom*Force classes.
    
    """

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

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

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

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

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

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

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

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

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

    return system
def loop_extrusion(STEPS, LE_FORCE_SCALE, MATRIX_LENGTH, STEPS_PER_CYCLE, STEPS_PER_IT):
    STATE_FNAME = '2sided-state.csv'
    #STEPS = 10000
    #LE_FORCE_SCALE = 3
    #MATRIX_LENGTH = 200
    #STEPS_PER_CYCLE = 10
    #STEPS_PER_IT = 1

#Macierz z parametrami sił wiązań
#Dodano funkcje generacji macierzy o wartościach sinusoidalnych. Funkcja ta przyjmuje dwa argumenty. Pierwszy oznacza liczbę kroków które ma posiadać macierz a drugi
#stanowi regulacje maksymalnej siły (tzn jeśli wstawimy 3 to maksymalna siła bedzie tyle wynosić)
    LE_FORCE_MATRIX = gen_sin_array(MATRIX_LENGTH,LE_FORCE_SCALE)
    LE_FORCE_MATRIX[1][0] = 0
    LE_FORCE_MATRIX[2][-1] = 0
#print(LE_FORCE_MATRIX)

    pdb = PDBFile('initial_structure.pdb')
    forcefield = ForceField('polymer_ff.xml')
    system = forcefield.createSystem(pdb.topology, nonbondedCutoff=1 * u.nanometer)
    integrator = mm.LangevinIntegrator(100 * u.kelvin, 0.2, 1 * u.femtoseconds)

# Distance constraint
    for i in range(system.getNumParticles() - 1):
        system.addConstraint(i, i + 1, 0.1 * u.nanometer)

# Pinning ends with rubber
    pin_force = mm.CustomExternalForce("k*((x-x0)^2+(y-y0)^2+(z-z0)^2)")
    pin_force.addGlobalParameter("k", 50 * u.kilocalories_per_mole / u.angstroms ** 2)
    pin_force.addPerParticleParameter("x0")
    pin_force.addPerParticleParameter("y0")
    pin_force.addPerParticleParameter("z0")
    pin_force.addParticle(0, [15 * u.angstrom, 0 * u.angstrom, 0 * u.angstrom])
    pin_force.addParticle(system.getNumParticles() - 1, [-15 * u.angstrom, 0 * u.angstrom, 0 * u.angstrom])
    system.addForce(pin_force)


# Loop extrusion force
    le_force = mm.HarmonicBondForce()
    le_force.addBond(48, 50, 1 * u.angstrom, LE_FORCE_SCALE * u.kilocalories_per_mole / u.angstroms ** 2)
    for i in range(2, 35):
        p1, p2 = 49 - i, 49 + i
        le_force.addBond(p1, p2, 1 * u.angstrom, 0.000001 * u.kilocalories_per_mole / u.angstroms ** 2)
    system.addForce(le_force)
    

    simulation = Simulation(pdb.topology, system, integrator)
    simulation.context.setPositions(pdb.positions)
    simulation.minimizeEnergy()
    simulation.reporters.append(DCDReporter('wyniki/2sided-trj.dcd', 1))
    simulation.reporters.append(StateDataReporter(stdout, 1000, step=True, potentialEnergy=True, temperature=True))
    simulation.reporters.append(StateDataReporter(STATE_FNAME, 10, step=True, potentialEnergy=True))

    simulation.step(1)

    for i in range(2, 35):
        p1, p2 = 49 - i, 49 + i
        for j in range(MATRIX_LENGTH):
            le_force_one = LE_FORCE_MATRIX[1][j] * u.kilocalories_per_mole / u.angstroms ** 2 #ROSNĄCA
            le_force_two = LE_FORCE_MATRIX[2][j] * u.kilocalories_per_mole / u.angstroms ** 2 #MALEJĄCA
            le_force.setBondParameters(i - 2, p1 + 1, p2 - 1, 1 * u.angstrom,
                                    le_force_two)
            le_force.setBondParameters(i - 1, p1, p2, 1 * u.angstrom, le_force_one)
            le_force.updateParametersInContext(simulation.context)
        #print(le_force_one)
        #print(le_force_two)
        #simulation.minimizeEnergy()
            simulation.step(STEPS_PER_IT)
#    for i in range(STEPS_PER_CYCLE):
#        simulation.step(1)
        simulation.step(200)
        plot_data(STATE_FNAME, '2sided-energy.png')

    print('#1: repr stick; color white; color red :1,100; repr sphere :1,100; vdwdefine 0.5')
    print('#1: color green :49,51; repr sphere :49,51; color #ffffa2e8a2e8 :50;')
    for i in range(1, 35):
        p1, p2 = 50 - i - 1, 50 + i + 1
        print(
            f'#{i*STEPS_PER_CYCLE+1}: color green :{p1},{p2}; repr sphere :{p1},{p2}; repr stick :{p1+1},{p2-1}; color #ffffa2e8a2e8 :{p1+1}-{p2-1};')

    print("Done")
예제 #22
0
def add_force(cgmodel, force_type=None):
    """

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

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

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

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

        :Example:

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

        """
    if force_type == "Bond":

        bond_force = mm.HarmonicBondForce()
        bond_list = []

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

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

        cgmodel.system.addForce(bond_force)
        force = bond_force

    if force_type == "Nonbonded":

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

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

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

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

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

    return (cgmodel, force)
예제 #23
0
def build_alchemically_modified_system(reference_system,
                                       receptor_atoms,
                                       ligand_atoms,
                                       annihilate=True):
    """
    Build alchemically-modified system where ligand is decoupled or annihilated.
    
    """

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

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

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

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

    return system
예제 #24
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
예제 #25
0
def harmonic_bonds(
    sim_object,
    bonds,
    bondWiggleDistance=0.05,
    bondLength=1.0,
    name="harmonic_bonds",
    override_checks=False,
):
    """Adds harmonic bonds.
    
    Bonds are parametrized in the following way. 
    
    * A length of a bond at rest is `bondLength`
    * Bond energy equal to 1kT at bondWiggleDistance 
    
    Note that bondWiggleDistance is not the standard deviation of the bond extension:
    that is actually smaller by a factor of sqrt(2). 
    

    Parameters
    ----------
    
    bonds : iterable of (int, int)
        Pairs of particle indices to be connected with a bond.
    bondWiggleDistance : float or iterable of float
        Distance at which bond energy equals kT. 
        Can be provided per-particle.
        If 0 then set k=0.
    bondLength : float or iterable of float
        The length of the bond.
        Can be provided per-particle.
    override_checks: bool
        If True then do not check that no bonds are repeated.
        False by default.
    """

    # check for repeated bonds
    if not override_checks:
        _check_bonds(bonds, sim_object.N)

    force = openmm.HarmonicBondForce()
    force.name = name

    bondLength = _to_array_1d(bondLength, len(bonds)) * sim_object.length_scale
    bondWiggleDistance = (
        _to_array_1d(bondWiggleDistance, len(bonds)) * sim_object.length_scale
    )

    # using kbondScalingFactor because force accepts parameters with units
    kbond = sim_object.kbondScalingFactor / (bondWiggleDistance ** 2)
    kbond[bondWiggleDistance == 0] = 0

    for bond_idx, (i, j) in enumerate(bonds):
        if (i >= sim_object.N) or (j >= sim_object.N):
            raise ValueError(
                "\nCannot add bond with monomers %d,%d that"
                "are beyound the polymer length %d" % (i, j, sim_object.N)
            )

        force.addBond(
            int(i), int(j), float(bondLength[bond_idx]), float(kbond[bond_idx])
        )

    return force
예제 #26
0
def add_force(cgmodel, force_type=None, rosetta_functional_form=False):
    """

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

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

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

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

    :Example:

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

    """
    if force_type == "Bond":

        bond_force = mm.HarmonicBondForce()
        bond_list = []

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

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

        cgmodel.system.addForce(bond_force)
        force = bond_force

    if force_type == "Nonbonded":

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

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

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

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

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

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

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

        else:
            nonbonded_force = mm.NonbondedForce()

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

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

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

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

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

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

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

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

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

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

        cgmodel.system.addForce(torsion_force)

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

    return (cgmodel, force)
예제 #27
0
    def apply(self, configuration, seed_kind='all'):
        """Generates force field XML tree"""
        scalings = self.check_compatibility(configuration)

        # initialize XML object
        forcefield = ET.Element('ForceField')
        sys = configuration.system
        sys.set_standard_masses()

        # add atom types
        atom_types_xml = ET.Element('AtomTypes')
        for i in range(len(sys.ffatypes)):
            index = np.nonzero(sys.ffatype_ids == i)[0][0]
            atom_type = sys.ffatypes[i]
            mass = sys.masses[index] / molmod.units.amu
            element = molmod.periodic.periodic[sys.numbers[index]].symbol
            attrib = {
                'name': atom_type,
                'class': atom_type,
                'element': element,
                'mass': str(mass),  # xml expects str, not float
            }
            atom_types_xml.append(ET.Element('Type', attrib=attrib))
        forcefield.append(atom_types_xml)

        # add template definitions
        residues_xml = ET.Element('Residues')
        for i, template in enumerate(configuration.templates):
            residue_xml = ET.Element('Residue', {'name': 'T' + str(i)})
            types = nx.get_node_attributes(template, 'atom_type')

            # generate atom names within template
            count = np.ones(118, dtype=np.int32)
            index_to_name = {}
            for node in template.nodes():
                if node < configuration.ext_node_count:
                    number = sys.numbers[node]
                    e = mm.app.Element.getByAtomicNumber(number)
                    atom_name = e.symbol + str(count[number])
                    index_to_name[node] = atom_name
                    count[number] += 1

            for node in template.nodes():
                if node < configuration.ext_node_count:
                    atom_name = index_to_name[node]
                    atom_type = types[node]
                    e = ET.Element('Atom', {
                        'name': atom_name,
                        'type': atom_type
                    })
                    residue_xml.append(e)
            for j, (atom0, atom1) in enumerate(template.edges()):
                is_external0 = (atom0 >= configuration.ext_node_count)
                is_external1 = (atom1 >= configuration.ext_node_count)
                if (not is_external0) and (not is_external1):
                    # this is a real bond
                    attrib = {
                        'atomName1': index_to_name[atom0],
                        'atomName2': index_to_name[atom1],
                    }
                    e = ET.Element('Bond', attrib=attrib)
                    residue_xml.append(e)
                elif (is_external0 != is_external1):
                    # this is an external bond
                    if is_external0:
                        atom = atom1
                    else:
                        atom = atom0
                    attrib = {
                        'atomName': index_to_name[atom],
                    }
                    e = ET.Element('ExternalBond', attrib=attrib)
                    residue_xml.append(e)
                else:
                    raise ValueError('Encountered a bond between two external'
                                     ' nodes: {} and {}'.format(atom0, atom1))

            residues_xml.append(residue_xml)
        forcefield.append(residues_xml)

        # get kwargs for apply_generators_to_xml from configuration
        if len(configuration.get_prefixes('nonbonded')) > 0:
            if configuration.box is not None:
                nonbondedMethod = mm.app.PME
                nonbondedCutoff = configuration.rcut
                if configuration.switch_width is not None:
                    useSwitchingFunction = True
                    switchingDistance = configuration.rcut - configuration.switch_width
                else:
                    useSwitchingFunction = False
                    switchingDistance = 0.0
                useLongRangeCorrection = configuration.tailcorrections
            else:
                nonbondedMethod = mm.app.NoCutoff
                nonbondedCutoff = 0.0
                switchingDistance = 0.0
                useSwitchingFunction = 0
                useLongRangeCorrection = 0
        else:  # set dummy values
            nonbondedMethod = mm.app.NoCutoff
            nonbondedCutoff = 0.0
            useSwitchingFunction = False
            switchingDistance = 0.0
            useLongRangeCorrection = False

        # generate yaff_seed and apply generators to the XML object
        yaff_seed = configuration.create_seed(kind=seed_kind)  # unnecessary?
        forces = apply_generators_to_xml(
            yaff_seed,
            forcefield,
            nonbondedMethod=nonbondedMethod,
            nonbondedCutoff=nonbondedCutoff,
            useSwitchingFunction=useSwitchingFunction,
            switchingDistance=switchingDistance,
            useLongRangeCorrection=useLongRangeCorrection,
            scalings=scalings,
        )
        for force in forces:
            forcefield.append(force)
        forcefield = ET.ElementTree(element=forcefield)  # convert to tree
        ET.indent(forcefield)
        with tempfile.NamedTemporaryFile(delete=False, mode='w') as tf:
            forcefield.write(tf, encoding='unicode')
        tf.close()
        #pars = ET.tostring(forcefield.getroot(), encoding='unicode')
        #print(pars)
        ff = mm.app.ForceField(tf.name)

        # create OpenMM system object
        topology, _ = configuration.create_topology()
        system = ff.createSystem(
            topology,
            nonbondedMethod=nonbondedMethod,
            nonbondedCutoff=nonbondedCutoff / 10,  # random value
            ignoreExternalBonds=False,
            removeCMMotion=False,
            switchDistance=switchingDistance / 10,
        )
        if configuration.box is not None:
            # dirty fixes!
            dummy = mm.HarmonicBondForce()
            dummy.setUsesPeriodicBoundaryConditions(True)
            system.addForce(dummy)  # add empty force to define periodicity
            for force in system.getForces():
                try:
                    force.setUsesPeriodicBoundaryConditions(True)
                    logger.critical('force {} set to use PBCs'.format(force))
                except:
                    continue
        return OpenMMSeed(system, forcefield)
예제 #28
0
    temperature = 300 * units.kelvin
    kB = units.BOLTZMANN_CONSTANT_kB * units.AVOGADRO_CONSTANT_NA
    kT = kB * temperature
    beta = 1.0 / kT
    sigma = 3.816371 * units.angstroms
    r0 = 0.0 * units.angstroms
    K = 1.0 / (beta * sigma**2)
    print "sigma = %.3f A, r0 = %.3f A, K = %.16f kcal/mol/A**2" % (
        sigma / units.angstroms, r0 / units.angstroms, K /
        (units.kilocalories_per_mole / units.angstrom**2))
    if add_restraint:
        add_new = False
        if add_new:
            # Add new HarmonicBondForce
            print "Adding restraint as new HarmonicBondForce."
            force = openmm.HarmonicBondForce()
            force.addBond(iatom, jatom, r0, K)
            systems['complex']['standard'].addForce(force)
        else:
            # Add to existing HarmonicBondForce
            print "Appending restraint to existing HarmonicBondForce."
            for force_index in range(
                    systems['complex']['standard'].getNumForces()):
                force = systems['complex']['standard'].getForce(force_index)
                if isinstance(force, openmm.HarmonicBondForce):
                    # We found it!
                    force.addBond(iatom, jatom, r0, K)
                    break

    # Create softcore force type variants.
    for system_type in system_types:
예제 #29
0
    def createSystem(self, nonbondedMethod=ff.NoCutoff, nonbondedCutoff=1.0*unit.nanometer,
                     constraints=None, rigidWater=True, implicitSolvent=None, soluteDielectric=1.0, solventDielectric=78.5, ewaldErrorTolerance=0.0005, removeCMMotion=True, hydrogenMass=None):
        """Construct an OpenMM System representing the topology described by this
        prmtop file.

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

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

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

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

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

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

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

                # Record the types of all atoms.

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

                # Add atoms.

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

                # Add bonds.

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

                # Add angles.

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

                # Add torsions.

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

                # Add CMAP terms.

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

                # Set nonbonded parameters for particles.

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

                # Record nonbonded exceptions.

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


        # Create nonbonded exceptions.

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

        # Finish configuring the NonbondedForce.

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

        # Adjust masses.

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

        # Add a CMMotionRemover.

        if removeCMMotion:
            sys.addForce(mm.CMMotionRemover())
        return sys
예제 #30
0
def build_softcore_system(reference_system,
                          receptor_atoms,
                          ligand_atoms,
                          valence_lambda,
                          coulomb_lambda,
                          vdw_lambda,
                          annihilate=False):
    """
    Build alchemically-modified system where ligand is decoupled or annihilated using *SoftcoreForce classes.
    
    """

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

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

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

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

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

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

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

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

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

    return system