예제 #1
0
 def __del__(self):
     PDBFile.writeFooter(self._topology, self._out)
     self._out.close()
예제 #2
0
 def __del__(self):
     if self._topology is not None:
         PDBFile.writeFooter(self._topology, self._out)
     self._out.close()
예제 #3
0
 def close(self):
     PDBFile.writeFooter(self.topology, self.outfile)
     self.outfile.flush()
예제 #4
0
 def __del__(self):
     if self._topology is not None:
         PDBFile.writeFooter(self._topology, self._out)
     self._out.close()
예제 #5
0
 def __del__(self):
     PDBFile.writeFooter(self._topology, self._out)
     self._out.close()
예제 #6
0
def prep_system(box_width):
    # if model not in supported_models:
    # raise Exception("Specified water model '%s' is not in list of supported models: %s" % (model, str(supported_models)))

    # Load forcefield for solvent model and ions.
    # force_fields = ['tip3p.xml']
    # if ionic_strength != 0.0*unit.molar:
    # force_fields.append('amber99sb.xml')  # For the ions.
    ff = app.ForceField('tip3p.xml')

    # Create empty topology and coordinates.
    top = app.Topology()
    pos = unit.Quantity((), unit.angstroms)

    # Create new Modeller instance.
    m = app.Modeller(top, pos)

    boxSize = Vec3(box_width, box_width, box_width) * unit.nanometers
    # boxSize = unit.Quantity(numpy.ones([3]) * box_edge / box_edge.unit, box_edge.unit)
    m.addSolvent(ff, boxSize=boxSize, model='tip3p')

    system = ff.createSystem(m.getTopology(),
                             nonbondedMethod=app.NoCutoff,
                             constraints=None,
                             rigidWater=False)

    positions = m.getPositions()

    positions = unit.Quantity(np.array(positions / positions.unit),
                              positions.unit)

    # pdb_str = io.StringIO()
    fname = "debug.pdb"

    fhandle = open(fname, "w")

    PDBFile.writeHeader(m.getTopology(), fhandle)
    PDBFile.writeModel(m.getTopology(), positions, fhandle, 0)
    PDBFile.writeFooter(m.getTopology(), fhandle)

    return system, positions, np.eye(3) * box_width, fname

    assert 0

    # , positiveIon=positive_ion,
    # negativeIon=negative_ion, ionicStrength=ionic_strength)

    # Get new topology and coordinates.
    newtop = m.getTopology()
    newpos = m.getPositions()

    # Convert positions to numpy.
    positions = unit.Quantity(numpy.array(newpos / newpos.unit), newpos.unit)

    # Create OpenMM System.
    system = ff.createSystem(newtop,
                             nonbondedMethod=nonbondedMethod,
                             nonbondedCutoff=cutoff,
                             constraints=None,
                             rigidWater=constrained,
                             removeCMMotion=False)

    # Set switching function and dispersion correction.
    forces = {
        system.getForce(index).__class__.__name__: system.getForce(index)
        for index in range(system.getNumForces())
    }

    forces['NonbondedForce'].setUseSwitchingFunction(False)
    if switch_width is not None:
        forces['NonbondedForce'].setUseSwitchingFunction(True)
        forces['NonbondedForce'].setSwitchingDistance(cutoff - switch_width)

    forces['NonbondedForce'].setUseDispersionCorrection(dispersion_correction)
    forces['NonbondedForce'].setEwaldErrorTolerance(ewaldErrorTolerance)

    n_atoms = system.getNumParticles()
    self.ndof = 3 * n_atoms - (constrained * n_atoms)

    self.topology = m.getTopology()
    self.system = system
    self.positions = positions
