コード例 #1
0
    def __init__(self, variables, system, *args, **kwargs):
        driving_force = openmm.CustomCVForce(_get_energy_function(variables))
        for name, value in _get_parameters(variables).items():
            driving_force.addGlobalParameter(name, value)
        for v in variables:
            driving_force.addCollectiveVariable(v.id, deepcopy(v.force))
            for colvar in v.colvars:
                driving_force.addCollectiveVariable(colvar.id, deepcopy(colvar.force))

        np = system.getNumParticles()
        nb_forces = [f for f in system.getForces()
                     if isinstance(f, (openmm.NonbondedForce, openmm.CustomNonbondedForce))]
        a, _, _ = system.getDefaultPeriodicBoxVectors()
        for i, v in enumerate(variables):
            system.addParticle(v._particle_mass(a.x))
            for nb_force in nb_forces:
                if isinstance(nb_force, openmm.NonbondedForce):
                    nb_force.addParticle(0.0, 1.0, 0.0)
                else:
                    nb_force.addParticle([0.0]*nb_force.getNumPerParticleParameters())
            parameter = driving_force.getCollectiveVariable(2*i)
            parameter.setParticleParameters(0, np+i, [])
        system.addForce(driving_force)
        super().__init__(system, *args, **kwargs)
        self.setParameter('Lx', a.x)
        self.variables = variables
        self.driving_force = driving_force
コード例 #2
0
    def _compute_forces(self, ufed, dataframe):
        collective_variables = [
            colvar.id for v in ufed.variables for colvar in v.colvars
        ]
        extended_variables = [v.id for v in ufed.variables]
        all_variables = collective_variables + extended_variables

        force = openmm.CustomCVForce(_get_energy_function(ufed.variables))
        for key, value in _get_parameters(ufed.variables).items():
            force.addGlobalParameter(key, value)
        for variable in all_variables:
            force.addGlobalParameter(variable, 0)
        for xv in extended_variables:
            force.addEnergyParameterDerivative(xv)

        system = openmm.System()
        system.addForce(force)
        system.addParticle(0)
        platform = openmm.Platform.getPlatformByName('Reference')
        context = openmm.Context(system, openmm.CustomIntegrator(0), platform)
        context.setPositions([openmm.Vec3(0, 0, 0)])

        n = len(dataframe.index)
        forces = [np.empty(n) for xv in extended_variables]
        for j, row in dataframe.iterrows():
            for variable in all_variables:
                context.setParameter(variable, row[variable])
            state = context.getState(getParameterDerivatives=True)
            derivatives = state.getEnergyParameterDerivatives()
            for i, xv in enumerate(extended_variables):
                forces[i][j] = -derivatives[xv]
        return forces
コード例 #3
0
 def _interpolation_grid_force(self):
     self._widths = []
     self._bounds = []
     self._extra_points = []
     for v in self.bias_variables:
         extra_points = min(self.grid_expansion, v.grid_size) if v.periodic else 0
         extra_range = extra_points*v._range/(v.grid_size - 1)
         self._widths.append(v.grid_size + 2*extra_points)
         self._bounds += [v.min_value - extra_range, v.max_value + extra_range]
         self._extra_points.append(extra_points)
     self._bias = np.zeros(np.prod(self._widths))
     num_bias_variables = len(self.bias_variables)
     if num_bias_variables == 1:
         self._table = openmm.Continuous1DFunction(self._bias, *self._bounds)
     elif num_bias_variables == 2:
         self._table = openmm.Continuous2DFunction(*self._widths, self._bias, *self._bounds)
     else:
         self._table = openmm.Continuous3DFunction(*self._widths, self._bias, *self._bounds)
     expression = f'bias({",".join(v.id for v in self.bias_variables)})'
     for i, v in enumerate(self.bias_variables):
         expression += f';{v.id}={v._get_energy_function(i+1)}'
     force = openmm.CustomCVForce(expression)
     for i in range(num_bias_variables):
         x = openmm.CustomExternalForce('x')
         x.addParticle(0, [])
         force.addCollectiveVariable(f'x{i+1}', x)
     force.addTabulatedFunction('bias', self._table)
     force.addGlobalParameter('Lx', 0)
     return force
コード例 #4
0
ファイル: ufedmm.py プロジェクト: ajsilveira/ufedmm
 def __init__(self, variables, height, frequency, grid_expansion):
     self.bias_variables = [cv for cv in variables if cv.sigma is not None]
     self.height = height
     self.frequency = frequency
     self.grid_expansion = grid_expansion
     self._widths = []
     self._bounds = []
     self._expanded = []
     self._extra_points = []
     for cv in self.bias_variables:
         expanded = cv.periodic  # and len(self.bias_variables) > 1
         extra_points = min(grid_expansion, cv.grid_size) if expanded else 0
         extra_range = extra_points * cv._range / (cv.grid_size - 1)
         self._widths += [cv.grid_size + 2 * extra_points]
         self._bounds += [
             cv.min_value - extra_range, cv.max_value + extra_range
         ]
         self._expanded += [expanded]
         self._extra_points += [extra_points]
     self._bias = np.zeros(tuple(reversed(self._widths)))
     if len(variables) == 1:
         self._table = openmm.Continuous1DFunction(
             self._bias.flatten(),
             *self._bounds,
             # self.bias_variables[0].periodic,
         )
     elif len(variables) == 2:
         self._table = openmm.Continuous2DFunction(
             *self._widths,
             self._bias.flatten(),
             *self._bounds,
         )
     elif len(variables) == 3:
         self._table = openmm.Continuous3DFunction(
             *self._widths,
             self._bias.flatten(),
             *self._bounds,
         )
     else:
         raise ValueError(
             'UFED requires 1, 2, or 3 biased collective variables')
     parameter_list = ', '.join(f's_{cv.id}' for cv in self.bias_variables)
     self.force = openmm.CustomCVForce(f'bias({parameter_list})')
     for cv in self.bias_variables:
         expression = f'{cv.min_value}+{cv._range}*(x/Lx-floor(x/Lx))'
         parameter = openmm.CustomExternalForce(expression)
         parameter.addGlobalParameter('Lx', 0.0)
         parameter.addParticle(0, [])
         self.force.addCollectiveVariable(f's_{cv.id}', parameter)
     self.force.addTabulatedFunction('bias', self._table)
コード例 #5
0
def slab_correction(system):
    '''
    Apply Yeh's long range coulomb correction for slab geometry in z direction
    to eliminate the undesired interactions between periodic slabs.

    It's useful for 2-D systems simulated under 3-D periodic condition.
    For this correction to work correctly:

    * A vacuum space two times larger than slab thickness is required.
    * All particles should never diffuse across the z boundaries.
    * The box size should not change during the simulation.

    Parameters
    ----------
    system : mm.System
        The OpenMM system to be simulated

    Returns
    -------
    force : mm.CustomCVForce
    '''
    muz = mm.CustomExternalForce('q*z')
    muz.addPerParticleParameter('q')
    nbforce = [
        f for f in system.getForces() if f.__class__ == mm.NonbondedForce
    ][0]
    qsum = 0
    for i in range(nbforce.getNumParticles()):
        q = nbforce.getParticleParameters(i)[0].value_in_unit(qe)
        muz.addParticle(i, [q])
        qsum += q
    if abs(qsum) > 1E-4:
        raise Exception('Slab correction is not valid for non-neutral system')

    box = system.getDefaultPeriodicBoxVectors()
    vol = (box[0][0] * box[1][1] * box[2][2]).value_in_unit(nm**3)
    # convert from e^2/nm to kJ/mol  # 138.93545915168772
    _eps0 = CONST.EPS0 * unit.farad / unit.meter  # vacuum dielectric constant
    _conv = (1 / (4 * CONST.PI * _eps0) * qe**2 / nm /
             unit.item).value_in_unit(kJ_mol)
    prefactor = 2 * CONST.PI / vol * _conv
    cvforce = mm.CustomCVForce(f'{prefactor}*muz*muz')
    cvforce.addCollectiveVariable('muz', muz)
    system.addForce(cvforce)

    return cvforce
