Ejemplo n.º 1
0
    def get_standard_state_correction(self):
        """
        Compute the standard state correction for the arbitrary restraint energy function.

        Returns
        -------
        DeltaG : float
           Computed standard-state correction in dimensionless units (kT)

        Notes
        -----
        Uses analytical approach from [1], but this approach is known to be inexact.

        """
        class Bunch(object):
            """Make a dict accessible via an object accessor"""
            def __init__(self, adict):
                self.__dict__.update(adict)

        # Retrieve constants for convenience.
        p = Bunch(self._parameters)
        kT = self.kT
        pi = np.pi

        # Eq 32 of Ref [1]
        DeltaG = -np.log( \
            (8. * pi**2 * V0) / (p.r_aA0**2 * unit.sin(p.theta_A0) * unit.sin(p.theta_B0)) \
            * unit.sqrt(p.K_r * p.K_thetaA * p.K_thetaB * p.K_phiA * p.K_phiB * p.K_phiC) / (2 * pi * kT)**3 \
            )
        # Return standard state correction (in kT).
        return DeltaG
Ejemplo n.º 2
0
 def testUnitMathModule(self):
     """ Tests the unit_math functions on Quantity objects """
     self.assertEqual(u.sqrt(1.0*u.kilogram*u.joule),
                      1.0*u.kilogram*u.meter/u.second)
     self.assertEqual(u.sqrt(1.0*u.kilogram*u.calorie),
                      math.sqrt(4.184)*u.kilogram*u.meter/u.second)
     self.assertEqual(u.sqrt(9), 3) # Test on a scalar
     self.assertEqual(u.sin(90*u.degrees), 1)
     self.assertEqual(u.sin(math.pi/2*u.radians), 1)
     self.assertEqual(u.sin(math.pi/2), 1)
     self.assertEqual(u.cos(180*u.degrees), -1)
     self.assertEqual(u.cos(math.pi*u.radians), -1)
     self.assertEqual(u.cos(math.pi), -1)
     self.assertAlmostEqual(u.tan(45*u.degrees), 1)
     self.assertAlmostEqual(u.tan(math.pi/4*u.radians), 1)
     self.assertAlmostEqual(u.tan(math.pi/4), 1)
     acos = u.acos(1.0)
     asin = u.asin(1.0)
     atan = u.atan(1.0)
     self.assertTrue(u.is_quantity(acos))
     self.assertTrue(u.is_quantity(asin))
     self.assertTrue(u.is_quantity(atan))
     self.assertEqual(acos.unit, u.radians)
     self.assertEqual(asin.unit, u.radians)
     self.assertEqual(atan.unit, u.radians)
     self.assertEqual(acos.value_in_unit(u.degrees), 0)
     self.assertEqual(acos / u.radians, 0)
     self.assertEqual(asin.value_in_unit(u.degrees), 90)
     self.assertEqual(asin / u.radians, math.pi/2)
     self.assertAlmostEqual(atan.value_in_unit(u.degrees), 45)
     self.assertAlmostEqual(atan / u.radians, math.pi/4)
     # Check some sequence maths
     seq = [1, 2, 3, 4] * u.meters
     self.assertEqual(u.sum(seq), 10*u.meters)
     self.assertEqual(u.dot(seq, seq), (1+4+9+16)*u.meters**2)
     self.assertEqual(u.norm(seq), math.sqrt(30)*u.meters)
