Exemplo n.º 1
0
 def initialize(self, integrator):
     sigma_v = unit.sqrt(self._kT_value / self._m_value)
     integrator.setGlobalVariableByName(
         self._v, sigma_v * integrator._random.normal())
     if self._thermostat == 'Nose-Hoover':
         sigma_v_eta = unit.sqrt(self._kT_value / self._Q_eta_value)
         integrator.setGlobalVariableByName(
             self._v_eta, sigma_v_eta * integrator._random.normal())
Exemplo n.º 2
0
def compute_Ka(DG, dDG):
    """Compute the association constant from the free energy.

    Parameters
    ----------
    DG : simtk.Quantity
        Free energy
    dDG : simtk.Quantity
        Uncertainty in free energy

    Returns
    -------
    Ka : simtk.Quantity
        Association constant.
    dKa : simtk.Quantity
        Association constant uncertainty.

    """
    concentration_unit = u.molar
    Ka = np.exp(-DG / (R * T)) * 1 / concentration_unit
    # Propagate error.
    if dDG is None:
        dKa = None
    else:
        dKadDG = -Ka / (R * T)  # Derivative dKa(DG)/dDG.
        dKa = u.sqrt(dKadDG**2 * dDG**2)

    return Ka, dKa
Exemplo n.º 3
0
def compute_TDS(DG, dDG, DH, dDH):
    """Compute the entropy from free energy and enthalpy.

    Parameters
    ----------
    DG : simtk.Quantity
        Free energy.
    dDG : simtk.Quantity
        Free energy uncertainty.
    DH : simtk.Quantity
        Enthalpy.
    dDH : simtk.Quantity
        Enthalpy uncertainty.

    Returns
    -------
    TDS : simtk.Quantity
        Entrop.
    dTDS : simtk.Quantity
        Binding free energy uncertainty.

    """
    TDS = DH - DG
    dTDS = u.sqrt(dDH**2 + dDG**2)
    return TDS, dTDS
Exemplo n.º 4
0
    def generate_maxwell_boltzmann_velocities(self, temperature):
        """
        Generate random velocities for the solute.

        """
        assert self.masses_list is not None
        assert self.n_atoms is not None

        # Initiate array
        vel = unit.Quantity(
            np.zeros([self.n_atoms, 3],
                     np.float64), unit.nanometer / unit.picosecond
        )  # velocities[i,k] is the kth component of the velocity of atom i
        kT = temperature * unit.BOLTZMANN_CONSTANT_kB
        kT = kT.in_units_of(unit.kilogram * unit.meter * unit.meter /
                            (unit.second * unit.second))

        # Assign velocities from the Maxwell-Boltzmann distribution.
        for atom_index in range(self.n_atoms):
            mass = self.masses_list[atom_index]
            if mass._value > 1e-8:
                mass = unit.Quantity(mass._value * 1.66054e-27, unit.kilogram)
                # Standard deviation of velocity distribution for each coordinate for this atom
                sigma = unit.sqrt(kT / mass)
            else:
                sigma = 0.0 * unit.nanometer / unit.picosecond

            for k in range(3):
                # 0.001 is to take into account the ns / ps
                vel[atom_index, k] = (sigma * np.random.standard_normal())

        return vel.in_units_of(unit.nanometer / unit.picosecond)
Exemplo n.º 5
0
def test_propertyestimator():
    """Test PhysicalPropertyEstimator on synthetic data.

    """
    # Get synthetic dataset and parameters
    from openforcefield import testsystems
    dataset = testsystems.TestDataset()
    parameters = testsystems.TestParameterSet()

    # Create a PropertyEstimator
    from openforcefield import PropertyEstimator
    estimator = PropertyEstimator()

    # Compute properties
    computed_properties = estimator.computeProperties(dataset, parameters)

    # Assert that computed properties are within statistical error
    for (measured_property, computed_property) in zip(dataset, computed_properties):
        error_value = (computed_property.value - measured_property.value)
        error_uncertainty = unit.sqrt(computed_property.uncertainty**2 + measured_property.uncertainty**2)
        relative_error = error_value / error_uncertainty
        if (relative_error > SIGMA_CUTOFF):
            msg  = 'Computed property %s differs from measured property by more than SIGMA_CUTOFF (%f):\n' % (computed_property.name, SIGMA_CUTOFF)
            msg += 'Measured: %12.3f +- %12.3f %s' % (measured_property.value / measured_property.unit, measured_property.uncertainty / measured_property.unit, str(measured_property.unit))
            msg += 'Computed: %12.3f +- %12.3f %s' % (computed_property.value / measured_property.unit, computed_property.uncertainty / measured_property.unit, str(measured_property.unit))
            msg += 'ERROR   : %12.3f +- %12.3f %s (%12.3f SIGMA)' % (error_value / measured_property.unit, error_uncertainty / measured_property.unit, str(measured_property.unit), relative_error)
            raise Exception(msg)
Exemplo n.º 6
0
    def createDisulfideBonds(self, positions):
        """Identify disulfide bonds based on proximity and add them to the
        Topology.

        Parameters
        ----------
        positions : list
            The list of atomic positions based on which to identify bonded atoms
        """
        def isCyx(res):
            names = [atom.name for atom in res._atoms]
            return 'SG' in names and 'HG' not in names

        cyx = [res for res in self.residues() if res.name == 'CYS' and isCyx(res)]
        atomNames = [[atom.name for atom in res._atoms] for res in cyx]
        for i in range(len(cyx)):
            sg1 = cyx[i]._atoms[atomNames[i].index('SG')]
            pos1 = positions[sg1.index]
            for j in range(i):
                sg2 = cyx[j]._atoms[atomNames[j].index('SG')]
                pos2 = positions[sg2.index]
                delta = [x-y for (x,y) in zip(pos1, pos2)]
                distance = sqrt(delta[0]*delta[0] + delta[1]*delta[1] + delta[2]*delta[2])
                if distance < 0.3*nanometers:
                    self.addBond(sg1, sg2)
Exemplo n.º 7
0
def compute_DG(Ka, dKa):
    """Compute the free energy from the association constant.

    Parameters
    ----------
    Ka : simtk.Quantity
        Association constant.
    dKa : simtk.Quantity
        Association constant uncertainty.

    Returns
    -------
    DG : simtk.Quantity
        Binding free energy.
    dDG : simtk.Quantity
        Binding free energy uncertainty.

    """
    concentration_unit = 1 / Ka.unit
    DG = -R * T * np.log(Ka * concentration_unit)
    # Propagate error.
    if dKa is None:
        dDG = None
    else:
        dDGdKa = -R * T / Ka  # Derivative dDG(Ka)/dKa.
        # Have to use u.sqrt to avoid bug with simtk.unit
        dDG = u.sqrt(dDGdKa**2 * dKa**2)
    return DG, dDG
Exemplo n.º 8
0
    def createDisulfideBonds(self, positions):
        """Identify disulfide bonds based on proximity and add them to the Topology.

        Parameters:
         - positions (list) The list of atomic positions based on which to identify bonded atoms
        """
        def isCyx(res):
            names = [atom.name for atom in res._atoms]
            return 'SG' in names and 'HG' not in names

        cyx = [
            res for res in self.residues() if res.name == 'CYS' and isCyx(res)
        ]
        atomNames = [[atom.name for atom in res._atoms] for res in cyx]
        for i in range(len(cyx)):
            sg1 = cyx[i]._atoms[atomNames[i].index('SG')]
            pos1 = positions[sg1.index]
            for j in range(i):
                sg2 = cyx[j]._atoms[atomNames[j].index('SG')]
                pos2 = positions[sg2.index]
                delta = [x - y for (x, y) in zip(pos1, pos2)]
                distance = sqrt(delta[0] * delta[0] + delta[1] * delta[1] +
                                delta[2] * delta[2])
                if distance < 0.3 * nanometers:
                    self.addBond(sg1, sg2)
Exemplo n.º 9
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
def test_propertyestimator():
    """Test PhysicalPropertyEstimator on synthetic data.

    """
    # Get synthetic dataset and parameters
    from openforcefield.tests import testsystems
    dataset = testsystems.TestDataset()
    parameters = testsystems.TestParameterSet()

    # Create a PropertyEstimator
    from openforcefield.propertyestimator import PropertyEstimator
    estimator = PropertyEstimator()

    # Compute properties
    computed_properties = estimator.computeProperties(dataset, parameters)

    # Assert that computed properties are within statistical error
    for (measured_property, computed_property) in zip(dataset, computed_properties):
        error_value = (computed_property.value - measured_property.value)
        error_uncertainty = unit.sqrt(computed_property.uncertainty**2 + measured_property.uncertainty**2)
        relative_error = error_value / error_uncertainty
        if (relative_error > SIGMA_CUTOFF):
            msg  = 'Computed property %s differs from measured property by more than SIGMA_CUTOFF (%f):\n' % (computed_property.name, SIGMA_CUTOFF)
            msg += 'Measured: %12.3f +- %12.3f %s' % (measured_property.value / measured_property.unit, measured_property.uncertainty / measured_property.unit, str(measured_property.unit))
            msg += 'Computed: %12.3f +- %12.3f %s' % (computed_property.value / measured_property.unit, computed_property.uncertainty / measured_property.unit, str(measured_property.unit))
            msg += 'ERROR   : %12.3f +- %12.3f %s (%12.3f SIGMA)' % (error_value / measured_property.unit, error_uncertainty / measured_property.unit, str(measured_property.unit), relative_error)
            raise Exception(msg)
Exemplo n.º 11
0
def add_rosetta_exception_parameters(cgmodel, nonbonded_force,
                                     particle_index_1, particle_index_2):
    """
    """
    exception_list = []
    for exception in range(nonbonded_force.getNumExceptions()):
        index_1, index_2, charge, sigma, epsilon = nonbonded_force.getExceptionParameters(
            exception)
        if [index_1, index_2] not in exception_list and [
                index_2, index_1
        ] not in exception_list:
            exception_list.append([index_1, index_2])

    if [particle_index_1, particle_index_2] not in exception_list and [
            particle_index_2,
            particle_index_1,
    ] not in exception_list:
        charge_1 = cgmodel.get_particle_charge(particle_index_1)
        sigma_1 = cgmodel.get_particle_sigma(particle_index_1).in_units_of(
            unit.nanometer)
        epsilon_1 = cgmodel.get_particle_epsilon(particle_index_1).in_units_of(
            unit.kilojoule_per_mole)
        charge_2 = cgmodel.get_particle_charge(particle_index_2)
        sigma_2 = cgmodel.get_particle_sigma(particle_index_2).in_units_of(
            unit.nanometer)
        epsilon_2 = cgmodel.get_particle_epsilon(particle_index_2).in_units_of(
            unit.kilojoule_per_mole)
        sigma = (sigma_1 + sigma_2) / 2.0
        epsilon = 0.2 * unit.sqrt(epsilon_1 * epsilon_2)
        nonbonded_force.addException(particle_index_1, particle_index_2,
                                     0.2 * charge_1 * charge_2, sigma, epsilon)
    return nonbonded_force
Exemplo n.º 12
0
    def __init__(self, **kwargs):
        super(HarmonicOscillatorSimulatedTempering, self).__init__(**kwargs)
        self.description = 'Harmonic oscillator simulated tempering simulation'

        # Create topology, positions, and system.
        from openmmtools.testsystems import HarmonicOscillator
        K = 1.0 * unit.kilocalories_per_mole / unit.angstroms**2  # 3D harmonic oscillator spring constant
        mass = 39.948 * unit.amu  # 3D harmonic oscillator particle mass
        period = 2.0 * np.pi * unit.sqrt(
            mass / K)  # harmonic oscillator period
        timestep = 0.01 * period
        testsystem = HarmonicOscillator(K=K, mass=mass)
        self.topology = testsystem.topology
        self.positions = testsystem.positions
        self.system = testsystem.system

        # Create thermodynamic states.
        Tmin = 100 * unit.kelvin
        Tmax = 1000 * unit.kelvin
        ntemps = 8  # number of temperatures
        from sams import ThermodynamicState
        temperatures = unit.Quantity(
            np.logspace(np.log10(Tmin / unit.kelvin),
                        np.log10(Tmax / unit.kelvin), ntemps), unit.kelvin)
        self.thermodynamic_states = [
            ThermodynamicState(system=self.system, temperature=temperature)
            for temperature in temperatures
        ]

        # Compute analytical logZ for each thermodynamic state.
        self.logZ = np.zeros([ntemps], np.float64)
        for (index,
             thermodynamic_state) in enumerate(self.thermodynamic_states):
            beta = thermodynamic_state.beta
            self.logZ[index] = -1.5 * np.log(beta * K * unit.angstrom**2)
        self.logZ[:] -= self.logZ[0]

        # Create SAMS samplers
        from sams.samplers import SamplerState, MCMCSampler, ExpandedEnsembleSampler, SAMSSampler
        thermodynamic_state_index = 0  # initial thermodynamic state index
        thermodynamic_state = self.thermodynamic_states[
            thermodynamic_state_index]
        sampler_state = SamplerState(positions=self.positions)
        self.mcmc_sampler = MCMCSampler(
            sampler_state=sampler_state,
            thermodynamic_state=thermodynamic_state,
            ncfile=self.ncfile)
        self.mcmc_sampler.pdbfile = open('output.pdb', 'w')
        self.mcmc_sampler.topology = self.topology
        self.mcmc_sampler.timestep = timestep
        self.mcmc_sampler.collision_rate = 1.0 / (100 * timestep)
        self.mcmc_sampler.nsteps = 1000
        self.mcmc_sampler.verbose = True
        self.exen_sampler = ExpandedEnsembleSampler(self.mcmc_sampler,
                                                    self.thermodynamic_states)
        self.exen_sampler.verbose = True
        self.sams_sampler = SAMSSampler(self.exen_sampler,
                                        update_stages='two-stage',
                                        update_method='optimal')
        self.sams_sampler.verbose = True
Exemplo n.º 13
0
    def _automatic_parameter_selection(self, positions, receptor_atoms, ligand_atoms):
        """
        Determine parameters and restrained atoms automatically, rejecting choices where standard state correction will be incorrectly computed.

        Parameters
        ----------
        positions : simtk.unit.Quantity of natoms x 3 with units compatible with nanometers
            Reference positions to use for imposing restraints
        receptor_atoms : list of int
            A complete list of receptor atoms
        ligand_atoms : list of int
            A complete list of ligand atoms
        """
        NSIGMA = 4
        temperature = 300 * unit.kelvin
        kT = kB * temperature
        attempt = 0
        MAX_ATTEMPTS = 100
        reject = True
        logger.debug('Automatically selecting restraint atoms and parameters:')
        while reject and attempt < MAX_ATTEMPTS:
            logger.debug('Attempt %d / %d at automatically selecting atoms and restraint parameters...' % (attempt, MAX_ATTEMPTS))

            # Select atoms to be used in restraint.
            self._restraint_atoms = self._select_restraint_atoms(positions, receptor_atoms, ligand_atoms)

            # Determine restraint parameters
            self._determine_restraint_parameters()

            # Terminate if we satisfy criteria
            reject = False
            for name in ['A', 'B']:
                theta0 = self._parameters['theta_' + name + '0']
                K = self._parameters['K_theta' + name]
                sigma = unit.sqrt(NSIGMA * kT / (K/2.))
                if (theta0 < sigma) or (theta0 > (np.pi*unit.radians - sigma)):
                    logger.debug('Reject because theta_' + name + '0 is too close to 0 or pi for standard state correction to be accurate.')
                    reject = True

            r0 = self._parameters['r_aA0']
            K = self._parameters['K_r']
            sigma = unit.sqrt(NSIGMA * kT / (K/2.))
            if (r0 < sigma):
                logger.debug('Reject because r_aA0 is too close to 0 for standard state correction to be accurate.')
                reject = True

            attempt += 1
Exemplo n.º 14
0
    def __init__(self,
                 sigma1=5.0 * unit.angstrom,
                 sigma2=10.0 * unit.angstrom,
                 temperature=300.0 * unit.kelvin,
                 zeta=(0.0, 0.0),
                 mass=39.948 * unit.amu,
                 collision_rate=5.0 / unit.picosecond,
                 platform_name='CPU'):
        """
        Initialize the 3D harmonic oscillator with two force constants
        """

        if len(zeta) != 2:
            raise Exception(
                'zeta must be a list or tuple of floats with length 2')

        # System specific
        self.mass = mass
        self.temperature = temperature
        self.kT = kB * self.temperature
        self.kT_unitless = strip_in_unit_system(self.kT)
        self.platform_name = platform_name

        # Force Constants
        K1 = (self.kT / sigma1**2).in_unit_system(unit.md_unit_system)
        K2 = (self.kT / sigma2**2).in_unit_system(unit.md_unit_system)

        self.K = (K1, K2)
        self.zeta = (zeta[0], zeta[1])
        self.sigma = (strip_in_unit_system(sigma1),
                      strip_in_unit_system(sigma2))

        # Choose the initial force constant of the harmonic oscillator
        self.state = np.random.choice((0, 1))
        self.K_current = self.K[self.state]
        self.zeta_current = self.zeta[self.state]
        self.sigma_current = self.sigma[self.state]

        # self.K_current = np.random.choice((self.K1, self.K2))
        # self.K_current_unitless = strip_in_unit_system(self.K_current)

        # Integrator specific
        self.collision_rate = collision_rate
        tau = 2 * np.pi * unit.sqrt(self.mass /
                                    (K1 / 2.0 + K2 / 2.0))  # time constant
        self.timestep = tau / 20.0

        # The initial configuration of the oscillator
        self.position = np.zeros([1, 3], np.float32)
        self.velocity = None

        # The openmm system
        (self.context, self.integrator,
         self.system) = self.make_harmonic_context(self.K_current)

        # The sampling statistics
        self.state_counter = 0
        self.nmoves = 0
        self.radii = []