コード例 #6
0
ファイル: ufedmm.py プロジェクト: ajsilveira/ufedmm
    def __init__(self,
                 variables,
                 temperature,
                 height=None,
                 frequency=None,
                 grid_expansion=20):
        self.variables = variables
        self.temperature = _standardize(temperature)
        self.height = _standardize(height)
        self.frequency = frequency
        self.grid_expansion = grid_expansion

        energy_terms = []
        definitions = []
        for i, cv in enumerate(self.variables):
            energy_terms.append(
                f'0.5*K_{cv.id}*min(d{cv.id},{cv._range}-d{cv.id})^2')
            definitions.append(f'd{cv.id}=abs({cv.id}-s_{cv.id})')
        expression = '; '.join([' + '.join(energy_terms)] + definitions)
        self.driving_force = openmm.CustomCVForce(expression)
        for i, cv in enumerate(self.variables):
            self.driving_force.addGlobalParameter(f'K_{cv.id}',
                                                  cv.force_constant)
            self.driving_force.addCollectiveVariable(cv.id, cv.openmm_force)
            expression = f'{cv.min_value}+{cv._range}*(x/Lx-floor(x/Lx))'
            parameter = openmm.CustomExternalForce(expression)
            parameter.addGlobalParameter('Lx', 0.0)
            parameter.addParticle(0, [])
            self.driving_force.addCollectiveVariable(f's_{cv.id}', parameter)

        if (all(cv.sigma is None for cv in self.variables) or height is None
                or frequency is None):
            self.bias_force = self._metadynamics = None
        else:
            self._metadynamics = _Metadynamics(
                self.variables,
                self.height,
                frequency,
                grid_expansion,
            )
            self.bias_force = self._metadynamics.force
コード例 #7
0
def setup_fah_run(destination_path,
                  protein_pdb_filename,
                  oemol=None,
                  cache=None,
                  restrain_rmsd=False):
    """
    Prepare simulation

    Parameters
    ----------
    destination_path : str
        The path to the RUN to be created
    protein_pdb_filename : str
        Path to protein PDB file
    oemol : openeye.oechem.OEMol, optional, default=None
        The molecule to parameterize, with SDData attached
        If None, don't include the small molecule
    restrain_rmsd : bool, optional, default=False
        If True, restrain RMSD during first equilibration phase
    """
    # Parameters
    from simtk import unit, openmm
    protein_forcefield = 'amber14/protein.ff14SB.xml'
    solvent_forcefield = 'amber14/tip3p.xml'
    small_molecule_forcefield = 'openff-1.2.0'
    water_model = 'tip3p'
    solvent_padding = 10.0 * unit.angstrom
    ionic_strength = 70 * unit.millimolar  # assay buffer: 20 mM HEPES pH 7.3, 1 mM TCEP, 50 mM NaCl, 0.01% Tween-20, 10% glycerol
    pressure = 1.0 * unit.atmospheres
    collision_rate = 1.0 / unit.picoseconds
    temperature = 300.0 * unit.kelvin
    timestep = 4.0 * unit.femtoseconds
    iterations = 1000  # 1 ns equilibration
    nsteps_per_iteration = 250

    # Prepare phases
    import os
    system_xml_filename = os.path.join(destination_path, 'system.xml.bz2')
    integrator_xml_filename = os.path.join(destination_path,
                                           'integrator.xml.bz2')
    state_xml_filename = os.path.join(destination_path, 'state.xml.bz2')

    # Check if we can skip setup
    openmm_files_exist = os.path.exists(
        system_xml_filename) and os.path.exists(
            state_xml_filename) and os.path.exists(integrator_xml_filename)
    if openmm_files_exist:
        return

    # Create barostat
    barostat = openmm.MonteCarloBarostat(pressure, temperature)

    # Create RUN directory if it does not yet exist
    os.makedirs(destination_path, exist_ok=True)

    # Load any molecule(s)
    molecule = None
    if oemol is not None:
        from openforcefield.topology import Molecule
        molecule = Molecule.from_openeye(oemol, allow_undefined_stereo=True)
        molecule.name = 'MOL'  # Ensure residue is MOL
        print([res for res in molecule.to_topology().to_openmm().residues()])

    # Create SystemGenerator
    import os
    from simtk.openmm import app
    forcefield_kwargs = {
        'removeCMMotion': False,
        'hydrogenMass': 3.0 * unit.amu,
        'constraints': app.HBonds,
        'rigidWater': True
    }
    periodic_kwargs = {
        'nonbondedMethod': app.PME,
        'ewaldErrorTolerance': 2.5e-04
    }
    forcefields = [protein_forcefield, solvent_forcefield]
    from openmmforcefields.generators import SystemGenerator
    openmm_system_generator = SystemGenerator(
        forcefields=forcefields,
        molecules=molecule,
        small_molecule_forcefield=small_molecule_forcefield,
        cache=cache,
        barostat=barostat,
        forcefield_kwargs=forcefield_kwargs,
        periodic_forcefield_kwargs=periodic_kwargs)

    # Read protein
    print(f'Reading protein from {protein_pdb_filename}...')
    pdbfile = app.PDBFile(protein_pdb_filename)
    modeller = app.Modeller(pdbfile.topology, pdbfile.positions)

    if oemol is not None:
        # Add small molecule to the system
        modeller.add(molecule.to_topology().to_openmm(),
                     molecule.conformers[0])
        # DEBUG : Check residue name
        with open(os.path.join(destination_path, 'initial-complex.pdb'),
                  'wt') as outfile:
            app.PDBFile.writeFile(modeller.topology, modeller.positions,
                                  outfile)

    # Add solvent
    print('Adding solvent...')
    kwargs = {'padding': solvent_padding}
    modeller.addSolvent(openmm_system_generator.forcefield,
                        model='tip3p',
                        ionicStrength=ionic_strength,
                        **kwargs)

    # Create an OpenMM system
    print('Creating OpenMM system...')
    system = openmm_system_generator.create_system(modeller.topology)

    # Add a virtual bond between protein and ligand to make sure they are not imaged separately
    if oemol is not None:
        import mdtraj as md
        mdtop = md.Topology.from_openmm(
            modeller.topology)  # excludes solvent and ions
        for res in mdtop.residues:
            print(res)
        protein_atom_indices = mdtop.select(
            '(protein and name CA)')  # protein CA atoms
        ligand_atom_indices = mdtop.select(
            '((resname MOL) and (mass > 1))')  # ligand heavy atoms
        protein_atom_index = int(protein_atom_indices[0])
        ligand_atom_index = int(ligand_atom_indices[0])
        force = openmm.CustomBondForce('0')
        force.addBond(protein_atom_index, ligand_atom_index, [])
        system.addForce(force)

    # Add RMSD restraints if requested
    if restrain_rmsd:
        print('Adding RMSD restraint...')
        kB = unit.AVOGADRO_CONSTANT_NA * unit.BOLTZMANN_CONSTANT_kB
        kT = kB * temperature
        import mdtraj as md
        mdtop = md.Topology.from_openmm(
            pdbfile.topology)  # excludes solvent and ions
        #heavy_atom_indices = mdtop.select('mass > 1') # heavy solute atoms
        rmsd_atom_indices = mdtop.select(
            '(protein and (name CA)) or ((resname MOL) and (mass > 1))'
        )  # CA atoms and ligand heavy atoms
        rmsd_atom_indices = [int(index) for index in rmsd_atom_indices]
        custom_cv_force = openmm.CustomCVForce('(K_RMSD/2)*RMSD^2')
        custom_cv_force.addGlobalParameter('K_RMSD', kT / unit.angstrom**2)
        rmsd_force = openmm.RMSDForce(modeller.positions, rmsd_atom_indices)
        custom_cv_force.addCollectiveVariable('RMSD', rmsd_force)
        force_index = system.addForce(custom_cv_force)

    # Create OpenM Context
    platform = openmm.Platform.getPlatformByName('OpenCL')
    platform.setPropertyDefaultValue('Precision', 'mixed')
    from openmmtools import integrators
    integrator = integrators.LangevinIntegrator(temperature, collision_rate,
                                                timestep)
    context = openmm.Context(system, integrator, platform)
    context.setPositions(modeller.positions)

    # Report initial potential energy
    state = context.getState(getEnergy=True)
    print(
        f'Initial potential energy is {state.getPotentialEnergy()/unit.kilocalories_per_mole:.3f} kcal/mol'
    )

    # Store snapshots in MDTraj trajectory to examine RMSD
    import mdtraj as md
    import numpy as np
    mdtop = md.Topology.from_openmm(pdbfile.topology)
    atom_indices = mdtop.select('all')  # all solute atoms
    protein_atom_indices = mdtop.select(
        'protein and (mass > 1)')  # heavy solute atoms
    if oemol is not None:
        ligand_atom_indices = mdtop.select(
            '(resname MOL) and (mass > 1)')  # ligand heavy atoms
    trajectory = md.Trajectory(
        np.zeros([iterations + 1, len(atom_indices), 3], np.float32), mdtop)
    trajectory.xyz[0, :, :] = context.getState(getPositions=True).getPositions(
        asNumpy=True)[atom_indices] / unit.nanometers

    # Minimize
    print('Minimizing...')
    openmm.LocalEnergyMinimizer.minimize(context)

    # Equilibrate (with RMSD restraint if needed)
    import numpy as np
    from rich.progress import track
    import time
    initial_time = time.time()
    for iteration in track(range(iterations), 'Equilibrating...'):
        integrator.step(nsteps_per_iteration)
        trajectory.xyz[iteration + 1, :, :] = context.getState(
            getPositions=True).getPositions(
                asNumpy=True)[atom_indices] / unit.nanometers
    elapsed_time = (time.time() - initial_time) * unit.seconds
    ns_per_day = (context.getState().getTime() /
                  elapsed_time) / (unit.nanoseconds / unit.day)
    print(f'Performance: {ns_per_day:8.3f} ns/day')

    if restrain_rmsd:
        # Disable RMSD restraint
        context.setParameter('K_RMSD', 0.0)

        print('Minimizing...')
        openmm.LocalEnergyMinimizer.minimize(context)

        for iteration in track(range(iterations),
                               'Equilibrating without RMSD restraint...'):
            integrator.step(nsteps_per_iteration)

    # Retrieve state
    state = context.getState(getPositions=True,
                             getVelocities=True,
                             getEnergy=True,
                             getForces=True)
    system.setDefaultPeriodicBoxVectors(*state.getPeriodicBoxVectors())
    modeller.topology.setPeriodicBoxVectors(state.getPeriodicBoxVectors())
    print(
        f'Final potential energy is {state.getPotentialEnergy()/unit.kilocalories_per_mole:.3f} kcal/mol'
    )

    # Equilibrate again if we restrained the RMSD
    if restrain_rmsd:
        print('Removing RMSD restraint from system...')
        system.removeForce(force_index)

    #if oemol is not None:
    #    # Check final RMSD
    #    print('checking RMSD...')
    #    trajectory.superpose(trajectory, atom_indices=protein_atom_indices)
    #    protein_rmsd = md.rmsd(trajectory, trajectory[-1], atom_indices=protein_atom_indices)[-1] * 10 # Angstroms
    #    oechem.OESetSDData(oemol, 'equil_protein_rmsd', f'{protein_rmsd:.2f} A')
    #    ligand_rmsd = md.rmsd(trajectory, trajectory[-1], atom_indices=ligand_atom_indices)[-1] * 10 # Angstroms
    #    oechem.OESetSDData(oemol, 'equil_ligand_rmsd', f'{ligand_rmsd:.2f} A')
    #    print('RMSD after equilibration: protein {protein_rmsd:8.2f} A | ligand {ligand_rmsd:8.3f} A')

    # Save as OpenMM
    print('Exporting for OpenMM FAH simulation...')
    import bz2
    with bz2.open(integrator_xml_filename, 'wt') as f:
        f.write(openmm.XmlSerializer.serialize(integrator))
    with bz2.open(state_xml_filename, 'wt') as f:
        f.write(openmm.XmlSerializer.serialize(state))
    with bz2.open(system_xml_filename, 'wt') as f:
        f.write(openmm.XmlSerializer.serialize(system))
    with bz2.open(os.path.join(destination_path, 'equilibrated-all.pdb.gz'),
                  'wt') as f:
        app.PDBFile.writeFile(modeller.topology, state.getPositions(), f)
    with open(os.path.join(destination_path, 'equilibrated-solute.pdb'),
              'wt') as f:
        import mdtraj
        mdtraj_topology = mdtraj.Topology.from_openmm(modeller.topology)
        mdtraj_trajectory = mdtraj.Trajectory(
            [state.getPositions(asNumpy=True) / unit.nanometers],
            mdtraj_topology)
        selection = mdtraj_topology.select('not water')
        mdtraj_trajectory = mdtraj_trajectory.atom_slice(selection)
        app.PDBFile.writeFile(mdtraj_trajectory.topology.to_openmm(),
                              mdtraj_trajectory.openmm_positions(0), f)
    if oemol is not None:
        # Write molecule as SDF, SMILES, and mol2
        for extension in ['sdf', 'mol2', 'smi', 'csv']:
            filename = os.path.join(destination_path, f'molecule.{extension}')
            with oechem.oemolostream(filename) as ofs:
                oechem.OEWriteMolecule(ofs, oemol)

    # Clean up
    del context, integrator
