Esempio n. 1
0
def integrator(i_type, temperature, friction, tolerance, timestep):
    from simtk import openmm as mm
    if i_type == 'variable':
        integrator = mm.VariableLangevinIntegrator(temperature, friction,
                                                   tolerance)
    elif i_type == 'fixed':
        integrator = mm.LangevinIntegrator(temperature, friction, timestep)
    return integrator
Esempio n. 2
0
    def _make_simulation(self):
        from simtk import openmm as mm
        integrator = mm.VariableLangevinIntegrator(self._temperature,
                                                   self._friction,
                                                   self._integrator_tolerance)
        integrator.setConstraintTolerance(self._constraint_tolerance)

        # Make a new simulation.
        from simtk.openmm import app
        s = app.Simulation(self._topology, self._system, integrator,
                           self._platform)
        self._simulation = s
Esempio n. 3
0
    def __init__(self, **kwargs):
        """
        All numbers here are floats. Units specified in a parameter. 

        Parameters
        ----------
        
        N : int
            number of particles 
        
        error_tol : float, optional
            Error tolerance parameter for variableLangevin integrator
            Values of around 0.01 are reasonable for a "nice" simulation
            (i.e. simulation with soft forces etc). 
            Simulations with strong forces may need 0.001 or less
            OpenMM manual recommends 0.001, but our forces tend to be "softer" than theirs

        timestep : number
            timestep in femtoseconds. Mandatory for non-variable integrators.
            Ignored for variableLangevin integrator. Value of 70-80 are appropriate

        collision_rate : number
            collision rate in inverse picoseconds. values of 0.01 or 0.05 are often used. 
            Consult with lab members on values.

            In brief, equilibrium simulations likely do not care about the exact dynamics 
            you're using, and therefore can be simulated in a "ballistic" dynamics with 
            col_rate of around 0.001-0.01.

            Dynamical simulations and active simulations may be more sensitive to col_rate,
            though this is still under discussion/investigation.

            Johannes converged on using 0.1 for loop extrusion simulations, just to be safe.

        PBCbox : (float,float,float) or False; default:False
            Controls periodic boundary conditions
            If PBCbox is False, do not use periodic boundary conditions
            If intending to use PBC, then set PBCbox to (x,y,z) where x,y,z are dimensions
            of the bounding box for PBC

        GPU : GPU index as a string ("0" for first, "1" for second etc.) 
            Machines with 1 GPU automatically select their GPU.

        integrator : "langevin", "variableLangevin", "verlet", "variableVerlet",
                     "brownian", optional Integrator to use
                     (see Openmm class reference)
                     
        mass : number or np.array
            Particle mass (default 100 amu)

        temperature : simtk.units.quantity(units.kelvin), optional
            Temperature of the simulation. Devault value is 300 K.

        verbose : bool, optional
            If True, prints a lot of stuff in the command line.

        length_scale : float, optional
            The geometric scaling factor of the system.
            By default, length_scale=1.0 and harmonic bonds and repulsive
            forces have the scale of 1 nm.

        max_Ek: float, optional
            raise error if kinetic energy in (kT/particle) exceeds this value 

        platform : string, optional
            Platform to use: 
            CUDA (preferred fast GPU platform)
            OpenCL (maybe slower GPU platofrm, does not need CUDA installed)
            CPU (medium speed parallelized CPU platform) 
            reference (slow CPU platform for debug)

        verbose : bool, optional
            Shout out loud about every change.
        
        precision: str, optional (not recommended to change)
            mixed is optimal for most situations. 
            If you are using double precision, it will be slower by a factor of 10 or so. 
            
        save_decimals: int or False, optional 
            Round to this number of decimals before saving. ``False`` is no rounding.  
            Default is 2. It gives maximum error of 0.005, which is nearly always harmless
            but saves up to 40% of storage space (0.6 of the original)
            Using one decimal is safe most of the time, and reduces storage to 40% of int32. 
            NOTE that using periodic boundary conditions will make storage advantage less. 
            

        """
        default_args = {
            "platform": "CUDA",
            "GPU": "0",
            "integrator": "variablelangevin",
            "temperature": 300,
            "PBCbox": False,
            "length_scale": 1.0,
            "mass": 100,
            "reporters": [],
            "max_Ek": 10,
            "precision": "mixed",
            "save_decimals": 2,
            "verbose": False,
        }
        valid_names = list(default_args.keys()) + [
            "N",
            "error_tol",
            "collision_rate",
            "timestep",
        ]
        for i in kwargs.keys():
            if i not in valid_names:
                raise ValueError(
                    "incorrect argument provided: {0}. Allowed are {1}".format(
                        i, valid_names))

        if None in kwargs.values():
            raise ValueError(
                "None is not allowed in arguments due to HDF5 incompatiliblity. Use False instead."
            )
        default_args.update(kwargs)
        kwargs = default_args
        self.kwargs = kwargs

        platform = kwargs["platform"]
        self.GPU = kwargs["GPU"]  # setting default GPU

        properties = {}
        if self.GPU.lower() != "default":
            if platform.lower() in ["cuda", "opencl"]:
                properties["DeviceIndex"] = str(self.GPU)
                properties["Precision"] = kwargs["precision"]
        self.properties = properties

        if platform.lower() == "opencl":
            platform_object = openmm.Platform.getPlatformByName("OpenCL")
        elif platform.lower() == "reference":
            platform_object = openmm.Platform.getPlatformByName("Reference")
        elif platform.lower() == "cuda":
            platform_object = openmm.Platform.getPlatformByName("CUDA")
        elif platform.lower() == "cpu":
            platform_object = openmm.Platform.getPlatformByName("CPU")
        else:
            raise RuntimeError("Undefined platform: {0}".format(platform))
        self.platform = platform_object

        self.temperature = kwargs["temperature"]

        self.collisionRate = kwargs["collision_rate"] * (1 /
                                                         simtk.unit.picosecond)

        self.integrator_type = kwargs["integrator"]
        if isinstance(self.integrator_type, string_types):
            self.integrator_type = str(self.integrator_type)
            if self.integrator_type.lower() == "langevin":
                self.integrator = openmm.LangevinIntegrator(
                    self.temperature,
                    kwargs["collision_rate"] * (1 / simtk.unit.picosecond),
                    kwargs["timestep"] * simtk.unit.femtosecond,
                )
            elif self.integrator_type.lower() == "variablelangevin":
                self.integrator = openmm.VariableLangevinIntegrator(
                    self.temperature,
                    kwargs["collision_rate"] * (1 / simtk.unit.picosecond),
                    kwargs["error_tol"],
                )
            elif self.integrator_type.lower() == "verlet":
                self.integrator = openmm.VariableVerletIntegrator(
                    kwargs["timestep"] * simtk.unit.femtosecond)
            elif self.integrator_type.lower() == "variableverlet":
                self.integrator = openmm.VariableVerletIntegrator(
                    kwargs["error_tol"])

            elif self.integrator_type.lower() == "brownian":
                self.integrator = openmm.BrownianIntegrator(
                    self.temperature,
                    kwargs["collision_rate"] * (1 / simtk.unit.picosecond),
                    kwargs["timestep"] * simtk.unit.femtosecond,
                )
        else:
            logging.info("Using the provided integrator object")
            self.integrator = self.integrator_type
            self.integrator_type = "UserDefined"
            kwargs["integrator"] = "user_defined"

        self.N = kwargs["N"]

        self.verbose = kwargs["verbose"]
        self.reporters = kwargs["reporters"]
        self.forces_applied = False
        self.length_scale = kwargs["length_scale"]
        self.eK_critical = kwargs["max_Ek"]  # Max allowed kinetic energy

        self.step = 0
        self.block = 0
        self.time = 0

        self.nm = simtk.unit.nanometer

        self.kB = simtk.unit.BOLTZMANN_CONSTANT_kB * simtk.unit.AVOGADRO_CONSTANT_NA
        self.kT = self.kB * self.temperature * simtk.unit.kelvin  # thermal energy

        # All masses are the same,
        # unless individual mass multipliers are specified in self.load()
        self.conlen = 1.0 * simtk.unit.nanometer * self.length_scale

        self.kbondScalingFactor = float(
            (2 * self.kT / self.conlen**2) /
            (simtk.unit.kilojoule_per_mole / simtk.unit.nanometer**2))

        self.system = openmm.System()

        # adding PBC
        self.PBC = False
        if kwargs["PBCbox"] is not False:
            self.PBC = True
            PBCbox = np.array(kwargs["PBCbox"])
            self.system.setDefaultPeriodicBoxVectors(
                [float(PBCbox[0]), 0.0, 0.0],
                [0.0, float(PBCbox[1]), 0.0],
                [0.0, 0.0, float(PBCbox[2])],
            )

        self.force_dict = {}  # Dictionary to store forces

        # saving arguments - not trying to save reporters because they are not serializable
        kwCopy = {i: j for i, j in kwargs.items() if i != "reporters"}
        for reporter in self.reporters:
            reporter.report("initArgs", kwCopy)