Exemplo n.º 15
0
    def end_to_end_CA_distance(self, topology, positions):
        residues = list(topology.residues())
        # get the index of the first and last alpha carbons
        i1 = [a.index for a in residues[0].atoms() if a.name == 'CA'][0]
        i2 = [a.index for a in residues[-1].atoms() if a.name == 'CA'][0]

        # get the current distanc be between the two alpha carbons
        return i1, i2, sqrt(sum((positions[i1] - positions[i2])**2))
Exemplo n.º 16
0
def check_harmonic_oscillator_ncmc(ncmc_nsteps=50, ncmc_integrator="VV"):
    """
    Test NCMC switching of a 3D harmonic oscillator.
    In this test, the oscillator center is dragged in space, and we check the computed free energy difference with BAR, which should be 0.
    """
    # Parameters for 3D harmonic oscillator
    mass = 39.948 * unit.amu # mass of particle (argon)
    sigma = 5.0 * unit.angstrom # standard deviation of harmonic oscillator
    collision_rate = 5.0/unit.picosecond # collision rate
    temperature = 300.0 * unit.kelvin # temperature
    platform_name = 'Reference' # platform anme
    NSIGMA_MAX = 6.0 # number of standard errors away from analytical solution tolerated before Exception is thrown

    # Compute derived quantities.
    kT = kB * temperature # thermal energy
    beta = 1.0 / kT # inverse energy
    K = kT / sigma**2 # spring constant
    tau = 2 * math.pi * unit.sqrt(mass/K) # time constant
    timestep = tau / 20.0
    platform = openmm.Platform.getPlatformByName(platform_name)

    # Create a 3D harmonic oscillator with context parameter controlling center of oscillator.
    system = openmm.System()
    system.addParticle(mass)
    energy_expression = '(K/2.0) * ((x-x0)^2 + y^2 + z^2);'
    force = openmm.CustomExternalForce(energy_expression)
    force.addGlobalParameter('K', K.in_unit_system(unit.md_unit_system))
    force.addGlobalParameter('x0', 0.0)
    force.addParticle(0, [])
    system.addForce(force)

    # Set the positions at the origin.
    positions = unit.Quantity(np.zeros([1, 3], np.float32), unit.angstroms)
    functions = { 'x0' : 'lambda' } # drag spring center x0

    from perses.annihilation import NCMCVVAlchemicalIntegrator, NCMCGHMCAlchemicalIntegrator
    if ncmc_integrator=="VV":
        ncmc_insert = NCMCVVAlchemicalIntegrator(temperature, system, functions, direction='insert', nsteps=ncmc_nsteps, timestep=timestep) # 'insert' drags lambda from 0 -> 1
        ncmc_delete = NCMCVVAlchemicalIntegrator(temperature, system, functions, direction='delete', nsteps=ncmc_nsteps, timestep=timestep) # 'insert' drags lambda from 0 -> 1
    elif ncmc_integrator=="GHMC":
        ncmc_insert = NCMCGHMCAlchemicalIntegrator(temperature, system, functions, direction='insert', collision_rate=9.1/unit.picoseconds, nsteps=ncmc_nsteps, timestep=timestep) # 'insert' drags lambda from 0 -> 1
        ncmc_delete = NCMCGHMCAlchemicalIntegrator(temperature, system, functions, direction='delete', collision_rate=9.1/unit.picoseconds, nsteps=ncmc_nsteps, timestep=timestep) # 'insert' drags lambda from 0 -> 1
    else:
        raise Exception("%s not recognized as integrator name. Options are VV and GHMC" % ncmc_integrator)

    # Run NCMC switching trials where the spring center is switched with lambda: 0 -> 1 over a finite number of steps.
    w_f = collect_switching_data(system, positions, functions, temperature, collision_rate, timestep, platform, ncmc_integrator=ncmc_insert, ncmc_nsteps=ncmc_nsteps, direction='insert')
    w_r = collect_switching_data(system, positions, functions, temperature, collision_rate, timestep, platform, ncmc_integrator=ncmc_delete, ncmc_nsteps=ncmc_nsteps, direction='delete')

    from pymbar import BAR
    [df, ddf] = BAR(w_f, w_r, method='self-consistent-iteration')
    print('%8.3f +- %.3f kT' % (df, ddf))
    if (abs(df) > NSIGMA_MAX * ddf):
        msg = 'Delta F (%d steps switching) = %f +- %f kT; should be within %f sigma of 0' % (ncmc_nsteps, df, ddf, NSIGMA_MAX)
        msg += '\n'
        msg += 'w_f = %s\n' % str(w_f)
        msg += 'w_r = %s\n' % str(w_r)
        raise Exception(msg)
Exemplo n.º 17
0
def computeHarmonicOscillatorExpectations(K, mass, temperature):
    """
    Compute mean and variance of potential and kinetic energies for a 3D harmonic oscillator.
    
    NOTES

    Numerical quadrature is used to compute the mean and standard deviation of the potential energy.
    Mean and standard deviation of the kinetic energy, as well as the absolute free energy, is computed analytically.
    
    ARGUMENTS
    
    K (simtk.unit.Quantity) - spring constant
    mass (simtk.unit.Quantity) - mass of particle
    temperature (simtk.unit.Quantity) - temperature
    
    RETURNS
    
    values (dict)
    
    """

    values = dict()

    # Compute thermal energy and inverse temperature from specified temperature.
    kB = units.BOLTZMANN_CONSTANT_kB * units.AVOGADRO_CONSTANT_NA
    kT = kB * temperature # thermal energy
    beta = 1.0 / kT # inverse temperature
   
    # Compute standard deviation along one dimension.
    sigma = 1.0 / units.sqrt(beta * K) 

    # Define limits of integration along r.
    r_min = 0.0 * units.nanometers # initial value for integration
    r_max = 10.0 * sigma      # maximum radius to integrate to

    # Compute mean and std dev of potential energy.
    V = lambda r : (K/2.0) * (r*units.nanometers)**2 / units.kilojoules_per_mole # potential in kJ/mol, where r in nm
    q = lambda r : 4.0 * math.pi * r**2 * math.exp(-beta * (K/2.0) * (r*units.nanometers)**2) # q(r), where r in nm
    (IqV2, dIqV2) = scipy.integrate.quad(lambda r : q(r) * V(r)**2, r_min / units.nanometers, r_max / units.nanometers)
    (IqV, dIqV)   = scipy.integrate.quad(lambda r : q(r) * V(r), r_min / units.nanometers, r_max / units.nanometers)
    (Iq, dIq)     = scipy.integrate.quad(lambda r : q(r), r_min / units.nanometers, r_max / units.nanometers)
    values['potential'] = dict()
    values['potential']['mean'] = (IqV / Iq) * units.kilojoules_per_mole
    values['potential']['stddev'] = (IqV2 / Iq) * units.kilojoules_per_mole   
    
    # Compute mean and std dev of kinetic energy.
    values['kinetic'] = dict()
    values['kinetic']['mean'] = (3./2.) * kT
    values['kinetic']['stddev'] = math.sqrt(3./2.) * kT

    # Compute dimensionless free energy.
    # f = - \ln \int_{-\infty}^{+\infty} \exp[-\beta K x^2 / 2] 
    #   = - \ln \int_{-\infty}^{+\infty} \exp[-x^2 / 2 \sigma^2] 
    #   = - \ln [\sqrt{2 \pi} \sigma]
    values['f'] = - numpy.log(2 * numpy.pi * (sigma / units.angstroms)**2) * (3.0/2.0)

    return values   
Exemplo n.º 18
0
def computeHarmonicOscillatorExpectations(K, mass, temperature):
    """
   Compute mean and variance of potential and kinetic energies for harmonic oscillator.

   Numerical quadrature is used.

   ARGUMENTS

   K - spring constant
   mass - mass of particle
   temperature - temperature

   RETURNS

   values

   """

    values = dict()

    # Compute thermal energy and inverse temperature from specified temperature.
    kB = units.BOLTZMANN_CONSTANT_kB * units.AVOGADRO_CONSTANT_NA
    kT = kB * temperature  # thermal energy
    beta = 1.0 / kT  # inverse temperature

    # Compute standard deviation along one dimension.
    sigma = 1.0 / units.sqrt(beta * K)

    # Define limits of integration along r.
    r_min = 0.0 * units.nanometers  # initial value for integration
    r_max = 10.0 * sigma  # maximum radius to integrate to

    # Compute mean and std dev of potential energy.
    V = lambda r: (K / 2.0) * (
        r * units.nanometers
    )**2 / units.kilojoules_per_mole  # potential in kJ/mol, where r in nm
    q = lambda r: 4.0 * math.pi * r**2 * math.exp(-beta * (K / 2.0) * (
        r * units.nanometers)**2)  # q(r), where r in nm
    (IqV2, dIqV2) = scipy.integrate.quad(lambda r: q(r) * V(r)**2,
                                         r_min / units.nanometers,
                                         r_max / units.nanometers)
    (IqV, dIqV) = scipy.integrate.quad(lambda r: q(r) * V(r),
                                       r_min / units.nanometers,
                                       r_max / units.nanometers)
    (Iq, dIq) = scipy.integrate.quad(lambda r: q(r), r_min / units.nanometers,
                                     r_max / units.nanometers)
    values['potential'] = dict()
    values['potential']['mean'] = (IqV / Iq) * units.kilojoules_per_mole
    values['potential']['stddev'] = (IqV2 / Iq) * units.kilojoules_per_mole

    # Compute mean and std dev of kinetic energy.
    values['kinetic'] = dict()
    values['kinetic']['mean'] = (3. / 2.) * kT
    values['kinetic']['stddev'] = math.sqrt(3. / 2.) * kT

    return values
Exemplo n.º 19
0
def get_harmonic_testsystem(temperature=300 * unit.kelvin,
                            sigma=1.0 * unit.angstroms,
                            mass=39.948 * unit.amus):
    """
    simple function to generate a tractable Harmonic Oscillator testsystem

    arguments
        temperature : float * unit.kelvin (or any temperature unit)
            temperature of oscillator
        sigma : float * unit.angstroms (or any length unit)
            the standard deviation of the Harmonic Oscillator
        mass : float * unit.amus (or any mass unit)
            reduced mass of the particles
    returns
        testsystem : openmmtools.testsystems.HarmonicOscillator
            test system to return
        period : float * unit.picoseconds (or any time unit)
            period of oscillator
        collision_rate : float / unit.picoseconds (or any time unit)
            collision rate of oscillator
        timestep : float * unit.picoseconds (or any time unit)
            timestep of the oscillator for MD
        alchemical_functions : dict
            dict of alchemical functions; {<name of alchemical parameter>: lepton-readable function}
    """
    from openmmtools.testsystems import HarmonicOscillator

    #define parameters for the harmonic oscillator
    kT = kB * temperature
    beta = 1. / kT
    K = kT / sigma**2
    period = unit.sqrt(mass / K)
    timestep = period / 20.
    collision_rate = 1. / period

    #define some alchemical parameters
    parameters = dict()
    parameters['testsystems_HarmonicOscillator_x0'] = (0 * sigma, 2 * sigma)
    parameters['testsystems_HarmonicOscillator_U0'] = (0 * kT, 1 * kT)
    lambda_name = 'fractional_iteration'
    alchemical_functions = {
        name:
        f'(1-{lambda_name})*{value[0].value_in_unit_system(unit.md_unit_system)} + {lambda_name}*{value[1].value_in_unit_system(unit.md_unit_system)}'
        for (name, value) in parameters.items()
    }

    testsystem = HarmonicOscillator(K=K, mass=mass)
    system = testsystem.system
    positions = testsystem.positions
    return testsystem, period, collision_rate, timestep, alchemical_functions
Exemplo n.º 20
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)
def computeHarmonicOscillatorExpectations(K, mass, temperature):
   """
   Compute mean and variance of potential and kinetic energies for harmonic oscillator.

   Numerical quadrature is used.

   ARGUMENTS

   K - spring constant
   mass - mass of particle
   temperature - temperature

   RETURNS

   values

   """

   values = dict()

   # Compute thermal energy and inverse temperature from specified temperature.
   kB = units.BOLTZMANN_CONSTANT_kB * units.AVOGADRO_CONSTANT_NA
   kT = kB * temperature # thermal energy
   beta = 1.0 / kT # inverse temperature
   
   # Compute standard deviation along one dimension.
   sigma = 1.0 / units.sqrt(beta * K) 

   # Define limits of integration along r.
   r_min = 0.0 * units.nanometers # initial value for integration
   r_max = 10.0 * sigma      # maximum radius to integrate to

   # Compute mean and std dev of potential energy.
   V = lambda r : (K/2.0) * (r*units.nanometers)**2 / units.kilojoules_per_mole # potential in kJ/mol, where r in nm
   q = lambda r : 4.0 * math.pi * r**2 * math.exp(-beta * (K/2.0) * (r*units.nanometers)**2) # q(r), where r in nm
   (IqV2, dIqV2) = scipy.integrate.quad(lambda r : q(r) * V(r)**2, r_min / units.nanometers, r_max / units.nanometers)
   (IqV, dIqV)   = scipy.integrate.quad(lambda r : q(r) * V(r), r_min / units.nanometers, r_max / units.nanometers)
   (Iq, dIq)     = scipy.integrate.quad(lambda r : q(r), r_min / units.nanometers, r_max / units.nanometers)
   values['potential'] = dict()
   values['potential']['mean'] = (IqV / Iq) * units.kilojoules_per_mole
   values['potential']['stddev'] = (IqV2 / Iq) * units.kilojoules_per_mole   
   
   # Compute mean and std dev of kinetic energy.
   values['kinetic'] = dict()
   values['kinetic']['mean'] = (3./2.) * kT
   values['kinetic']['stddev'] = math.sqrt(3./2.) * kT

   return values
Exemplo n.º 22
0
 def __init__(self, forceField='amber14-all', water=None, boxLength=25*unit.angstroms,
              bareSystem=False):
     pdb = app.PDBFile(os.path.join(afed.__path__[0], 'data', 'alanine-dipeptide.pdb'))
     if water is None:
         force_field = app.ForceField(f'{forceField}.xml')
         self._topology = pdb.topology
         self._positions = pdb.positions
     else:
         force_field = app.ForceField(f'{forceField}.xml', f'{water}.xml')
         modeller = app.Modeller(pdb.topology, pdb.positions)
         modeller.addSolvent(force_field, model=water, boxSize=boxLength*openmm.Vec3(1, 1, 1))
         self._topology = modeller.topology
         self._positions = modeller.positions
     self._system = force_field.createSystem(
         self._topology,
         nonbondedMethod=app.NoCutoff if water is None else app.PME,
         constraints=None,
         rigidWater=False,
         removeCMMotion=False,
     )
     if bareSystem:
         return
     atoms = [(a.name, a.residue.name) for a in self._topology.atoms()]
     psi_atoms = [('N', 'ALA'), ('CA', 'ALA'), ('C', 'ALA'), ('N', 'NME')]
     self._psi_angle = openmm.CustomTorsionForce('theta')
     self._psi_angle.addTorsion(*[atoms.index(i) for i in psi_atoms], [])
     phi_atoms = [('C', 'ACE'), ('N', 'ALA'), ('CA', 'ALA'), ('C', 'ALA')]
     self._phi_angle = openmm.CustomTorsionForce('theta')
     self._phi_angle.addTorsion(*[atoms.index(i) for i in phi_atoms], [])
     period = 360*unit.degrees
     self._psi = afed.DrivenCollectiveVariable('psi', self._psi_angle, unit.radians, period)
     self._phi = afed.DrivenCollectiveVariable('phi', self._phi_angle, unit.radians, period)
     value = 180*unit.degrees
     minval = -value
     maxval = value
     T = 1500*unit.kelvin
     mass = 168.0*unit.dalton*(unit.angstroms/unit.radian)**2
     velocity_scale = unit.sqrt(unit.BOLTZMANN_CONSTANT_kB*unit.AVOGADRO_CONSTANT_NA*T/mass)
     self._psi_driver = afed.DriverParameter('psi_s', unit.radians, value, T, velocity_scale,
                                             minval, maxval, periodic=True)
     self._phi_driver = afed.DriverParameter('phi_s', unit.radians, value, T, velocity_scale,
                                             minval, maxval, periodic=True)
     self._driving_force = afed.HarmonicDrivingForce()
     K = 2.78E3*unit.kilocalories_per_mole/unit.radians**2
     self._driving_force.addPair(self._psi, self._psi_driver, K)
     self._driving_force.addPair(self._phi, self._phi_driver, K)
     self._system.addForce(self._driving_force)