コード例 #8
0
pdb = app.PDBFile(pdb_filename)

print("Loading forcefield: %s" % ffxml_filenames)
forcefield = app.ForceField(*ffxml_filenames)

# Create the system
print('Creating OpenMM System...')
system = forcefield.createSystem(pdb.topology,
                                 nonbondedMethod=app.PME,
                                 constraints=app.HBonds,
                                 removeCMMotion=True)

# Add forces
print("Adding CV force...")
rmsd = openmm.RMSDForce(pdb.positions, list(range(702)))
cv_force = openmm.CustomCVForce("rmsd")
cv_force.addCollectiveVariable('rmsd', rmsd)
bv = mtd.BiasVariable(cv_force, 0, 2.5, 0.1, False)

# Add a barostat
# print('Adding barostat...')
# barostat = openmm.MonteCarloBarostat(pressure, temperature)
# system.addForce(barostat)

# Create simulation
print("Creating Simulation...")
integrator = openmm.LangevinIntegrator(temperature, collision_rate, timestep)
meta = mtd.Metadynamics(system, [bv], temperature, 3,
                        1.2 * unit.kilojoules_per_mole, 100)
simulation = app.Simulation(pdb.topology, system, integrator)
simulation.context.setPositions(pdb.positions)
コード例 #9
0
def prepare_simulation(molecule, basedir, save_openmm=False):
    """
    Prepare simulation systems

    Parameters
    ----------
    molecule : openeye.oechem.OEMol
       The molecule to set up
    basedir : str
       The base directory for docking/ and fah/ directories
    save_openmm : bool, optional, default=False
       If True, save gzipped OpenMM System, State, Integrator
    """
    # Parameters
    from simtk import unit, openmm
    water_model = 'tip3p'
    solvent_padding = 10.0 * unit.angstrom
    box_size = openmm.vec3.Vec3(3.4,3.4,3.4)*unit.nanometers
    ionic_strength = 100 * unit.millimolar # 100
    pressure = 1.0 * unit.atmospheres
    collision_rate = 1.0 / unit.picoseconds
    temperature = 300.0 * unit.kelvin
    timestep = 4.0 * unit.femtoseconds
    nsteps_per_iteration = 250
    iterations = 10000 # 10 ns (covalent score)

    protein_forcefield = 'amber14/protein.ff14SB.xml'
    small_molecule_forcefield = 'openff-1.1.0'
    #small_molecule_forcefield = 'gaff-2.11' # only if you really like atomtypes
    solvation_forcefield = 'amber14/tip3p.xml'

    # Create SystemGenerators
    import os
    from simtk.openmm import app
    from openforcefield.topology import Molecule
    off_molecule = Molecule.from_openeye(molecule, allow_undefined_stereo=True)
    print(off_molecule)
    barostat = openmm.MonteCarloBarostat(pressure, temperature)

    # docking directory
    docking_basedir = os.path.join(basedir, 'docking')

    # gromacs directory
    gromacs_basedir = os.path.join(basedir, 'gromacs')
    os.makedirs(gromacs_basedir, exist_ok=True)

    # openmm directory
    openmm_basedir = os.path.join(basedir, 'openmm')
    os.makedirs(openmm_basedir, exist_ok=True)

    # Cache directory
    cache = os.path.join(openmm_basedir, f'{molecule.GetTitle()}.json')

    common_kwargs = {'removeCMMotion': False, 'ewaldErrorTolerance': 5e-04,
                     'nonbondedMethod': app.PME, 'hydrogenMass': 3.0*unit.amu}
    unconstrained_kwargs = {'constraints': None, 'rigidWater': False}
    constrained_kwargs = {'constraints': app.HBonds, 'rigidWater': True}
    forcefields = [protein_forcefield, solvation_forcefield]
    from openmmforcefields.generators import SystemGenerator
    parmed_system_generator = SystemGenerator(forcefields=forcefields,
                                              molecules=[off_molecule], small_molecule_forcefield=small_molecule_forcefield, cache=cache,
                                              barostat=barostat,
                                              forcefield_kwargs={**common_kwargs, **unconstrained_kwargs})
    openmm_system_generator = SystemGenerator(forcefields=forcefields,
                                              molecules=[off_molecule], small_molecule_forcefield=small_molecule_forcefield, cache=cache,
                                              barostat=barostat,
                                              forcefield_kwargs={**common_kwargs, **constrained_kwargs})

    # Prepare phases
    import os
    print(f'Setting up simulation for {molecule.GetTitle()}...')
    for phase in ['complex', 'ligand']:
        phase_name = f'{molecule.GetTitle()} - {phase}'
        print(phase_name)

        pdb_filename = os.path.join(docking_basedir, phase_name + '.pdb')
        gro_filename = os.path.join(gromacs_basedir, phase_name + '.gro')
        top_filename = os.path.join(gromacs_basedir, phase_name + '.top')

        system_xml_filename = os.path.join(openmm_basedir, phase_name+'.system.xml.gz')
        integrator_xml_filename = os.path.join(openmm_basedir, phase_name+'.integrator.xml.gz')
        state_xml_filename = os.path.join(openmm_basedir, phase_name+'.state.xml.gz')

        # Check if we can skip setup
        gromacs_files_exist = os.path.exists(gro_filename) and os.path.exists(top_filename)
        openmm_files_exist = os.path.exists(system_xml_filename) and os.path.exists(state_xml_filename) and os.path.exists(integrator_xml_filename)
        if gromacs_files_exist and (not save_openmm or openmm_files_exist):
            continue

        # Filter out UNK atoms by spruce
        with open(pdb_filename, 'r') as infile:
            lines = [ line for line in infile if 'UNK' not in line ]
        from io import StringIO
        pdbfile_stringio = StringIO(''.join(lines))

        # Read the unsolvated system into an OpenMM Topology
        pdbfile = app.PDBFile(pdbfile_stringio)
        topology, positions = pdbfile.topology, pdbfile.positions

        # Add solvent
        print('Adding solvent...')
        modeller = app.Modeller(topology, positions)
        if phase == 'ligand':
            kwargs = {'boxSize' : box_size}
        else:
            kwargs = {'padding' : solvent_padding}
        modeller.addSolvent(openmm_system_generator.forcefield, model='tip3p', ionicStrength=ionic_strength, **kwargs)

        # Create an OpenMM system
        system = openmm_system_generator.create_system(modeller.topology)

        # If monitoring covalent distance, add an unused force
        warheads_found = find_warheads(molecule)
        covalent = (len(warheads_found) > 0)
        if covalent and phase=='complex':

            # Find warhead atom indices
            sulfur_atom_index = None
            for atom in topology.atoms():
                if (atom.residue.name == 'CYS') and (atom.residue.id == '145') and (atom.name == 'SG'):
                    sulfur_atom_index = atom.index
                    break
            if sulfur_atom_index is None:
                raise Exception('CYS145 SG atom cannot be found')

            print('Adding CustomCVForces...')
            custom_cv_force = openmm.CustomCVForce('0')
            for warhead_type, warhead_atom_index in warheads_found.items():
                distance_force = openmm.CustomBondForce('r')
                distance_force.setUsesPeriodicBoundaryConditions(True)
                distance_force.addBond(sulfur_atom_index, warhead_atom_index, [])
                custom_cv_force.addCollectiveVariable(warhead_type, distance_force)
            force_index = system.addForce(custom_cv_force)

        # Create OpenM Context
        platform = openmm.Platform.getPlatformByName('CUDA')
        platform.setPropertyDefaultValue('Precision', 'mixed')
        integrator = openmm.LangevinIntegrator(temperature, collision_rate, timestep)
        context = openmm.Context(system, integrator, platform)
        context.setPositions(modeller.positions)

        # Report initial potential energy
        state = context.getState(getEnergy=True)
        print(f'{molecule.GetTitle()} {phase} : Initial potential energy is {state.getPotentialEnergy()/unit.kilocalories_per_mole:.3f} kcal/mol')

        # Minimize
        print('Minimizing...')
        openmm.LocalEnergyMinimizer.minimize(context)

        # Equilibrate
        print('Equilibrating...')
        from tqdm import tqdm
        import numpy as np
        distances = np.zeros([iterations], np.float32)
        for iteration in tqdm(range(iterations)):
            integrator.step(nsteps_per_iteration)
            if covalent and phase=='complex':
                # Get distance in Angstroms
                distances[iteration] = min(custom_cv_force.getCollectiveVariableValues(context)[:]) * 10

        # Retrieve state
        state = context.getState(getPositions=True, getVelocities=True, getEnergy=True, getForces=True)
        system.setDefaultPeriodicBoxVectors(*state.getPeriodicBoxVectors())
        modeller.topology.setPeriodicBoxVectors(state.getPeriodicBoxVectors())
        print(f'{molecule.GetTitle()} {phase} : Final potential energy is {state.getPotentialEnergy()/unit.kilocalories_per_mole:.3f} kcal/mol')

        # Remove CustomCVForce
        if covalent and phase=='complex':
            print('Removing CustomCVForce...')
            system.removeForce(force_index)
            from pymbar.timeseries import detectEquilibration
            t0, g, Neff = detectEquilibration(distances)
            distances = distances[t0:]
            distance_min = distances.min()
            distance_mean = distances.mean()
            distance_stddev = distances.std()
            oechem.OESetSDData(molecule, 'covalent_distance_min', str(distance_min))
            oechem.OESetSDData(molecule, 'covalent_distance_mean', str(distance_mean))
            oechem.OESetSDData(molecule, 'covalent_distance_stddev', str(distance_stddev))
            print(f'Covalent distance: mean {distance_mean:.3f} A : stddev {distance_stddev:.3f} A')

        # Save as OpenMM
        if save_openmm:
            print('Saving as OpenMM...')
            import gzip
            with gzip.open(integrator_xml_filename, 'wt') as f:
                f.write(openmm.XmlSerializer.serialize(integrator))
            with gzip.open(state_xml_filename,'wt') as f:
                f.write(openmm.XmlSerializer.serialize(state))
            with gzip.open(system_xml_filename,'wt') as f:
                f.write(openmm.XmlSerializer.serialize(system))
            with gzip.open(os.path.join(openmm_basedir, phase_name+'-explicit.pdb.gz'), 'wt') as f:
                app.PDBFile.writeFile(modeller.topology, state.getPositions(), f)
            with gzip.open(os.path.join(openmm_basedir, phase_name+'-solute.pdb.gz'), 'wt') as f:
                import mdtraj
                mdtraj_topology = mdtraj.Topology.from_openmm(modeller.topology)
                mdtraj_trajectory = mdtraj.Trajectory([state.getPositions(asNumpy=True) / unit.nanometers], mdtraj_topology)
                selection = mdtraj_topology.select('not water')
                mdtraj_trajectory = mdtraj_trajectory.atom_slice(selection)
                app.PDBFile.writeFile(mdtraj_trajectory.topology.to_openmm(), mdtraj_trajectory.openmm_positions(0), f)

        # Convert to gromacs via ParmEd
        print('Saving as gromacs...')
        import parmed
        parmed_system = parmed_system_generator.create_system(modeller.topology)
        #parmed_system.setDefaultPeriodicBoxVectors(*state.getPeriodicBoxVectors())
        structure = parmed.openmm.load_topology(modeller.topology, parmed_system, xyz=state.getPositions(asNumpy=True))
        structure.save(gro_filename, overwrite=True)
        structure.save(top_filename, overwrite=True)