예제 #7
0
def run_simulation(mol_name, mol, smirnoff_params, pdb, inference):
    omm_forcefield = app.ForceField('amber99sb.xml', 'tip3p.xml')
    system = omm_forcefield.createSystem(
        pdb.topology,
        nonbondedMethod=app.NoCutoff,
        constraints=None,
        rigidWater=False)
    
    coords = []
    for x,y,z in pdb.positions:
        coords.append([to_md_units(x),to_md_units(y),to_md_units(z)])
    coords = np.array(coords)
    host_conf = coords

    host_potentials, (host_params, host_param_groups), host_masses = serialize.deserialize_system(system)
    smirnoff = ForceField("test_forcefields/smirnoff99Frosst.offxml")

    # CAREFUL THIS SHOULD BE THE SINGLE SET OF CONSISTENT FORCEFIELDS
    am1 = True
    if am1:
        guest_potentials, smirnoff_params, smirnoff_param_groups, guest_conf, guest_masses = forcefield.parameterize(mol, smirnoff, am1)
    else:
        guest_potentials, _, smirnoff_param_groups, guest_conf, guest_masses = forcefield.parameterize(mol, smirnoff, am1)

    # guest_conf = rescale_and_center(guest_conf, scale_factor=3.0)

    combined_potentials, combined_params, combined_param_groups, combined_conf, combined_masses = forcefield.combiner(
        host_potentials, guest_potentials, 
        host_params, smirnoff_params, 
        host_param_groups, smirnoff_param_groups, 
        host_conf, guest_conf, 
        host_masses, guest_masses)

    num_host_atoms = host_conf.shape[0]

    def filter_groups(param_groups, groups):
        roll = np.zeros_like(param_groups)
        for g in groups:
            roll = np.logical_or(roll, param_groups == g)
        return roll

    # host_dp_idxs = np.argwhere(filter_groups(host_param_groups, [7])).reshape(-1)
    # guest_dp_idxs = np.argwhere(filter_groups(smirnoff_param_groups, [7])).reshape(-1)

    # 1. host bond lengths
    # 7. host charges
    # 8. host vdw sigma
    # 9. host vdw epsilon

    # 17. charge

    combined_dp_idxs = np.argwhere(filter_groups(combined_param_groups, [17])).reshape(-1)
    if inference:
        combined_dp_idxs = []

    outfile = open("frames/"+mol_name+".pdb", "w")

    combined_pdb = Chem.CombineMols(Chem.MolFromPDBFile(pdb.filepath, removeHs=False), mol)
    combined_pdb_str = StringIO(Chem.MolToPDBBlock(combined_pdb))

    cpdb = app.PDBFile(combined_pdb_str)
    PDBFile.writeHeader(cpdb.topology, outfile)

    def write_fn(x, frame_idx):
        PDBFile.writeModel(cpdb.topology, x, outfile, frame_idx)

    dG, dG_grads, all_xis = minimize(
        mol_name,
        num_host_atoms,
        combined_potentials,
        combined_params,
        combined_conf,
        combined_masses,
        combined_dp_idxs,
        writer_fn=write_fn
    )

    PDBFile.writeFooter(cpdb.topology, outfile)
    outfile.flush()

    # print("CDPI", combined_dp_idxs)
    # (ytz): we need to offset this by number of host params to compute the indices
    # of the original ligand parameters.
    if not inference:
        ligand_dp_idxs = combined_dp_idxs - len(host_params)
    else:
        ligand_dp_idxs = None

    return dG, dG_grads, ligand_dp_idxs