Exemplo n.º 23
0
    def __init__(self, **kwargs):
        super(HarmonicOscillatorSimulatedTempering, self).__init__(**kwargs)
        self.description = 'Harmonic oscillator simulated tempering simulation'

        # Create topology, positions, and system.
        from openmmtools.testsystems import HarmonicOscillator
        K = 1.0 * unit.kilocalories_per_mole / unit.angstroms**2 # 3D harmonic oscillator spring constant
        mass = 39.948 * unit.amu # 3D harmonic oscillator particle mass
        period = 2.0 * np.pi * unit.sqrt(mass / K) # harmonic oscillator period
        timestep = 0.01 * period
        testsystem = HarmonicOscillator(K=K, mass=mass)
        self.topology = testsystem.topology
        self.positions = testsystem.positions
        self.system = testsystem.system

        # Create thermodynamic states.
        Tmin = 100 * unit.kelvin
        Tmax = 1000 * unit.kelvin
        ntemps = 8 # number of temperatures
        from sams import ThermodynamicState
        temperatures = unit.Quantity(np.logspace(np.log10(Tmin / unit.kelvin), np.log10(Tmax / unit.kelvin), ntemps), unit.kelvin)
        self.thermodynamic_states = [ ThermodynamicState(system=self.system, temperature=temperature) for temperature in temperatures ]

        # Compute analytical logZ for each thermodynamic state.
        self.logZ = np.zeros([ntemps], np.float64)
        for (index, thermodynamic_state) in enumerate(self.thermodynamic_states):
            beta = thermodynamic_state.beta
            self.logZ[index] = - 1.5 * np.log(beta * K * unit.angstrom**2)
        self.logZ[:] -= self.logZ[0]

        # Create SAMS samplers
        from sams.samplers import SamplerState, MCMCSampler, ExpandedEnsembleSampler, SAMSSampler
        thermodynamic_state_index = 0 # initial thermodynamic state index
        thermodynamic_state = self.thermodynamic_states[thermodynamic_state_index]
        sampler_state = SamplerState(positions=self.positions)
        self.mcmc_sampler = MCMCSampler(sampler_state=sampler_state, thermodynamic_state=thermodynamic_state, ncfile=self.ncfile)
        self.mcmc_sampler.pdbfile = open('output.pdb', 'w')
        self.mcmc_sampler.topology = self.topology
        self.mcmc_sampler.timestep = timestep
        self.mcmc_sampler.collision_rate = 1.0 / (100 * timestep)
        self.mcmc_sampler.nsteps = 1000
        self.mcmc_sampler.verbose = True
        self.exen_sampler = ExpandedEnsembleSampler(self.mcmc_sampler, self.thermodynamic_states)
        self.exen_sampler.verbose = True
        self.sams_sampler = SAMSSampler(self.exen_sampler, update_stages='two-stage', update_method='optimal')
        self.sams_sampler.verbose = True
Exemplo n.º 24
0
 def get14Interactions(self):
     """Return list of atom pairs, chargeProduct, rMin and epsilon for each 1-4 interaction"""
     dihedralPointers = self._raw_data["DIHEDRALS_INC_HYDROGEN"] \
                       +self._raw_data["DIHEDRALS_WITHOUT_HYDROGEN"]
     returnList=[]
     charges=self.getCharges()
     nonbondTerms = self.getNonbondTerms()
     for ii in range(0,len(dihedralPointers),5):
          if int(dihedralPointers[ii+2])>0 and int(dihedralPointers[ii+3])>0:
              iAtom = int(dihedralPointers[ii])/3
              lAtom = int(dihedralPointers[ii+3])/3
              chargeProd = charges[iAtom]*charges[lAtom]
              (rVdwI, epsilonI) = nonbondTerms[iAtom]
              (rVdwL, epsilonL) = nonbondTerms[lAtom]
              rMin = (rVdwI+rVdwL)
              epsilon = units.sqrt(epsilonI*epsilonL)
              returnList.append((iAtom, lAtom, chargeProd, rMin, epsilon))
     return returnList
Exemplo n.º 25
0
def generateMaxwellBoltzmannVelocities(system, temperature):
    """Generate Maxwell-Boltzmann velocities.
   
   ARGUMENTS
   
   system (simtk.openmm.System) - the system for which velocities are to be assigned
   temperature (simtk.unit.Quantity of temperature) - the temperature at which velocities are to be assigned
   
   RETURNS
   
   velocities (simtk.unit.Quantity of numpy Nx3 array, units length/time) - particle velocities
   
   TODO

   This could be sped up by introducing vector operations.
   
   """

    # Get number of atoms
    natoms = system.getNumParticles()

    # Create storage for velocities.
    velocities = units.Quantity(
        numpy.zeros([natoms, 3],
                    numpy.float32), units.nanometer / units.picosecond
    )  # velocities[i,k] is the kth component of the velocity of atom i

    # Compute thermal energy and inverse temperature from specified temperature.
    kB = units.BOLTZMANN_CONSTANT_kB * units.AVOGADRO_CONSTANT_NA
    kT = kB * temperature  # thermal energy
    beta = 1.0 / kT  # inverse temperature

    # Assign velocities from the Maxwell-Boltzmann distribution.
    for atom_index in range(natoms):
        mass = system.getParticleMass(atom_index)  # atomic mass
        sigma = units.sqrt(
            kT / mass
        )  # standard deviation of velocity distribution for each coordinate for this atom
        for k in range(3):
            velocities[atom_index, k] = sigma * numpy.random.normal()

    # Return velocities
    return velocities
Exemplo n.º 26
0
    def _assign_Maxwell_Boltzmann_velocities(self, system, temperature):
        """Generate Maxwell-Boltzmann velocities.

        @param system the system for which velocities are to be assigned
        @type simtk.chem.openmm.System or System
        
        @param temperature the temperature at which velocities are to be assigned
        @type Quantity with units of temperature

        @return velocities drawn from the Maxwell-Boltzmann distribution at the appropriate temperature
        @returntype (natoms x 3) numpy array wrapped in Quantity with units of velocity

        TODO

        This could be sped up by introducing vector operations.

        """

        # Get number of atoms
        natoms = system.getNumParticles()

        # Create storage for velocities.
        velocities = units.Quantity(
            numpy.zeros([natoms, 3],
                        numpy.float32), units.nanometer / units.picosecond
        )  # velocities[i,k] is the kth component of the velocity of atom i

        # Compute thermal energy and inverse temperature from specified temperature.
        kT = kB * temperature  # thermal energy
        beta = 1.0 / kT  # inverse temperature

        # Assign velocities from the Maxwell-Boltzmann distribution.
        for atom_index in range(natoms):
            mass = system.getParticleMass(atom_index)  # atomic mass
            sigma = units.sqrt(
                kT / mass
            )  # standard deviation of velocity distribution for each coordinate for this atom
            for k in range(3):
                velocities[atom_index, k] = sigma * numpy.random.normal()

        # Return velocities
        return velocities
def generateMaxwellBoltzmannVelocities(system, temperature):
   """Generate Maxwell-Boltzmann velocities.
   
   ARGUMENTS
   
   system (simtk.openmm.System) - the system for which velocities are to be assigned
   temperature (simtk.unit.Quantity of temperature) - the temperature at which velocities are to be assigned
   
   RETURNS
   
   velocities (simtk.unit.Quantity of numpy Nx3 array, units length/time) - particle velocities
   
   TODO

   This could be sped up by introducing vector operations.
   
   """
   
   # Get number of atoms
   natoms = system.getNumParticles()
   
   # Create storage for velocities.        
   velocities = units.Quantity(numpy.zeros([natoms, 3], numpy.float32), units.nanometer / units.picosecond) # velocities[i,k] is the kth component of the velocity of atom i
   
   # Compute thermal energy and inverse temperature from specified temperature.
   kB = units.BOLTZMANN_CONSTANT_kB * units.AVOGADRO_CONSTANT_NA
   kT = kB * temperature # thermal energy
   beta = 1.0 / kT # inverse temperature
   
   # Assign velocities from the Maxwell-Boltzmann distribution.
   for atom_index in range(natoms):
      mass = system.getParticleMass(atom_index) # atomic mass
      sigma = units.sqrt(kT / mass) # standard deviation of velocity distribution for each coordinate for this atom
      for k in range(3):
         velocities[atom_index,k] = sigma * numpy.random.normal()

   # Return velocities
   return velocities
Exemplo n.º 28
0
    def _update_nonbonded_force(self, group, nbforce, parameters,
                                pbc_for_exceptions):
        internal_exception_pairs = []
        for index in range(nbforce.getNumExceptions()):
            i, j, _, _, epsilon = nbforce.getExceptionParameters(index)
            i_in_group, j_in_group = i in group, j in group
            if i_in_group and j_in_group:
                internal_exception_pairs.append(set([i, j]))
            elif (i_in_group or j_in_group):
                raise ValueError(
                    "No exceptions are allowed for in-group/out-group interactions"
                )

        for i, j in itertools.combinations(group, 2):
            if set([i, j]) not in internal_exception_pairs:
                chargeprod = parameters[i].charge * parameters[j].charge
                sigma = (parameters[i].sigma + parameters[j].sigma) / 2
                epsilon = unit.sqrt(parameters[i].epsilon *
                                    parameters[j].epsilon)
                nbforce.addException(i, j, chargeprod, sigma, epsilon)

        if pbc_for_exceptions:
            nbforce.setExceptionsUsePeriodicBoundaryConditions(True)
Exemplo n.º 29
0
    def _assign_Maxwell_Boltzmann_velocities(self, system, temperature):
        """Generate Maxwell-Boltzmann velocities.

        @param system the system for which velocities are to be assigned
        @type simtk.chem.openmm.System or System
        
        @param temperature the temperature at which velocities are to be assigned
        @type Quantity with units of temperature

        @return velocities drawn from the Maxwell-Boltzmann distribution at the appropriate temperature
        @returntype (natoms x 3) numpy array wrapped in Quantity with units of velocity

        TODO

        This could be sped up by introducing vector operations.

        """

        # Get number of atoms
        natoms = system.getNumParticles()

        # Create storage for velocities.        
        velocities = units.Quantity(numpy.zeros([natoms, 3], numpy.float32), units.nanometer / units.picosecond) # velocities[i,k] is the kth component of the velocity of atom i
  
        # Compute thermal energy and inverse temperature from specified temperature.
        kT = kB * temperature # thermal energy
        beta = 1.0 / kT # inverse temperature
  
        # Assign velocities from the Maxwell-Boltzmann distribution.
        for atom_index in range(natoms):
            mass = system.getParticleMass(atom_index) # atomic mass
            sigma = units.sqrt(kT / mass) # standard deviation of velocity distribution for each coordinate for this atom
            for k in range(3):
                velocities[atom_index,k] = sigma * numpy.random.normal()

        # Return velocities
        return velocities
Exemplo n.º 30
0
    def createMergedTopology(self, alchemical_lambda, mm=None, verbose=False):
        """
        Create a merged topology file with the specified alchemical lambda value for interpolating between molecules A and B.

        ARGUMENTS

        alchemical_lambda (float) - the alchemical lambda in interval [0,1] for interpolating between molecule A (alchemical_lambda = 0) and molecule B (alchemical_lambda = 1),

        OPTIONAL ARGUMENTS

        mm (implements simtk.openmm interface) - OpenMM API implementation to use (default: simtk.openmm)

        TODO

        EXAMPLES

        NOTES

        Merged molecule will contain atom groups in this order:

        S_AB : [atoms in A and B]
        S_A  : [atoms in A and not B]
        S_B  : [atoms in B and not A]

        Masses in S_AB are the geometric product of their masses in A and B.

        """

        # Record timing statistics.
        if verbose: print "Creating merged topology corresponding to alchemical lamdba of %f..." % alchemical_lambda

        # Get local references to systems A and B.
        system_A = self.system_A
        system_B = self.system_B
        corresponding_atoms = self.corresponding_atoms

        #
        # Construct atom sets and correspondence lists for A, B, and merged topology.
        # 

        # Determine number of atoms in each system.
        natoms_A = system_A.getNumParticles()  # number of atoms in molecule A
        natoms_B = system_B.getNumParticles()  # number of atoms in molecule B
        natoms_AandB = len(corresponding_atoms)  # number of atoms in both A and B
        natoms_AnotB = natoms_A - natoms_AandB          # number of atoms in A and not B
        natoms_BnotA = natoms_B - natoms_AandB          # number of atoms in B and not A
        natoms_merged = natoms_AandB + natoms_AnotB + natoms_BnotA  # number of atoms in merged topology

        # Determine sets of atoms shared and not shared.
        atomset_A_AandB = set([ index_A for (index_A, index_B) in corresponding_atoms ]) # atoms in molecule A and B (A molecule numbering)
        atomset_A_AnotB = set(range(natoms_A)) - atomset_A_AandB       # atoms in molecule A and not B (A molecule numbering)
        atomset_A_BnotA = set()

        atomset_B_AandB = set([ index_B for (index_A, index_B) in corresponding_atoms ]) # atoms in molecule B and A (B molecule numbering)
        atomset_B_BnotA = set(range(natoms_B)) - atomset_B_AandB       # atoms in molecule B and not A (B molecule numbering)
        atomset_B_AnotB = set()

        atomset_merged_AandB = set(range(natoms_AandB))                                                               # atoms present in A and B (merged molecule numbering)
        atomset_merged_AnotB = set(range(natoms_AandB, natoms_AandB + natoms_AnotB))                                  # atoms present in A and not B (merged molcule numbering)
        atomset_merged_BnotA = set(range(natoms_AandB + natoms_AnotB, natoms_AandB + natoms_AnotB + natomsBnotA))     # atoms present in B and not A (merged molecule numbering)

        # Construct lists of corresponding atom indices.
        atom_index = dict()

        #
        # Construct merged OpenMM system.
        #

        import simtk.unit as units
        
        # Select OpenMM API implementation to use.
        if not mm:
            import simtk.openmm 
            mm = simtk.openmm

        # Create new System object.
        system = mm.System()

        # Populate merged sytem with atoms.
        # Masses of atoms in both A and B are geometric mean; otherwise standard mass.
        # Add particles in A and B.
        for (index_A, index_B) in corresponding_atoms:
            # Create particle with geometric mean of masses.
            mass_A = system_A.getParticleMass(index_A)
            mass_B = system_B.getParticleMass(index_B)
            mass = units.sqrt(mass_A * mass_B)
            system.addParticle(mass)
        for index_A in atomlist_A_AnotB:
            mass_A = system_A.getParticleMass(index_A)
            system.addParticle(mass_A)
        for index_B in atomlist_B_BnotA:
            mass_B = system_B.getParticleMass(index_B)
            system.addParticle(mass_B)
        
        # Define helper function.
        def find_force(system, classname):
            """
            Find the specified Force object in an OpenMM System by classname.

            ARGUMENTS

            system (simtk.openmm.System) - system containing forces to be searched
            classname (string) - classname of Force object to locate

            RETURNS

            force (simtk.openmm.Force) - the first Force object encountered with the specified classname, or None if one could not be found
            """
            nforces = system.getNumForces()
            force = None
            for index in range(nforces):
                if isinstance(system.getForce(index), getattr(mm, classname)):
                    force = system.getForce(index)
            return force

        # Add bonds.
        # NOTE: This does not currently deal with bonds that are broken or formed during the transformation.
        force_A = find_force(system_A, 'HarmonicBondForce')             
        for index in range(force_A.getNumBonds()):            
            # Get bond parameters from molecule A.
            [iatom_A, jatom_A, length_A, k_A] = force.getBondParameters(index)
            # Translate atom indices to merged atom indices.
            (iatom_merged, jatom_merged) = (atom_indices['A'][iatom_A]['merged'], atom_indices['B'][jatom_A]['merged']) 
            # Store bond parameters for random access.
            bonds[(iatom_merged, jatom_merged)] = (length-A, k_A)
        force_B = find_force(system_B, 'HarmonicBondForce')             
        for index in range(force_B.getNumBonds()):            
            # Get bond parameters from molecule A.
            [iatom_B, jatom_B, length_B, k_B] = force.getBondParameters(index)
            # Translate atom indices to merged atom indices.
            (iatom_merged, jatom_merged) = (atom_indices['B'][iatom_B]['merged'], atom_indices['B'][jatom_B]['merged']) 
            # Store bond parameters for random access.
            if (iatom_merged, jatom_merged) in bonds:
                # Mix bonds.
                (length_A, k_A) = bonds[(iatom_merged, jatom_merged)]
                (length, k) = ( (1.0-alchemical_lambda)*length_A + alchemical_lambda*length_B, (1.0-alchemical_lambda)*k_A + alchemical_lambda*k_B )
                bonds[(iatom_merged, jatom_merged)] = (length, k)
            else:
                bonds[(iatom_merged, jatom_merged)] = (length_B, k_B)
        # Add bonds to merged topology.
        force = mm.HarmonicBondForce()
        for (iatom, jatom) in bonds:
            # Retrieve bond parameters.
            (length, j) = bonds[(iatom, jatom)]
            # Add bond.
            force.addBond(iatom, jatom, length, k)
        # Add the Force to the merged topology sytem.
        system.addForce(force)
Exemplo n.º 31
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)
            atomConstraints[c[0]].append((c[1], c[2]))
            atomConstraints[c[1]].append((c[0], c[2]))
    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 = units.sqrt(l1*l1 + l2*l2 - 2*l1*l2*units.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*units.elementary_charge**2, 1.0*units.angstroms, 0.0*units.kilocalories_per_mole)
    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]/units.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]/units.sqrt(distOH[res]**2-(0.5*distHH[res])**2)
                    angleHOH = 2*math.asin(0.5*distHH[res]/distOH[res])
                    lenCross = (distOH[res].value_in_unit(units.nanometer)**2)*math.sin(angleHOH)*units.nanometer
                    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")

    # TODO: Add GBVI terms?

    return system