コード例 #10
0
ファイル: systems.py プロジェクト: atoms-ufrj/atomsmm
    def __init__(self,
                 system,
                 rcutIn,
                 rswitchIn,
                 alchemical_atoms=[],
                 coupling_parameter='lambda',
                 coupling_function='lambda',
                 middle_scale=True,
                 coulomb_scaling=False,
                 lambda_coul=0,
                 use_softcore=False,
                 split_alchemical=True):
        self.this = copy.deepcopy(system).this
        Kc = 138.935456637  # Coulomb constant in kJ.nm/mol.e^2

        self._parameter = coupling_parameter
        self._coulomb_scaling = coulomb_scaling
        self._middle_scale = middle_scale
        self._use_softcore = use_softcore

        # Define specific sets of atoms:
        all_atoms = set(range(self.getNumParticles()))
        solute_atoms = set(alchemical_atoms)
        solvent_atoms = all_atoms - solute_atoms

        # Define force-switched potential expressions:
        rci = rcutIn.value_in_unit(unit.nanometer)
        rsi = rswitchIn.value_in_unit(unit.nanometer)
        fsp = self._force_switched_potential(rci, rsi, Kc)

        mixing_rules = '; chargeprod = charge1*charge2'
        mixing_rules += '; sigma = 0.5*(sigma1 + sigma2)'
        mixing_rules += '; epsilon = sqrt(epsilon1*epsilon2)'

        # Nonbonded force will only account for solvent-solvent interactions:
        for force in self.getForces():
            if isinstance(force, openmm.NonbondedForce):
                # Store a copy of the nonbonded force before changes are made:
                nonbonded = copy.deepcopy(force)

                # Place it at due group:
                force.setForceGroup(2 if middle_scale else 1)
                force.setReciprocalSpaceForceGroup(2 if middle_scale else 1)

                # Delete all solute interaction parameters:
                self._solute_charges = {}
                for i in solute_atoms:
                    charge, _, _ = force.getParticleParameters(i)
                    self._solute_charges[i] = charge
                    force.setParticleParameters(i, 0.0, 1.0, 0.0)

                # Identify solute-solute exceptions and turn all of them into exclusions:
                exception_pairs = []
                for index in range(force.getNumExceptions()):
                    i, j, _, _, _ = nonbonded.getExceptionParameters(index)
                    if set([i, j]).issubset(solute_atoms):
                        exception_pairs.append(set([i, j]))
                        force.setExceptionParameters(index, i, j, 0.0, 1.0,
                                                     0.0)

                # Identify all other solute-solute interactions. In the system's nonbonded force,
                # turn them into exclusion exceptions. In the stored copy, turn them into general
                # exceptions for the sake of forthcoming imports:
                for i, j in itertools.combinations(solute_atoms, 2):
                    if set([i, j]) not in exception_pairs:
                        force.addException(i, j, 0.0, 1.0, 0.0)
                        q1, sig1, eps1 = nonbonded.getParticleParameters(i)
                        q2, sig2, eps2 = nonbonded.getParticleParameters(j)
                        nonbonded.addException(i, j, q1 * q2,
                                               (sig1 + sig2) / 2,
                                               np.sqrt(eps1 * eps2))

                # Add a force-switched potential with internal cutoff if a RESPA-related
                # middle scale has been requested:
                if middle_scale:
                    near_force = openmm.CustomNonbondedForce(fsp +
                                                             mixing_rules)
                    self._import_from_nonbonded(near_force, force)
                    near_force.setCutoffDistance(rcutIn)
                    near_force.setUseSwitchingFunction(False)
                    near_force.setUseLongRangeCorrection(False)
                    near_force.addGlobalParameter('respa_switch', 0)
                    near_force.setForceGroup(1)
                    self.addForce(near_force)

                    # Because all exceptions in nonbonded become exclusions in near_force,
                    # capture all non-exclusion exceptions into a custom bonded force:
                    exceptions = openmm.CustomBondForce(
                        f'step({rci}-r)*U; U = {fsp}')
                    exceptions.addGlobalParameter('respa_switch', 0)
                    for parameter in ['chargeprod', 'sigma', 'epsilon']:
                        exceptions.addPerBondParameter(parameter)
                    for index in range(force.getNumExceptions()):
                        i, j, chargeprod, sigma, epsilon = force.getExceptionParameters(
                            index)
                        if chargeprod / chargeprod.unit != 0.0 or epsilon / epsilon.unit != 0.0:
                            exceptions.addBond(i, j,
                                               (chargeprod, sigma, epsilon))
                    if exceptions.getNumBonds() > 0:
                        exceptions.setForceGroup(1)
                        self.addForce(exceptions)

                # Store a pointer to the altered non-bonded force:
                self._nonbonded_force = force
            else:
                # Place bonded and other forces at group 0:
                force.setForceGroup(0)

        # Return if no solute atoms were specified:
        if not solute_atoms:
            return

        # To allow decoupling rather than annihilation, solute-solute interactions are handled
        # by a custom bond force without a cut-off:
        ljc = f'4*epsilon*x*(x - 1) + {Kc}*chargeprod/r; x = (sigma/r)^6'
        full_range = openmm.CustomBondForce(ljc)
        full_range.setForceGroup(2 if middle_scale else 1)
        intrasolute_forces = [full_range]

        # If a RESPA-related middle scale has been requested, also create a short-ranged version:
        if middle_scale:
            short_range = openmm.CustomBondForce(f'step({rci}-r)*U; U = {fsp}')
            short_range.addGlobalParameter('respa_switch', 0)
            short_range.setForceGroup(1)
            intrasolute_forces.append(short_range)

        for force in intrasolute_forces:
            for parameter in ['chargeprod', 'sigma', 'epsilon']:
                force.addPerBondParameter(parameter)
            self.addForce(force)

        # Add interactions due to solute-solute pairs previously treated as exceptions:
        for index in range(nonbonded.getNumExceptions()):
            i, j, chargeprod, sigma, epsilon = nonbonded.getExceptionParameters(
                index)
            if set([i, j]).issubset(solute_atoms):
                for force in intrasolute_forces:
                    force.addBond(i, j, (chargeprod, sigma, epsilon))

        # NOTE: if Coulomb scaling treatment was requested, the electrostatic part of full-ranged
        # solute-solvent interactions will be enabled by reactivating solute charges while keeping
        # all intra-solute interactions excluded.

        # If both Coulomb scaling and a RESPA-related middle scale were requested, the electrostatic
        # part of short-ranged solute-solvent interactions must be added as well:
        if coulomb_scaling and middle_scale:
            # Create a force-switched electrostatic potential and add it to the system:
            fsep = self._force_switched_eletrostatic_potential(rci, rsi, Kc)
            short_range = openmm.CustomNonbondedForce(fsep + mixing_rules)
            self._import_from_nonbonded(short_range, nonbonded)
            short_range.setCutoffDistance(rcutIn)
            short_range.setUseSwitchingFunction(False)
            short_range.setUseLongRangeCorrection(False)
            short_range.addGlobalParameter('respa_switch', 0)
            short_range.setForceGroup(1)
            short_range.addInteractionGroup(solute_atoms, solvent_atoms)
            self.addForce(short_range)
            self._fsep_force = short_range

        if use_softcore:
            # Softcore potential is fully considered in the middle time scale:
            ljsoft = f'4*{coupling_parameter}*epsilon*x*(x - 1)'
            ljsoft += f'; x = 1/((r/sigma)^6 + 0.5*(1-{coupling_parameter}))'
            full_range = openmm.CustomNonbondedForce(ljsoft + mixing_rules)
            self._import_from_nonbonded(full_range,
                                        nonbonded,
                                        import_globals=True)
            full_range.addInteractionGroup(solute_atoms, solvent_atoms)
            full_range.addGlobalParameter(coupling_parameter, 1.0)
            full_range.addEnergyParameterDerivative(coupling_parameter)
            full_range.setForceGroup(2 if middle_scale else 1)
            self.addForce(full_range)

            # Store force object related to alchemical coupling/decoupling:
            self._alchemical_vdw_force = full_range

            if middle_scale:
                # In the current version, the full softcore potential is allocated in the middle
                # time scale because it would be difficult to apply the force-switch strategy. This
                # might be reviewed in the future.
                short_range = copy.deepcopy(full_range)
                short_range.setEnergyFunction(f'respa_switch*{ljsoft}' +
                                              mixing_rules)
                short_range.addGlobalParameter('respa_switch', 0)
                short_range.setForceGroup(1)
                self.addForce(short_range)

        else:
            # The van der Waals part of solute-solvent interactions are defined as collective
            # variables multiplied by a coupling function:
            potential = '((gt0-gt1)*S + gt1)*alchemical_vdw_energy'
            potential += f'; gt0 = step({coupling_parameter})'
            potential += f'; gt1 = step({coupling_parameter}-1)'
            potential += f'; S = {coupling_function}'
            cv_force = openmm.CustomCVForce(potential)
            cv_force.addGlobalParameter(coupling_parameter, 1.0)
            cv_force.addEnergyParameterDerivative(coupling_parameter)

            # For the van der Waals part of solute-solvent interactions, it is considered that no
            # exceptions exist which involve a solute atom and a solvent atom (this might be
            # reviewed in future versions):
            lj = f'4*epsilon*x*(x - 1); x = (sigma/r)^6'
            full_range = openmm.CustomNonbondedForce(lj + mixing_rules)
            self._import_from_nonbonded(full_range,
                                        nonbonded,
                                        import_globals=True)
            full_range.addInteractionGroup(solute_atoms, solvent_atoms)
            full_range_cv_force = copy.deepcopy(cv_force)
            full_range_cv_force.addCollectiveVariable('alchemical_vdw_energy',
                                                      full_range)
            full_range_cv_force.setForceGroup(2 if middle_scale else 1)
            self.addForce(full_range_cv_force)

            # Store force object related to alchemical coupling/decoupling:
            self._alchemical_vdw_force = full_range_cv_force

            if middle_scale and split_alchemical:
                fsljp = self._force_switched_potential(rci, rsi, 0.0)
                short_range = openmm.CustomNonbondedForce(fsljp + mixing_rules)
                self._import_from_nonbonded(short_range, nonbonded)
                short_range.setCutoffDistance(rcutIn)
                short_range.setUseSwitchingFunction(False)
                short_range.setUseLongRangeCorrection(False)
                short_range.addGlobalParameter('respa_switch', 0)
                short_range.addInteractionGroup(solute_atoms, solvent_atoms)
                short_range_cv_force = cv_force
                short_range_cv_force.addCollectiveVariable(
                    'alchemical_vdw_energy', short_range)
                short_range_cv_force.setForceGroup(1)
                self.addForce(short_range_cv_force)
            elif middle_scale:
                short_range = copy.deepcopy(full_range)
                short_range.setEnergyFunction(f'respa_switch*{lj}' +
                                              mixing_rules)
                short_range.addGlobalParameter('respa_switch', 0)
                short_range.setForceGroup(1)
                self.addForce(short_range)

        # Store Coulomb scaling constant as zero, but reset it if a different value has been passed:
        self._lambda_coul = 0
        self.reset_coulomb_scaling_factor(lambda_coul)