Esempio n. 4
0
    equilibrationContext.setPositions(positions)

    # equilibrate the system
    equilibrationContext.setVelocitiesToTemperature(temperatureEq)
    equilibrationContext.getIntegrator().step(equilibrationSteps)

    # get the positions and velocities in the equilibrium state
    equilibriumState = equilibrationContext.getState(getPositions=True,
                                                     getVelocities=True)
    equilibriumPositions = equilibriumState.getPositions()
    equilibriumVelocities = equilibriumState.getVelocities()
    del (equilibrationContext)

    # create the simulation integrator, either variable or standard
    if variableIntegrator:
        integrator = mm.VariableLangevinIntegrator(temperature, frictionCoeff,
                                                   errorTolerance)
    else:
        integrator = mm.LangevinIntegrator(temperature, frictionCoeff,
                                           stepSize)
    integrator.setConstraintTolerance(constraintTolerance)

    # create the context
    context = mm.Context(system, integrator, platform, properties)

    # transfer the positions and velocities from the equilibration context to simulation context
    context.setPositions(equilibriumPositions)
    context.setVelocities(equilibriumVelocities)

    # run the simulation, accumulating the kinetic energy and checking constraints
    passed = True
    for i in range(checks):
Esempio n. 5
0
def solvate_and_minimize(topology, positions, phase=''):
    """
    Solvate the given system and minimize.

    Parameters
    ----------
    topology : simtk.openmm.Topology
        The topology
    positions : simtk.unit.Quantity of numpy.array of (natoms,3) with units compatible with nanometer.
        The positions
    phase : string, optional, default=''
        The phase prefix to prepend to written files.

    Returns
    -------
    topology : simtk.openmm.app.Topology
        The new Topology object
    system : simtk.openm.System
        The solvated system.
    positions : simtk.unit.Quantity of dimension natoms x 3 with units compatible with angstroms
        The minimized positions.

    """

    # Solvate (if desired) and create system.
    logger.info("Solvating...")
    forcefield = app.ForceField(*ffxmls)
    modeller = app.Modeller(topology, positions)
    modeller.addHydrogens(forcefield=forcefield, pH=pH) # DEBUG
    if is_periodic:
        # Add solvent if in a periodic box.
        modeller.addSolvent(forcefield, padding=padding, model=water_name)
    system = forcefield.createSystem(modeller.topology, nonbondedMethod=nonbonded_method, nonbondedCutoff=nonbonded_cutoff, constraints=constraints)
    if is_periodic:
        system.addForce(openmm.MonteCarloBarostat(pressure, temperature, barostat_frequency))
    logger.info("System has %d atoms." % system.getNumParticles())

    # DEBUG
    print "modeller.topology.chains(): %s" % str([ chain.id for chain in modeller.topology.chains() ])

    # Serialize to XML files.
    logger.info("Serializing to XML...")
    system_filename = os.path.join(workdir, 'system.xml')
    write_file(system_filename, openmm.XmlSerializer.serialize(system))

    if minimize:
        # Create simulation.
        logger.info("Creating simulation...")
        errorTol = 0.001
        integrator = openmm.VariableLangevinIntegrator(temperature, collision_rate, errorTol)
        simulation = app.Simulation(modeller.topology, system, integrator)
        simulation.context.setPositions(modeller.positions)

        # Print potential energy.
        potential = simulation.context.getState(getEnergy=True).getPotentialEnergy()
        logger.info("Potential energy is %12.3f kcal/mol" % (potential / unit.kilocalories_per_mole))

        # Write modeller positions.
        logger.info("Writing modeller output...")
        filename = os.path.join(workdir, phase + 'modeller.pdb')
        app.PDBFile.writeFile(simulation.topology, modeller.positions, open(filename, 'w'))

        # Minimize energy.
        logger.info("Minimizing energy...")
        simulation.minimizeEnergy(maxIterations=max_minimization_iterations)
        #integrator.step(max_minimization_iterations)
        state = simulation.context.getState(getEnergy=True)
        potential_energy = state.getPotentialEnergy()
        if np.isnan(potential_energy / unit.kilocalories_per_mole):
            raise Exception("Potential energy is NaN after minimization.")
        logger.info("Potential energy after minimiziation: %.3f kcal/mol" % (potential_energy / unit.kilocalories_per_mole))
        modeller.positions = simulation.context.getState(getPositions=True).getPositions()

        # Print potential energy.
        potential = simulation.context.getState(getEnergy=True).getPotentialEnergy()
        logger.info("Potential energy is %12.3f kcal/mol" % (potential / unit.kilocalories_per_mole))

        # Write minimized positions.
        filename = os.path.join(workdir, phase + 'minimized.pdb')
        app.PDBFile.writeFile(simulation.topology, modeller.positions, open(filename, 'w'))

        # Clean up
        del simulation

    # Return the modeller instance.
    return [modeller.topology, system, modeller.positions]