Ejemplo n.º 3
0
    def createSystem(self, nonbondedMethod=ff.NoCutoff,
                     nonbondedCutoff=1.0*u.nanometer,
                     constraints=None,
                     rigidWater=True,
                     implicitSolvent=None,
                     implicitSolventKappa=None,
                     implicitSolventSaltConc=0.0*u.moles/u.liter,
                     temperature=298.15*u.kelvin,
                     soluteDielectric=1.0,
                     solventDielectric=78.5,
                     removeCMMotion=True,
                     hydrogenMass=None,
                     ewaldErrorTolerance=0.0005,
                     flexibleConstraints=True,
                     verbose=False):
        """
        Construct an OpenMM System representing the topology described by the
        prmtop file.

        Parameters:
         -  nonbondedMethod (object=NoCutoff) The method to use for nonbonded
               interactions. Allowed values are NoCutoff, CutoffNonPeriodic,
               CutoffPeriodic, Ewald, or PME.
         -  nonbondedCutoff (distance=1*nanometer) The cutoff distance to use
               for nonbonded interactions.
         -  constraints (object=None) Specifies which bonds or angles should be
               implemented with constraints. Allowed values are None, HBonds,
               AllBonds, or HAngles.
         -  rigidWater (boolean=True) If true, water molecules will be fully
               rigid regardless of the value passed for the constraints argument
         -  implicitSolvent (object=None) If not None, the implicit solvent
               model to use. Allowed values are HCT, OBC1, OBC2, or GBn
         -  implicitSolventKappa (float=None): Debye screening parameter to
               model salt concentrations in GB solvent.
         -  implicitSolventSaltConc (float=0.0*u.moles/u.liter): Salt
               concentration for GB simulations. Converted to Debye length
               `kappa'
         -  temperature (float=298.15*u.kelvin): Temperature used in the salt
               concentration-to-kappa conversion for GB salt concentration term
         -  soluteDielectric (float=1.0) The solute dielectric constant to use
               in the implicit solvent model.
         -  solventDielectric (float=78.5) The solvent dielectric constant to
               use in the implicit solvent model.
         -  removeCMMotion (boolean=True) If true, a CMMotionRemover will be
               added to the System.
         -  hydrogenMass (mass=None) The mass to use for hydrogen atoms bound to
               heavy atoms. Any mass added to a hydrogen is subtracted from the
               heavy atom to keep their total mass the same.
         -  ewaldErrorTolerance (float=0.0005) The error tolerance to use if the
               nonbonded method is Ewald or PME.
         -  flexibleConstraints (bool=True) Are our constraints flexible or not?
         -  verbose (bool=False) Optionally prints out a running progress report
        """
        # Rebuild the topology file if necessary, and flush the atom property
        # data to the atom list
        if self._topology_changed():
            self.remake_parm()
        else:
            self.atom_list.refresh_data()
        LJ_radius, LJ_depth = self.openmm_LJ() # Get our LJ parameters
        LJ_14_radius, LJ_14_depth = self.openmm_14_LJ()

        # Set the cutoff distance in nanometers
        cutoff = None
        if nonbondedMethod is not ff.NoCutoff:
            cutoff = nonbondedCutoff
            # Remove units from cutoff
            if u.is_quantity(cutoff):
                cutoff = cutoff.value_in_unit(u.nanometers)

        if nonbondedMethod not in (ff.NoCutoff, ff.CutoffNonPeriodic,
                                   ff.CutoffPeriodic, ff.Ewald, ff.PME):
            raise ValueError('Illegal value for nonbonded method')
        if self.ptr('ifbox') == 0 and nonbondedMethod in (ff.CutoffPeriodic,
                                                          ff.Ewald, ff.PME):
            raise ValueError('Illegal nonbonded method for a '
                             'non-periodic system')
        if implicitSolvent not in (HCT, OBC1, OBC2, GBn, GBn2, None):
            raise ValueError('Illegal implicit solvent model choice.')
        if not constraints in (None, ff.HAngles, ff.HBonds, ff.AllBonds):
            raise ValueError('Illegal constraints choice')
      
        # Define conversion factors
        length_conv = u.angstrom.conversion_factor_to(u.nanometer)
        _ambfrc = u.kilocalorie_per_mole/(u.angstrom*u.angstrom)
        _openmmfrc = u.kilojoule_per_mole/(u.nanometer*u.nanometer)
        bond_frc_conv = _ambfrc.conversion_factor_to(_openmmfrc)
        _ambfrc = u.kilocalorie_per_mole/(u.radians*u.radians)
        _openmmfrc = u.kilojoule_per_mole/(u.radians*u.radians)
        angle_frc_conv = _ambfrc.conversion_factor_to(_openmmfrc)
        dihe_frc_conv = u.kilocalorie_per_mole.conversion_factor_to(
                            u.kilojoule_per_mole)
        ene_conv = dihe_frc_conv
      
        # Create the system
        system = mm.System()
        if verbose: print('Adding particles...')
        for mass in self.parm_data['MASS']:
            system.addParticle(mass)
        # Set up the constraints
        if verbose and (constraints is not None and not rigidWater):
            print('Adding constraints...')
        if constraints in (ff.HBonds, ff.AllBonds, ff.HAngles):
            for bond in self.bonds_inc_h:
                system.addConstraint(bond.atom1.starting_index,
                                     bond.atom2.starting_index,
                                     bond.bond_type.req*length_conv)
        if constraints in (ff.AllBonds, ff.HAngles):
            for bond in self.bonds_without_h:
                system.addConstraint(bond.atom1.starting_index,
                                     bond.atom2.starting_index,
                                     bond.bond_type.req*length_conv)
        if rigidWater and constraints is None:
            for bond in self.bonds_inc_h:
                if (bond.atom1.residue.resname in WATNAMES and
                    bond.atom2.residue.resname in WATNAMES):
                    system.addConstraint(bond.atom1.starting_index,
                                         bond.atom2.starting_index,
                                         bond.bond_type.req*length_conv)
        # Add Bond forces
        if verbose: print('Adding bonds...')
        force = mm.HarmonicBondForce()
        force.setForceGroup(self.BOND_FORCE_GROUP)
        if flexibleConstraints or (constraints not in (ff.HBonds, ff.AllBonds,
                                                       ff.HAngles)):
            for bond in self.bonds_inc_h:
                force.addBond(bond.atom1.starting_index,
                              bond.atom2.starting_index,
                              bond.bond_type.req*length_conv,
                              2*bond.bond_type.k*bond_frc_conv)
        if flexibleConstraints or (constraints not in (ff.AllBonds,ff.HAngles)):
            for bond in self.bonds_without_h:
                force.addBond(bond.atom1.starting_index,
                              bond.atom2.starting_index,
                              bond.bond_type.req*length_conv,
                              2*bond.bond_type.k*bond_frc_conv)
        system.addForce(force)
        # Add Angle forces
        if verbose: print('Adding angles...')
        force = mm.HarmonicAngleForce()
        force.setForceGroup(self.ANGLE_FORCE_GROUP)
        if constraints is ff.HAngles:
            num_constrained_bonds = system.getNumConstraints()
            atom_constraints = [[]] * system.getNumParticles()
            for i in range(num_constrained_bonds):
                c = system.getConstraintParameters(i)
                dist = c[2].value_in_unit(u.nanometer)
                atom_constraints[c[0]].append((c[1], dist))
                atom_constraints[c[1]].append((c[0], dist))
        for angle in self.angles_inc_h:
            if constraints is ff.HAngles:
                a1 = angle.atom1.element
                a2 = angle.atom2.element
                a3 = angle.atom3.element
                nh = int(a1==1) + int(a2==1) + int(a3==1)
                constrained = (nh >= 2 or (nh == 1 and a2 == 8))
            else:
                constrained = False # no constraints
            if constrained:
                l1 = l2 = None
                for bond in angle.atom2.bonds:
                    if bond.atom1 is angle.atom1 or bond.atom2 is angle.atom1:
                        l1 = bond.bond_type.req * length_conv
                    elif bond.atom1 is angle.atom3 or bond.atom2 is angle.atom3:
                        l2 = bond.bond_type.req * length_conv
                # Compute the distance between the atoms and add a constraint
                length = sqrt(l1*l1 + l2*l2 - 2*l1*l2*
                              cos(angle.angle_type.theteq))
                system.addConstraint(bond.atom1.starting_index,
                                     bond.atom2.starting_index, length)
            if flexibleConstraints or not constrained:
                force.addAngle(angle.atom1.starting_index,
                               angle.atom2.starting_index,
                               angle.atom3.starting_index,
                               angle.angle_type.theteq,
                               2*angle.angle_type.k*angle_frc_conv)
        for angle in self.angles_without_h:
            force.addAngle(angle.atom1.starting_index,
                           angle.atom2.starting_index,
                           angle.atom3.starting_index,
                           angle.angle_type.theteq,
                           2*angle.angle_type.k*angle_frc_conv)
        system.addForce(force)
        # Add dihedral forces
        if verbose: print('Adding torsions...')
        force = mm.PeriodicTorsionForce()
        force.setForceGroup(self.DIHEDRAL_FORCE_GROUP)
        for tor in self.dihedrals_inc_h + self.dihedrals_without_h:
            force.addTorsion(tor.atom1.starting_index,
                             tor.atom2.starting_index,
                             tor.atom3.starting_index,
                             tor.atom4.starting_index,
                             int(tor.dihed_type.per),
                             tor.dihed_type.phase,
                             tor.dihed_type.phi_k*dihe_frc_conv)
        system.addForce(force)

        # Add nonbonded terms now
        if verbose: print('Adding nonbonded interactions...')
        force = mm.NonbondedForce()
        force.setForceGroup(self.NONBONDED_FORCE_GROUP)
        if self.ptr('ifbox') == 0: # non-periodic
            if nonbondedMethod is ff.NoCutoff:
                force.setNonbondedMethod(mm.NonbondedForce.NoCutoff)
            elif nonbondedMethod is ff.CutoffNonPeriodic:
                if cutoff is None:
                    raise ValueError('No cutoff value specified')
                force.setNonbondedMethod(mm.NonbondedForce.CutoffNonPeriodic)
                force.setCutoffDistance(cutoff)
            else:
                raise ValueError('Illegal nonbonded method for non-periodic '
                                 'system')
        else: # periodic
            # Set up box vectors (from inpcrd if available, or fall back to
            # prmtop definitions
            system.setDefaultPeriodicBoxVectors(*self.box_vectors)

            # Set cutoff
            if cutoff is None:
                # Compute cutoff automatically
                box = self.box_lengths
                min_box_width = min((box[0]/u.nanometers,
                                     box[1]/u.nanometers,
                                     box[2]/u.nanometers))
                CLEARANCE_FACTOR = 0.97
                cutoff = u.Quantity((min_box_width*CLEARANCE_FACTOR)/2.0,
                                    u.nanometers)
            if nonbondedMethod is not ff.NoCutoff:
                force.setCutoffDistance(cutoff)

            # Set nonbonded method.
            if nonbondedMethod is ff.NoCutoff:
                force.setNonbondedMethod(mm.NonbondedForce.NoCutoff)
            elif nonbondedMethod is ff.CutoffNonPeriodic:
                force.setNonbondedMethod(mm.NonbondedForce.CutoffNonPeriodic)
            elif nonbondedMethod is ff.CutoffPeriodic:
                force.setNonbondedMethod(mm.NonbondedForce.CutoffPeriodic)
            elif nonbondedMethod is ff.Ewald:
                force.setNonbondedMethod(mm.NonbondedForce.Ewald)
            elif nonbondedMethod is ff.PME:
                force.setNonbondedMethod(mm.NonbondedForce.PME)
            else:
                raise ValueError('Cutoff method is not understood')

            if ewaldErrorTolerance is not None:
                force.setEwaldErrorTolerance(ewaldErrorTolerance)

        # Add per-particle nonbonded parameters (LJ params)
        sigma_scale = 2**(-1/6) * 2
        for i, atm in enumerate(self.atom_list):
            force.addParticle(atm.charge,
                              sigma_scale*LJ_radius[atm.nb_idx-1]*length_conv,
                              LJ_depth[atm.nb_idx-1]*ene_conv)

        # Add 1-4 interactions
        excluded_atom_pairs = set() # save these pairs so we don't zero them out
        sigma_scale = 2**(-1/6)
        for tor in self.dihedrals_inc_h + self.dihedrals_without_h:
            if min(tor.signs) < 0: continue # multi-terms and impropers
            charge_prod = (tor.atom1.charge * tor.atom4.charge /
                           tor.dihed_type.scee)
            epsilon = (sqrt(LJ_14_depth[tor.atom1.nb_idx-1] * ene_conv *
                            LJ_14_depth[tor.atom4.nb_idx-1] * ene_conv) /
                            tor.dihed_type.scnb)
            sigma = (LJ_14_radius[tor.atom1.nb_idx-1] +
                     LJ_14_radius[tor.atom4.nb_idx-1])*length_conv*sigma_scale
            force.addException(tor.atom1.starting_index,
                               tor.atom4.starting_index,
                               charge_prod, sigma, epsilon)
            excluded_atom_pairs.add(
                    min( (tor.atom1.starting_index, tor.atom4.starting_index),
                         (tor.atom4.starting_index, tor.atom1.starting_index) )
            )

        # Add excluded atoms
        for atom in self.atom_list:
            # Exclude all bonds and angles
            for atom2 in atom.bond_partners:
                if atom2.starting_index > atom.starting_index:
                    force.addException(atom.starting_index,
                                       atom2.starting_index, 0.0, 0.1, 0.0)
            for atom2 in atom.angle_partners:
                if atom2.starting_index > atom.starting_index:
                    force.addException(atom.starting_index,
                                       atom2.starting_index, 0.0, 0.1, 0.0)
            for atom2 in atom.exclusion_partners:
                if atom2.starting_index > atom.starting_index:
                    force.addException(atom.starting_index,
                                       atom2.starting_index, 0.0, 0.1, 0.0)
            for atom2 in atom.dihedral_partners:
                if atom2.starting_index <= atom.starting_index: continue
                if ((atom.starting_index, atom2.starting_index) in
                    excluded_atom_pairs):
                    continue
                force.addException(atom.starting_index,
                                   atom2.starting_index, 0.0, 0.1, 0.0)
        system.addForce(force)

        # Add virtual sites for water
        # First tag the residues that have an extra point in them
        for res in self.residue_list: res.has_ep = False
        ep = [atom for atom in self.atom_list if atom.atname in EPNAMES]
        for atom in ep: atom.residue.has_ep = True
        if len(ep) > 0:
            numRes = ep[-1].residue.idx + 1
            waterO = [[] for i in range(numRes)]
            waterH = [[] for i in range(numRes)]
            waterEP = [[] for i in range(numRes)]
            for atom in self.atom_list:
                if atom.residue.has_ep:
                    if atom.element == 8:
                        waterO[res].append(atom)
                    elif atom.element == 1:
                        waterH[res].append(atom)
                    elif atom.element == 0:
                        waterEP[res].append(atom)
            # Record bond lengths for faster access
            distOH = [None] * numRes
            distHH = [None] * numRes
            distOE = [None] * numRes
            for bond in self.bonds_inc_h + self.bonds_without_h:
                a1 = bond.atom1
                a2 = bond.atom2
                if a1.residue.has_ep:
                    res = a1.residue.idx
                    if a1.element == 1 or a2.element == 1:
                        if a1.element == 1 and a2.element == 1:
                            distHH[res] = bond.bond_type.req * u.angstroms
                        if a1.element == 8 or a2.element == 8:
                            distOH[res] = bond.bond_type.req * u.angstroms
                    elif ((a1.element == 8 or a2.element == 8) and
                          (a1.element == 0 or a2.element == 0)):
                        distOE[res] = bond.bond_type.req * u.angstroms
            # Loop over residues and add the virtual points
            out_of_plane_angle = 54.735 * u.degrees
            cosOOP = u.cos(out_of_plane_angle)
            sinOOP = u.sin(out_of_plane_angle)
            for residue in self.residue_list:
                if not residue.has_ep: continue
                res = residue.idx
                if len(waterO[res]) == 1 and len(waterH[res]) == 2:
                    if len(waterEP[res]) == 1:
                        # 4-point water
                        weightH = distOE[res] / sqrt(distOH[res] * distOH[res] -
                                               0.25 * distHH[res] * distHH[res])
                        system.setVirtualSite(
                                waterEP[res][0],
                                mm.ThreeParticleAverageSite(waterO[res][0],
                                waterH[res][0], waterH[res][1],
                                1-weightH, weightH/2, weightH/2)
                        )
                elif len(waterEP[res]) == 2:
                    # 5-point water
                    weightH = (cosOOP * distOE[res] /
                               sqrt(distOH[res] * distOH[res] -
                                 0.25 * distHH[res] * distHH[res])
                    )
                    angleHOH = 2 * asin(0.5 * distHH[res] / distOH[res])
                    lenCross = distOH[res] * distOH[res] * sin(angleHOH)
                    weightCross = sinOOP * distOE[res] / lenCross
                    site1 = mm.OutOfPlaneSite(waterO[res][0], waterH[res][0],
                            waterH[res][1], weightH/2, weightH/2, weightCross)
                    site2 = mm.OutOfPlaneSite(waterO[res][0], waterH[res][0],
                            waterH[res][1], weightH/2, weightH/2, -weightCross)
                    system.setVirtualSite(waterEP[res][0], site1)
                    system.setVirtualSite(waterEP[res][1], site2)

        # Add GB model if we're doing one
        if implicitSolvent is not None:
            if verbose: print('Adding GB parameters...')
            gb_parms = self._get_gb_params(implicitSolvent)

            # If implicitSolventKappa is None, compute it from salt
            # concentration
            if implicitSolventKappa is None:
                if u.is_quantity(implicitSolventSaltConc):
                    sc = implicitSolventSaltConc.value_in_unit(u.moles/u.liter)
                    implicitSolventSaltConc = sc
                if u.is_quantity(temperature):
                    temperature = temperature.value_in_unit(u.kelvin)
                # The constant is 1 / sqrt( epsilon_0 * kB / (2 * NA * q^2 *
                # 1000) ) where NA is avogadro's number, epsilon_0 is the
                # permittivity of free space, q is the elementary charge (this
                # number matches sander/pmemd's kappa conversion factor)
                implicitSolventKappa = 50.33355 * sqrt(implicitSolventSaltConc /
                                                solventDielectric / temperature)
                # Multiply by 0.73 to account for ion exclusions, and multiply
                # by 10 to convert to 1/nm from 1/angstroms
                implicitSolventKappa *= 7.3
            elif implicitSolvent is None:
                implicitSolventKappa = 0.0

            if u.is_quantity(implicitSolventKappa):
                implicitSolventKappa = implicitSolventKappa.value_in_unit(
                                            (1.0/u.nanometer).unit)
            if implicitSolvent is HCT:
                gb = GBSAHCTForce(solventDielectric, soluteDielectric, None,
                                  cutoff, kappa=implicitSolventKappa)
            elif implicitSolvent is OBC1:
                gb = GBSAOBC1Force(solventDielectric, soluteDielectric, None,
                                   cutoff, kappa=implicitSolventKappa)
            elif implicitSolvent is OBC2:
                gb = GBSAOBC2Force(solventDielectric, soluteDielectric, None,
                                   cutoff, kappa=implicitSolventKappa)
            elif implicitSolvent is GBn:
                gb = GBSAGBnForce(solventDielectric, soluteDielectric, None,
                                  cutoff, kappa=implicitSolventKappa)
            elif implicitSolvent is GBn2:
                gb = GBSAGBn2Force(solventDielectric, soluteDielectric, None,
                                   cutoff, kappa=implicitSolventKappa)
            for i, atom in enumerate(self.atom_list):
                gb.addParticle([atom.charge] + list(gb_parms[i]))
            # Set cutoff method
            if nonbondedMethod is ff.NoCutoff:
                gb.setNonbondedMethod(mm.NonbondedForce.NoCutoff)
            elif nonbondedMethod is ff.CutoffNonPeriodic:
                gb.setNonbondedMethod(mm.NonbondedForce.CutoffNonPeriodic)
                gb.setCutoffDistance(cutoff)
            elif nonbondedMethod is ff.CutoffPeriodic:
                gb.setNonbondedMethod(mm.NonbondedForce.CutoffPeriodic)
                gb.setCutoffDistance(cutoff)
            else:
                raise ValueError('Illegal nonbonded method for use with GBSA')
            gb.setForceGroup(self.GB_FORCE_GROUP)
            system.addForce(gb)
            force.setReactionFieldDielectric(1.0) # applies to NonbondedForce

        # See if we repartition the hydrogen masses
        if hydrogenMass is not None:
            for bond in self.bonds_inc_h:
                atom1, atom2 = bond.atom1, bond.atom2
                if atom1.element == 1:
                    atom1, atom2 = atom2, atom1 # now atom2 is hydrogen for sure
                if atom1.element != 1:
                    transfer_mass = hydrogenMass - atom2.mass
                    new_mass1 = (system.getParticleMass(atom1.index) -
                                 transfer_mass)
                    system.setParticleMass(atom2.index, hydrogenMass)
                    system.setParticleMass(atom1.index, new_mass1)
        # See if we want to remove COM motion
        if removeCMMotion:
            system.addForce(mm.CMMotionRemover())

        # Cache our system for easy access
        self._system = system

        return system