コード例 #11
0
def restrain_particle_number(system,
                             particles,
                             direction,
                             bound,
                             sigma,
                             target,
                             k,
                             weights=None):
    '''
    Restrain the number of selected particles in a region.

    The region is defined by direction (x, y or z) and bound (lower and upper).
    Each particle is consider as a Gaussian distribution with standard deviation equal to sigma.
    The number of particles is restrained to the target value
    using a harmonic function with force constant k.

    Parameters
    ----------
    system : mm.System
    particles : list of int
    direction : ['x', 'y', 'z']
    bound : list of float
    sigma : float
        Variance of the particle Gaussian in unit of nm
    target : float
    k : float
        Strength of the harmonic restraint in unit of kJ/mol
    weights : list of float, optional

    Returns
    -------
    force : mm.CustomCVForce

    '''
    if direction not in ['x', 'y', 'z']:
        raise Exception('direction can only be x, y or z')
    _min, _max = bound
    if unit.is_quantity(_min):
        _min = _min.value_in_unit(nm)
    if unit.is_quantity(_max):
        _max = _max.value_in_unit(nm)
    if unit.is_quantity(sigma):
        sigma = sigma.value_in_unit(nm)
    if unit.is_quantity(k):
        k = k.value_in_unit(kJ_mol)
    if weights is None:
        weights = [1.0] * len(particles)
    if len(weights) != len(particles):
        raise Exception('particles and weights should have the same length')

    if _min is not None:
        str_min = f'erf(({_min}-{direction})/{2 ** 0.5 * sigma})'
    else:
        str_min = '-1'

    if _max is not None:
        str_max = f'erf(({_max}-{direction})/{2 ** 0.5 * sigma})'
    else:
        str_max = '1'

    nforce = mm.CustomExternalForce(f'0.5*({str_max}-{str_min})*weight')
    nforce.addPerParticleParameter('weight')
    for i, w in zip(particles, weights):
        nforce.addParticle(i, [w])

    cvforce = mm.CustomCVForce(f'0.5*{k}*(number-{target})^2')
    cvforce.addCollectiveVariable('number', nforce)
    system.addForce(cvforce)

    return cvforce