예제 #8
0
    def integrate(self, topology_proposal, initial_positions, direction='insert', platform=None):
        """
        Performs NCMC switching to either delete or insert atoms according to the provided `topology_proposal`.

        For `delete`, the system is first modified from fully interacting to alchemically modified, and then NCMC switching is used to eliminate atoms.
        For `insert`, the system begins with eliminated atoms in an alchemically noninteracting form and NCMC switching is used to turn atoms on, followed by making system real.
        The contribution of transforming the real system to/from an alchemical system is included.

        Parameters
        ----------
        topology_proposal : TopologyProposal
            Contains old/new Topology and System objects and atom mappings.
        initial_positions : simtk.unit.Quantity with dimension [natoms, 3] with units of distance.
            Positions of the atoms at the beginning of the NCMC switching.
        direction : str, optional, default='insert'
            Direction of alchemical switching:
                'insert' causes lambda to switch from 0 to 1 over nsteps steps of integration
                'delete' causes lambda to switch from 1 to 0 over nsteps steps of integration
        platform : simtk.openmm.Platform, optional, default=None
            If not None, this platform is used for integration.

        Returns
        -------
        final_positions : simtk.unit.Quantity of dimensions [nparticles,3] with units compatible with angstroms
            The final positions after `nsteps` steps of alchemical switching
        logP : float
            The log acceptance probability of the switch
        potential : simtk.unit.Quantity with units compatible with kilocalories_per_mole
            For `delete`, the potential energy of the final (alchemically eliminated) conformation.
            For `insert`, the potential energy of the initial (alchemically eliminated) conformation.

        """
        if direction not in ['insert', 'delete']:
            raise Exception("'direction' must be one of ['insert', 'delete']; was '%s' instead" % direction)

        if (self.nsteps == 0):
            # Special case of instantaneous insertion/deletion.
            logP = 0.0
            final_positions = copy.deepcopy(initial_positions)
            from perses.tests.utils import compute_potential
            if direction == 'delete':
                potential = self.beta * compute_potential(topology_proposal.old_system, initial_positions, platform=self.platform)
            elif direction == 'insert':
                potential = self.beta * compute_potential(topology_proposal.new_system, initial_positions, platform=self.platform)
            return [final_positions, logP, potential]

        # Create alchemical system.
        [unmodified_system, alchemical_system] = self.make_alchemical_system(topology_proposal, direction=direction)

        # DEBUG: Compute initial potential of unmodified system and alchemical system to make sure finite.
        from perses.tests.utils import compute_potential
        #print(compute_potential(unmodified_system, initial_positions, platform=self.platform))
        #print(compute_potential(alchemical_system, initial_positions, platform=self.platform))

        # Select subset of switching functions based on which alchemical parameters are present in the system.
        available_parameters = self._getAvailableParameters(alchemical_system)
        functions = { parameter_name : self.functions[parameter_name] for parameter_name in self.functions if (parameter_name in available_parameters) }

        # Create an NCMC velocity Verlet integrator.
        integrator = NCMCAlchemicalIntegrator(self.temperature, alchemical_system, functions, nsteps=self.nsteps, timestep=self.timestep, direction=direction)
        # Set the constraint tolerance if specified.
        if self.constraint_tolerance is not None:
            integrator.setConstraintTolerance(self.constraint_tolerance)
        # Create a context on the specified platform.
        if self.platform is not None:
            context = openmm.Context(alchemical_system, integrator, self.platform)
        else:
            context = openmm.Context(alchemical_system, integrator)
        context.setPositions(initial_positions)
        context.applyConstraints(integrator.getConstraintTolerance())
        # Set velocities to temperature and apply velocity constraints.
        context.setVelocitiesToTemperature(self.temperature)
        context.applyVelocityConstraints(integrator.getConstraintTolerance())

        # Set initial context parameters.
        if direction == 'insert':
            for parameter_name in available_parameters:
                context.setParameter(parameter_name, 0)
        elif direction == 'delete':
            for parameter_name in available_parameters:
                context.setParameter(parameter_name, 1)

        # Compute initial potential of alchemical state.
        initial_potential = self.beta * context.getState(getEnergy=True).getPotentialEnergy()
        if np.isnan(initial_potential):
            raise NaNException("Initial potential of 'insert' operation is NaN (unmodified potential was %.3f kT, alchemical potential was %.3f kT before changing lambda)" % (unmodified_potential, alchemical_potential))
        from perses.tests.utils import compute_potential_components
        #print("initial potential before '%s' : %f kT" % (direction, initial_potential))
        #print("initial potential components:   %s" % str(compute_potential_components(context))) # DEBUG

        # Take a single integrator step since all switching steps are unrolled in NCMCAlchemicalIntegrator.
        try:
            # Write PDB file if requested.
            if self.write_pdb_interval:
                if direction == 'insert':
                    topology = topology_proposal.new_topology
                    indices = topology_proposal.unique_new_atoms
                else:
                    topology = topology_proposal.old_topology
                    indices = topology_proposal.unique_old_atoms

                # Write atom indices that are changing
                import pickle
                filename = 'ncmc-%s-%d-atomindices.pkl' % (direction, self.nattempted)
                outfile = open(filename, 'wb')
                pickle.dump(indices, outfile)
                outfile.close()

                from simtk.openmm.app import PDBFile
                filename = 'ncmc-%s-%d.pdb' % (direction, self.nattempted)
                outfile = open(filename, 'w')
                PDBFile.writeHeader(topology, file=outfile)
                modelIndex = 0
                PDBFile.writeModel(topology, context.getState(getPositions=True).getPositions(asNumpy=True), file=outfile, modelIndex=modelIndex)
                try:
                    for step in range(self.nsteps):
                        integrator.step(1)
                        if (step+1)%self.write_pdb_interval == 0:
                            modelIndex += 1
                            PDBFile.writeModel(topology, context.getState(getPositions=True).getPositions(asNumpy=True), file=outfile, modelIndex=modelIndex)
                except ValueError as e:
                    # System is exploding and coordinates won't fit in PDB ATOM fields
                    print(e)

                PDBFile.writeFooter(topology, file=outfile)
                outfile.close()
            else:
                integrator.step(self.nsteps)

        except Exception as e:
            # Trap NaNs as a special exception (allowing us to reject later, if desired)
            if str(e) == "Particle coordinate is nan":
                raise NaNException(str(e))
            else:
                raise e

        # Set final context parameters.
        if direction == 'insert':
            for parameter_name in available_parameters:
                context.setParameter(parameter_name, 1)
        elif direction == 'delete':
            for parameter_name in available_parameters:
                context.setParameter(parameter_name, 0)

        # Compute final potential of alchemical state.
        final_potential = self.beta * context.getState(getEnergy=True).getPotentialEnergy()
        if np.isnan(final_potential):
            raise NaNException("Final potential of 'delete' operation is NaN")
        #print("final potential before '%s' : %f kT" % (direction, final_potential))
        #print("final potential components: %s" % str(compute_potential_components(context))) # DEBUG
        #print('')

        # Store final positions and log acceptance probability.
        final_positions = context.getState(getPositions=True).getPositions(asNumpy=True)
        logP_NCMC = integrator.getLogAcceptanceProbability(context)
        # DEBUG
        logging.debug("NCMC logP %+10.1f | initial_total_energy %+10.1f kT | final_total_energy %+10.1f kT." % (logP_NCMC, integrator.getGlobalVariableByName('initial_total_energy'), integrator.getGlobalVariableByName('final_total_energy')))
        # Clean up NCMC switching integrator.
        del context, integrator

        # Compute contribution from transforming real system to/from alchemical system.
        logP_alchemical_correction = self._computeAlchemicalCorrection(unmodified_system, alchemical_system, initial_positions, final_positions, direction=direction)

        # Compute total logP
        logP = logP_NCMC + logP_alchemical_correction

        # Clean up alchemical system.
        del alchemical_system

        # Select whether to return initial or final potential.
        if direction == 'insert':
            potential = initial_potential
        elif direction == 'delete':
            potential = final_potential

        # Keep track of statistics.
        self.nattempted += 1

        # Return
        return [final_positions, logP, potential]