Ejemplo n.º 4
0
def readAmberSystem(prmtop_filename=None, prmtop_loader=None, shake=None, gbmodel=None, soluteDielectric=1.0, solventDielectric=78.5, nonbondedCutoff=None, nonbondedMethod='NoCutoff', scee=1.2, scnb=2.0, mm=None, verbose=False, EwaldErrorTolerance=None, flexibleConstraints=True, rigidWater=True):
    """
    Create an OpenMM System from an Amber prmtop file.

    ARGUMENTS (specify  one or the other, but not both)
      prmtop_filename (String) - name of Amber prmtop file (new-style only)
      prmtop_loader (PrmtopLoader) - the loaded prmtop file

    OPTIONAL ARGUMENTS
      shake (String) - if 'h-bonds', will SHAKE all bonds to hydrogen and water; if 'all-bonds', will SHAKE all bonds and water (default: None)
      gbmodel (String) - if 'OBC', OBC GBSA will be used; if 'GBVI', GB/VI will be used (default: None)
      soluteDielectric (float) - The solute dielectric constant to use in the implicit solvent model (default: 1.0)
      solventDielectric (float) - The solvent dielectric constant to use in the implicit solvent model (default: 78.5)
      nonbondedCutoff (float) - if specified, will set nonbondedCutoff (default: None)
      scnb (float) - 1-4 Lennard-Jones scaling factor (default: 1.2)
      scee (float) - 1-4 electrostatics scaling factor (default: 2.0)
      mm - if specified, this module will be used in place of pyopenmm (default: None)
      verbose (boolean) - if True, print out information on progress (default: False)
      flexibleConstraints (boolean) - if True, flexible bonds will be added in addition ot constrained bonds
      rigidWater (boolean=True) If true, water molecules will be fully rigid regardless of the value passed for the shake argument

    NOTES

    Even if bonds are SHAKEn, their harmonic stretch terms are still included in the potential.

    TODO

    Should these option names be changed to reflect their 'sander' counterparts?

    EXAMPLES

    Create a system of alanine dipeptide in implicit solvent.

    >>> directory = os.path.join(os.getenv('YANK_INSTALL_DIR'), 'test', 'systems', 'alanine-dipeptide-gbsa')
    >>> prmtop_filename = os.path.join(directory, 'alanine-dipeptide.prmtop')
    >>> system = readAmberSystem(prmtop_filename)

    Parse a prmtop file of alanine dipeptide in explicit solvent.

    >>> directory = os.path.join(os.getenv('YANK_INSTALL_DIR'), 'test', 'systems', 'alanine-dipeptide-explicit')
    >>> prmtop_filename = os.path.join(directory, 'alanine-dipeptide.prmtop')
    >>> system = readAmberSystem(prmtop_filename)

    """

    if prmtop_filename is None and prmtop_loader is None:
        raise Exception("Must specify a filename or loader")
    if prmtop_filename is not None and prmtop_loader is not None:
        raise Exception("Cannot specify both a filename and a loader")
    if prmtop_filename is not None:
        # Load prmtop file.
        if verbose: print "Reading prmtop file '%s'..." % prmtop_filename
        prmtop = PrmtopLoader(prmtop_filename)
    else:
        prmtop = prmtop_loader

    if prmtop.getIfCap()>0:
        raise Exception("CAP option not currently supported")

    if prmtop.getIfPert()>0:
        raise Exception("perturbation not currently supported")

    if prmtop.getIfBox()>1:
        raise Exception("only standard periodic boxes are currently supported")

    # Use pyopenmm implementation of OpenMM by default.
    if mm is None:
        mm = simtk.openmm

    # Create OpenMM System.
    if verbose: print "Creating OpenMM system..."
    system = mm.System()

    # Populate system with atomic masses.
    if verbose: print "Adding particles..."
    for mass in prmtop.getMasses():
        system.addParticle(mass)

    # Add constraints.
    isWater = [prmtop.getResidueLabel(i) == 'WAT' for i in range(prmtop.getNumAtoms())]
    if shake in ('h-bonds', 'all-bonds', 'h-angles'):
        for (iAtom, jAtom, k, rMin) in prmtop.getBondsWithH():
            system.addConstraint(iAtom, jAtom, rMin)
    if shake in ('all-bonds', 'h-angles'):
        for (iAtom, jAtom, k, rMin) in prmtop.getBondsNoH():
            system.addConstraint(iAtom, jAtom, rMin)
    if rigidWater and shake == None:
        for (iAtom, jAtom, k, rMin) in prmtop.getBondsWithH():
            if isWater[iAtom] and isWater[jAtom]:
                system.addConstraint(iAtom, jAtom, rMin)

    # Add harmonic bonds.
    if verbose: print "Adding bonds..."
    force = mm.HarmonicBondForce()
    if flexibleConstraints or (shake not in ('h-bonds', 'all-bonds', 'h-angles')):
        for (iAtom, jAtom, k, rMin) in prmtop.getBondsWithH():
            if flexibleConstraints or not (rigidWater and isWater[iAtom] and isWater[jAtom]):
                force.addBond(iAtom, jAtom, rMin, 2*k)
    if flexibleConstraints or (shake not in ('all-bonds', 'h-angles')):
        for (iAtom, jAtom, k, rMin) in prmtop.getBondsNoH():
            force.addBond(iAtom, jAtom, rMin, 2*k)
    system.addForce(force)

    # Add harmonic angles.
    if verbose: print "Adding angles..."
    force = mm.HarmonicAngleForce()
    if shake == 'h-angles':
        numConstrainedBonds = system.getNumConstraints()
        atomConstraints = [[]]*system.getNumParticles()
        for i in range(system.getNumConstraints()):
            c = system.getConstraintParameters(i)
            distance = c[2].value_in_unit(units.nanometer)
            atomConstraints[c[0]].append((c[1], distance))
            atomConstraints[c[1]].append((c[0], distance))
    for (iAtom, jAtom, kAtom, k, aMin) in prmtop.getAngles():
        if shake == 'h-angles':
            type1 = prmtop.getAtomType(iAtom)
            type2 = prmtop.getAtomType(jAtom)
            type3 = prmtop.getAtomType(kAtom)
            numH = len([type for type in (type1, type3) if type.startswith('H')])
            constrained = (numH == 2 or (numH == 1 and type2.startswith('O')))
        else:
            constrained = False
        if constrained:
            # Find the two bonds that make this angle.
            l1 = None
            l2 = None
            for bond in atomConstraints[jAtom]:
                if bond[0] == iAtom:
                    l1 = bond[1]
                elif bond[0] == kAtom:
                    l2 = bond[1]

            # Compute the distance between atoms and add a constraint
            length = math.sqrt(l1*l1 + l2*l2 - 2*l1*l2*math.cos(aMin))
            system.addConstraint(iAtom, kAtom, length)
        if flexibleConstraints or not constrained:
            force.addAngle(iAtom, jAtom, kAtom, aMin, 2*k)
    system.addForce(force)

    # Add torsions.
    if verbose: print "Adding torsions..."
    force = mm.PeriodicTorsionForce()
    for (iAtom, jAtom, kAtom, lAtom, forceConstant, phase, periodicity) in prmtop.getDihedrals():
        force.addTorsion(iAtom, jAtom, kAtom, lAtom, periodicity, phase, forceConstant)
    system.addForce(force)

    # Add nonbonded interactions.
    if verbose: print "Adding nonbonded interactions..."
    force = mm.NonbondedForce()
    if (prmtop.getIfBox() == 0):
        # System is non-periodic.
        if nonbondedMethod == 'NoCutoff':
            force.setNonbondedMethod(mm.NonbondedForce.NoCutoff)
        elif nonbondedMethod == 'CutoffNonPeriodic':
            if nonbondedCutoff is None:
                raise Exception("No cutoff value specified")
            force.setNonbondedMethod(mm.NonbondedForce.CutoffNonPeriodic)
            force.setCutoffDistance(nonbondedCutoff)
        else:
            raise Exception("Illegal nonbonded method for a non-periodic system")
    else:
        # System is periodic.
        # Set periodic box vectors for periodic system
        (boxBeta, boxX, boxY, boxZ) = prmtop.getBoxBetaAndDimensions()
        d0 = units.Quantity(0.0, units.angstroms)
        xVec = units.Quantity((boxX, d0,   d0))
        yVec = units.Quantity((d0,   boxY, d0))
        zVec = units.Quantity((d0,   d0,   boxZ))
        system.setDefaultPeriodicBoxVectors(xVec, yVec, zVec)

        # Set cutoff.
        if nonbondedCutoff is None:
            # Compute cutoff automatically.
            min_box_width = min([boxX / units.nanometers, boxY / units.nanometers, boxZ / units.nanometers])
            CLEARANCE_FACTOR = 0.97 # reduce the cutoff to be a bit smaller than 1/2 smallest box length
            nonbondedCutoff = units.Quantity((min_box_width * CLEARANCE_FACTOR) / 2.0, units.nanometers)
        if nonbondedMethod != 'NoCutoff':
            force.setCutoffDistance(nonbondedCutoff)

        # Set nonbonded method.
        if nonbondedMethod == 'NoCutoff':
            force.setNonbondedMethod(mm.NonbondedForce.NoCutoff)
        elif nonbondedMethod == 'CutoffNonPeriodic':
            force.setNonbondedMethod(mm.NonbondedForce.CutoffNonPeriodic)
        elif nonbondedMethod == 'CutoffPeriodic':
            force.setNonbondedMethod(mm.NonbondedForce.CutoffPeriodic)
        elif nonbondedMethod == 'Ewald':
            force.setNonbondedMethod(mm.NonbondedForce.Ewald)
        elif nonbondedMethod == 'PME':
            force.setNonbondedMethod(mm.NonbondedForce.PME)
        else:
            raise Exception("Cutoff method not understood.")

        if EwaldErrorTolerance is not None:
            force.setEwaldErrorTolerance(EwaldErrorTolerance)

    # Add per-particle nonbonded parameters.
    sigmaScale = 2**(-1./6.) * 2.0
    for (charge, (rVdw, epsilon)) in zip(prmtop.getCharges(), prmtop.getNonbondTerms()):
        sigma = rVdw * sigmaScale
        force.addParticle(charge, sigma, epsilon)

    # Add 1-4 Interactions
    excludedAtomPairs = set()
    sigmaScale = 2**(-1./6.)
    for (iAtom, lAtom, chargeProd, rMin, epsilon) in prmtop.get14Interactions():
        chargeProd /= scee
        epsilon /= scnb
        sigma = rMin * sigmaScale
        force.addException(iAtom, lAtom, chargeProd, sigma, epsilon)
        excludedAtomPairs.add(min((iAtom, lAtom), (lAtom, iAtom)))

    # Add Excluded Atoms
    excludedAtoms=prmtop.getExcludedAtoms()
    excludeParams = (0.0, 0.1, 0.0)
    for iAtom in range(prmtop.getNumAtoms()):
        for jAtom in excludedAtoms[iAtom]:
            if min((iAtom, jAtom), (jAtom, iAtom)) in excludedAtomPairs: continue
            force.addException(iAtom, jAtom, excludeParams[0], excludeParams[1], excludeParams[2])

    system.addForce(force)

    # Add virtual sites for water.
    epNames = ['EP', 'LP']
    ep = [i for i in range(prmtop.getNumAtoms()) if isWater[i] and prmtop.getAtomName(i)[:2] in epNames]
    if len(ep) > 0:
        epRes = set((prmtop.getResidueNumber(i) for i in ep))
        numRes = max(epRes)+1
        # For each residue that contains an "extra point", find the oxygen, hydrogens, and points.
        waterO = []
        waterH = []
        waterEP = []
        for i in range(numRes):
            waterO.append([])
            waterH.append([])
            waterEP.append([])
        for i in range(prmtop.getNumAtoms()):
            res = prmtop.getResidueNumber(i)
            if res in epRes:
                name = prmtop.getAtomName(i)
                if name[0] == 'O':
                    waterO[res].append(i)
                if name[0] == 'H':
                    waterH[res].append(i)
                if name[:2] in epNames:
                    waterEP[res].append(i)
        # Record bond lengths for faster access.
        distOH = [None]*numRes
        distHH = [None]*numRes
        distOE = [None]*numRes
        for (atom1, atom2, k, dist) in prmtop.getBondsWithH()+prmtop.getBondsNoH():
            res = prmtop.getResidueNumber(atom1)
            if res in epRes:
                name1 = prmtop.getAtomName(atom1)
                name2 = prmtop.getAtomName(atom2)
                if name1[0] == 'H' or name2[0] == 'H':
                    if name1[0] == 'H' and name2[0] == 'H':
                        distHH[res] = dist
                    if name1[0] == 'O' or name2[0] == 'O':
                        distOH[res] = dist
                elif (name1[0] == 'O' or name2[0] == 'O') and ((name1[:2] in epNames or name2[:2] in epNames)):
                    distOE[res] = dist
        # Loop over residues and add the virtual sites.
        outOfPlaneAngle = 54.735*units.degree
        cosOOP = units.cos(outOfPlaneAngle)
        sinOOP = units.sin(outOfPlaneAngle)
        for res in range(numRes):
            if len(waterO[res]) == 1 and len(waterH[res]) == 2:
                if len(waterEP[res]) == 1:
                    # Four point water
                    weightH = distOE[res]/math.sqrt(distOH[res]**2-(0.5*distHH[res])**2)
                    system.setVirtualSite(waterEP[res][0], mm.ThreeParticleAverageSite(waterO[res][0], waterH[res][0], waterH[res][1], 1-weightH, weightH/2, weightH/2))
                elif len(waterEP[res]) == 2:
                    # Five point water
                    weightH = cosOOP*distOE[res]/math.sqrt(distOH[res]**2-(0.5*distHH[res])**2)
                    angleHOH = 2*math.asin(0.5*distHH[res]/distOH[res])
                    lenCross = (distOH[res]**2)*math.sin(angleHOH)
                    weightCross = sinOOP*distOE[res]/lenCross
                    system.setVirtualSite(waterEP[res][0], mm.OutOfPlaneSite(waterO[res][0], waterH[res][0], waterH[res][1], weightH/2, weightH/2, weightCross))
                    system.setVirtualSite(waterEP[res][1], mm.OutOfPlaneSite(waterO[res][0], waterH[res][0], waterH[res][1], weightH/2, weightH/2, -weightCross))

    # Add GBSA model.
    if gbmodel is not None:
        if verbose: print "Adding GB parameters..."
        charges = prmtop.getCharges()
        symbls = None
        cutoff = None
        if nonbondedMethod != 'NoCutoff':
            cutoff = nonbondedCutoff
            if units.is_quantity(cutoff):
                cutoff = cutoff.value_in_unit(units.nanometers)
        if gbmodel == 'GBn':
            symbls = prmtop.getAtomTypes()
        gb_parms = prmtop.getGBParms(symbls)
        if gbmodel == 'HCT':
            gb = customgb.GBSAHCTForce(solventDielectric, soluteDielectric, 'ACE', cutoff)
        elif gbmodel == 'OBC1':
            gb = customgb.GBSAOBC1Force(solventDielectric, soluteDielectric, 'ACE', cutoff)
        elif gbmodel == 'OBC2':
            gb = mm.GBSAOBCForce()
            gb.setSoluteDielectric(soluteDielectric)
            gb.setSolventDielectric(solventDielectric)
        elif gbmodel == 'GBn':
            gb = customgb.GBSAGBnForce(solventDielectric, soluteDielectric, 'ACE', cutoff)
        else:
            raise Exception("Illegal value specified for implicit solvent model")
        for iAtom in range(prmtop.getNumAtoms()):
            if gbmodel == 'OBC2':
                gb.addParticle(charges[iAtom], gb_parms[iAtom][0], gb_parms[iAtom][1])
            else:
                gb.addParticle([charges[iAtom], gb_parms[iAtom][0], gb_parms[iAtom][1]])
        system.addForce(gb)
        if nonbondedMethod == 'NoCutoff':
            gb.setNonbondedMethod(mm.NonbondedForce.NoCutoff)
        elif nonbondedMethod == 'CutoffNonPeriodic':
            gb.setNonbondedMethod(mm.NonbondedForce.CutoffNonPeriodic)
            gb.setCutoffDistance(nonbondedCutoff)
        elif nonbondedMethod == 'CutoffPeriodic':
            gb.setNonbondedMethod(mm.NonbondedForce.CutoffPeriodic)
            gb.setCutoffDistance(nonbondedCutoff)
        else:
            raise Exception("Illegal nonbonded method for use with GBSA")
        force.setReactionFieldDielectric(1.0)

    # TODO: Add GBVI terms?

    return system