コード例 #12
0
    0.208691
])
weights2 = list([
    -0.002846, 0.023837, 0.535975, 0.777245, 0.040945, 0.1803, -0.247547,
    0.112163
])
for n in range(8):
    w1[sel_feat[n]] = weights1[n]
    w2[sel_feat[n]] = weights2[n]

# Specify a unique CustomCVForce
expression_1 = str()
for i in range(len(sel_feat) - 1):
    expression_1 += f'({w1[sel_feat[i]]} * feat_{i}) + '
expression_1 += f'({w1[sel_feat[i+1]]} * feat_{i+1})'
cv_force_1 = mm.CustomCVForce(expression_1)
for i in range(len(sel_feat)):
    print(w1[sel_feat[i]])
    print(feat_dict[sel_feat[i] - 1])
    cv_force_1.addCollectiveVariable(f'feat_{i}', feat_dict[sel_feat[i] - 1])
bv_1 = metadynamics.BiasVariable(cv_force_1,
                                 cv_min_1,
                                 cv_max_1,
                                 cv_std_1,
                                 periodic=False)

expression_2 = str()
for i in range(len(sel_feat) - 1):
    expression_2 += f'({w2[sel_feat[i]]} * feat_{i}) + '
expression_2 += f'({w2[sel_feat[i+1]]} * feat_{i+1})'
cv_force_2 = copy.deepcopy(cv_force_1)
コード例 #13
0
# distances
dis_0 = mm.CustomBondForce("r")
dis_0.addBond(int(dis[0][0]), int(dis[0][1]))
dis_1 = mm.CustomBondForce("r")
dis_1.addBond(int(dis[1][0]), int(dis[1][1]))
dis_2 = mm.CustomBondForce("r")
dis_2.addBond(int(dis[2][0]), int(dis[2][1]))
dis_3 = mm.CustomBondForce("r")
dis_3.addBond(int(dis[3][0]), int(dis[3][1]))
dis_4 = mm.CustomBondForce("r")
dis_4.addBond(int(dis[4][0]), int(dis[4][1]))
print("Done populating dihedrals and distances.")