#    ntrials = integrator.getGlobalVariable(global_variables['ntrials'])
#    print "accepted %d / %d (%.3f %%)" % (naccept, ntrials, float(naccept)/float(ntrials)*100.0)

    # Accumulate statistics.
    x_n[iteration] = state.getPositions(asNumpy=True)[0,0] / units.angstroms
    potential_n[iteration] = final_potential_energy / kT
    kinetic_n[iteration] = final_kinetic_energy / kT
    temperature_n[iteration] = instantaneous_temperature / units.kelvin
    delta_n[iteration] = delta_total_energy / kT


# Compute expected statistics for harmonic oscillator.
K = 100.0 * units.kilocalories_per_mole / units.angstroms**2
beta = 1.0 / kT
x_mean_exact = 0.0 # mean, in angstroms
x_std_exact = 1.0 / units.sqrt(beta * K) / units.angstroms # std dev, in angstroms

# Analyze statistics.
g = statisticalInefficiency(potential_n) 
Neff = niterations / g # number of effective samples

x_mean = x_n.mean()
dx_mean = x_n.std() / numpy.sqrt(Neff)
x_mean_error = x_mean - x_mean_exact

x_var = x_n.var()
dx_var = x_var * numpy.sqrt(2. / (Neff-1))

x_std = x_n.std()
dx_std = 0.5 * dx_var / x_std 
x_std_error = x_std - x_std_exact
def build_softcore_system(reference_system, receptor_atoms, ligand_atoms, valence_lambda, coulomb_lambda, vdw_lambda, annihilate=True):
    """
    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 for receptor atoms.
                    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 annihilate and (iatom in ligand_atoms) and (jatom in ligand_atoms):
                    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)                    
            if not annihilate:
                # Build a list of exceptions.
                exceptions = set()                    
                for exception_index in range(reference_force.getNumExceptions()):
                    [iatom, jatom, chargeprod, sigma, epsilon] = reference_force.getExceptionParameters(exception_index)
                    exceptions.add( (iatom,jatom) )
                    exceptions.add( (jatom,iatom) )
                # Add new intraligand interactions.
                for iatom in ligand_atoms:
                    for jatom in ligand_atoms:
                        if (iatom,jatom) not in exceptions:
                            exceptions.add( (iatom,jatom) )
                            exceptions.add( (jatom,iatom) )                            
                            [charge1, sigma1, epsilon1] = reference_force.getParticleParameters(iatom)
                            [charge2, sigma2, epsilon2] = reference_force.getParticleParameters(jatom)
                            chargeprod = charge1*charge2
                            sigma = 0.5 * (sigma1 + sigma2)
                            epsilon = units.sqrt(epsilon1*epsilon2)
                            #chargeprod = (charge1/units.elementary_charge) * (charge2/units.elementary_charge)
                            #sigma = 0.5 * (sigma1/units.nanometers + sigma2/units.nanometers)
                            #epsilon = numpy.sqrt((epsilon1/units.kilojoules_per_mole) * (epsilon2/units.kilojoules_per_mole))
                            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()

            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 for receptor atoms.
                    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
# Roughly corresponds to conditions from http://www.cstl.nist.gov/srs/LJ_PURE/mc.htm
nparticles = 500
mass = 39.9 * unit.amu
sigma = 3.4 * unit.angstrom
epsilon = 0.238 * unit.kilocalories_per_mole
#reduced_density = 0.860     # reduced_density = density * (sigma**3)
reduced_density = 0.960     # reduced_density = density * (sigma**3)
reduced_temperature = 0.850 # reduced_temperature = kB * temperature / epsilon
reduced_pressure = 1.2660   # reduced_pressure = pressure * (sigma**3) / epsilon

platform_name = 'CUDA'    # OpenMM platform name to use for simulation
platform = openmm.Platform.getPlatformByName(platform_name)
data_directory = 'data'     # Directory in which data is to be stored

r0 = 2.0**(1./6.) * sigma   # minimum potential distance for Lennard-Jones interaction
characteristic_timescale = unit.sqrt((mass * r0**2) / (72 * epsilon)) # characteristic timescale for bound Lennard-Jones interaction
                                                            # http://borisv.lk.net/matsc597c-1997/simulations/Lecture5/node3.html
timestep = 0.01 * characteristic_timescale # integrator timestep

# From http://www.cstl.nist.gov/srs/LJ_PURE/md.htm
#characteristic_timescale = unit.sqrt(mass * sigma**2 / epsilon)
#timestep = 0.05 * characteristic_timescale

print "characteristic timescale = %.3f ps" % (characteristic_timescale / unit.picoseconds)
print "timestep = %.12f ps" % (timestep / unit.picoseconds)

collision_rate = 5.0 / unit.picoseconds # collision rate for Langevin thermostat
barostat_frequency = 25 # number of steps between barostat updates

# Set parameters for number of simulation replicates, number of iterations per simulation, and number of steps per iteration.
nreplicates = 100
Exemplo n.º 35
0
    def createMergedTopology(self, alchemical_lambda, mm=None, verbose=False):
        """
        Create a merged topology file with the specified alchemical lambda value for interpolating between molecules A and B.

        ARGUMENTS

        alchemical_lambda (float) - the alchemical lambda in interval [0,1] for interpolating between molecule A (alchemical_lambda = 0) and molecule B (alchemical_lambda = 1),

        OPTIONAL ARGUMENTS

        mm (implements simtk.openmm interface) - OpenMM API implementation to use (default: simtk.openmm)

        TODO

        EXAMPLES

        NOTES

        Merged molecule will contain atom groups in this order:

        S_AB : [atoms in A and B]
        S_A  : [atoms in A and not B]
        S_B  : [atoms in B and not A]

        Masses in S_AB are the geometric product of their masses in A and B.

        """

        # Record timing statistics.
        if verbose:
            print "Creating merged topology corresponding to alchemical lamdba of %f..." % alchemical_lambda

        # Get local references to systems A and B.
        system_A = self.system_A
        system_B = self.system_B
        corresponding_atoms = self.corresponding_atoms

        #
        # Construct atom sets and correspondence lists for A, B, and merged topology.
        #

        # Determine number of atoms in each system.
        natoms_A = system_A.getNumParticles()  # number of atoms in molecule A
        natoms_B = system_B.getNumParticles()  # number of atoms in molecule B
        natoms_AandB = len(
            corresponding_atoms)  # number of atoms in both A and B
        natoms_AnotB = natoms_A - natoms_AandB  # number of atoms in A and not B
        natoms_BnotA = natoms_B - natoms_AandB  # number of atoms in B and not A
        natoms_merged = natoms_AandB + natoms_AnotB + natoms_BnotA  # number of atoms in merged topology

        # Determine sets of atoms shared and not shared.
        atomset_A_AandB = set([
            index_A for (index_A, index_B) in corresponding_atoms
        ])  # atoms in molecule A and B (A molecule numbering)
        atomset_A_AnotB = set(
            range(natoms_A)
        ) - atomset_A_AandB  # atoms in molecule A and not B (A molecule numbering)
        atomset_A_BnotA = set()

        atomset_B_AandB = set([
            index_B for (index_A, index_B) in corresponding_atoms
        ])  # atoms in molecule B and A (B molecule numbering)
        atomset_B_BnotA = set(
            range(natoms_B)
        ) - atomset_B_AandB  # atoms in molecule B and not A (B molecule numbering)
        atomset_B_AnotB = set()

        atomset_merged_AandB = set(
            range(natoms_AandB
                  ))  # atoms present in A and B (merged molecule numbering)
        atomset_merged_AnotB = set(
            range(natoms_AandB, natoms_AandB + natoms_AnotB)
        )  # atoms present in A and not B (merged molcule numbering)
        atomset_merged_BnotA = set(
            range(natoms_AandB + natoms_AnotB,
                  natoms_AandB + natoms_AnotB + natomsBnotA)
        )  # atoms present in B and not A (merged molecule numbering)

        # Construct lists of corresponding atom indices.
        atom_index = dict()

        #
        # Construct merged OpenMM system.
        #

        import simtk.unit as units

        # Select OpenMM API implementation to use.
        if not mm:
            import simtk.openmm
            mm = simtk.openmm

        # Create new System object.
        system = mm.System()

        # Populate merged sytem with atoms.
        # Masses of atoms in both A and B are geometric mean; otherwise standard mass.
        # Add particles in A and B.
        for (index_A, index_B) in corresponding_atoms:
            # Create particle with geometric mean of masses.
            mass_A = system_A.getParticleMass(index_A)
            mass_B = system_B.getParticleMass(index_B)
            mass = units.sqrt(mass_A * mass_B)
            system.addParticle(mass)
        for index_A in atomlist_A_AnotB:
            mass_A = system_A.getParticleMass(index_A)
            system.addParticle(mass_A)
        for index_B in atomlist_B_BnotA:
            mass_B = system_B.getParticleMass(index_B)
            system.addParticle(mass_B)

        # Define helper function.
        def find_force(system, classname):
            """
            Find the specified Force object in an OpenMM System by classname.

            ARGUMENTS

            system (simtk.openmm.System) - system containing forces to be searched
            classname (string) - classname of Force object to locate

            RETURNS

            force (simtk.openmm.Force) - the first Force object encountered with the specified classname, or None if one could not be found
            """
            nforces = system.getNumForces()
            force = None
            for index in range(nforces):
                if isinstance(system.getForce(index), getattr(mm, classname)):
                    force = system.getForce(index)
            return force

        # Add bonds.
        # NOTE: This does not currently deal with bonds that are broken or formed during the transformation.
        force_A = find_force(system_A, 'HarmonicBondForce')
        for index in range(force_A.getNumBonds()):
            # Get bond parameters from molecule A.
            [iatom_A, jatom_A, length_A, k_A] = force.getBondParameters(index)
            # Translate atom indices to merged atom indices.
            (iatom_merged,
             jatom_merged) = (atom_indices['A'][iatom_A]['merged'],
                              atom_indices['B'][jatom_A]['merged'])
            # Store bond parameters for random access.
            bonds[(iatom_merged, jatom_merged)] = (length - A, k_A)
        force_B = find_force(system_B, 'HarmonicBondForce')
        for index in range(force_B.getNumBonds()):
            # Get bond parameters from molecule A.
            [iatom_B, jatom_B, length_B, k_B] = force.getBondParameters(index)
            # Translate atom indices to merged atom indices.
            (iatom_merged,
             jatom_merged) = (atom_indices['B'][iatom_B]['merged'],
                              atom_indices['B'][jatom_B]['merged'])
            # Store bond parameters for random access.
            if (iatom_merged, jatom_merged) in bonds:
                # Mix bonds.
                (length_A, k_A) = bonds[(iatom_merged, jatom_merged)]
                (length, k) = ((1.0 - alchemical_lambda) * length_A +
                               alchemical_lambda * length_B,
                               (1.0 - alchemical_lambda) * k_A +
                               alchemical_lambda * k_B)
                bonds[(iatom_merged, jatom_merged)] = (length, k)
            else:
                bonds[(iatom_merged, jatom_merged)] = (length_B, k_B)
        # Add bonds to merged topology.
        force = mm.HarmonicBondForce()
        for (iatom, jatom) in bonds:
            # Retrieve bond parameters.
            (length, j) = bonds[(iatom, jatom)]
            # Add bond.
            force.addBond(iatom, jatom, length, k)
        # Add the Force to the merged topology sytem.
        system.addForce(force)
Exemplo n.º 36
0
def equilibrate_ghmc(system,
                     timestep,
                     collision_rate,
                     temperature,
                     masses,
                     sqrt_kT_over_m,
                     positions,
                     platform,
                     debug=False):

    nsteps = 500  # number of steps
    kT = kB * temperature
    beta = 1.0 / kT  # inverse temperature

    print "Equilibrating for %d GHMC steps (%.3f ps)..." % (
        nsteps, (nsteps * timestep) / units.picoseconds)
    initial_time = time.time()

    # Assign Maxwell-Boltzmann velocities
    velocities = sqrt_kT_over_m * numpy.random.standard_normal(
        size=sqrt_kT_over_m.shape)

    # Compute Langevin velocity modification factors.
    gamma = collision_rate * masses
    sigma2 = (2.0 * kT * gamma)
    alpha_factor = (1.0 - (timestep / 4.0) * collision_rate) / (
        1.0 + (timestep / 4.0) * collision_rate)
    x = (timestep / 2.0 * sigma2).in_units_of(
        units.kilogram**2 / units.mole**2 * units.meter**2 / units.second**2)
    y = units.Quantity(numpy.sqrt(x / x.unit), units.sqrt(1.0 * x.unit))
    beta_factor = (y / (1.0 + (timestep / 4.0) * collision_rate) /
                   masses).in_units_of(velocities.unit)

    # Create integrator and context.
    integrator = openmm.VerletIntegrator(timestep)
    context = openmm.Context(system, integrator, platform)

    # Compute forces and total energy.
    context.setPositions(positions)
    context.setVelocities(velocities)
    state = context.getState(getForces=True, getEnergy=True)
    forces = state.getForces(asNumpy=True)
    kinetic_energy = state.getKineticEnergy()
    potential_energy = state.getPotentialEnergy()
    total_energy = kinetic_energy + potential_energy

    # Create storage for proposed positions and velocities.
    proposed_positions = copy.deepcopy(positions)
    proposed_velocities = copy.deepcopy(velocities)

    naccepted = 0  # number of accepted GHMC steps
    for step in range(nsteps):
        #
        # Velocity modification step.
        #

        velocities[:, :] = velocities[:, :] * alpha_factor + units.Quantity(
            numpy.random.standard_normal(size=positions.shape) *
            (beta_factor / beta_factor.unit), beta_factor.unit)
        kinetic_energy = 0.5 * (masses * velocities**2).in_units_of(
            potential_energy.unit
        ).sum(
        ) * potential_energy.unit  # have to do this because sum(...) and .sum() don't respect units
        total_energy = kinetic_energy + potential_energy

        #
        # Metropolis-wrapped Velocity Verlet step
        #

        proposed_positions[:, :] = positions[:, :]
        proposed_velocities[:, :] = velocities[:, :]

        # Half-kick velocities
        proposed_velocities[:, :] += 0.5 * forces[:, :] / masses[:, :] * timestep

        # Full-kick positions
        proposed_positions[:, :] += proposed_velocities[:, :] * timestep

        # Update force at new positions.
        context.setVelocities(proposed_velocities)
        context.setPositions(proposed_positions)
        state = context.getState(getForces=True, getEnergy=True)
        proposed_forces = state.getForces(asNumpy=True)
        proposed_potential_energy = state.getPotentialEnergy()

        # Half-kick velocities
        proposed_velocities[:, :] += 0.5 * proposed_forces[:, :] / masses[:, :] * timestep
        proposed_kinetic_energy = 0.5 * (
            masses * proposed_velocities**2
        ).in_units_of(potential_energy.unit).sum(
        ) * potential_energy.unit  # have to do this because sum(...) and .sum() don't respect units

        # Compute new total energy.
        proposed_total_energy = proposed_kinetic_energy + proposed_potential_energy

        # Accept or reject, inverting momentum if rejected.
        du = beta * (proposed_total_energy - total_energy)
        if (du < 0.0) or (numpy.random.uniform() < numpy.exp(-du)):
            # Accept and update positions, velocities, forces, and energies.
            naccepted += 1
            positions[:, :] = proposed_positions[:, :]
            velocities[:, :] = proposed_velocities[:, :]
            forces[:, :] = proposed_forces[:, :]
            potential_energy = proposed_potential_energy
            kinetic_energy = proposed_kinetic_energy
        else:
            # Reject, requiring negation of velocities.
            velocities[:, :] = -velocities[:, :]

        #
        # Velocity modification step.
        #

        velocities[:, :] = velocities[:, :] * alpha_factor + units.Quantity(
            numpy.random.standard_normal(size=positions.shape) *
            (beta_factor / beta_factor.unit), beta_factor.unit)
        kinetic_energy = 0.5 * (masses * velocities**2).in_units_of(
            potential_energy.unit
        ).sum(
        ) * potential_energy.unit  # have to do this because sum(...) and .sum() don't respect units
        total_energy = kinetic_energy + potential_energy

    # Print final statistics.
    fraction_accepted = float(naccepted) / float(nsteps)
    final_time = time.time()
    elapsed_time = final_time - initial_time
    if debug:
        print "%12.3f s elapsed | accepted %6.3f%%" % (
            elapsed_time, fraction_accepted * 100.0)

    return [positions, velocities, fraction_accepted]
Exemplo n.º 37
0
def main():
    import doctest
    import argparse

    parser = argparse.ArgumentParser(description="Check OpenMM computed energies and forces across all platforms for a suite of test systems.")
    parser.add_argument('-o', '--outfile', dest='logfile', action='store', type=str, default=None)
    parser.add_argument('-v', dest='verbose', action='store_true')
    parser.add_argument('-i', '--input', dest="input_data_path", action="store", type=str)
    parser.add_argument('-t', '--tuneplatform', dest="tune_pme_platform", action="store", type=str, default=None)
    parser.add_argument('-p', '--precision', dest="precision", action="store", type=str, default='single')
    args = parser.parse_args()

    verbose = args.verbose # Don't display extra debug information.
    config_root_logger(verbose, log_file_path=args.logfile)

    # Print version.
    logger.info("OpenMM version: %s" % openmm.version.version)
    logger.info("")

    # List all available platforms
    logger.info("Available platforms:")
    for platform_index in range(openmm.Platform.getNumPlatforms()):
        platform = openmm.Platform.getPlatform(platform_index)
        logger.info("%5d %s" % (platform_index, platform.getName()))
    logger.info("")

    # Test all systems on Reference platform.
    platform = openmm.Platform.getPlatformByName("Reference")
    print('Testing Reference platform...')
    doctest.testmod()

    # Compute energy error made on all test systems for other platforms.
    # Make a count of how often set tolerance is exceeded.
    tests_failed = 0 # number of times tolerance is exceeded
    tests_passed = 0 # number of times tolerance is not exceeded
    logger.info("%16s%16s %16s          %16s          %16s          %16s" % ("platform", "precision", "potential", "error", "force mag", "rms error"))
    reference_platform = openmm.Platform.getPlatformByName("Reference")
    n_runs=get_num_runs(args.input_data_path)
    for run in range(n_runs):
        print("Deserializing XML files for RUN%d" % run)
        state = XmlSerializer.deserialize(read_file(os.path.join(args.input_data_path,"RUN%d" % run, "state0.xml")))
        integrator = XmlSerializer.deserialize(read_file(os.path.join(args.input_data_path,"RUN%d" % run, "integrator.xml")))
        system = XmlSerializer.deserialize(read_file(os.path.join(args.input_data_path,"RUN%d" % run, "system.xml")))
        
        # Update system periodic box vectors based on state.
        system.setDefaultPeriodicBoxVectors(*state.getPeriodicBoxVectors())

        # Create test system instance.
        positions = state.getPositions()

        # Get PME parameters
        forces = [ system.getForce(force_index) for force_index in range(system.getNumForces()) ]
        force_dict = { force.__class__.__name__ : force for force in forces }
        print("PME parameters:")
        force = force_dict['NonbondedForce']
        print(force.getPMEParameters())
        (alpha, nx, ny, nz) = force.getPMEParameters()
        if alpha == 0.0 / unit.nanometers:
            # Set PME parameters explicitly.
            print("Setting PME parameters explicitly...")
            (alpha, nx, ny, nz) = calc_pme_parameters(system)
            print (alpha, nx, ny, nz)
            print(type(nx))
            force.setPMEParameters(alpha, int(nx), int(ny), int(nz))
            print(force.getPMEParameters())

        if args.tune_pme_platform:
            # Tune PME parameters for specified platform.
            from optimizepme import optimizePME
            properties = dict()
            platform = openmm.Platform.getPlatformByName(args.tune_pme_platform)
            print("Tuning PME parameters for platform '%s' precision model '%s'..." % (platform.getName(), args.precision))
            if (platform.getName() == 'OpenCL'):
                properties['OpenCLPrecision'] = args.precision
            elif (platform.getName() == 'CUDA'):
                properties['CudaPrecision'] = args.precision
            minCutoff = 0.8 * unit.nanometers
            maxCutoff = 1.2 * unit.nanometers
            optimizePME(system, integrator, positions, platform, properties, minCutoff, maxCutoff)

        class_name = 'RUN%d' % run
        logger.info("%s (%d atoms)" % (class_name, system.getNumParticles()))

        # Compute reference potential and force
        [reference_potential, reference_force] = compute_potential_and_force(system, positions, reference_platform)

        # Test all platforms.
        test_success = True
        for platform_index in range(openmm.Platform.getNumPlatforms()):
            try:
                platform = openmm.Platform.getPlatform(platform_index)
                platform_name = platform.getName()

                # Define precision models to test.
                if platform_name == 'Reference':
                    precision_models = ['double']
                else:
                    precision_models = ['single']
                    if platform.supportsDoublePrecision():
                        precision_models.append('double')

                for precision_model in precision_models:
                    # Set precision.
                    if platform_name == 'CUDA':
                        platform.setPropertyDefaultValue('CudaPrecision', precision_model)
                    if platform_name == 'OpenCL':
                        platform.setPropertyDefaultValue('OpenCLPrecision', precision_model)

                    # Compute potential and force.
                    [platform_potential, platform_force] = compute_potential_and_force(system, positions, platform)

                    # Compute error in potential.
                    potential_error = platform_potential - reference_potential

                    # Compute per-atom RMS (magnitude) and RMS error in force.
                    force_unit = unit.kilocalories_per_mole / unit.nanometers
                    natoms = system.getNumParticles()
                    force_mse = (((reference_force - platform_force) / force_unit)**2).sum() / natoms * force_unit**2
                    force_rmse = unit.sqrt(force_mse)

                    force_ms = ((platform_force / force_unit)**2).sum() / natoms * force_unit**2
                    force_rms = unit.sqrt(force_ms)

                    logger.info("%16s%16s %16.6f kcal/mol %16.6f kcal/mol %16.6f kcal/mol/nm %16.6f kcal/mol/nm" % (platform_name, precision_model, platform_potential / unit.kilocalories_per_mole, potential_error / unit.kilocalories_per_mole, force_rms / force_unit, force_rmse / force_unit))

                    # Mark whether tolerance is exceeded or not.
                    if abs(potential_error) > ENERGY_TOLERANCE:
                        test_success = False
                        logger.info("%32s WARNING: Potential energy error (%.6f kcal/mol) exceeds tolerance (%.6f kcal/mol).  Test failed." % ("", potential_error/unit.kilocalories_per_mole, ENERGY_TOLERANCE/unit.kilocalories_per_mole))
                    if abs(force_rmse) > FORCE_RMSE_TOLERANCE:
                        test_success = False
                        logger.info("%32s WARNING: Force RMS error (%.6f kcal/mol/nm) exceeds tolerance (%.6f kcal/mol/nm).  Test failed." % ("", force_rmse/force_unit, FORCE_RMSE_TOLERANCE/force_unit))
                        if verbose:
                            for atom_index in range(natoms):
                                for k in range(3):
                                    logger.info("%12.6f" % (reference_force[atom_index,k]/force_unit), end="")
                                logger.info(" : ", end="")
                                for k in range(3):
                                    logger.info("%12.6f" % (platform_force[atom_index,k]/force_unit), end="")
            except Exception as e:
                logger.info(e)

        if test_success:
            tests_passed += 1
        else:
            tests_failed += 1

        if (test_success is False):
            # Write XML files of failed tests to aid in debugging.
            
            # Place forces into different force groups.
            forces = [ system.getForce(force_index) for force_index in range(system.getNumForces()) ]
            force_group_names = dict()
            group_index = 0
            for force_index in range(system.getNumForces()):
                force_name = forces[force_index].__class__.__name__
                if force_name == 'NonbondedForce':
                    forces[force_index].setForceGroup(group_index+1)
                    force_group_names[group_index] = 'NonbondedForce (direct)'
                    group_index += 1
                    forces[force_index].setReciprocalSpaceForceGroup(group_index+1)
                    force_group_names[group_index] = 'NonbondedForce (reciprocal)'
                    group_index += 1
                else:
                    forces[force_index].setForceGroup(group_index+1)
                    force_group_names[group_index] = force_name
                    group_index += 1
            ngroups = len(force_group_names)

            # Test by force group.
            logger.info("Breakdown of discrepancies by Force component:")
            nforces = system.getNumForces()
            for force_group in range(ngroups):
                force_name = force_group_names[force_group]
                logger.info(force_name)
                [reference_potential, reference_force] = compute_potential_and_force_by_force_group(system, positions, reference_platform, force_group)
                logger.info("%16s%16s %16s          %16s          %16s          %16s" % ("platform", "precision", "potential", "error", "force mag", "rms error"))

                for platform_index in range(openmm.Platform.getNumPlatforms()):
                    try:
                        platform = openmm.Platform.getPlatform(platform_index)
                        platform_name = platform.getName()
                        
                        # Define precision models to test.
                        if platform_name == 'Reference':
                            precision_models = ['double']
                        else:
                            precision_models = ['single']
                            if platform.supportsDoublePrecision():
                                precision_models.append('double')

                        for precision_model in precision_models:
                            # Set precision.
                            if platform_name == 'CUDA':
                                platform.setPropertyDefaultValue('CudaPrecision', precision_model)
                            if platform_name == 'OpenCL':
                                platform.setPropertyDefaultValue('OpenCLPrecision', precision_model)
                                
                            # Compute potential and force.
                            [platform_potential, platform_force] = compute_potential_and_force_by_force_group(system, positions, platform, force_group)

                            # Compute error in potential.
                            potential_error = platform_potential - reference_potential

                            # Compute per-atom RMS (magnitude) and RMS error in force.
                            force_unit = unit.kilocalories_per_mole / unit.nanometers
                            natoms = system.getNumParticles()
                            force_mse = (((reference_force - platform_force) / force_unit)**2).sum() / natoms * force_unit**2
                            force_rmse = unit.sqrt(force_mse)

                            force_ms = ((platform_force / force_unit)**2).sum() / natoms * force_unit**2
                            force_rms = unit.sqrt(force_ms)

                            logger.info("%16s%16s %16.6f kcal/mol %16.6f kcal/mol %16.6f kcal/mol/nm %16.6f kcal/mol/nm" % (platform_name, precision_model, platform_potential / unit.kilocalories_per_mole, potential_error / unit.kilocalories_per_mole, force_rms / force_unit, force_rmse / force_unit))

                    except Exception as e:
                        logger.info(e)
                        pass
        logger.info("")

    logger.info("%d tests failed" % tests_failed)
    logger.info("%d tests passed" % tests_passed)

    if (tests_failed > 0):
        # Signal failure of test.
        sys.exit(1)
    else:
        sys.exit(0)
Exemplo n.º 38
0
def G_ana(frameno, pp=False, **kwargs):
    """
    Compute correction free energy for finize-size effects
    Using the analytical correction scheme
    Ref Eqn. 14
    """
    i = int(frameno)
    MD  = pc['MD']
    Q_L = pc['Q_L']
    Q_P = pc['Q_P']
    E_S = pc['eps_s']
    E_0 = pc['eps_0']
    REL_E = pc['rel_eps']
    Ns  = pc['Ns']
    V   = pc['V']
    L   = pc['L']
    Z_ls = pc['Z_ls'] #Cubic lattice-sum Wigner integration constant


    #Constant of integration
    pre_fac = 1/(8*np.pi*E_0)
    chg_diff = ((Q_P + Q_L)**2 - Q_P**2)

    #Correction for periodicity-induced net-charge interactions
    #Solvent independent; (+) if QP/QL are same sign
    #Eqn. 15
    NET = -Z_ls * pre_fac * chg_diff * (1/L) * u.AVOGADRO_CONSTANT_NA

    #Correction for periodicity-induced undersolvation
    #Solvent dependent; (-) if QP/QL are same sign
    #Eqn. 16 == Eqn 15 * -(1 - 1/E_S)
    #USV = Z_ls* pre_fac * chg_diff * (1/L) * u.AVOGADRO_CONSTANT_NA
    USV = Z_ls* pre_fac * REL_E * chg_diff * (1/L) * u.AVOGADRO_CONSTANT_NA

    #Get integrated potentials
    # Input in kcal/mol; Convert to J/mol?
    # Average potentials are in [UNIT CHARGE]/[ANGS]
    with open("HET-LIG/pbeq/resu/{}.res".format(i)) as f1:
        I_L = u.Quantity( np.float(f1.read().split()[2])*u.kilocalories_per_mole)
        I_L = I_L.in_units_of(u.kilojoules_per_mole) * (u.angstroms**3 / u.elementary_charge)
    with open("HOM-LIG/pbeq/resu/{}.res".format(i)) as f2:
        I_0 = u.Quantity( np.float(f2.read().split()[2])*u.kilocalories_per_mole)
        I_0 = I_0.in_units_of(u.kilojoules_per_mole)  * (u.angstroms**3 / u.elementary_charge)
    with open("HET-PROT/pbeq/resu/{}.res".format(i)) as f3:
        I_P = u.Quantity( np.float(f3.read().split()[2])*u.kilocalories_per_mole)
        I_P = I_P.in_units_of(u.kilojoules_per_mole) * (u.angstroms**3 / u.elementary_charge)

    #Correction for residual integrated potential effects
    #Eqn. 17
    RIP = ((I_P + I_L)*(Q_P + Q_L) - I_P * Q_P) * 1/V

    #Solvation contribution to the residual integrated potential of ligand
    #Eqn 27
    IL_slv = 0
    IL_slv = I_L - I_0

    #Calculate effective radius of solvation for ligand
    #Eqn 26
    sol_v = (pre_fac * (4*np.pi/3) * REL_E * Q_L)**-1
    rad = SI(sol_v * IL_slv / u.AVOGADRO_CONSTANT_NA)
    if rad._value < 0:
        print("!!!WARNING: Eff. solvation radius for ligand is negative.\n Setting R_L-{}=0".format(i))
        rad = u.Quantity(0 * u.angstroms**2)
    R_L = u.sqrt(rad)

    #Emperical correction
    #Eqn 25.
    emp_c = -pre_fac * (16*np.pi**2)/45 * REL_E
    EMP = emp_c * chg_diff * (R_L**5 / V**2) * u.AVOGADRO_CONSTANT_NA

    #Correction for discrete solvent effects
    #Eqn. 35
    gamma_s = quad_moment()
    density = Ns / V
    DSC = (-gamma_s * Q_L) / (6 * E_0) * density * u.AVOGADRO_CONSTANT_NA

    #Analytical correction
    #Eqn 14
    ANA = NET + USV + RIP + EMP
    COR = ANA + DSC
    NPBC = MD + COR
    if pp == True:
        print(pc)
        print("I_P = {}".format(I_P))
        print("I_L = {}".format(I_L))
        print("I_0 = {}".format(I_0 ))
        print("R_L = {}".format(R_L))
        print("NET = {}".format( KJ(NET)) )
        print("USV = {}".format( KJ(USV)) )
        print("RIP = {}".format( KJ(RIP)) )
        print("EMP = {}".format( KJ(EMP)) )
        print("DSC = {}".format( KJ(DSC)) )
        print("ANA = {}".format( KJ(ANA)) )
        print("COR = {}".format( KJ(COR)) )

    return KCAL(ANA), KCAL(DSC), KCAL(COR), KCAL(NPBC)
Exemplo n.º 39
0
    def _addAtomsToTopology(self, heavyAtomsOnly, omitUnknownMolecules):
        """Create a new Topology in which missing atoms have been added."""
        
        newTopology = app.Topology()
        newPositions = []*unit.nanometer
        newAtoms = []
        existingAtomMap = {}
        addedAtomMap = {}
        addedOXT = []
        for chain in self.topology.chains():
            if omitUnknownMolecules and not any(residue.name in self.templates for residue in chain.residues()):
                continue
            chainResidues = list(chain.residues())
            newChain = newTopology.addChain()
            for indexInChain, residue in enumerate(chain.residues()):
                
                # Insert missing residues here.
                
                if (chain.index, indexInChain) in self.missingResidues:
                    insertHere = self.missingResidues[(chain.index, indexInChain)]
                    endPosition = self._computeResidueCenter(residue)
                    if indexInChain > 0:
                        startPosition = self._computeResidueCenter(chainResidues[indexInChain-1])
                    else:
                        outward = endPosition-self.centroid
                        norm = unit.norm(outward)
                        if norm > 0*unit.nanometer:
                            outward *= len(insertHere)*0.5*unit.nanometer/norm
                        startPosition = endPosition+outward
                    self._addMissingResiduesToChain(newChain, insertHere, startPosition, endPosition, residue, newAtoms, newPositions)
                
                # Create the new residue and add existing heavy atoms.
                                
                newResidue = newTopology.addResidue(residue.name, newChain)
                addResiduesAfter = (residue == chainResidues[-1] and (chain.index, indexInChain+1) in self.missingResidues)
                for atom in residue.atoms():
                    if not heavyAtomsOnly or (atom.element is not None and atom.element != hydrogen):
                        if atom.name == 'OXT' and (chain.index, indexInChain+1) in self.missingResidues:
                            continue # Remove terminal oxygen, since we'll add more residues after this one
                        newAtom = newTopology.addAtom(atom.name, atom.element, newResidue)
                        existingAtomMap[atom] = newAtom
                        newPositions.append(self.positions[atom.index])
                if residue in self.missingAtoms:
                    
                    # Find corresponding atoms in the residue and the template.
                    
                    template = self.templates[residue.name]
                    atomPositions = dict((atom.name, self.positions[atom.index]) for atom in residue.atoms())
                    points1 = []
                    points2 = []
                    for atom in template.topology.atoms():
                        if atom.name in atomPositions:
                            points1.append(atomPositions[atom.name].value_in_unit(unit.nanometer))
                            points2.append(template.positions[atom.index].value_in_unit(unit.nanometer))
                    
                    # Compute the optimal transform to overlay them.
                    
                    (translate2, rotate, translate1) = _overlayPoints(points1, points2)
                    
                    # Add the missing atoms.
                    
                    addedAtomMap[residue] = {}
                    for atom in self.missingAtoms[residue]:
                        newAtom = newTopology.addAtom(atom.name, atom.element, newResidue)
                        newAtoms.append(newAtom)
                        addedAtomMap[residue][atom] = newAtom
                        templatePosition = template.positions[atom.index].value_in_unit(unit.nanometer)
                        newPositions.append((mm.Vec3(*np.dot(rotate, templatePosition+translate2))+translate1)*unit.nanometer)
                if residue in self.missingTerminals:
                    terminalsToAdd = self.missingTerminals[residue]
                else:
                    terminalsToAdd = None

                # If this is the end of the chain, add any missing residues that come after it.
                
                if residue == chainResidues[-1] and (chain.index, indexInChain+1) in self.missingResidues:
                    insertHere = self.missingResidues[(chain.index, indexInChain+1)]
                    if len(insertHere) > 0:
                        startPosition = self._computeResidueCenter(residue)
                        outward = startPosition-self.centroid
                        norm = unit.norm(outward)
                        if norm > 0*unit.nanometer:
                            outward *= len(insertHere)*0.5*unit.nanometer/norm
                        endPosition = startPosition+outward
                        self._addMissingResiduesToChain(newChain, insertHere, startPosition, endPosition, residue, newAtoms, newPositions)
                        newResidue = list(newChain.residues())[-1]
                        if newResidue.name in proteinResidues:
                            terminalsToAdd = ['OXT']
                        else:
                            terminalsToAdd = None
                
                # If a terminal OXT is missing, add it.
                
                if terminalsToAdd is not None:
                    atomPositions = dict((atom.name, newPositions[atom.index].value_in_unit(unit.nanometer)) for atom in newResidue.atoms())
                    if 'OXT' in terminalsToAdd:
                        newAtom = newTopology.addAtom('OXT', oxygen, newResidue)
                        newAtoms.append(newAtom)
                        addedOXT.append(newAtom)
                        d_ca_o = atomPositions['O']-atomPositions['CA']
                        d_ca_c = atomPositions['C']-atomPositions['CA']
                        d_ca_c /= unit.sqrt(unit.dot(d_ca_c, d_ca_c))
                        v = d_ca_o - d_ca_c*unit.dot(d_ca_c, d_ca_o)
                        newPositions.append((atomPositions['O']+2*v)*unit.nanometer)
        newTopology.setUnitCellDimensions(self.topology.getUnitCellDimensions())
        newTopology.createStandardBonds()
        newTopology.createDisulfideBonds(newPositions)
        
        # Return the results.
        
        return (newTopology, newPositions, newAtoms, existingAtomMap)
Exemplo n.º 40
0
sigma = 3.4 * unit.angstrom
epsilon = 0.238 * unit.kilocalories_per_mole
#reduced_density = 0.860     # reduced_density = density * (sigma**3)
reduced_density = 0.960  # reduced_density = density * (sigma**3)
reduced_temperature = 0.850  # reduced_temperature = kB * temperature / epsilon
reduced_pressure = 1.2660  # reduced_pressure = pressure * (sigma**3) / epsilon

platform_name = 'CUDA'  # OpenMM platform name to use for simulation
platform = openmm.Platform.getPlatformByName(platform_name)
data_directory = 'data'  # Directory in which data is to be stored

r0 = 2.0**(
    1. /
    6.) * sigma  # minimum potential distance for Lennard-Jones interaction
characteristic_timescale = unit.sqrt(
    (mass * r0**2) /
    (72 *
     epsilon))  # characteristic timescale for bound Lennard-Jones interaction
# http://borisv.lk.net/matsc597c-1997/simulations/Lecture5/node3.html
timestep = 0.01 * characteristic_timescale  # integrator timestep

# From http://www.cstl.nist.gov/srs/LJ_PURE/md.htm
#characteristic_timescale = unit.sqrt(mass * sigma**2 / epsilon)
#timestep = 0.05 * characteristic_timescale

print "characteristic timescale = %.3f ps" % (characteristic_timescale /
                                              unit.picoseconds)
print "timestep = %.12f ps" % (timestep / unit.picoseconds)

collision_rate = 5.0 / unit.picoseconds  # collision rate for Langevin thermostat
barostat_frequency = 25  # number of steps between barostat updates
Exemplo n.º 41
0
        output_dict[system_name].update(system_data)
        system_data = output_dict[system_name]  # Shortcut.

        # If this data has two values, combine: First deal with measured values
        # Note that Kd/Ka values should be combined as free energies (since that's the normally distributed quantity)
        for data_type in ['Kd', 'Ka', 'DH']:
            if data_type + '_1' in system_data:
                if 'DH' in data_type:  #just take mean
                    final_val = np.mean([
                        system_data[data_type + '_1'],
                        system_data[data_type + '_2']
                    ])
                    system_data[data_type] = final_val

                    # Also compute uncertainty -- the larger of the propagated uncertainty and the standard error in the mean
                    final_unc = u.sqrt(system_data['d' + data_type + '_1']**2 +
                                       system_data['d' + data_type + '_2']**2)
                    std_err = u.sqrt(
                        0.5 * ((system_data[data_type + '_1'] - final_val)**2 +
                               (system_data[data_type + '_2'] - final_val)**2))
                    if std_err > final_unc:
                        final_unc = std_err
                    system_data['d' + data_type] = final_unc
                #Otherwise first convert to free energy then take mean
                elif 'Kd' in data_type:  #First convert to free energy then take mean
                    # If we have Kd data instead of Ka data, convert
                    if 'Kd_1' in system_data and not 'Ka_1' in system_data:
                        system_data['Ka_1'] = 1. / system_data['Kd_1']
                        # Handle uncertainty -- 1/Kd^2 * dKd
                        system_data['dKa_1'] = system_data['dKd_1'] / (
                            system_data['Kd_1']**2)
                        system_data['Ka_2'] = 1. / system_data['Kd_2']
Exemplo n.º 42
0
    nsteps = 250 # number of steps per interation
    deviceid = 0
    print 'nsteps: ', nsteps

    # Create system.     
    [system, coordinates] = wcadimer.WCADimer()

    # Form vectors of masses and sqrt(kT/m) for force propagation and velocity randomization.
    print "Creating masses array..."
    nparticles = system.getNumParticles()
    masses = units.Quantity(numpy.zeros([nparticles,3], numpy.float64), units.amu)
    for particle_index in range(nparticles):
        masses[particle_index,:] = system.getParticleMass(particle_index)
    sqrt_kT_over_m = units.Quantity(numpy.zeros([nparticles,3], numpy.float64), units.nanometers / units.picosecond)
    for particle_index in range(nparticles):
        sqrt_kT_over_m[particle_index,:] = units.sqrt(kT / masses[particle_index,0]) # standard deviation of velocity distribution for each coordinate for this atom

    # List all available platforms
    print "Available platforms:"
    for platform_index in range(openmm.Platform.getNumPlatforms()):
        platform = openmm.Platform.getPlatform(platform_index)
        print "%5d %s" % (platform_index, platform.getName())
    print ""

    # Select platform.
    #platform = openmm.Platform.getPlatformByName("CPU")
    platform = openmm.Platform.getPlatformByName("CUDA")
    min_platform = openmm.Platform.getPlatformByName("Reference")

    for prop in platform.getPropertyNames():
        print prop, platform.getPropertyDefaultValue(prop)
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 annihilate and (iatom in ligand_atoms) and (jatom in ligand_atoms):
                    epsilon *= vdw_lambda 
                    chargeprod *= coulomb_lambda
                # Add modified exception parameters.
                force.addException(iatom, jatom, chargeprod, sigma, epsilon)
            if not annihilate:
                print ligand_atoms
                # Build a list of exceptions.
                exceptions = set()                    
                for exception_index in range(reference_force.getNumExceptions()):
                    [iatom, jatom, chargeprod, sigma, epsilon] = reference_force.getExceptionParameters(exception_index)
                    exceptions.add( (iatom,jatom) )
                    exceptions.add( (jatom,iatom) )
                # Add new intraligand interactions.
                for iatom in ligand_atoms:
                    for jatom in ligand_atoms:
                        if (iatom,jatom) not in exceptions:
                            exceptions.add( (iatom,jatom) )
                            exceptions.add( (jatom,iatom) )                            
                            [charge1, sigma1, epsilon1] = reference_force.getParticleParameters(iatom)
                            [charge2, sigma2, epsilon2] = reference_force.getParticleParameters(jatom)
                            chargeprod = charge1*charge2 
                            sigma = 0.5 * (sigma1 + sigma2)
                            epsilon = units.sqrt(epsilon1*epsilon2)
                            print "chargeprod = %s, sigma = %s, epsilon = %s" % (str(chargeprod), str(sigma), str(epsilon))
                            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)
            force.setNonbondedMethod( reference_force.getNonbondedMethod() )
            force.setCutoffDistance( reference_force.getCutoffDistance() )
            system.addForce(force)
            
        elif isinstance(reference_force, openmm.GBSAOBCForce):
            # GBSAOBCForce

            # Annihilate ligand interactions.
            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)

            if not annihilate:
                # Add ligand solvated component back if decoupling.
                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*(1-coulomb_lambda), radius, scaling_factor, 1.0 - coulomb_lambda])
                    else:
                        # Turn off receptor contributions.
                        force.addParticle([charge, radius, scaling_factor, 0.0])
                # Add force to new system.
                system.addForce(force)
                
        else:
            # Don't add unrecognized forces.
            pass

    return system
Exemplo n.º 44
0
    #    naccept = integrator.getGlobalVariable(global_variables['naccept'])
    #    ntrials = integrator.getGlobalVariable(global_variables['ntrials'])
    #    print "accepted %d / %d (%.3f %%)" % (naccept, ntrials, float(naccept)/float(ntrials)*100.0)

    # Accumulate statistics.
    x_n[iteration] = state.getPositions(asNumpy=True)[0, 0] / units.angstroms
    potential_n[iteration] = final_potential_energy / kT
    kinetic_n[iteration] = final_kinetic_energy / kT
    temperature_n[iteration] = instantaneous_temperature / units.kelvin
    delta_n[iteration] = delta_total_energy / kT

# Compute expected statistics for harmonic oscillator.
K = 100.0 * units.kilocalories_per_mole / units.angstroms**2
beta = 1.0 / kT
x_mean_exact = 0.0  # mean, in angstroms
x_std_exact = 1.0 / units.sqrt(
    beta * K) / units.angstroms  # std dev, in angstroms

# Analyze statistics.
g = statisticalInefficiency(potential_n)
Neff = niterations / g  # number of effective samples

x_mean = x_n.mean()
dx_mean = x_n.std() / numpy.sqrt(Neff)
x_mean_error = x_mean - x_mean_exact

x_var = x_n.var()
dx_var = x_var * numpy.sqrt(2. / (Neff - 1))

x_std = x_n.std()
dx_std = 0.5 * dx_var / x_std
x_std_error = x_std - x_std_exact
Exemplo n.º 45
0
kB = units.BOLTZMANN_CONSTANT_kB * units.AVOGADRO_CONSTANT_NA

#=============================================================================================
# DEFAULT PARAMETERS
#=============================================================================================

natoms = 216                             # number of particles

# WCA fluid parameters (argon).
mass     = 39.9 * units.amu              # reference mass
sigma    = 3.4 * units.angstrom          # reference lengthscale
epsilon  = 120.0 * units.kelvin * kB     # reference energy

r_WCA    = 2.**(1./6.) * sigma           # interaction truncation range
tau = units.sqrt(sigma**2 * mass / epsilon) # characteristic timescale

# Simulation conditions.
temperature = 0.824 / (kB / epsilon)     # temperature
kT = kB * temperature                    # thermal energy
beta = 1.0 / kT                          # inverse temperature    
density  = 0.96 / sigma**3               # default density
stable_timestep = 0.001 * tau            # stable timestep
collision_rate = 1 / tau                 # collision rate for Langevin interator

# Dimer potential parameters.
h = 5.0 * kT                             # barrier height
r0 = r_WCA                               # compact state distance
w = 0.5 * r_WCA                          # extended state distance is r0 + 2*w

#=============================================================================================
Exemplo n.º 46
0
# Add driven collective variables (dihedral angles):
atoms = [(a.name, a.residue.name) for a in topology.atoms()]
psi = openmm.CustomTorsionForce('theta')
psi.addTorsion(*[atoms.index(i) for i in psi_atoms], [])
psi = afed.DrivenCollectiveVariable('psi', psi, unit.radians, period=360*unit.degrees)

phi = openmm.CustomTorsionForce('theta')
phi.addTorsion(*[atoms.index(i) for i in phi_atoms], [])
phi = afed.DrivenCollectiveVariable('phi', phi, unit.radians, period=360*unit.degrees)

# Add driver parameters [Ref: Chen et al., JCP 137 (2), art. 024102, 2012]:
T_dihedrals = 1500*unit.kelvin
mass_dihedrals = 168*unit.dalton*(unit.angstroms/unit.radian)**2
K_dihedrals = 2780*unit.kilocalories_per_mole/unit.radians**2
velocity_scale = unit.sqrt(unit.BOLTZMANN_CONSTANT_kB*unit.AVOGADRO_CONSTANT_NA*T_dihedrals/mass_dihedrals)

psi_driver = afed.DriverParameter('psi_s', unit.radians, psi.evaluate(positions), T_dihedrals,
                                  velocity_scale, -180*unit.degrees, 180*unit.degrees, periodic=True)
phi_driver = afed.DriverParameter('phi_s', unit.radians, phi.evaluate(positions), T_dihedrals,
                                  velocity_scale, -180*unit.degrees, 180*unit.degrees, periodic=True)

# Add driving force:
dihedrals = afed.HarmonicDrivingForce()
dihedrals.addPair(psi, psi_driver, K_dihedrals)
dihedrals.addPair(phi, phi_driver, K_dihedrals)
dihedrals.setForceGroup(0)  # parameter velocity integration at fastest time scale
system.addForce(dihedrals)

# Define AFED integrator:
integrator = afed.MassiveMiddleNHCIntegrator(
Exemplo n.º 47
0
def main():
    import doctest
    import argparse

    parser = argparse.ArgumentParser(description="Check OpenMM computed energies and forces across all platforms for a suite of test systems.")
    parser.add_argument('-o', '--outfile', dest='logfile', action='store', type=str, default=None)
    parser.add_argument('-v', dest='verbose', action='store_true')
    args = parser.parse_args()

    verbose = args.verbose # Don't display extra debug information.
    config_root_logger(verbose, log_file_path=args.logfile)

    # Print version.
    logger.info("OpenMM version: %s" % openmm.version.version)
    logger.info("")

    # List all available platforms
    logger.info("Available platforms:")
    for platform_index in range(openmm.Platform.getNumPlatforms()):
        platform = openmm.Platform.getPlatform(platform_index)
        logger.info("%5d %s" % (platform_index, platform.getName()))
    logger.info("")

    # Test all systems on Reference platform.
    platform = openmm.Platform.getPlatformByName("Reference")
    print('Testing Reference platform...')
    doctest.testmod()

    # Compute energy error made on all test systems for other platforms.
    # Make a count of how often set tolerance is exceeded.
    tests_failed = 0 # number of times tolerance is exceeded
    tests_passed = 0 # number of times tolerance is not exceeded
    logger.info("%16s%16s %16s          %16s          %16s          %16s" % ("platform", "precision", "potential", "error", "force mag", "rms error"))
    reference_platform = openmm.Platform.getPlatformByName("Reference")
    testsystem_classes = get_all_subclasses(testsystems.TestSystem)
    for testsystem_class in testsystem_classes:
        class_name = testsystem_class.__name__

        try:
            testsystem = testsystem_class()
        except ImportError as e:
            logger.info(e)
            logger.info("Skipping %s due to missing dependency" % class_name)
            continue

        # Create test system instance.
        testsystem = testsystem_class()
        [system, positions] = [testsystem.system, testsystem.positions]

        logger.info("%s (%d atoms)" % (class_name, testsystem.system.getNumParticles()))

        # Compute reference potential and force
        [reference_potential, reference_force] = compute_potential_and_force(system, positions, reference_platform)

        # Test all platforms.
        test_success = True
        for platform_index in range(openmm.Platform.getNumPlatforms()):
            try:
                platform = openmm.Platform.getPlatform(platform_index)
                platform_name = platform.getName()

                # Define precision models to test.
                if platform_name == 'Reference':
                    precision_models = ['double']
                else:
                    precision_models = ['single']
                    if platform.supportsDoublePrecision():
                        precision_models.append('double')

                for precision_model in precision_models:
                    # Set precision.
                    if platform_name == 'CUDA':
                        platform.setPropertyDefaultValue('CudaPrecision', precision_model)
                    if platform_name == 'OpenCL':
                        platform.setPropertyDefaultValue('OpenCLPrecision', precision_model)

                    # Compute potential and force.
                    [platform_potential, platform_force] = compute_potential_and_force(system, positions, platform)

                    # Compute error in potential.
                    potential_error = platform_potential - reference_potential

                    # Compute per-atom RMS (magnitude) and RMS error in force.
                    force_unit = units.kilocalories_per_mole / units.nanometers
                    natoms = system.getNumParticles()
                    force_mse = (((reference_force - platform_force) / force_unit)**2).sum() / natoms * force_unit**2
                    force_rmse = units.sqrt(force_mse)

                    force_ms = ((platform_force / force_unit)**2).sum() / natoms * force_unit**2
                    force_rms = units.sqrt(force_ms)

                    logger.info("%16s%16s %16.6f kcal/mol %16.6f kcal/mol %16.6f kcal/mol/nm %16.6f kcal/mol/nm" % (platform_name, precision_model, platform_potential / units.kilocalories_per_mole, potential_error / units.kilocalories_per_mole, force_rms / force_unit, force_rmse / force_unit))

                    # Mark whether tolerance is exceeded or not.
                    if abs(potential_error) > ENERGY_TOLERANCE:
                        test_success = False
                        logger.info("%32s WARNING: Potential energy error (%.6f kcal/mol) exceeds tolerance (%.6f kcal/mol).  Test failed." % ("", potential_error/units.kilocalories_per_mole, ENERGY_TOLERANCE/units.kilocalories_per_mole))
                    if abs(force_rmse) > FORCE_RMSE_TOLERANCE:
                        test_success = False
                        logger.info("%32s WARNING: Force RMS error (%.6f kcal/mol/nm) exceeds tolerance (%.6f kcal/mol/nm).  Test failed." % ("", force_rmse/force_unit, FORCE_RMSE_TOLERANCE/force_unit))
                        if verbose:
                            for atom_index in range(natoms):
                                for k in range(3):
                                    logger.info("%12.6f" % (reference_force[atom_index,k]/force_unit), end="")
                                logger.info(" : ", end="")
                                for k in range(3):
                                    logger.info("%12.6f" % (platform_force[atom_index,k]/force_unit), end="")
            except Exception as e:
                logger.info(e)

        if test_success:
            tests_passed += 1
        else:
            tests_failed += 1

        if (test_success is False):
            # Write XML files of failed tests to aid in debugging.
            logger.info("Writing failed test system to '%s'.{system,state}.xml ..." % testsystem.name)
            [system_xml, state_xml] = testsystem.serialize()
            xml_file = open(testsystem.name + '.system.xml', 'w')
            xml_file.write(system_xml)
            xml_file.close()
            xml_file = open(testsystem.name + '.state.xml', 'w')
            xml_file.write(state_xml)
            xml_file.close()

            
            # Place forces into different force groups.
            forces = [ system.getForce(force_index) for force_index in range(system.getNumForces()) ]
            force_group_names = dict()
            group_index = 0
            for force_index in range(system.getNumForces()):
                force_name = forces[force_index].__class__.__name__
                if force_name == 'NonbondedForce':
                    forces[force_index].setForceGroup(group_index+1)
                    force_group_names[group_index] = 'NonbondedForce (direct)'
                    group_index += 1
                    forces[force_index].setReciprocalSpaceForceGroup(group_index+1)
                    force_group_names[group_index] = 'NonbondedForce (reciprocal)'
                    group_index += 1
                else:
                    forces[force_index].setForceGroup(group_index+1)
                    force_group_names[group_index] = force_name
                    group_index += 1
            ngroups = len(force_group_names)

            # Test by force group.
            logger.info("Breakdown of discrepancies by Force component:")
            nforces = system.getNumForces()
            for force_group in range(ngroups):
                force_name = force_group_names[force_group]
                logger.info(force_name)
                [reference_potential, reference_force] = compute_potential_and_force_by_force_group(system, positions, reference_platform, force_group)
                logger.info("%16s%16s %16s          %16s          %16s          %16s" % ("platform", "precision", "potential", "error", "force mag", "rms error"))

                for platform_index in range(openmm.Platform.getNumPlatforms()):
                    try:
                        platform = openmm.Platform.getPlatform(platform_index)
                        platform_name = platform.getName()
                        
                        # Define precision models to test.
                        if platform_name == 'Reference':
                            precision_models = ['double']
                        else:
                            precision_models = ['single']
                            if platform.supportsDoublePrecision():
                                precision_models.append('double')

                        for precision_model in precision_models:
                            # Set precision.
                            if platform_name == 'CUDA':
                                platform.setPropertyDefaultValue('CudaPrecision', precision_model)
                            if platform_name == 'OpenCL':
                                platform.setPropertyDefaultValue('OpenCLPrecision', precision_model)
                                
                            # Compute potential and force.
                            [platform_potential, platform_force] = compute_potential_and_force_by_force_group(system, positions, platform, force_group)

                            # Compute error in potential.
                            potential_error = platform_potential - reference_potential

                            # Compute per-atom RMS (magnitude) and RMS error in force.
                            force_unit = units.kilocalories_per_mole / units.nanometers
                            natoms = system.getNumParticles()
                            force_mse = (((reference_force - platform_force) / force_unit)**2).sum() / natoms * force_unit**2
                            force_rmse = units.sqrt(force_mse)

                            force_ms = ((platform_force / force_unit)**2).sum() / natoms * force_unit**2
                            force_rms = units.sqrt(force_ms)

                            logger.info("%16s%16s %16.6f kcal/mol %16.6f kcal/mol %16.6f kcal/mol/nm %16.6f kcal/mol/nm" % (platform_name, precision_model, platform_potential / units.kilocalories_per_mole, potential_error / units.kilocalories_per_mole, force_rms / force_unit, force_rmse / force_unit))

                    except Exception as e:
                        logger.info(e)
                        pass
        logger.info("")

    logger.info("%d tests failed" % tests_failed)
    logger.info("%d tests passed" % tests_passed)

    if (tests_failed > 0):
        # Signal failure of test.
        sys.exit(1)
    else:
        sys.exit(0)
Exemplo n.º 48
0
def createUnfoldedSurrogate2(topology, reference_system, locality=5):
    # Create system deep copy.
    system = copy.deepcopy(reference_system)

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

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

            # Create CustomBondForce instead.
            energy_expression = "E_LJ + E_Coulomb + E_GB;"
            energy_expression += "E_LJ = 4*epsilon*((sigma/r)^12 - (sigma/r)^6);"
            energy_expression += "E_Coulomb = 138.935456*chargeprod/r;"
            energy_expression += "E_GB = -0.5*(1/epsilon_solute - 1/epsilon_solvent)*chargeprod/f_GB;"
            energy_expression += "f_GB = (r^2 + RiRj*exp(-r/(4*RiRj)))^0.5;"
            energy_expression += "RiRj = 0.25;"
            custom_bond_force = openmm.CustomBondForce(energy_expression)
            custom_bond_force.addPerBondParameter(
                "chargeprod")  # charge product
            custom_bond_force.addPerBondParameter(
                "sigma")  # Lennard-Jones sigma
            custom_bond_force.addPerBondParameter(
                "epsilon")  # Lennard-Jones epsilon
            custom_bond_force.addGlobalParameter("epsilon_solvent", 78.5)
            custom_bond_force.addGlobalParameter("epsilon_solute", 1)
            system.addForce(custom_bond_force)

            # Add exclusions.
            print("Building exclusions...")
            from sets import Set
            exceptions = Set()
            for index in range(reference_force.getNumExceptions()):
                [atom1_index, atom2_index, chargeprod, sigma,
                 epsilon] = reference_force.getExceptionParameters(index)
                custom_bond_force.addBond(atom1_index, atom2_index,
                                          [chargeprod, sigma, epsilon])

                if atom2_index < atom1_index:
                    exceptions.add((atom2_index, atom1_index))
                else:
                    exceptions.add((atom1_index, atom2_index))

            # Add local interactions.
            print("Adding local interactions...")
            for atom1 in topology.atoms():
                [charge1, sigma1,
                 epsilon1] = reference_force.getParticleParameters(atom1.index)
                for atom2 in topology.atoms():
                    if (atom1.index < atom2.index) and (
                            abs(atom1.residue.index - atom2.residue.index) <=
                            locality) and ((atom1.index, atom2.index)
                                           not in exceptions):
                        [charge2, sigma2, epsilon2
                         ] = reference_force.getParticleParameters(atom1.index)
                        chargeprod = charge1 * charge2
                        sigma = 0.5 * (sigma1 + sigma2)
                        epsilon = unit.sqrt(epsilon1 * epsilon2)
                        custom_bond_force.addBond(atom1.index, atom2.index,
                                                  [chargeprod, sigma, epsilon])

            print("%d custom bond terms added." %
                  custom_bond_force.getNumBonds())

        elif force_name == 'GBSAOBCForce':
            forces_to_remove.append(force_index)

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

    return system
Exemplo n.º 49
0
def computeHarmonicOscillatorExpectations(K, mass, temperature):
    """
    Compute mean and variance of potential and kinetic energies for a 3D harmonic oscillator.

    NOTES

    Numerical quadrature is used to compute the mean and standard deviation of the potential energy.
    Mean and standard deviation of the kinetic energy, as well as the absolute free energy, is computed analytically.

    ARGUMENTS

    K (simtk.unit.Quantity) - spring constant
    mass (simtk.unit.Quantity) - mass of particle
    temperature (simtk.unit.Quantity) - temperature

    RETURNS

    values (dict)

    TODO

    Replace this with built-in analytical expectations for new repex.testsystems classes.

    """

    values = dict()

    # Compute thermal energy and inverse temperature from specified temperature.
    kB = units.BOLTZMANN_CONSTANT_kB * units.AVOGADRO_CONSTANT_NA
    kT = kB * temperature # thermal energy
    beta = 1.0 / kT # inverse temperature

    # Compute standard deviation along one dimension.
    sigma = 1.0 / units.sqrt(beta * K)

    # Define limits of integration along r.
    r_min = 0.0 * units.nanometers # initial value for integration
    r_max = 10.0 * sigma      # maximum radius to integrate to

    # Compute mean and std dev of potential energy.
    V = lambda r : (K/2.0) * (r*units.nanometers)**2 / units.kilojoules_per_mole # potential in kJ/mol, where r in nm
    q = lambda r : 4.0 * math.pi * r**2 * math.exp(-beta * (K/2.0) * (r*units.nanometers)**2) # q(r), where r in nm
    (IqV2, dIqV2) = scipy.integrate.quad(lambda r : q(r) * V(r)**2, r_min / units.nanometers, r_max / units.nanometers)
    (IqV, dIqV)   = scipy.integrate.quad(lambda r : q(r) * V(r), r_min / units.nanometers, r_max / units.nanometers)
    (Iq, dIq)     = scipy.integrate.quad(lambda r : q(r), r_min / units.nanometers, r_max / units.nanometers)
    values['potential'] = dict()
    values['potential']['mean'] = (IqV / Iq) * units.kilojoules_per_mole
    values['potential']['stddev'] = (IqV2 / Iq) * units.kilojoules_per_mole

    # Compute mean and std dev of kinetic energy.
    values['kinetic'] = dict()
    values['kinetic']['mean'] = (3./2.) * kT
    values['kinetic']['stddev'] = math.sqrt(3./2.) * kT

    # Compute dimensionless free energy.
    # f = - \ln \int_{-\infty}^{+\infty} \exp[-\beta K x^2 / 2]
    #   = - \ln \int_{-\infty}^{+\infty} \exp[-x^2 / 2 \sigma^2]
    #   = - \ln [\sqrt{2 \pi} \sigma]
    values['f'] = - numpy.log(2 * numpy.pi * (sigma / units.angstroms)**2) * (3.0/2.0)

    return values
def runTest(path, systemName, platformName, eps, tolerance, precision, queue):
    """This function runs the test for a single system."""
    print "\nsystemName =", systemName

    # load the system from the xml file
    system = loadXMLFile(path, systemName)
    
    # set Ewald error tolerance to a sufficiently small value so the requested accuracy will be achievable
    for f in system.getForces():
        try:
            f.setEwaldErrorTolerance(tolerance/2)
        except:
            pass

    # read in the particle positions from the .pos file
    positions = loadPosFile(path, systemName)

    numParticles = len(positions)
    print "numParticles =", numParticles, "(from the .pos file)"

    integrator = mm.LangevinIntegrator(300*unit.kelvin, 1/unit.picosecond, 0.002*unit.picoseconds)

    print "mm.Platform.getNumPlatforms =", mm.Platform.getNumPlatforms() 
    platform = mm.Platform.getPlatformByName(platformName)
    print "platform.getName() =", platform.getName()

    print "Building \'context\' on \'platform\'."
    context = mm.Context(system, integrator, platform, properties)
    context.setPositions(positions)
    state0 = context.getState(getForces=True)
    forces0 = state0.getForces()


    # make sure the size of the force vector is equal to the number of particles
    assert (len(forces0)==numParticles)

    # calculate the norm of the forces
    force0NormSum = 0.0*unit.kilojoules**2/unit.mole**2/unit.nanometer**2
    for f in forces0:
        force0NormSum += unit.dot(f,f)
    force0Norm = unit.sqrt(force0NormSum)
    print "force0Norm =", force0Norm

    epsilon = eps*unit.nanometer
    step = epsilon/force0Norm
    print "step =", step

    # perturb the coordinates along the direction of forces0 and evaluate the energy
    context.setPositions([p-2*f*step for p,f in zip(positions, forces0)])
    pe1 = context.getState(getEnergy=True).getPotentialEnergy()
    context.setPositions([p-f*step for p,f in zip(positions, forces0)])
    pe2 = context.getState(getEnergy=True).getPotentialEnergy()
    context.setPositions([p+f*step for p,f in zip(positions, forces0)])
    pe3 = context.getState(getEnergy=True).getPotentialEnergy()
    context.setPositions([p+2*f*step for p,f in zip(positions, forces0)])
    pe4 = context.getState(getEnergy=True).getPotentialEnergy()

    # use a finite difference approximation to calculate the expected force0Norm
    expectedForceNorm = (-pe1+8*pe2-8*pe3+pe4)/(12*epsilon)
    relativeDifference = abs(expectedForceNorm-force0Norm)/force0Norm

    print "pe1 =", pe1
    print "pe2 =", pe2
    print "pe3 =", pe3
    print "pe4 =", pe4
    print "expectedForceNorm =", expectedForceNorm
    print "relativeDifference =", relativeDifference

    # check that the energy is within the desired range
    if relativeDifference > tolerance:
        print "*** ERROR EXCEEDS TOLERANCE ***"
        queue.put(False)
    else:
        queue.put(True)
    print "Test of", systemName, "complete."
    del(context)
Exemplo n.º 51
0
def createUnfoldedSurrogate(topology, reference_system, locality=5):
    """
    Create a surrogate for the unfolded state in which non-local residue interactions are excluded.

    Parameters
    ----------
    topology : simtk.openmm.app.Topology
        The Topology object for the reference system.
    reference_system : simtk.openmm.System
        The System object for the reference system to be modified.
    locality : int, optional
        The number of residues beyond which nonbonded interactions will be excluded.


    Returns
    -------
    system : simtk.openmm.System
        A System object corresponding to the desired construct.

    TODO
    ----
    * Replace NonbondedForce with CustomBondForce for improved efficiency.
    * Replace CustomGBForce with CustomBondForce for improved efficiency.

    """

    # Make a copy of the reference system if a working copy is not specified.
    system = copy.deepcopy(reference_system)

    # Get NonbondedForce objects for system copy.
    nonbonded_force = _findForces(system, 'NonbondedForce', first_only=True)
    gb_force = _findForces(system, 'CustomGBForce', first_only=True)

    # Add exclusions for non-local interactions.
    for atom1 in topology.atoms():
        [charge1, sigma1,
         epsilon1] = nonbonded_force.getParticleParameters(atom1.index)
        for atom2 in topology.atoms():
            if (atom1.index < atom2.index):
                if (abs(atom1.residue.index - atom2.residue.index) <=
                        locality):
                    # Create exclusion.
                    try:
                        [charge2, sigma2, epsilon2
                         ] = nonbonded_force.getParticleParameters(atom1.index)
                        chargeprod = charge1 * charge2
                        sigma = 0.5 * (sigma1 + sigma2)
                        epsilon = unit.sqrt(epsilon1 * epsilon2)
                        nonbonded_force.addException(atom1.index, atom2.index,
                                                     chargeprod, sigma,
                                                     epsilon, False)
                    except:
                        # Exception already exists; don't modify it.
                        pass
                else:
                    # Exclude GB interactions between nonlocal particles.
                    gb_force.addExclusion(atom1.index, atom2.index)

    # Turn off standard particle interactions.
    zero_charge = 0.0 * unit.elementary_charge
    unit_sigma = 1.0 * unit.angstroms
    zero_epsilon = 0.0 * unit.kilocalories_per_mole
    for atom in topology.atoms():
        nonbonded_force.setParticleParameters(atom.index, zero_charge,
                                              unit_sigma, zero_epsilon)

    # Return modified system.
    return system
Exemplo n.º 52
0
        test_success = True
        for platform_index in range(openmm.Platform.getNumPlatforms()):
            try:

                platform = openmm.Platform.getPlatform(platform_index)
                platform_name = platform.getName()
                [platform_potential, platform_force] = compute_potential_and_force(system, positions, platform)

                # Compute error in potential.
                potential_error = platform_potential - reference_potential

                # Compute per-atom RMS (magnitude) and RMS error in force.
                force_unit = units.kilocalories_per_mole / units.nanometers
                natoms = system.getNumParticles()
                force_mse = (((reference_force - platform_force) / force_unit)**2).sum() / natoms * force_unit**2
                force_rmse = units.sqrt(force_mse)

                force_ms = ((platform_force / force_unit)**2).sum() / natoms * force_unit**2
                force_rms = units.sqrt(force_ms)

                print "%32s %16.6f kcal/mol %16.6f kcal/mol %16.6f kcal/mol %16.6f kcal/mol" % (platform_name, platform_potential / units.kilocalories_per_mole, potential_error / units.kilocalories_per_mole, force_rms / force_unit, force_rmse / force_unit)

                # Mark whether tolerance is exceeded or not.
                if abs(potential_error) > ENERGY_TOLERANCE:
                    test_success = False
                    print "%32s WARNING: Potential energy error (%.6f kcal/mol) exceeds tolerance (%.6f kcal/mol).  Test failed." % ("", potential_error/units.kilocalories_per_mole, ENERGY_TOLERANCE/units.kilocalories_per_mole)
                if abs(force_rmse) > FORCE_RMSE_TOLERANCE:
                    test_success = False
                    print "%32s WARNING: Force RMS error (%.6f kcal/mol) exceeds tolerance (%.6f kcal/mol).  Test failed." % ("", force_rmse/force_unit, FORCE_RMSE_TOLERANCE/force_unit)
                    if debug:
                        for atom_index in range(natoms):
Exemplo n.º 53
0
    def _addAtomsToTopology(self, heavyAtomsOnly, omitUnknownMolecules):
        """Create a new Topology in which missing atoms have been added.

        Parameters
        ----------
        heavyAtomsOnly : bool
            If True, only heavy atoms will be added to the topology.
        omitUnknownMolecules : bool
            If True, unknown molecules will be omitted from the topology.

        Returns
        -------
        newTopology : simtk.openmm.app.Topology
            A new Topology object containing atoms from the old.         
        newPositions : list of simtk.unit.Quantity with units compatible with nanometers
            Atom positions for the new Topology object.
        newAtoms : simtk.openmm.app.Topology.Atom
            New atom objects.
        existingAtomMap : dict
            Mapping from old atoms to new atoms.

        """
        
        newTopology = app.Topology()
        newPositions = []*unit.nanometer
        newAtoms = []
        existingAtomMap = {}
        addedAtomMap = {}
        addedOXT = []
        residueCenters = [self._computeResidueCenter(res).value_in_unit(unit.nanometers) for res in self.topology.residues()]*unit.nanometers
        for chain in self.topology.chains():
            if omitUnknownMolecules and not any(residue.name in self.templates for residue in chain.residues()):
                continue
            chainResidues = list(chain.residues())
            newChain = newTopology.addChain(chain.id)
            for indexInChain, residue in enumerate(chain.residues()):
                
                # Insert missing residues here.
                
                if (chain.index, indexInChain) in self.missingResidues:
                    insertHere = self.missingResidues[(chain.index, indexInChain)]
                    endPosition = self._computeResidueCenter(residue)
                    if indexInChain > 0:
                        startPosition = self._computeResidueCenter(chainResidues[indexInChain-1])
                        loopDirection = _findUnoccupiedDirection((startPosition+endPosition)/2, residueCenters)
                    else:
                        outward = _findUnoccupiedDirection(endPosition, residueCenters)*unit.nanometers
                        norm = unit.norm(outward)
                        if norm > 0*unit.nanometer:
                            outward *= len(insertHere)*0.5*unit.nanometer/norm
                        startPosition = endPosition+outward
                        loopDirection = None
                    firstIndex = int(residue.id)-len(insertHere)
                    self._addMissingResiduesToChain(newChain, insertHere, startPosition, endPosition, loopDirection, residue, newAtoms, newPositions, firstIndex)
                
                # Create the new residue and add existing heavy atoms.
                                
                newResidue = newTopology.addResidue(residue.name, newChain, residue.id)
                addResiduesAfter = (residue == chainResidues[-1] and (chain.index, indexInChain+1) in self.missingResidues)
                for atom in residue.atoms():
                    if not heavyAtomsOnly or (atom.element is not None and atom.element != hydrogen):
                        if atom.name == 'OXT' and (chain.index, indexInChain+1) in self.missingResidues:
                            continue # Remove terminal oxygen, since we'll add more residues after this one
                        newAtom = newTopology.addAtom(atom.name, atom.element, newResidue)
                        existingAtomMap[atom] = newAtom
                        newPositions.append(self.positions[atom.index])
                if residue in self.missingAtoms:
                    
                    # Find corresponding atoms in the residue and the template.
                    
                    template = self.templates[residue.name]
                    atomPositions = dict((atom.name, self.positions[atom.index]) for atom in residue.atoms())
                    points1 = []
                    points2 = []
                    for atom in template.topology.atoms():
                        if atom.name in atomPositions:
                            points1.append(atomPositions[atom.name].value_in_unit(unit.nanometer))
                            points2.append(template.positions[atom.index].value_in_unit(unit.nanometer))
                    
                    # Compute the optimal transform to overlay them.
                    
                    (translate2, rotate, translate1) = _overlayPoints(points1, points2)
                    
                    # Add the missing atoms.
                    
                    addedAtomMap[residue] = {}
                    for atom in self.missingAtoms[residue]:
                        newAtom = newTopology.addAtom(atom.name, atom.element, newResidue)
                        newAtoms.append(newAtom)
                        addedAtomMap[residue][atom] = newAtom
                        templatePosition = template.positions[atom.index].value_in_unit(unit.nanometer)
                        newPositions.append((mm.Vec3(*np.dot(rotate, templatePosition+translate2))+translate1)*unit.nanometer)
                if residue in self.missingTerminals:
                    terminalsToAdd = self.missingTerminals[residue]
                else:
                    terminalsToAdd = None

                # If this is the end of the chain, add any missing residues that come after it.
                
                if residue == chainResidues[-1] and (chain.index, indexInChain+1) in self.missingResidues:
                    insertHere = self.missingResidues[(chain.index, indexInChain+1)]
                    if len(insertHere) > 0:
                        startPosition = self._computeResidueCenter(residue)
                        outward = _findUnoccupiedDirection(startPosition, residueCenters)*unit.nanometers
                        norm = unit.norm(outward)
                        if norm > 0*unit.nanometer:
                            outward *= len(insertHere)*0.5*unit.nanometer/norm
                        endPosition = startPosition+outward
                        firstIndex = int(residue.id)+1
                        self._addMissingResiduesToChain(newChain, insertHere, startPosition, endPosition, None, residue, newAtoms, newPositions, firstIndex)
                        newResidue = list(newChain.residues())[-1]
                        if newResidue.name in proteinResidues:
                            terminalsToAdd = ['OXT']
                        else:
                            terminalsToAdd = None
                
                # If a terminal OXT is missing, add it.
                
                if terminalsToAdd is not None:
                    atomPositions = dict((atom.name, newPositions[atom.index].value_in_unit(unit.nanometer)) for atom in newResidue.atoms())
                    if 'OXT' in terminalsToAdd:
                        newAtom = newTopology.addAtom('OXT', oxygen, newResidue)
                        newAtoms.append(newAtom)
                        addedOXT.append(newAtom)
                        d_ca_o = atomPositions['O']-atomPositions['CA']
                        d_ca_c = atomPositions['C']-atomPositions['CA']
                        d_ca_c /= unit.sqrt(unit.dot(d_ca_c, d_ca_c))
                        v = d_ca_o - d_ca_c*unit.dot(d_ca_c, d_ca_o)
                        newPositions.append((atomPositions['O']+2*v)*unit.nanometer)
        newTopology.setUnitCellDimensions(self.topology.getUnitCellDimensions())
        newTopology.createStandardBonds()
        newTopology.createDisulfideBonds(newPositions)
        
        # Return the results.
        
        return (newTopology, newPositions, newAtoms, existingAtomMap)
Exemplo n.º 54
0
    def run_dynamics(self, 
                    x0:np.ndarray,
                    n_steps:int = 100,
                    stepsize:unit.quantity.Quantity = 1.0*unit.femtosecond,
                    collision_rate:unit.quantity.Quantity = 10/unit.picoseconds,
                    progress_bar:bool = False
            ):
        """Unadjusted Langevin dynamics.

        Parameters
        ----------
        x0 : array of floats, unit'd (distance unit)
            initial configuration
        force : callable, accepts a unit'd array and returns a unit'd array
            assumes input is in units of distance
            output is in units of energy / distance
        n_steps : integer
            number of Langevin steps
        stepsize : float > 0, in units of time
            finite timestep parameter
        collision_rate : float > 0, in units of 1/time
            controls the rate of interaction with the heat bath
        progress_bar : bool
            use tqdm to show progress bar

        Returns
        -------
        traj : [n_steps + 1 x dim] array of floats, unit'd
            trajectory of samples generated by Langevin dynamics

        """
        assert(type(x0) == unit.Quantity)
        assert(type(stepsize) == unit.Quantity)
        assert(type(collision_rate) == unit.Quantity)
        assert(type(self.temperature) == unit.Quantity)

        # generate mass arrays
        mass_dict_in_daltons = {'H': 1.0, 'C': 12.0, 'N': 14.0, 'O': 16.0}
        masses = np.array([mass_dict_in_daltons[a] for a in self.atom_list]) * unit.daltons
        sigma_v = np.array([unit.sqrt(kB * self.temperature / m) / speed_unit for m in masses]) * speed_unit
        v0 = np.random.randn(len(sigma_v),3) * sigma_v[:,None]
        # convert initial state numpy arrays with correct attached units
        x = np.array(x0.value_in_unit(distance_unit)) * distance_unit
        v = np.array(v0.value_in_unit(speed_unit)) * speed_unit

        # traj is accumulated as a list of arrays with attached units
        traj = [x]

        # dimensionless scalars
        a = np.exp(- collision_rate * stepsize)
        b = np.sqrt(1 - np.exp(-2 * collision_rate * stepsize))

        # compute force on initial configuration
        F, E = self.force.calculate_force(x)
        # energy is saved as a list
        energy = [E]

        trange = range(n_steps)
        if progress_bar:
            trange = tqdm(trange)
        for _ in trange:
            # v
            v += (stepsize * 0.5) * F / masses[:,None]
            # r
            x += (stepsize * 0.5) * v
            # o
            v = (a * v) + (b * sigma_v[:,None] * np.random.randn(*x.shape))
            # r
            x += (stepsize * 0.5) * v
            F, E = self.force.calculate_force(x)
            energy.append(E)
            # v
            v += (stepsize * 0.5) * F / masses[:,None]

            norm_F = np.linalg.norm(F)
            # report gradient norm
            if progress_bar:
                trange.set_postfix({'|force|': norm_F})
            # check positions and forces are finite
            if (not np.isfinite(x).all()) or (not np.isfinite(norm_F)):
                print("Numerical instability encountered!")
                return traj
            traj.append(x)
        return traj, energy
Exemplo n.º 55
0
def computeHarmonicOscillatorExpectations(K, mass, temperature):
    """
    Compute moments of potential and kinetic energy distributions and free energies for harmonic oscillator.
        
    ARGUMENTS
    
    K - spring constant
    mass - mass of particle
    temperature - temperature
    
    RETURNS
    
    values (dict) - values['potential'] is a dict with 'mean' and 'stddev' of potential energy distribution;
                    values['kinetic'] is a dict with 'mean' and 'stddev' of kinetic energy distribution;
                    values['free energies'] is the free energy due to the 'potential', 'kinetic', or 'total' part of the partition function
    
    NOTES

    Numerical quadrature is used to evaluate the moments of the potential energy distribution.

    EXAMPLES
    
    >>> import simtk.unit as units
    >>> temperature = 298.0 * units.kelvin
    >>> sigma = 0.5 * units.angstroms # define standard deviation for harmonic oscillator
    >>> mass = 12.0 * units.amu # mass of harmonic oscillator
    >>> kT = kB * temperature # thermal energy
    >>> beta = 1.0 / kT # inverse temperature
    >>> K = kT / sigma**2 # spring constant consistent with variance sigma**2
    >>> values = computeHarmonicOscillatorExpectations(K, mass, temperature)

    """

    values = dict()
    
    # Compute thermal energy and inverse temperature from specified temperature.
    kB = units.BOLTZMANN_CONSTANT_kB * units.AVOGADRO_CONSTANT_NA
    kT = kB * temperature # thermal energy
    beta = 1.0 / kT # inverse temperature
   
    # Compute standard deviation along one dimension.
    sigma = 1.0 / units.sqrt(beta * K) 

    # Define limits of integration along r.
    r_min = 0.0 * units.nanometers # initial value for integration
    r_max = 10.0 * sigma      # maximum radius to integrate to

    # Compute mean and std dev of potential energy.
    import scipy.integrate
    V = lambda r : (K/2.0) * (r*units.nanometers)**2 / units.kilojoules_per_mole # potential in kJ/mol, where r in nm
    q = lambda r : 4.0 * math.pi * r**2 * math.exp(-beta * (K/2.0) * (r*units.nanometers)**2) # q(r), where r in nm
    (IqV2, dIqV2) = scipy.integrate.quad(lambda r : q(r) * V(r)**2, r_min / units.nanometers, r_max / units.nanometers)
    (IqV, dIqV)   = scipy.integrate.quad(lambda r : q(r) * V(r), r_min / units.nanometers, r_max / units.nanometers)
    (Iq, dIq)     = scipy.integrate.quad(lambda r : q(r), r_min / units.nanometers, r_max / units.nanometers)
    values['potential'] = dict()
    values['potential']['mean'] = (IqV / Iq) * units.kilojoules_per_mole
    values['potential']['stddev'] = (IqV2 / Iq) * units.kilojoules_per_mole   
   
    # Compute mean and std dev of kinetic energy.
    values['kinetic'] = dict()
    values['kinetic']['mean'] = (3./2.) * kT
    values['kinetic']['stddev'] = math.sqrt(3./2.) * kT

    # Compute free energies.
    V0 = (units.liter / units.AVOGADRO_CONSTANT_NA / units.mole).in_units_of(units.angstroms**3) # standard state reference volume (1M)
    values['free energies'] = dict()
    values['free energies']['potential'] = - numpy.log((numpy.sqrt(2.0 * math.pi) * sigma)**3 / V0)
    values['free energies']['kinetic'] = (3./2.) 
    values['free energies']['total'] = values['free energies']['potential'] + values['free energies']['kinetic']
    
    return values
Exemplo n.º 56
0
 def _calculateElongation(self, state):
     positions = state.getPositions(asNumpy=True)
     displacement = positions[self._index1] - positions[self._index2]
     distance = unit.sqrt(unit.sum(displacement**2))
     return distance.value_in_unit(unit.nanometers)
     
Exemplo n.º 57
0
def check_harmonic_oscillator_ncmc(ncmc_nsteps=50, ncmc_integrator="VV"):
    """
    Test NCMC switching of a 3D harmonic oscillator.
    In this test, the oscillator center is dragged in space, and we check the computed free energy difference with BAR, which should be 0.
    """
    # Parameters for 3D harmonic oscillator
    mass = 39.948 * unit.amu  # mass of particle (argon)
    sigma = 5.0 * unit.angstrom  # standard deviation of harmonic oscillator
    collision_rate = 5.0 / unit.picosecond  # collision rate
    temperature = 300.0 * unit.kelvin  # temperature
    platform_name = 'Reference'  # platform anme
    NSIGMA_MAX = 6.0  # number of standard errors away from analytical solution tolerated before Exception is thrown

    # Compute derived quantities.
    kT = kB * temperature  # thermal energy
    beta = 1.0 / kT  # inverse energy
    K = kT / sigma**2  # spring constant
    tau = 2 * math.pi * unit.sqrt(mass / K)  # time constant
    timestep = tau / 20.0
    platform = openmm.Platform.getPlatformByName(platform_name)

    # Create a 3D harmonic oscillator with context parameter controlling center of oscillator.
    system = openmm.System()
    system.addParticle(mass)
    energy_expression = '(K/2.0) * ((x-x0)^2 + y^2 + z^2);'
    force = openmm.CustomExternalForce(energy_expression)
    force.addGlobalParameter('K', K.in_unit_system(unit.md_unit_system))
    force.addGlobalParameter('x0', 0.0)
    force.addParticle(0, [])
    system.addForce(force)

    # Set the positions at the origin.
    positions = unit.Quantity(np.zeros([1, 3], np.float32), unit.angstroms)
    functions = {'x0': 'lambda'}  # drag spring center x0

    from perses.annihilation.ncmc_integrator import NCMCVVAlchemicalIntegrator, NCMCGHMCAlchemicalIntegrator
    if ncmc_integrator == "VV":
        ncmc_insert = NCMCVVAlchemicalIntegrator(
            temperature,
            system,
            functions,
            direction='insert',
            nsteps=ncmc_nsteps,
            timestep=timestep)  # 'insert' drags lambda from 0 -> 1
        ncmc_delete = NCMCVVAlchemicalIntegrator(
            temperature,
            system,
            functions,
            direction='delete',
            nsteps=ncmc_nsteps,
            timestep=timestep)  # 'insert' drags lambda from 0 -> 1
    elif ncmc_integrator == "GHMC":
        ncmc_insert = NCMCGHMCAlchemicalIntegrator(
            temperature,
            system,
            functions,
            direction='insert',
            collision_rate=9.1 / unit.picoseconds,
            nsteps=ncmc_nsteps,
            timestep=timestep)  # 'insert' drags lambda from 0 -> 1
        ncmc_delete = NCMCGHMCAlchemicalIntegrator(
            temperature,
            system,
            functions,
            direction='delete',
            collision_rate=9.1 / unit.picoseconds,
            nsteps=ncmc_nsteps,
            timestep=timestep)  # 'insert' drags lambda from 0 -> 1
    else:
        raise Exception(
            "%s not recognized as integrator name. Options are VV and GHMC" %
            ncmc_integrator)

    # Run NCMC switching trials where the spring center is switched with lambda: 0 -> 1 over a finite number of steps.
    w_f = collect_switching_data(system,
                                 positions,
                                 functions,
                                 temperature,
                                 collision_rate,
                                 timestep,
                                 platform,
                                 ncmc_integrator=ncmc_insert,
                                 ncmc_nsteps=ncmc_nsteps,
                                 direction='insert')
    w_r = collect_switching_data(system,
                                 positions,
                                 functions,
                                 temperature,
                                 collision_rate,
                                 timestep,
                                 platform,
                                 ncmc_integrator=ncmc_delete,
                                 ncmc_nsteps=ncmc_nsteps,
                                 direction='delete')

    from pymbar import BAR
    [df, ddf] = BAR(w_f, w_r, method='self-consistent-iteration')
    print('%8.3f +- %.3f kT' % (df, ddf))
    if (abs(df) > NSIGMA_MAX * ddf):
        msg = 'Delta F (%d steps switching) = %f +- %f kT; should be within %f sigma of 0' % (
            ncmc_nsteps, df, ddf, NSIGMA_MAX)
        msg += '\n'
        msg += 'w_f = %s\n' % str(w_f)
        msg += 'w_r = %s\n' % str(w_r)
        raise Exception(msg)
Exemplo n.º 58
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))