Ejemplo n.º 5
0
 def testUnitMathModuleBadInput(self):
     """ Tests that bad units to unit_math fails appropriately """
     self.assertRaises(ArithmeticError, lambda: u.sqrt(9*u.meters))
     self.assertRaises(TypeError, lambda: u.sin(1*u.meters))
Ejemplo n.º 6
0
def readAmberSystem(prmtop_filename=None,
                    prmtop_loader=None,
                    shake=None,
                    gbmodel=None,
                    soluteDielectric=1.0,
                    solventDielectric=78.5,
                    nonbondedCutoff=None,
                    nonbondedMethod='NoCutoff',
                    scee=1.2,
                    scnb=2.0,
                    mm=None,
                    verbose=False,
                    EwaldErrorTolerance=None,
                    flexibleConstraints=True,
                    rigidWater=True):
    """
    Create an OpenMM System from an Amber prmtop file.

    ARGUMENTS (specify  one or the other, but not both)
      prmtop_filename (String) - name of Amber prmtop file (new-style only)
      prmtop_loader (PrmtopLoader) - the loaded prmtop file

    OPTIONAL ARGUMENTS
      shake (String) - if 'h-bonds', will SHAKE all bonds to hydrogen and water; if 'all-bonds', will SHAKE all bonds and water (default: None)
      gbmodel (String) - if 'OBC', OBC GBSA will be used; if 'GBVI', GB/VI will be used (default: None)
      soluteDielectric (float) - The solute dielectric constant to use in the implicit solvent model (default: 1.0)
      solventDielectric (float) - The solvent dielectric constant to use in the implicit solvent model (default: 78.5)
      nonbondedCutoff (float) - if specified, will set nonbondedCutoff (default: None)
      scnb (float) - 1-4 Lennard-Jones scaling factor (default: 1.2)
      scee (float) - 1-4 electrostatics scaling factor (default: 2.0)
      mm - if specified, this module will be used in place of pyopenmm (default: None)
      verbose (boolean) - if True, print out information on progress (default: False)
      flexibleConstraints (boolean) - if True, flexible bonds will be added in addition ot constrained bonds
      rigidWater (boolean=True) If true, water molecules will be fully rigid regardless of the value passed for the shake argument

    NOTES

    Even if bonds are SHAKEn, their harmonic stretch terms are still included in the potential.

    TODO

    Should these option names be changed to reflect their 'sander' counterparts?

    EXAMPLES

    Create a system of alanine dipeptide in implicit solvent.

    >>> directory = os.path.join(os.getenv('YANK_INSTALL_DIR'), 'test', 'systems', 'alanine-dipeptide-gbsa')
    >>> prmtop_filename = os.path.join(directory, 'alanine-dipeptide.prmtop')
    >>> system = readAmberSystem(prmtop_filename)

    Parse a prmtop file of alanine dipeptide in explicit solvent.

    >>> directory = os.path.join(os.getenv('YANK_INSTALL_DIR'), 'test', 'systems', 'alanine-dipeptide-explicit')
    >>> prmtop_filename = os.path.join(directory, 'alanine-dipeptide.prmtop')
    >>> system = readAmberSystem(prmtop_filename)

    """

    if prmtop_filename is None and prmtop_loader is None:
        raise Exception("Must specify a filename or loader")
    if prmtop_filename is not None and prmtop_loader is not None:
        raise Exception("Cannot specify both a filename and a loader")
    if prmtop_filename is not None:
        # Load prmtop file.
        if verbose: print "Reading prmtop file '%s'..." % prmtop_filename
        prmtop = PrmtopLoader(prmtop_filename)
    else:
        prmtop = prmtop_loader

    if prmtop.getIfCap() > 0:
        raise Exception("CAP option not currently supported")

    if prmtop.getIfPert() > 0:
        raise Exception("perturbation not currently supported")

    if prmtop.getIfBox() > 1:
        raise Exception("only standard periodic boxes are currently supported")

    # Use pyopenmm implementation of OpenMM by default.
    if mm is None:
        mm = simtk.openmm

    # Create OpenMM System.
    if verbose: print "Creating OpenMM system..."
    system = mm.System()

    # Populate system with atomic masses.
    if verbose: print "Adding particles..."
    for mass in prmtop.getMasses():
        system.addParticle(mass)

    # Add constraints.
    isWater = [
        prmtop.getResidueLabel(i) == 'WAT' for i in range(prmtop.getNumAtoms())
    ]
    if shake in ('h-bonds', 'all-bonds', 'h-angles'):
        for (iAtom, jAtom, k, rMin) in prmtop.getBondsWithH():
            system.addConstraint(iAtom, jAtom, rMin)
    if shake in ('all-bonds', 'h-angles'):
        for (iAtom, jAtom, k, rMin) in prmtop.getBondsNoH():
            system.addConstraint(iAtom, jAtom, rMin)
    if rigidWater and shake == None:
        for (iAtom, jAtom, k, rMin) in prmtop.getBondsWithH():
            if isWater[iAtom] and isWater[jAtom]:
                system.addConstraint(iAtom, jAtom, rMin)

    # Add harmonic bonds.
    if verbose: print "Adding bonds..."
    force = mm.HarmonicBondForce()
    if flexibleConstraints or (shake
                               not in ('h-bonds', 'all-bonds', 'h-angles')):
        for (iAtom, jAtom, k, rMin) in prmtop.getBondsWithH():
            if flexibleConstraints or not (rigidWater and isWater[iAtom]
                                           and isWater[jAtom]):
                force.addBond(iAtom, jAtom, rMin, 2 * k)
    if flexibleConstraints or (shake not in ('all-bonds', 'h-angles')):
        for (iAtom, jAtom, k, rMin) in prmtop.getBondsNoH():
            force.addBond(iAtom, jAtom, rMin, 2 * k)
    system.addForce(force)

    # Add harmonic angles.
    if verbose: print "Adding angles..."
    force = mm.HarmonicAngleForce()
    if shake == 'h-angles':
        numConstrainedBonds = system.getNumConstraints()
        atomConstraints = [[]] * system.getNumParticles()
        for i in range(system.getNumConstraints()):
            c = system.getConstraintParameters(i)
            distance = c[2].value_in_unit(units.nanometer)
            atomConstraints[c[0]].append((c[1], distance))
            atomConstraints[c[1]].append((c[0], distance))
    for (iAtom, jAtom, kAtom, k, aMin) in prmtop.getAngles():
        if shake == 'h-angles':
            type1 = prmtop.getAtomType(iAtom)
            type2 = prmtop.getAtomType(jAtom)
            type3 = prmtop.getAtomType(kAtom)
            numH = len(
                [type for type in (type1, type3) if type.startswith('H')])
            constrained = (numH == 2 or (numH == 1 and type2.startswith('O')))
        else:
            constrained = False
        if constrained:
            # Find the two bonds that make this angle.
            l1 = None
            l2 = None
            for bond in atomConstraints[jAtom]:
                if bond[0] == iAtom:
                    l1 = bond[1]
                elif bond[0] == kAtom:
                    l2 = bond[1]

            # Compute the distance between atoms and add a constraint
            length = math.sqrt(l1 * l1 + l2 * l2 -
                               2 * l1 * l2 * math.cos(aMin))
            system.addConstraint(iAtom, kAtom, length)
        if flexibleConstraints or not constrained:
            force.addAngle(iAtom, jAtom, kAtom, aMin, 2 * k)
    system.addForce(force)

    # Add torsions.
    if verbose: print "Adding torsions..."
    force = mm.PeriodicTorsionForce()
    for (iAtom, jAtom, kAtom, lAtom, forceConstant, phase,
         periodicity) in prmtop.getDihedrals():
        force.addTorsion(iAtom, jAtom, kAtom, lAtom, periodicity, phase,
                         forceConstant)
    system.addForce(force)

    # Add nonbonded interactions.
    if verbose: print "Adding nonbonded interactions..."
    force = mm.NonbondedForce()
    if (prmtop.getIfBox() == 0):
        # System is non-periodic.
        if nonbondedMethod == 'NoCutoff':
            force.setNonbondedMethod(mm.NonbondedForce.NoCutoff)
        elif nonbondedMethod == 'CutoffNonPeriodic':
            if nonbondedCutoff is None:
                raise Exception("No cutoff value specified")
            force.setNonbondedMethod(mm.NonbondedForce.CutoffNonPeriodic)
            force.setCutoffDistance(nonbondedCutoff)
        else:
            raise Exception(
                "Illegal nonbonded method for a non-periodic system")
    else:
        # System is periodic.
        # Set periodic box vectors for periodic system
        (boxBeta, boxX, boxY, boxZ) = prmtop.getBoxBetaAndDimensions()
        d0 = units.Quantity(0.0, units.angstroms)
        xVec = units.Quantity((boxX, d0, d0))
        yVec = units.Quantity((d0, boxY, d0))
        zVec = units.Quantity((d0, d0, boxZ))
        system.setDefaultPeriodicBoxVectors(xVec, yVec, zVec)

        # Set cutoff.
        if nonbondedCutoff is None:
            # Compute cutoff automatically.
            min_box_width = min([
                boxX / units.nanometers, boxY / units.nanometers,
                boxZ / units.nanometers
            ])
            CLEARANCE_FACTOR = 0.97  # reduce the cutoff to be a bit smaller than 1/2 smallest box length
            nonbondedCutoff = units.Quantity(
                (min_box_width * CLEARANCE_FACTOR) / 2.0, units.nanometers)
        if nonbondedMethod != 'NoCutoff':
            force.setCutoffDistance(nonbondedCutoff)

        # Set nonbonded method.
        if nonbondedMethod == 'NoCutoff':
            force.setNonbondedMethod(mm.NonbondedForce.NoCutoff)
        elif nonbondedMethod == 'CutoffNonPeriodic':
            force.setNonbondedMethod(mm.NonbondedForce.CutoffNonPeriodic)
        elif nonbondedMethod == 'CutoffPeriodic':
            force.setNonbondedMethod(mm.NonbondedForce.CutoffPeriodic)
        elif nonbondedMethod == 'Ewald':
            force.setNonbondedMethod(mm.NonbondedForce.Ewald)
        elif nonbondedMethod == 'PME':
            force.setNonbondedMethod(mm.NonbondedForce.PME)
        else:
            raise Exception("Cutoff method not understood.")

        if EwaldErrorTolerance is not None:
            force.setEwaldErrorTolerance(EwaldErrorTolerance)

    # Add per-particle nonbonded parameters.
    sigmaScale = 2**(-1. / 6.) * 2.0
    for (charge, (rVdw, epsilon)) in zip(prmtop.getCharges(),
                                         prmtop.getNonbondTerms()):
        sigma = rVdw * sigmaScale
        force.addParticle(charge, sigma, epsilon)

    # Add 1-4 Interactions
    excludedAtomPairs = set()
    sigmaScale = 2**(-1. / 6.)
    for (iAtom, lAtom, chargeProd, rMin,
         epsilon) in prmtop.get14Interactions():
        chargeProd /= scee
        epsilon /= scnb
        sigma = rMin * sigmaScale
        force.addException(iAtom, lAtom, chargeProd, sigma, epsilon)
        excludedAtomPairs.add(min((iAtom, lAtom), (lAtom, iAtom)))

    # Add Excluded Atoms
    excludedAtoms = prmtop.getExcludedAtoms()
    excludeParams = (0.0, 0.1, 0.0)
    for iAtom in range(prmtop.getNumAtoms()):
        for jAtom in excludedAtoms[iAtom]:
            if min((iAtom, jAtom), (jAtom, iAtom)) in excludedAtomPairs:
                continue
            force.addException(iAtom, jAtom, excludeParams[0],
                               excludeParams[1], excludeParams[2])

    system.addForce(force)

    # Add virtual sites for water.
    epNames = ['EP', 'LP']
    ep = [
        i for i in range(prmtop.getNumAtoms())
        if isWater[i] and prmtop.getAtomName(i)[:2] in epNames
    ]
    if len(ep) > 0:
        epRes = set((prmtop.getResidueNumber(i) for i in ep))
        numRes = max(epRes) + 1
        # For each residue that contains an "extra point", find the oxygen, hydrogens, and points.
        waterO = []
        waterH = []
        waterEP = []
        for i in range(numRes):
            waterO.append([])
            waterH.append([])
            waterEP.append([])
        for i in range(prmtop.getNumAtoms()):
            res = prmtop.getResidueNumber(i)
            if res in epRes:
                name = prmtop.getAtomName(i)
                if name[0] == 'O':
                    waterO[res].append(i)
                if name[0] == 'H':
                    waterH[res].append(i)
                if name[:2] in epNames:
                    waterEP[res].append(i)
        # Record bond lengths for faster access.
        distOH = [None] * numRes
        distHH = [None] * numRes
        distOE = [None] * numRes
        for (atom1, atom2, k,
             dist) in prmtop.getBondsWithH() + prmtop.getBondsNoH():
            res = prmtop.getResidueNumber(atom1)
            if res in epRes:
                name1 = prmtop.getAtomName(atom1)
                name2 = prmtop.getAtomName(atom2)
                if name1[0] == 'H' or name2[0] == 'H':
                    if name1[0] == 'H' and name2[0] == 'H':
                        distHH[res] = dist
                    if name1[0] == 'O' or name2[0] == 'O':
                        distOH[res] = dist
                elif (name1[0] == 'O' or name2[0] == 'O') and (
                    (name1[:2] in epNames or name2[:2] in epNames)):
                    distOE[res] = dist
        # Loop over residues and add the virtual sites.
        outOfPlaneAngle = 54.735 * units.degree
        cosOOP = units.cos(outOfPlaneAngle)
        sinOOP = units.sin(outOfPlaneAngle)
        for res in range(numRes):
            if len(waterO[res]) == 1 and len(waterH[res]) == 2:
                if len(waterEP[res]) == 1:
                    # Four point water
                    weightH = distOE[res] / math.sqrt(distOH[res]**2 -
                                                      (0.5 * distHH[res])**2)
                    system.setVirtualSite(
                        waterEP[res][0],
                        mm.ThreeParticleAverageSite(waterO[res][0],
                                                    waterH[res][0],
                                                    waterH[res][1],
                                                    1 - weightH, weightH / 2,
                                                    weightH / 2))
                elif len(waterEP[res]) == 2:
                    # Five point water
                    weightH = cosOOP * distOE[res] / math.sqrt(
                        distOH[res]**2 - (0.5 * distHH[res])**2)
                    angleHOH = 2 * math.asin(0.5 * distHH[res] / distOH[res])
                    lenCross = (distOH[res]**2) * math.sin(angleHOH)
                    weightCross = sinOOP * distOE[res] / lenCross
                    system.setVirtualSite(
                        waterEP[res][0],
                        mm.OutOfPlaneSite(waterO[res][0], waterH[res][0],
                                          waterH[res][1], weightH / 2,
                                          weightH / 2, weightCross))
                    system.setVirtualSite(
                        waterEP[res][1],
                        mm.OutOfPlaneSite(waterO[res][0], waterH[res][0],
                                          waterH[res][1], weightH / 2,
                                          weightH / 2, -weightCross))

    # Add GBSA model.
    if gbmodel is not None:
        if verbose: print "Adding GB parameters..."
        charges = prmtop.getCharges()
        symbls = None
        if gbmodel == 'GBn':
            symbls = prmtop.getAtomTypes()
        gb_parms = prmtop.getGBParms(symbls)
        if gbmodel == 'HCT':
            gb = customgb.GBSAHCTForce(solventDielectric, soluteDielectric,
                                       'ACE')
        elif gbmodel == 'OBC1':
            gb = customgb.GBSAOBC1Force(solventDielectric, soluteDielectric,
                                        'ACE')
        elif gbmodel == 'OBC2':
            gb = mm.GBSAOBCForce()
            gb.setSoluteDielectric(soluteDielectric)
            gb.setSolventDielectric(solventDielectric)
        elif gbmodel == 'GBn':
            gb = customgb.GBSAGBnForce(solventDielectric, soluteDielectric,
                                       'ACE')
        else:
            raise Exception(
                "Illegal value specified for implicit solvent model")
        for iAtom in range(prmtop.getNumAtoms()):
            if gbmodel == 'OBC2':
                gb.addParticle(charges[iAtom], gb_parms[iAtom][0],
                               gb_parms[iAtom][1])
            else:
                gb.addParticle(
                    [charges[iAtom], gb_parms[iAtom][0], gb_parms[iAtom][1]])
        system.addForce(gb)
        if nonbondedMethod == 'NoCutoff':
            gb.setNonbondedMethod(mm.NonbondedForce.NoCutoff)
        elif nonbondedMethod == 'CutoffNonPeriodic':
            gb.setNonbondedMethod(mm.NonbondedForce.CutoffNonPeriodic)
            gb.setCutoffDistance(nonbondedCutoff)
        elif nonbondedMethod == 'CutoffPeriodic':
            gb.setNonbondedMethod(mm.NonbondedForce.CutoffPeriodic)
            gb.setCutoffDistance(nonbondedCutoff)
        else:
            raise Exception("Illegal nonbonded method for use with GBSA")
        force.setReactionFieldDielectric(1.0)

    # TODO: Add GBVI terms?

    return system