# Specify a unique CustomCVForce
cv_force = mm.CustomCVForce(
    'dih_0 + dih_1 + dih_2 + dih_3 + dih_4 + dih_5 + dih_6 + dih_7 + dis_0 + dis_1 + dis_2 + dis_3 + dis_4'
)
cv_force.addCollectiveVariable('dih_0', dih_0)
cv_force.addCollectiveVariable('dih_1', dih_1)
cv_force.addCollectiveVariable('dih_2', dih_2)
cv_force.addCollectiveVariable('dih_3', dih_3)
cv_force.addCollectiveVariable('dih_4', dih_4)
cv_force.addCollectiveVariable('dih_5', dih_5)
cv_force.addCollectiveVariable('dih_6', dih_6)
cv_force.addCollectiveVariable('dih_7', dih_7)
cv_force.addCollectiveVariable('dis_0', dis_0)
cv_force.addCollectiveVariable('dis_1', dis_1)
cv_force.addCollectiveVariable('dis_2', dis_2)
cv_force.addCollectiveVariable('dis_3', dis_3)
cv_force.addCollectiveVariable('dis_4', dis_4)
bv = mtd.BiasVariable(cv_force, -np.pi * 8, np.pi * 8 + 10 * 5, 0.5, True)
コード例 #14
0
ファイル: iapetus.py プロジェクト: LaYeqa/iapetus
    def _auto_create_thermodynamic_states(self,
                                          structure,
                                          topology,
                                          reference_thermodynamic_state,
                                          spacing=0.25 * unit.angstroms):
        """
        Create thermodynamic states for sampling along pore axis.
        """
        topo = md.Topology.from_openmm(topology)
        data = DataPoints(structure, topo)
        data.writeCoordinates(open('cylinder.xyz', 'w'))
        cylinder = CylinderFitting(data.coordinates)
        print(cylinder.vmdCommands(), file=open('vmd_commands.txt', 'w'))
        b_index, t_index = cylinder.atomsInExtremes(data.coordinates, n=5)
        cylinder.writeExtremesCoords(data.coordinates, b_index, t_index,
                                     open('extremes.xyz', 'w'))
        bottom_atoms = []
        top_atoms = []
        for idx in b_index:
            bottom_atoms.append(data.index[idx])
        for idx in t_index:
            top_atoms.append(data.index[idx])

        axis_distance = cylinder.height * unit.angstroms

        selection = '(residue {}) and (mass > 1.5)'.format(self.ligand_resseq)
        print('Determining ligand atoms using "{}"...'.format(selection))
        ligand_atoms = self.mdtraj_topology.select(selection)

        expansion_factor = 1.3
        nstates = int(expansion_factor * axis_distance / spacing) + 1
        print('nstates: {}'.format(nstates))
        sigma_y = axis_distance / float(
            nstates)  # stddev of force-free fluctuations in y-axis
        K_y = self.kT / (sigma_y**2)  # spring constant

        print('vertical sigma_y = {:.3f} A'.format(sigma_y / unit.angstroms))

        # Compute restraint width
        scale_factor = 0.25
        sigma_xz = scale_factor * cylinder.r * unit.angstroms  # stddev of force-free fluctuations in xz-plane
        K_xz = self.kT / (sigma_xz**2)  # spring constant

        Kmax = K_y
        Kmin = K_xz
        dr = axis_distance * (expansion_factor - 1.0) / 2.0
        rmax = axis_distance + dr
        rmin = -dr

        # Create restraint state that encodes this axis
        print('Creating restraint...')
        from yank.restraints import RestraintState

        # Collective variable definitions
        common = 'r = distance(g1,g2);'
        common += 'theta = angle(g1,g2,g3);'
        r_parallel = openmm.CustomCentroidBondForce(3,
                                                    'r*cos(theta);' + common)
        r_orthogonal = openmm.CustomCentroidBondForce(3,
                                                      'r*sin(theta);' + common)

        for cv in [r_parallel, r_orthogonal]:
            cv.addGroup([int(index) for index in ligand_atoms])
            cv.addGroup([int(index) for index in bottom_atoms])
            cv.addGroup([int(index) for index in top_atoms])
            cv.addBond([0, 1, 2], [])

        energy_parallel = '(K_parallel/2)*(r_parallel-r0)^2;'
        energy_parallel += 'r0 = lambda_restraints * (rmax - rmin) + rmin;'
        self.cvforce_parallel = openmm.CustomCVForce(energy_parallel)
        self.cvforce_parallel.addCollectiveVariable('r_parallel', r_parallel)

        energy_orthogonal = '(1/2)*(Kmin + S*(Kmax - Kmin))*(r_orthogonal^2);'
        energy_orthogonal += 'S = 1 - step(z_ext-z)*(1 + u^3*(15*u - 6*u^2 - 10));'
        energy_orthogonal += 'u = step(z-z_int)*(z - z_int)/(z_ext - z_int);'
        energy_orthogonal += 'z = abs(r0-z_c);'
        energy_orthogonal += 'r0 = lambda_restraints * (rmax - rmin) + rmin;'
        self.cvforce_orthogonal = openmm.CustomCVForce(energy_orthogonal)
        self.cvforce_orthogonal.addCollectiveVariable('r_orthogonal',
                                                      r_orthogonal)
        for force in [self.cvforce_parallel, self.cvforce_orthogonal]:
            force.addGlobalParameter('rmax', rmax)
            force.addGlobalParameter('rmin', rmin)
            force.addGlobalParameter('lambda_restraints', 1.0)

        self.cvforce_parallel.addGlobalParameter('K_parallel', K_y)

        self.cvforce_orthogonal.addGlobalParameter('Kmax', Kmax)
        self.cvforce_orthogonal.addGlobalParameter('Kmin', Kmin)
        self.cvforce_orthogonal.addGlobalParameter('z_c', axis_distance / 2.0)
        self.cvforce_orthogonal.addGlobalParameter('z_int',
                                                   0.8 * (axis_distance / 2.0))
        self.cvforce_orthogonal.addGlobalParameter('z_ext',
                                                   1.3 * (axis_distance / 2.0))

        self.system.addForce(self.cvforce_parallel)
        self.system.addForce(self.cvforce_orthogonal)
        # Update reference thermodynamic state
        print('Updating system in reference thermodynamic state...')
        self.reference_thermodynamic_state.set_system(self.system,
                                                      fix_state=True)

        ## Create alchemical state
        ##from openmmtools.alchemy import AlchemicalState
        ##alchemical_state = AlchemicalState.from_system(self.reference_thermodynamic_state.system)

        # Create restraint state
        restraint_state = RestraintState(lambda_restraints=1.0)

        # Create thermodynamic states to be sampled
        # TODO: Should we include an unbiased state?
        initial_time = time.time()
        thermodynamic_states = list()
        ##compound_state = states.CompoundThermodynamicState(self.reference_thermodynamic_state, composable_states=[alchemical_state, restraint_state])
        compound_state = states.CompoundThermodynamicState(
            self.reference_thermodynamic_state,
            composable_states=[restraint_state])
        for lambda_restraints in np.linspace(0, 1, nstates):
            thermodynamic_state = copy.deepcopy(compound_state)
            thermodynamic_state.lambda_restraints = lambda_restraints
            #    #thermodynamic_state.lambda_sterics = 1.0
            #    #thermodynamic_state.lambda_electrostatics = 1.0
            thermodynamic_states.append(thermodynamic_state)
        elapsed_time = time.time() - initial_time
        print('Creating thermodynamic states took %.3f s' % elapsed_time)

        return thermodynamic_states