Esempio n. 6
0
def minimize_potential_energy(chimera, ff: str,
                              output: str = "/tmp/build", keep_output_files=False, cuda=False,
                              restraint_backbone: bool = True) -> Tuple[unit.quantity.Quantity, Chimera]:
    """
    :param chimera: A chimera object where to perform the minimization
    :param forcefield: The forcefield to use for the minimization. Select between "amber" and "charmm"
    :param output: A folder where to keep the files. If not provided they will be stored in the /tmp folder and later removed.
    :param cuda: Whether to use GPU acceleration
    :param restraint_backbone: Keep the backbone atoms constraint in space

    :return: The chimera object that was minimized and the potential energy value.
    """

    if not os.path.exists(output):
        os.mkdir(output)

    smol = prepare_protein(chimera)
    smol.write(f"{output}/protein.pdb")
    pdb = PDBFile(f"{output}/protein.pdb")
    parm = load_file(f"{output}/protein.pdb")
    modeller = Modeller(pdb.topology, pdb.positions)

    if ff == 'amber':
        forcefield = ForceField('amber14-all.xml', 'amber14/tip3pfb.xml')
    if ff == 'charmm':
        forcefield = ForceField('charmm36.xml', 'charmm36/tip3p-pme-b.xml')

    modeller.addSolvent(forcefield, padding=1.0 * unit.nanometer)
    system = forcefield.createSystem(modeller.topology, nonbondedMethod=PME,
                                     nonbondedCutoff=1 * unit.nanometer, constraints=HBonds)
    if restraint_backbone:
        # Applies an external force on backbone atoms
        # This allows the backbone to stay rigid, while severe clashes can still be resolved
        force = mm.CustomExternalForce("k*((x-x0)^2+(y-y0)^2+(z-z0)^2)")
        force.addGlobalParameter("k", 5.0 * unit.kilocalories_per_mole / unit.angstroms ** 2)
        force.addPerParticleParameter("x0")
        force.addPerParticleParameter("y0")
        atoms = [atom for atom in modeller.topology.atoms()]
        for idx, atom_crd in enumerate(modeller.positions):
            if idx >= len(parm.atoms): continue
            if parm.atoms[idx] in ('CA', 'C', 'N'):
                force.addParticle(idx, atom_crd.value_in_unit(unit.nanometers))
        system.addForce(force)

    integrator = mm.LangevinIntegrator(temperature, friction, error_tolerance)
    simulation = Simulation(modeller.topology, system, integrator)
    simulation.context.setPositions(modeller.positions)

    # Get pre-minimization energy (scoring)
    state = simulation.context.getState(getEnergy=True, getForces=True)
    pre_energy = state.getPotentialEnergy().in_units_of(unit.kilocalories_per_mole)
    logger.info(f"Energy before minimization {pre_energy}")

    # Standard values for the integrator and tolerance constraint
    if not cuda:
        # Default value
        tolerance = 10 * unit.kilojoule / unit.mole
    else:
        # High tolerance so the CPU only pre-minimizes
        tolerance = 1e6

    # Setup CPU minimization
    integrator.setConstraintTolerance(distance_tolerance)
    simulation.minimizeEnergy(tolerance=tolerance)
    post_position = simulation.context.getState(getPositions=True).getPositions()
    post_state = simulation.context.getState(getEnergy=True, getForces=True)
    if cuda:
        min_coords = simulation.context.getState(getPositions=True)
        platform = mm.Platform.getPlatformByName('CUDA')
        properties = {'CudaPrecision': 'mixed'}
        gpu_integrator = mm.VariableLangevinIntegrator(temperature, friction, error_tolerance)
        gpu_integrator.setConstraintTolerance(distance_tolerance)
        gpu_min = Simulation(modeller.topology, system, gpu_integrator, platform, properties)
        gpu_min.context.setPositions(min_coords.getPositions())
        gpu_min.minimizeEnergy()
        post_position = gpu_min.context.getState(getPositions=True).getPositions()
        post_state = gpu_min.context.getState(getEnergy=True, getForces=True)

    post_energy = post_state.getPotentialEnergy().in_units_of(unit.kilocalories_per_mole)
    logger.info(f"Energy after minimization {post_energy}")

    PDBFile.writeFile(modeller.topology, post_position, open(f"{output}/structure_minimized.pdb", 'w'), keepIds=True)
    min_mol = Chimera(filename=f"{output}/structure_minimized.pdb")

    if keep_output_files is False:
        shutil.rmtree(output)

    return post_energy, min_mol