예제 #9
0
    def __init__(self, objs, out_filepath):
        """
        This class writes frames out in the PDBFormat. It supports both OpenMM topology
        formats and RDKit ROMol types. The molecules are ordered sequentially by the order
        in which they are in objs

        Usage:

        ```
        topol = app.PDBFile("1dfr.pdb").topology
        mol_a = Chem.MolFromMolBlock("...")
        mol_b = Chem.MolFromMolBlock("...")

        writer = PDBWriter([topol, mol_a, mol_b], out.pdb) # writer header
        writer.write_frame(coords) # coords must be in units of angstroms
        writer.close() # writes footer
        ```

        Parameters
        ----------
        obj: list of either Chem.Mol or app.Topoolgy types
            Molecules of interest

        out_filepath: str
            Where we write out the PDBFile to

        """

        assert len(objs) > 0

        rd_mols = []
        # super jank
        for obj in objs:
            if isinstance(obj, app.Topology):
                with tempfile.NamedTemporaryFile(mode='w') as fp:
                    # write
                    PDBFile.writeHeader(obj, fp)
                    PDBFile.writeModel(obj, np.zeros((obj.getNumAtoms(), 3)),
                                       fp, 0)
                    PDBFile.writeFooter(obj, fp)
                    fp.flush()
                    # read
                    rd_mols.append(Chem.MolFromPDBFile(fp.name,
                                                       removeHs=False))

            if isinstance(obj, Chem.Mol):
                rd_mols.append(obj)

        combined_mol = rd_mols[0]
        for mol in rd_mols[1:]:
            combined_mol = Chem.CombineMols(combined_mol, mol)

        with tempfile.NamedTemporaryFile(mode='w') as fp:
            # write
            Chem.MolToPDBFile(combined_mol, fp.name)
            fp.flush()
            # read
            combined_pdb = app.PDBFile(fp.name)
            self.topology = combined_pdb.topology

        self.out_handle = open(out_filepath, "w")
        PDBFile.writeHeader(self.topology, self.out_handle)
        self.topology = self.topology
        self.frame_idx = 0
예제 #10
0
 def close(self):
     """
     Write footer and close.
     """
     PDBFile.writeFooter(self.topology, self.out_handle)
     self.out_handle.flush()