コード例 #15
0
    def __init__(self,
                 system,
                 variables,
                 temperature,
                 biasFactor,
                 height,
                 frequency,
                 saveFrequency=None,
                 biasDir=None):
        """Create a Metadynamics object.

        Parameters
        ----------
        system: System
            the System to simulate.  A CustomCVForce implementing the bias is created and
            added to the System.
        variables: list of BiasVariables
            the collective variables to sample
        temperature: temperature
            the temperature at which the simulation is being run.  This is used in computing
            the free energy.
        biasFactor: float
            used in scaling the height of the Gaussians added to the bias.  The collective
            variables are sampled as if the effective temperature of the simulation were
            temperature*biasFactor.
        height: energy
            the initial height of the Gaussians to add
        frequency: int
            the interval in time steps at which Gaussians should be added to the bias potential
        saveFrequency: int (optional)
            the interval in time steps at which to write out the current biases to disk.  At
            the same time it writes biases, it also checks for updated biases written by other
            processes and loads them in.  This must be a multiple of frequency.
        biasDir: str (optional)
            the directory to which biases should be written, and from which biases written by
            other processes should be loaded
        """
        if not unit.is_quantity(temperature):
            temperature = temperature * unit.kelvin
        if not unit.is_quantity(height):
            height = height * unit.kilojoules_per_mole
        if biasFactor < 1.0:
            raise ValueError('biasFactor must be >= 1')
        if (saveFrequency is None
                and biasDir is not None) or (saveFrequency is not None
                                             and biasDir is None):
            raise ValueError('Must specify both saveFrequency and biasDir')
        if saveFrequency is not None and (saveFrequency < frequency
                                          or saveFrequency % frequency != 0):
            raise ValueError('saveFrequency must be a multiple of frequency')
        self.variables = variables
        self.temperature = temperature
        self.biasFactor = biasFactor
        self.height = height
        self.frequency = frequency
        self.biasDir = biasDir
        self.saveFrequency = saveFrequency
        self._id = np.random.randint(0x7FFFFFFF)
        self._saveIndex = 0
        self._selfBias = np.zeros(tuple(v.gridWidth for v in variables))
        self._totalBias = np.zeros(tuple(v.gridWidth for v in variables))
        self._loadedBiases = {}
        self._deltaT = temperature * (biasFactor - 1)
        varNames = ['cv%d' % i for i in range(len(variables))]
        self._force = mm.CustomCVForce('table(%s)' % ', '.join(varNames))
        for name, var in zip(varNames, variables):
            self._force.addCollectiveVariable(name, var.force)
        widths = [v.gridWidth for v in variables]
        mins = [v.minValue for v in variables]
        maxs = [v.maxValue for v in variables]
        if len(variables) == 1:
            self._table = mm.Continuous1DFunction(self._totalBias.flatten(),
                                                  mins[0], maxs[0])
        elif len(variables) == 2:
            self._table = mm.Continuous2DFunction(widths[0], widths[1],
                                                  self._totalBias.flatten(),
                                                  mins[0], maxs[0], mins[1],
                                                  maxs[1])
        elif len(variables) == 3:
            self._table = mm.Continuous3DFunction(widths[0], widths[1],
                                                  widths[2],
                                                  self._totalBias.flatten(),
                                                  mins[0], maxs[0], mins[1],
                                                  maxs[1], mins[2], maxs[2])
        else:
            raise ValueError(
                'Metadynamics requires 1, 2, or 3 collective variables')
        self._force.addTabulatedFunction('table', self._table)
        self._force.setForceGroup(31)
        system.addForce(self._force)
        self._syncWithDisk()
コード例 #16
0
    def _anneal_ligand(self, structure, index):
        """
        Anneal ligand interactions to clean up clashes.
        Returns
        -------
            positions : unit.Quantity
                Positions of all atoms after annealing the ligand
        """

        reference_system = copy.deepcopy(self.phases[index].system)
        protein = self.phases[index].mdtraj_top.select(
            f'protein and backbone and type CA').tolist()
        rmsd = openmm.RMSDForce(
            self.phases[index].structure.positions,
            protein + self.phases[index].lg1_idx + self.phases[index].lg2_idx)
        energy_expression = 'step(dRMSD) * (K_RMSD/2)*dRMSD^2; dRMSD = (RMSD-RMSD0);'
        restraint_force = openmm.CustomCVForce(energy_expression)
        restraint_force.addCollectiveVariable('RMSD', rmsd)
        restraint_force.addGlobalParameter(
            'K_RMSD', 2 * unit.kilocalories_per_mole / (unit.angstroms**2))
        restraint_force.addGlobalParameter('RMSD0', 2 * unit.angstroms)
        reference_system.addForce(restraint_force)

        alchemical_system = self._alchemically_modify_ligand(
            reference_system, index)

        from openmmtools.alchemy import AlchemicalState
        alchemical_state_zero = mmtools.alchemy.AlchemicalState.from_system(
            alchemical_system, parameters_name_suffix='zero')
        alchemical_state_one = mmtools.alchemy.AlchemicalState.from_system(
            alchemical_system, parameters_name_suffix='one')

        thermodynamic_state = states.ThermodynamicState(
            system=alchemical_system, temperature=300 * unit.kelvin)

        composable_states = [alchemical_state_zero, alchemical_state_one]
        compound_states = states.CompoundThermodynamicState(
            thermodynamic_state, composable_states=composable_states)
        sampler_state = states.SamplerState(
            positions=structure.positions,
            box_vectors=structure.topology.getPeriodicBoxVectors())
        # Anneal
        n_annealing_steps = 1000
        integrator = openmm.LangevinIntegrator(300 * unit.kelvin,
                                               90.0 / unit.picoseconds,
                                               1.0 * unit.femtoseconds)
        context, integrator = mmtools.cache.global_context_cache.get_context(
            compound_states, integrator)
        sampler_state.apply_to_context(context)
        compound_states.lambda_sterics_one = 0.0
        compound_states.lambda_electrostatics_one = 0.0
        compound_states.apply_to_context(context)
        print('Annealing sterics of ligand 1...')
        for step in progressbar.progressbar(range(n_annealing_steps)):
            compound_states.lambda_sterics_zero = float(step) / float(
                n_annealing_steps)
            compound_states.lambda_electrostatics_zero = 0.0
            compound_states.apply_to_context(context)
            integrator.step(1)
        print('Annealing electrostatics of ligand 1...')
        for step in progressbar.progressbar(range(n_annealing_steps)):
            compound_states.lambda_sterics_zero = 1.0
            compound_states.lambda_electrostatics_zero = float(step) / float(
                n_annealing_steps)
            compound_states.apply_to_context(context)
            integrator.step(1)
        compound_states.lambda_sterics_zero = 1.0
        compound_states.lambda_electrostatics_zero = 1.0
        compound_states.apply_to_context(context)
        print('Annealing sterics of ligand 2...')
        for step in progressbar.progressbar(range(n_annealing_steps)):
            compound_states.lambda_sterics_one = float(step) / float(
                n_annealing_steps)
            compound_states.lambda_electrostatics_one = 0.0
            compound_states.apply_to_context(context)
            integrator.step(1)
        print('Annealing electrostatics of ligand 2...')
        for step in progressbar.progressbar(range(n_annealing_steps)):
            compound_states.lambda_sterics_one = 1.0
            compound_states.lambda_electrostatics_one = float(step) / float(
                n_annealing_steps)
            compound_states.apply_to_context(context)
            integrator.step(1)
        compound_states.apply_to_context(context)
        sampler_state.update_from_context(context)
        # Compute the final energy of the system.
        final_energy = thermodynamic_state.reduced_potential(context)
        print('final alchemical energy {:8.3f}kT'.format(final_energy))

        return sampler_state.positions
コード例 #17
0
quintent_vol.addPerBondParameter('alpha1')
quintent_vol.addPerBondParameter('alpha2')
quintent_vol.addPerBondParameter('alpha3')
quintent_vol.addPerBondParameter('alpha4')
quintent_vol.addPerBondParameter('alpha5')
quintent_vol.addGlobalParameter('height', height)

for q in quintents:
    quintent_vol.addBond(
        [q[0], q[1], q[2], q[3], q[4]],
        [alpha[q[0]], alpha[q[1]], alpha[q[2]], alpha[q[3]], alpha[q[4]]])

vol0 = 0.5301799 * unit.nanometer**3
K = 50000 * unit.kilojoules_per_mole / unit.nanometer**6
energy = '(K/2)*((p + t + q + qui) - vol0)^2;'
cvforce = openmm.CustomCVForce(energy)
cvforce.addCollectiveVariable('p', pairs_vol)
cvforce.addCollectiveVariable('t', triplets_vol)
cvforce.addCollectiveVariable('q', quad_vol)
cvforce.addCollectiveVariable('qui', quintent_vol)
cvforce.addGlobalParameter('K', K)
cvforce.addGlobalParameter('vol0', vol0)
cvforce.setForceGroup(29)
system.addForce(cvforce)

K_c = 200 * unit.kilojoules_per_mole / unit.angstroms**2
force = openmm.CustomCentroidBondForce(2, '(K_c/2)*distance(g1, g2)^2')
force.addGlobalParameter('K_c', K_c)
force.addGroup([int(index) for index in lig1_heavy_atoms])
force.addGroup([int(index) for index in lig2_heavy_atoms])
force.addBond([0, 1], [])