Ejemplo n.º 7
0
    def get_standard_state_correction_static(method='numerical', **kwargs):
        """
        Compute the standard state correction for the arbitrary restraint energy function.

        Returns
        -------
        DeltaG : float
            Computed standard-state correction in dimensionless units (kT)

        Notes
        -----
        'analytical' uses analytical approach from [1], but this approach is known to be inexact.
        'numerical' uses numerical integral to the partition function contriubtions for r and theta, analytical for phi

        """

        class Bunch(object):
            """Make a dict accessible via an object accessor"""
            def __init__(self, adict):
                self.__dict__.update(adict)

        def strip(passed_unit):
            """Cast the passed_unit into md unit system for integrand lambda functions"""
            return passed_unit.value_in_unit_system(unit.md_unit_system)

        all_valid_keys = ['kT', 'parameters']
        for key in all_valid_keys:
            if key not in kwargs:
                raise KeyError('Missing {} from input arguments!'.format(key))
        kT = kwargs['kT']
        pi = np.pi
        p = Bunch(kwargs['parameters'])
        DeltaG = 0

        # Multiply by unit.radian**5 to remove the expected unit value
        # radians is a soft unit in this case, it cancels in the math, but not in the equations here.
        if method is 'analytical':
            # Eq 32 of Ref [1]
            DeltaG += -np.log( \
                (8. * pi ** 2 * V0) / (p.r_aA0 ** 2 * unit.sin(p.theta_A0) * unit.sin(p.theta_B0)) \
                * unit.sqrt(p.K_r * p.K_thetaA * p.K_thetaB * p.K_phiA * p.K_phiB * p.K_phiC) / (2 * pi * kT) ** 3 \
                * unit.radian**5)
        elif method is 'numerical':
            # Radial
            sigma = 1 / unit.sqrt(p.K_r / kT)
            rmin = min(0*unit.angstrom, p.r_aA0 - 8 * sigma)
            rmax = p.r_aA0 + 8 * sigma
            I = lambda r: r ** 2 * np.exp(-strip(p.K_r) / (2 * strip(kT)) * (r - strip(p.r_aA0)) ** 2)
            DGIntegral, dDGIntegral = scipy.integrate.quad(I, strip(rmin), strip(rmax)) * unit.nanometer**3
            ExpDeltaG = DGIntegral
            # Angular
            for name in ['A', 'B']:
                theta0 = getattr(p, 'theta_' + name + '0')
                K_theta = getattr(p, 'K_theta' + name)
                I = lambda theta: np.sin(theta) * np.exp(-strip(K_theta) / (2 * strip(kT)) * (theta - strip(theta0)) ** 2)
                DGIntegral, dDGIntegral = scipy.integrate.quad(I, 0, pi)
                ExpDeltaG *= DGIntegral
            # Torsion
            for name in ['A', 'B', 'C']:
                phi0 = getattr(p, 'phi_' + name + '0')
                K_phi = getattr(p, 'K_phi' + name)
                kshort = strip(K_phi/kT)
                ExpDeltaG *= math.sqrt(pi/2.0) * (
                    math.erf((strip(phi0)+pi)*unit.sqrt(kshort)/math.sqrt(2)) -
                    math.erf((strip(phi0)-pi)*unit.sqrt(kshort)/math.sqrt(2))
                ) / unit.sqrt(kshort)
            DeltaG += -np.log(8 * pi**2 * V0 / ExpDeltaG)
        else:
            raise ValueError('"method" must be "analytical" or "numerical"')

        return DeltaG