def pdb_fix_pdbfixer(pdbid, file_pathway, ph, chains_to_remove): """ Args: pdbid: 4 letter string specifying the PDB ID of the file yoou want to fix file_pathway: a string containing the pathway specifying how you want to organize the PDB files once written ph: the pH at which hydrogens will be determined and added chains_to_remove: dictionary containing pdbs with chains to remove Returns: nothing, but it does right PDB files """ print(pdbid) # Download the topology from rcsb based on pdbod fixer = PDBFixer(pdbid=pdbid) # Remove chains based on hand curated .csv file if pdbid in chains_to_remove['pdbid']: chains = chains_to_remove['chain_to_remove'][chain_to_remove['pdbid'].index(pdbid)] chains_list = chains.split() fixer.removeChains(chainIds=chains_list) # Determine the first and last residue resolved in chain 0 chains = [chain for chain in fixer.topology.chains()] resindices = [residue.index for residue in chains[0].residues()] resindices = natsorted(resindices) first_resindex = resindices[0] last_resindex = resindices[-1] # Find Missing residues and determine if they are C or N terminal fragments (which will be removed) fixer.findMissingResidues() if len(fixer.missingResidues) > 0: if sorted(fixer.missingResidues.keys())[0][-1] <= first_resindex: fixer.missingResidues.pop((sorted(fixer.missingResidues.keys())[0])) if sorted(fixer.missingResidues.keys())[-1][-1] >= last_resindex: fixer.missingResidues.pop((sorted(fixer.missingResidues.keys())[-1])) fixer.findNonstandardResidues() fixer.replaceNonstandardResidues() fixer.findMissingAtoms() fixer.addMissingAtoms() fixer.addMissingHydrogens(ph) # Write fixed PDB file, with all of the waters and ligands PDBFile.writeFile(fixer.topology, fixer.positions, open(os.path.join(file_pathway, '%s_fixed_ph%s.pdb' % (pdbid, ph)), 'w'), keepIds=keepNumbers) # Remove the ligand and write a pdb file fixer.removeHeterogens(True) PDBFile.writeFile(fixer.topology, fixer.positions, open(os.path.join(file_pathway, '%s_fixed_ph%s_apo.pdb' % (pdbid, ph)), 'w'), keepIds=keepNumbers) # Remove the waters and write a pdb file fixer.removeHeterogens(False) PDBFile.writeFile(fixer.topology, fixer.positions, open(os.path.join(file_pathway, '%s_fixed_ph%s_apo_nowater.pdb' % (pdbid, ph)), 'w'), keepIds=keepNumbers)
def __init__(self, file): """Load a prmtop file.""" top = Topology() ## The Topology read from the prmtop file self.topology = top # Load the prmtop file prmtop = amber_file_parser.PrmtopLoader(file) self._prmtop = prmtop # Add atoms to the topology PDBFile._loadNameReplacementTables() lastResidue = None c = top.addChain() for index in range(prmtop.getNumAtoms()): resNumber = prmtop.getResidueNumber(index) if resNumber != lastResidue: lastResidue = resNumber resName = prmtop.getResidueLabel(iAtom=index).strip() if resName in PDBFile._residueNameReplacements: resName = PDBFile._residueNameReplacements[resName] r = top.addResidue(resName, c) if resName in PDBFile._atomNameReplacements: atomReplacements = PDBFile._atomNameReplacements[resName] else: atomReplacements = {} atomName = prmtop.getAtomName(index).strip() if atomName in atomReplacements: atomName = atomReplacements[atomName] # Try to guess the element. upper = atomName.upper() if upper.startswith('CL'): element = elem.chlorine elif upper.startswith('NA'): element = elem.sodium elif upper.startswith('MG'): element = elem.magnesium else: try: element = elem.get_by_symbol(atomName[0]) except KeyError: element = None top.addAtom(atomName, element, r) # Add bonds to the topology atoms = list(top.atoms()) for bond in prmtop.getBondsWithH(): top.addBond(atoms[bond[0]], atoms[bond[1]]) for bond in prmtop.getBondsNoH(): top.addBond(atoms[bond[0]], atoms[bond[1]]) # Set the periodic box size. if prmtop.getIfBox(): top.setUnitCellDimensions(tuple(x.value_in_unit(unit.nanometer) for x in prmtop.getBoxBetaAndDimensions()[1:4])*unit.nanometer)
def fix_pdb(pdb_id, pdb_file, pdb_group): chains_to_retain = get_required_chains(pdb_group) chains_to_remove = [] for chain in PDBParser().get_structure(pdb_id, pdb_file)[0]: if chain.get_id() not in chains_to_retain: chains_to_remove.append(chain.get_id()) fixer = PDBFixer(filename=pdb_file) fixer.removeChains(chainIds=chains_to_remove) fixer.findMissingResidues() fixer.findMissingAtoms() fixer.addMissingAtoms() fixer.removeHeterogens(True) # KeepIds flag is critical here, otherwise we loose all information binding pdb_file = dirname(pdb_file) + '/' + pdb_id + '.pdb' PDBFile.writeFile(fixer.topology, fixer.positions, open(pdb_file, 'w'), keepIds=True) return pdb_file
def write_trajectory_dcd(netcdf_filename, topology, pdb_trajectory_filename, dcd_trajectory_filename): """ Write trajectory. Parameters ---------- netcdf_filename : str NetCDF filename. topology : Topology Topology object pdb_trajectory_filename : str PDB trajectory output filename dcd_trajectory_filename : str Output trajectory filename. """ ncfile = netCDF4.Dataset(netcdf_filename, 'r') [nsamples, nstates] = ncfile.variables['logZ'].shape # Write reference.pdb file from simtk.openmm.app import PDBFile outfile = open(pdb_trajectory_filename, 'w') positions = unit.Quantity(ncfile.variables['positions'][0,:,:], unit.nanometers) PDBFile.writeFile(topology, positions, file=outfile) outfile.close() # TODO: Export as DCD trajectory with MDTraj from mdtraj.formats import DCDTrajectoryFile with DCDTrajectoryFile(dcd_trajectory_filename, 'w') as f: f.write(ncfile.variables['positions'][:,:,:] * 10.0) # angstroms
def __init__(self, **kwargs): super(AlanineDipeptideExplicitSimulatedTempering, self).__init__(**kwargs) self.description = 'Alanine dipeptide in explicit solvent simulated tempering simulation' # Create topology, positions, and system. from openmmtools.testsystems import AlanineDipeptideExplicit testsystem = AlanineDipeptideExplicit( nonbondedMethod=app.CutoffPeriodic) self.topology = testsystem.topology self.positions = testsystem.positions self.system = testsystem.system # DEBUG: Write PDB from simtk.openmm.app import PDBFile outfile = open('initial.pdb', 'w') PDBFile.writeFile(self.topology, self.positions, outfile) outfile.close() # Add a MonteCarloBarostat temperature = 270 * unit.kelvin # will be replaced as thermodynamic state is updated pressure = 1.0 * unit.atmospheres barostat = openmm.MonteCarloBarostat(pressure, temperature) self.system.addForce(barostat) # Create thermodynamic states. Tmin = 270 * unit.kelvin Tmax = 600 * unit.kelvin ntemps = 256 # number of temperatures from sams import ThermodynamicState temperatures = unit.Quantity( np.logspace(np.log10(Tmin / unit.kelvin), np.log10(Tmax / unit.kelvin), ntemps), unit.kelvin) self.thermodynamic_states = [ ThermodynamicState(system=self.system, temperature=temperature, pressure=pressure) for temperature in temperatures ] # Create SAMS samplers from sams.samplers import SamplerState, MCMCSampler, ExpandedEnsembleSampler, SAMSSampler thermodynamic_state_index = 0 # initial thermodynamic state index thermodynamic_state = self.thermodynamic_states[ thermodynamic_state_index] sampler_state = SamplerState(positions=self.positions) self.mcmc_sampler = MCMCSampler( sampler_state=sampler_state, thermodynamic_state=thermodynamic_state, ncfile=self.ncfile) #self.mcmc_sampler.pdbfile = open('output.pdb', 'w') self.mcmc_sampler.topology = self.topology self.mcmc_sampler.nsteps = 500 self.mcmc_sampler.timestep = 2.0 * unit.femtoseconds self.mcmc_sampler.verbose = True self.exen_sampler = ExpandedEnsembleSampler(self.mcmc_sampler, self.thermodynamic_states) self.exen_sampler.verbose = True self.sams_sampler = SAMSSampler(self.exen_sampler) self.sams_sampler.verbose = True
def _geometry_forward(self, topology_proposal, old_sampler_state): """ Run geometry engine to propose new positions and compute logP Parameters ---------- topology_proposal : TopologyProposal Contains old/new Topology and System objects and atom mappings. old_sampler_state : openmmtools.states.SamplerState Configurational properties of the old system atoms. Returns ------- new_sampler_state : openmmtools.states.SamplerState Configurational properties of new atoms proposed by geometry engine calculation. geometry_logp_propose : float The log probability of the forward-only proposal """ if self.verbose: print("Geometry engine proposal...") # Generate coordinates for new atoms and compute probability ratio of old and new probabilities. initial_time = time.time() new_positions, geometry_logp_propose = self.geometry_engine.propose(topology_proposal, old_sampler_state.positions, self.sampler.thermodynamic_state.beta) if self.verbose: print('proposal took %.3f s' % (time.time() - initial_time)) if self.geometry_pdbfile is not None: print("Writing proposed geometry...") from simtk.openmm.app import PDBFile PDBFile.writeFile(topology_proposal.new_topology, new_positions, file=self.geometry_pdbfile) self.geometry_pdbfile.flush() new_sampler_state = SamplerState(new_positions, box_vectors=old_sampler_state.box_vectors) return new_sampler_state, geometry_logp_propose
def pdb2xyz(inputfile, outputPrefix, keepIntermediate=False): """pdb2xyz: Transform a pdb file to a goccs compatible xyz file with number of atoms, elements and coordinates into an ouputfile, prefixed with outputPrefix.xyz. If you set keepIntermediate to true then the pdb file written by PDBFixer will be kept in the output folder. """ pdbfixedfilename = outputPrefix + "_fixed.pdb" xyzoutfilename = outputPrefix + ".xyz" fixer = pdbfixer.PDBFixer(inputfile) fixer.removeHeterogens(False) PDBFile.writeFile(fixer.topology, fixer.positions, open(pdbfixedfilename, 'w')) parser = PDB.PDBParser() #parser = PDB.MMCIFParser() #in case it's a cif file structure = parser.get_structure("input", pdbfixedfilename) #print(dir(structure)) natoms = sum(1 for _ in structure.get_atoms()) #print("Writing output") outputhandle = open(xyzoutfilename, "w") outputhandle.write("""%d empty line\n""" % (natoms)) for atom in structure.get_atoms(): element = atom.element coords = atom.get_coord() outputhandle.write("%s %.3f %.3f %.3f\n" % (element, coords[0], coords[1], coords[2])) outputhandle.close() if not keepIntermediate: os.remove(pdbfixedfilename)
def add_missing_atoms(session, m, minimization_steps = 0, keep_waters = False): fname = m.filename from pdbfixer import PDBFixer pf = PDBFixer(filename = fname) pf.findMissingResidues() pf.findNonstandardResidues() pf.replaceNonstandardResidues() pf.findMissingAtoms() pf.addMissingAtoms() pf.removeHeterogens(keep_waters) pf.addMissingHydrogens(7.0) if minimization_steps > 0: minimize(pf, minimization_steps) from os.path import splitext fout = splitext(fname)[0] + '-pdbfixer.pdb' out = open(fout, 'w') from simtk.openmm.app import PDBFile PDBFile.writeFile(pf.topology, pf.positions, out) out.close() mfix = session.models.open([fout])[0] mfix.atoms.displays = True mfix.residues.ribbon_displays = False m.display = False log = session.logger log.info('Wrote %s' % fout)
def add_hydrogens_to_mol(mol): """ Add hydrogens to a molecule object TODO (LESWING) see if there are more flags to add here for default :param mol: Rdkit Mol :return: Rdkit Mol """ molecule_file = None try: pdbblock = Chem.MolToPDBBlock(mol) pdb_stringio = StringIO() pdb_stringio.write(pdbblock) pdb_stringio.seek(0) fixer = PDBFixer(pdbfile=pdb_stringio) fixer.addMissingHydrogens(7.4) hydrogenated_io = StringIO() PDBFile.writeFile(fixer.topology, fixer.positions, hydrogenated_io) hydrogenated_io.seek(0) return Chem.MolFromPDBBlock(hydrogenated_io.read(), sanitize=False, removeHs=False) except ValueError as e: logging.warning("Unable to add hydrogens", e) raise MoleculeLoadException(e) finally: try: os.remove(molecule_file) except (OSError, TypeError): pass
def from_pdb(cls, path, forcefield=None, **kwargs): """ Loads topology, positions and, potentially, velocities and vectors, from a PDB file Parameters ---------- path : str Path to PDB file forcefields : list of str Paths to FFXML and/or FRCMOD forcefields. REQUIRED. Returns ------- pdb : SystemHandler SystemHandler with topology, positions, and, potentially, velocities and box vectors. Forcefields are embedded in the `master` attribute. """ pdb = PDBFile(path) box = kwargs.pop('box', pdb.topology.getPeriodicBoxVectors()) positions = kwargs.pop('positions', pdb.positions) velocities = kwargs.pop('velocities', getattr(pdb, 'velocities', None)) if not forcefield: from .md import FORCEFIELDS as forcefield print('INFO: Forcefields for PDB not specified. Using default:\n ', ', '.join(forcefield)) pdb.forcefield = ForceField(*list(process_forcefield(*forcefield))) return cls(master=pdb, topology=pdb.topology, positions=positions, velocities=velocities, box=box, path=path, **kwargs)
def write_trajectory_dcd(netcdf_filename, topology, pdb_trajectory_filename, dcd_trajectory_filename): """ Write trajectory. Parameters ---------- netcdf_filename : str NetCDF filename. topology : Topology Topology object pdb_trajectory_filename : str PDB trajectory output filename dcd_trajectory_filename : str Output trajectory filename. """ ncfile = netCDF4.Dataset(netcdf_filename, 'r') [nsamples, nstates] = ncfile.variables['logZ'].shape # Write reference.pdb file from simtk.openmm.app import PDBFile outfile = open(pdb_trajectory_filename, 'w') positions = unit.Quantity(ncfile.variables['positions'][0,:,:], unit.angstroms) PDBFile.writeFile(topology, positions, file=outfile) outfile.close() # TODO: Export as DCD trajectory with MDTraj from mdtraj.formats import DCDTrajectoryFile with DCDTrajectoryFile(dcd_trajectory_filename, 'w') as f: f.write(ncfile.variables['positions'][:,:,:])
def add_hydrogens_to_mol(mol): """ Add hydrogens to a molecule object TODO (LESWING) see if there are more flags to add here for default :param mol: Rdkit Mol :return: Rdkit Mol """ molecule_file = None try: pdbblock = Chem.MolToPDBBlock(mol) pdb_stringio = StringIO() pdb_stringio.write(pdbblock) pdb_stringio.seek(0) fixer = PDBFixer(pdbfile=pdb_stringio) fixer.addMissingHydrogens(7.4) hydrogenated_io = StringIO() PDBFile.writeFile(fixer.topology, fixer.positions, hydrogenated_io) hydrogenated_io.seek(0) return Chem.MolFromPDBBlock( hydrogenated_io.read(), sanitize=False, removeHs=False) except ValueError as e: logging.warning("Unable to add hydrogens", e) raise MoleculeLoadException(e) finally: try: os.remove(molecule_file) except (OSError, TypeError): pass
def write_pdb(self, path): """ Outputs a PDB file with the current contents of the system """ if self.master is None and self.positions is None: raise ValueError('Topology and positions are needed to write output files.') with open(path, 'w') as f: PDBFile.writeFile(self.topology, self.positions, f)
def write_pdb(self, path): """ Outputs a PDB file with the current contents of the system """ if self.master is None and self.positions is None: raise ValueError( 'Topology and positions are needed to write output files.') with open(path, 'w') as f: PDBFile.writeFile(self.topology, self.positions, f)
def _create_chain_pdb(self, topology_pdb, positions): dirname = os.path.dirname(__file__) filename = "{}.pdb".format(self.sequence_str) if self.forceField_str == 'OPLS-AA': filename = "{}_aa.pdb".format(self.sequence_str) file_path = os.path.join(dirname, "data/{}".format(filename)) if not os.path.isfile(file_path) or self.overwrite_pdb: self.overwrite_pdb = False PDBFile.writeFile(topology_pdb, positions, open(file_path, 'w'))
def write_header(self): """ Confusingly this initializes writer as well because """ outfile = open(self.out_filepath, 'w') self.outfile = outfile cpdb = app.PDBFile(self.pdb_str) PDBFile.writeHeader(cpdb.topology, self.outfile) self.topology = cpdb.topology self.frame_idx = 0
def fix_pdb(pdb_file): fixer = PDBFixer(filename=pdb_file) fixer.findMissingResidues() fixer.findNonstandardResidues() fixer.replaceNonstandardResidues() fixer.removeHeterogens(True) fixer.findMissingAtoms() fixer.addMissingAtoms() fixer.addMissingHydrogens(7.0) PDBFile.writeFile(fixer.topology, fixer.positions, open(pdb_file, 'w'))
def writePDBFixed(self): 'Write the fixed (initial) structure to a pdb file.' from simtk.openmm.app import PDBFile PDBFile.writeFile(self._topology, self._positions, open(self.getTitle()[:-8] + 'fixed.pdb', 'w'), keepIds=True)
def update(self): """ Update the sampler with one step of sampling. """ if not self._initialized: self._initialize() if self.verbose: print("." * 80) print("MCMC sampler iteration %d" % self.iteration) initial_time = time.time() # Reset statistics if hasattr(self.integrator, 'setGlobalVariableByName'): self.integrator.setGlobalVariableByName('naccept', 0) # Take some steps self.integrator.step(self.nsteps) # Get new sampler state. self.sampler_state = SamplerState.createFromContext(self.context) # Report statistics if hasattr(self.integrator, 'getGlobalVariableByName'): naccept = self.integrator.getGlobalVariableByName('naccept') fraction_accepted = float(naccept) / float(self.nsteps) if self.verbose: print("Accepted %d / %d GHMC steps (%.2f%%)." % (naccept, self.nsteps, fraction_accepted * 100)) final_time = time.time() elapsed_time = final_time - initial_time self._timing['sample positions'] = elapsed_time if self.verbose: final_energy = self.context.getState(getEnergy=True).getPotentialEnergy() * self.thermodynamic_state.beta print('Final energy is %12.3f kT' % (final_energy)) print('elapsed time %8.3f s' % elapsed_time) if self.ncfile: self.ncfile.variables['positions'][self.iteration,:,:] = self.sampler_state.positions[:,:] / unit.nanometers for k in range(3): self.ncfile.variables['box_vectors'][self.iteration,k,:] = self.sampler_state.box_vectors[k,:] / unit.nanometers self.ncfile.variables['potential'][self.iteration] = self.thermodynamic_state.beta * self.context.getState(getEnergy=True).getPotentialEnergy() self.ncfile.variables['sample_positions_time'][self.iteration] = elapsed_time # Increment iteration count self.iteration += 1 if self.verbose: print("." * 80) if self.pdbfile is not None: print("Writing frame...") from simtk.openmm.app import PDBFile PDBFile.writeModel(self.topology, self.sampler_state.positions, self.pdbfile, self.iteration) self.pdbfile.flush()
def save_frames_pdb(pdb, start=0, stop=0, step=1, where='./', name='frame-%s'): isfname = isinstance(pdb, PDBFile) nformat = name if not isfname else pdb.split('.pdb') + name nformat = (nformat if '%' in nformat else nformat + '%s') + '.pdb' pdbfile = pdb if isfname else PDBFile(pdb) topology = pdbfile.getTopology() stop = stop if stop else pdbfile.getNumFrames() for idx in range(start, stop, step): positions = pdbfile.getPositions(frame=stop) filepath = normpath(where + '/' + nformat % step) PDBFile.writeFile(topology, positions, filepath)
def fix_pdb(self, infile, out=None, pH=7): with open(infile, 'r') as f: fixer = PDBFixer(pdbfile=f) fixer.findMissingResidues() fixer.findMissingAtoms() fixer.addMissingAtoms() fixer.addMissingHydrogens(pH=pH) if out is None: out = '{0[0]}{1}{0[1]}'.format(os.path.splitext(infile), '_fixed') with open(out, 'w') as f: PDBFile.writeFile(fixer.topology, fixer.positions, f)
def minimize_energy(pdb: PDBFile, simulation: Simulation, args: ListOfArgs): if args.MINIMIZE: print('Energy minimizing...') simulation.minimizeEnergy(tolerance=0.01 * simtk.unit.kilojoules_per_mole) if not args.MINIMIZED_FILE: base, _ = os.path.splitext(args.INITIAL_STRUCTURE_PATH) minimized_file_name = f'{base}_min.pdb' else: minimized_file_name = args.MINIMIZED_FILE # TODO: Nasty fix print(f' Saving minimized structure in {minimized_file_name}') state = simulation.context.getState(getPositions=True) PDBFile.writeFile(pdb.topology, state.getPositions(), open(minimized_file_name, 'w'))
def add_hydrogens_by_openmm(self): from simtk.openmm.app import ForceField, Modeller, PDBFile from pdbfixer import PDBFixer fixer = PDBFixer(self.name) field = ForceField('amber99sb.xml', 'tip3p.xml') fixer.findMissingResidues() fixer.findMissingAtoms() fixer.addMissingAtoms() fixer.addMissingHydrogens(7.0) modeller = Modeller(fixer.topology, fixer.positions) modeller.addHydrogens(forcefield=field) modeller.deleteWater() PDBFile.writeModel(modeller.topology, modeller.positions, open(self.shotname+'_h.pdb', 'w'))
def report(self, simulation, state): """Generate a report. Parameters: - simulation (Simulation) The Simulation to generate a report for - state (State) The current state of the simulation """ if self._nextModel == 0: PDBFile.writeHeader(simulation.topology, self._out) self._topology = simulation.topology self._nextModel += 1 PDBFile.writeModel(simulation.topology, state.getPositions(), self._out, self._nextModel) self._nextModel += 1
def write_frame(self, x): """ Write a coordinate frame Parameters ---------- x: np.ndarray coordinates in units of angstroms """ # if self.outfile is None: # raise ValueError("remember to call write_header first") self.frame_idx += 1 PDBFile.writeModel(self.topology, x, self.out_handle, self.frame_idx)
def pdbfixerTransform(filename, replace_nonstandard_residues, add_missing_residues, add_missing_atoms): """ Adds missing residues and/or missing atoms to a PDB file. Parameters ---------- filename : str Name of the input PDB file. replace_nonstandard_residues : bool Whether to replace nonstandard residues with their standard equivalents. add_missing_residues : bool Whether to add missing residues. add_missing_atoms : bool Whether to add missing atoms. Returns ------- filename_output : str Absolute path to the modified file. """ if not replace_nonstandard_residues and not add_missing_atoms \ and not add_missing_residues: return _os.path.abspath(filename) fix = _pdbfix.PDBFixer(filename=filename) if replace_nonstandard_residues: fix.findNonstandardResidues() fix.replaceNonstandardResidues() if add_missing_residues: fix.findMissingResidues() else: fix.missingResidues = [] if add_missing_atoms: fix.findMissingAtoms() else: fix.missingAtoms = [] fix.missingTerminals = [] fix.addMissingAtoms() filename_output = _os.path.splitext(filename)[0] + "_pdbfixer.pdb" _PDBFile.writeFile(fix.topology, fix.positions, open(filename_output, "w")) return fixPDBFixerPDB(filename_output, filename, replace_nonstandard_residues, add_missing_residues, add_missing_atoms, filename_output)
def cleanPdb(pdb_list, chain=None, fromFolder=None, toFolder="cleaned_pdbs"): os.system(f"mkdir -p {toFolder}") for pdb_id in pdb_list: # print(chain) pdb = f"{pdb_id.lower()[:4]}" pdbFile = pdb + ".pdb" if fromFolder is None: fromFile = os.path.join("original_pdbs", pdbFile) elif fromFolder[:4] == ".pdb": fromFile = fromFolder else: fromFile = os.path.join(fromFolder, pdbFile) if chain is None: # None mean deafult is chain A unless specified. if len(pdb_id) == 5: Chosen_chain = pdb_id[4].upper() else: assert (len(pdb_id) == 4) Chosen_chain = "A" elif chain == "-1" or chain == -1: Chosen_chain = getAllChains(fromFile) else: Chosen_chain = chain # clean pdb fixer = PDBFixer(filename=fromFile) # remove unwanted chains chains = list(fixer.topology.chains()) chains_to_remove = [ i for i, x in enumerate(chains) if x.id not in Chosen_chain ] fixer.removeChains(chains_to_remove) fixer.findMissingResidues() # add missing residues in the middle of a chain, not ones at the start or end of the chain. chains = list(fixer.topology.chains()) keys = fixer.missingResidues.keys() # print(keys) for key in list(keys): chain_tmp = chains[key[0]] if key[1] == 0 or key[1] == len(list(chain_tmp.residues())): del fixer.missingResidues[key] fixer.findNonstandardResidues() fixer.replaceNonstandardResidues() fixer.removeHeterogens(keepWater=False) fixer.findMissingAtoms() fixer.addMissingAtoms() fixer.addMissingHydrogens(7.0) PDBFile.writeFile(fixer.topology, fixer.positions, open(os.path.join(toFolder, pdbFile), 'w'))
def test_gaffResidueTemplateGenerator(): """ Test the GAFF residue template generator. """ # # Test where we generate parameters for only a ligand. # # Load the PDB file. from simtk.openmm.app import PDBFile pdb_filename = utils.get_data_filename("chemicals/imatinib/imatinib.pdb") pdb = PDBFile(pdb_filename) # Create a ForceField object. gaff_xml_filename = utils.get_data_filename("parameters/gaff.xml") forcefield = ForceField(gaff_xml_filename) # Add the residue template generator. from openmoltools.forcefield_generators import gaffTemplateGenerator forcefield.registerTemplateGenerator(gaffTemplateGenerator) # Parameterize system. system = forcefield.createSystem(pdb.topology, nonbondedMethod=NoCutoff) # Check potential is finite. check_potential_is_finite(system, pdb.positions) # Check energy matches prmtop route. check_energy_components_vs_prmtop( prmtop=utils.get_data_filename('chemicals/imatinib/imatinib.prmtop'), inpcrd=utils.get_data_filename('chemicals/imatinib/imatinib.inpcrd'), system=system) # # Test where we generate parameters for only a ligand in a protein. # # Load the PDB file. from simtk.openmm.app import PDBFile pdb_filename = utils.get_data_filename( "chemicals/proteins/T4-lysozyme-L99A-p-xylene-implicit.pdb") pdb = PDBFile(pdb_filename) # Create a ForceField object. gaff_xml_filename = utils.get_data_filename("parameters/gaff.xml") forcefield = ForceField('amber99sb.xml', gaff_xml_filename) # Add the residue template generator. from openmoltools.forcefield_generators import gaffTemplateGenerator forcefield.registerTemplateGenerator(gaffTemplateGenerator) # Parameterize system. system = forcefield.createSystem(pdb.topology, nonbondedMethod=NoCutoff) # Check potential is finite. check_potential_is_finite(system, pdb.positions)
def deserialize_input(self, content): """Retreive the state and topology from the message content The message protocol tries not to pass 'data' around within the messages, but instead pass paths to data. So far we're only sending paths on the local filesystem, but we might could generalize this to HTTP or S3 or something later. The assumption that data can be passed around on the local filesystem shouldn't be built deep into the code at all """ # todo: better name for this function? if content.starting_state.protocol == 'localfs': with open(content.starting_state.path) as f: self.log.info('Opening state file: %s', content.starting_state.path) state = XmlSerializer.deserialize(f.read()) else: raise ValueError('Unknown protocol') if content.topology_pdb.protocol == 'localfs': topology = PDBFile(content.topology_pdb.path).topology else: raise ValueError('Unknown protocol') return state, topology
def test_membraneModeller(): # pdb file containing a solvated single lipid molecule pdb = PDBFile('solvated-lipid.pdb') modeller = Modeller(pdb.topology,pdb.positions) modeller.modifyTopology() forcefield = ForceField('amber14-all.xml', 'amber14/tip3pfb.xml') modeller.addHydrogens(forcefield=forcefield) system = forcefield.createSystem(modeller.topology, nonbondedMethod=app.PME, rigidWater=True, nonbondedCutoff=1*unit.nanometer) integrator = mm.VerletIntegrator(0.5*unit.femtoseconds) platform = mm.Platform.getPlatformByName('Reference') simulation = app.Simulation(modeller.topology, system, integrator, platform) simulation.context.setPositions(modeller.positions) simulation.context.setVelocitiesToTemperature(300*unit.kelvin) # Minimize the system after adding hydrogens tolerance = 0.1*unit.kilojoules_per_mole/unit.angstroms simulation.minimizeEnergy(tolerance=tolerance,maxIterations=200) simulation.reporters.append(app.StateDataReporter('relax-hydrogens.log', 1000, step=True, potentialEnergy=True)) simulation.step(1000)
def test_membrane_modeller(): """Test the addition of hydrogens to a solvated DPPC molecule""" # pdb file corresponding to a solvated lipid molecule pdb = PDBFile(os.path.join(os.path.dirname(__file__), '../data/dppc/solvated-dppc.pdb')) modeller = MembraneModeller(pdb.topology,pdb.positions) modeller.modify_topology() forcefield = ForceField('amber14-all.xml', 'amber14/tip3pfb.xml') modeller.addHydrogens(forcefield=forcefield) system = forcefield.createSystem(modeller.topology, nonbondedMethod=app.PME, rigidWater=True, nonbondedCutoff=1*unit.nanometer) integrator = mm.VerletIntegrator(0.5*unit.femtoseconds) platform = mm.Platform.getPlatformByName('Reference') simulation = app.Simulation(modeller.topology, system, integrator, platform) simulation.context.setPositions(modeller.positions) simulation.context.setVelocitiesToTemperature(300*unit.kelvin) # Minimize the system after adding hydrogens simulation.minimizeEnergy(maxIterations=200) # Run a few MD steps to check the system has no overlaps simulation.step(1000) state = simulation.context.getState(getEnergy=True) pe = state.getPotentialEnergy()._value assert pe < 0.0
def create_pdb_object(solute_file_path): file_extension = solute_file_path.split(".")[-1] if file_extension == "pdb": pdb = PDBFile(solute_file_path) return pdb else: raise ValueError('Unrecognised file extension: %s -> A PDB must be given' % file_extension)
def test_porin_membrane_system(): """Test the addition of a ligand to a solvated porin""" # pdb file corresponding to a solvated porin pdb = PDBFile( os.path.join(os.path.dirname(__file__), '../data/porin/solvated-porin.pdb')) modeller = app.Modeller(pdb.topology, pdb.positions) forcefield = ForceField('amber14-all.xml', 'amber14/tip3pfb.xml') platform = mm.Platform.getPlatformByName('CPU') modeller.addHydrogens(forcefield=forcefield) # rigidWater False is required for ParMed to access water paramters system_md = forcefield.createSystem(modeller.topology, nonbondedMethod=app.PME, rigidWater=False, nonbondedCutoff=1 * unit.nanometer) ligand_system = PorinMembraneSystem('comp7', system_md, modeller.topology, modeller.positions, platform, tolerance=1 * unit.kilojoule / unit.mole, max_iterations=200) integrator = mm.LangevinIntegrator(300 * unit.kelvin, 1.0 / unit.picoseconds, 2 * unit.femtosecond) simulation = app.Simulation(ligand_system.structure.topology, ligand_system.system, integrator, platform) simulation.context.setPositions(ligand_system.structure.positions) state = simulation.context.getState(getEnergy=True) pe = state.getPotentialEnergy()._value assert pe < 0.0
def test_build_docked_coordinates_protocol(): """Tests docking a methanol molecule into alpha-Cyclodextrin.""" if not has_openeye(): pytest.skip("The `BuildDockedCoordinates` protocol requires OpenEye.") ligand_substance = Substance() ligand_substance.add_component( Component("CO", role=Component.Role.Ligand), ExactAmount(1), ) # TODO: This test could likely be made substantially faster # by storing the binary prepared receptor. Would this # be in breach of any oe license terms? with tempfile.TemporaryDirectory() as temporary_directory: build_docked_coordinates = BuildDockedCoordinates("build_methanol") build_docked_coordinates.ligand_substance = ligand_substance build_docked_coordinates.number_of_ligand_conformers = 5 build_docked_coordinates.receptor_coordinate_file = get_data_filename( "test/molecules/acd.mol2") build_docked_coordinates.execute(temporary_directory, ComputeResources()) docked_pdb = PDBFile( build_docked_coordinates.docked_complex_coordinate_path) assert docked_pdb.topology.getNumResidues() == 2
def test_solvate_existing_structure_protocol(): """Tests solvating a single methanol molecule in water.""" methanol_substance = Substance() methanol_substance.add_component(Substance.Component('CO'), Substance.ExactAmount(1)) water_substance = Substance() water_substance.add_component(Substance.Component('O'), Substance.MoleFraction(1.0)) with tempfile.TemporaryDirectory() as temporary_directory: build_methanol_coordinates = BuildCoordinatesPackmol('build_methanol') build_methanol_coordinates.max_molecules = 1 build_methanol_coordinates.substance = methanol_substance build_methanol_coordinates.execute(temporary_directory, ComputeResources()) solvate_coordinates = SolvateExistingStructure('solvate_methanol') solvate_coordinates.max_molecules = 9 solvate_coordinates.substance = water_substance solvate_coordinates.solute_coordinate_file = build_methanol_coordinates.coordinate_file_path solvate_coordinates.execute(temporary_directory, ComputeResources()) solvated_pdb = PDBFile(solvate_coordinates.coordinate_file_path) assert solvated_pdb.topology.getNumResidues() == 10
def _off_handler(cls, molecule, **kwargs): forcefield = cls._get_forcefield(**kwargs) topology = Topology.from_molecules(molecule) openmm_system = forcefield.create_openmm_system( topology, charge_from_molecules=[molecule]) # ligand_pmd.title = cls.smiles # for i in ligand_pmd.residues: # i.name = 'LIG' #XXX no longer needed when using omm_top to create parmed structure tmp_dir = tempfile.mkdtemp() # We need all molecules as both pdb files (as packmol input) # and mdtraj.Trajectory for restoring bonds later. pdb_filename = tempfile.mktemp(suffix=".pdb", dir=tmp_dir) # XXX legacy code for save a pdb copy for simulation box creation # Chem.MolToPDBFile(mol, pdb_filename) # from openeye import oechem # OpenEye Python toolkits # oechem.OEWriteMolecule( oechem.oemolostream( pdb_filename ), mol) # XXX swtich to off save pdb # ligand_pmd.save(pdb_filename, overwrite=True) molecule.to_file(pdb_filename, "pdb") omm_top = PDBFile(pdb_filename).topology # ligand_pmd = parmed.openmm.topsystem.load_topology(topology.to_openmm(), openmm_system, molecule._conformers[0]) #XXX off topology does not keep atom names and resnames, use omm topology instead ligand_pmd = parmed.openmm.topsystem.load_topology( omm_top, openmm_system, molecule._conformers[0]) return pdb_filename, ligand_pmd
def __init__(self, **kwargs): super(AlanineDipeptideExplicitSimulatedTempering, self).__init__(**kwargs) self.description = 'Alanine dipeptide in explicit solvent simulated tempering simulation' # Create topology, positions, and system. from openmmtools.testsystems import AlanineDipeptideExplicit testsystem = AlanineDipeptideExplicit(nonbondedMethod=app.CutoffPeriodic) self.topology = testsystem.topology self.positions = testsystem.positions self.system = testsystem.system # DEBUG: Write PDB from simtk.openmm.app import PDBFile outfile = open('initial.pdb', 'w') PDBFile.writeFile(self.topology, self.positions, outfile) outfile.close() # Add a MonteCarloBarostat temperature = 270 * unit.kelvin # will be replaced as thermodynamic state is updated pressure = 1.0 * unit.atmospheres barostat = openmm.MonteCarloBarostat(pressure, temperature) self.system.addForce(barostat) # Create thermodynamic states. Tmin = 270 * unit.kelvin Tmax = 600 * unit.kelvin ntemps = 256 # number of temperatures from sams import ThermodynamicState temperatures = unit.Quantity(np.logspace(np.log10(Tmin / unit.kelvin), np.log10(Tmax / unit.kelvin), ntemps), unit.kelvin) self.thermodynamic_states = [ ThermodynamicState(system=self.system, temperature=temperature, pressure=pressure) for temperature in temperatures ] # Create SAMS samplers from sams.samplers import SamplerState, MCMCSampler, ExpandedEnsembleSampler, SAMSSampler thermodynamic_state_index = 0 # initial thermodynamic state index thermodynamic_state = self.thermodynamic_states[thermodynamic_state_index] sampler_state = SamplerState(positions=self.positions) self.mcmc_sampler = MCMCSampler(sampler_state=sampler_state, thermodynamic_state=thermodynamic_state, ncfile=self.ncfile) #self.mcmc_sampler.pdbfile = open('output.pdb', 'w') self.mcmc_sampler.topology = self.topology self.mcmc_sampler.nsteps = 500 self.mcmc_sampler.timestep = 2.0 * unit.femtoseconds self.mcmc_sampler.verbose = True self.exen_sampler = ExpandedEnsembleSampler(self.mcmc_sampler, self.thermodynamic_states) self.exen_sampler.verbose = True self.sams_sampler = SAMSSampler(self.exen_sampler) self.sams_sampler.verbose = True
def download_pdb(pdbid, file_pathway): """ Args: pdbid: 4 letter string specifying the PDB ID of the file yoou want to fix file_pathway: a string containing the pathway specifying how you want to organize the PDB files once written Returns: nothing, but it does write the PDB file ***Note: this function does NOT fix any mistakes with the PDB file """ if not os.path.exists(file_pathway): os.makedirs(file_pathway) fixer = PDBFixer(pdbid=pdbid) PDBFile.writeFile(fixer.topology, fixer.positions, open(os.path.join(file_pathway, '%s.pdb' % pdbid), 'w'))
def check_hydrogens(molecule, ID): # Check that Hydrogens are in structure if len(molecule.top.select("name == H")) == 0: # If absent, then add Hydrogens using the Amber99sb force-field try: from simtk.openmm.app import PDBFile, Modeller, ForceField pdb = PDBFile(ID + ".pdb") modeller = Modeller(pdb.topology, pdb.positions) forcefield = ForceField('amber99sb.xml','tip3p.xml') modeller.addHydrogens(forcefield) PDBFile.writeFile(modeller.topology, modeller.positions, open(ID + ".pdb", 'w')) molecule = md.load(ID + ".pdb").remove_solvent() except: warnings.warn("""PDB topology missing Hydrogens. Either manually add or install OpenMM through SIMTK to automatically correct.""") pass return molecule
def report(self, simulation, state): """Generate a report. Parameters ---------- simulation : Simulation The Simulation to generate a report for state : State The current state of the simulation """ if self._nextModel == 0: PDBFile.writeHeader(simulation.topology, self._out) self._topology = simulation.topology self._nextModel += 1 PDBFile.writeModel(simulation.topology, state.getPositions(), self._out, self._nextModel) self._nextModel += 1 if hasattr(self._out, 'flush') and callable(self._out.flush): self._out.flush()
def update(self): """ Update the sampler with one step of sampling. """ if self.verbose: print("-" * 80) print("Expanded Ensemble sampler iteration %8d" % self.iteration) self.update_positions() self.update_state() self.iteration += 1 if self.verbose: print("-" * 80) if self.pdbfile is not None: print("Writing frame...") from simtk.openmm.app import PDBFile PDBFile.writeModel(self.topology, self.sampler.sampler_state.positions, self.pdbfile, self.iteration) self.pdbfile.flush()
def report(self, simulation, _): """Generate a report. Parameters ---------- simulation : Simulation The Simulation to generate a report for _ : State The current state of the simulation """ state = simulation.context.getState(getPositions=True, enforcePeriodicBox=self._enforcePeriodicBox) if self._nextModel == 0: PDBFile.writeHeader(simulation.topology, self._out) self._topology = simulation.topology self._nextModel += 1 PDBFile.writeModel(simulation.topology, state.getPositions(), self._out, self._nextModel) self._nextModel += 1 if hasattr(self._out, 'flush') and callable(self._out.flush): self._out.flush()
mcmc_sampler = MCMCSampler(sampler_state=sampler_state, thermodynamic_state=thermodynamic_state, ncfile=ncfile, platform=platform) mcmc_sampler.timestep = timestep mcmc_sampler.nsteps = 500 #mcmc_sampler.pdbfile = open('output.pdb', 'w') # uncomment this if you want to write a PDB trajectory as you simulate; WARNING: LARGE! mcmc_sampler.topology = topology mcmc_sampler.verbose = True exen_sampler = ExpandedEnsembleSampler(mcmc_sampler, thermodynamic_states) exen_sampler.verbose = True sams_sampler = SAMSSampler(exen_sampler) sams_sampler.verbose = True # DEBUG: Write PDB of initial frame print("Writing initial frame to 'initial.pdb'...") from simtk.openmm.app import PDBFile outfile = open('initial.pdb', 'w') PDBFile.writeFile(topology, positions, outfile) outfile.close() # Run the simulation print('Running simulation...') #exen_sampler.update_scheme = 'restricted-range' # scheme for deciding which alchemical state to jump to exen_sampler.update_scheme = 'global-jump' # scheme for deciding which alchemical state to jump to #exen_sampler.locality = thermodynamic_state_neighbors # neighbors to examine for each state sams_sampler.update_method = 'rao-blackwellized' # scheme for updating free energy estimates niterations = 20000 # number of iterations to run sams_sampler.run(niterations) # run sampler ncfile.close() # Analyze from sams import analysis # States
def __init__(self, file): """Load a prmtop file.""" ## The Topology read from the prmtop file self.topology = top = Topology() self.elements = [] # Load the prmtop file prmtop = amber_file_parser.PrmtopLoader(file) self._prmtop = prmtop # Add atoms to the topology PDBFile._loadNameReplacementTables() lastResidue = None c = top.addChain() for index in range(prmtop.getNumAtoms()): resNumber = prmtop.getResidueNumber(index) if resNumber != lastResidue: lastResidue = resNumber resName = prmtop.getResidueLabel(iAtom=index).strip() if resName in PDBFile._residueNameReplacements: resName = PDBFile._residueNameReplacements[resName] r = top.addResidue(resName, c) if resName in PDBFile._atomNameReplacements: atomReplacements = PDBFile._atomNameReplacements[resName] else: atomReplacements = {} atomName = prmtop.getAtomName(index).strip() if atomName in atomReplacements: atomName = atomReplacements[atomName] # Get the element from the prmtop file if available if prmtop.has_atomic_number: try: element = elem.Element.getByAtomicNumber(int(prmtop._raw_data['ATOMIC_NUMBER'][index])) except KeyError: element = None else: # Try to guess the element from the atom name. upper = atomName.upper() if upper.startswith('CL'): element = elem.chlorine elif upper.startswith('NA'): element = elem.sodium elif upper.startswith('MG'): element = elem.magnesium elif upper.startswith('ZN'): element = elem.zinc else: try: element = elem.get_by_symbol(atomName[0]) except KeyError: element = None top.addAtom(atomName, element, r) self.elements.append(element) # Add bonds to the topology atoms = list(top.atoms()) for bond in prmtop.getBondsWithH(): top.addBond(atoms[bond[0]], atoms[bond[1]]) for bond in prmtop.getBondsNoH(): top.addBond(atoms[bond[0]], atoms[bond[1]]) # Set the periodic box size. if prmtop.getIfBox(): box = prmtop.getBoxBetaAndDimensions() top.setPeriodicBoxVectors(computePeriodicBoxVectors(*(box[1:4] + box[0:1]*3)))
fixer.findMissingResidues() # only add missing residues in the middle of the chain, do not add terminal ones chains = list(fixer.topology.chains()) keys = fixer.missingResidues.keys() missingResidues = dict() for key in keys: chain = chains[key[0]] if not (key[1] == 0 or key[1] == len(list(chain.residues()))): missingResidues[key] = fixer.missingResidues[key] fixer.missingResidues = missingResidues fixer.findMissingAtoms() fixer.addMissingAtoms() PDBFile.writeFile(fixer.topology, fixer.positions, open('4h12_fixed.pdb', 'w')) # keep only protein and zinc ions traj = md.load('4h12_fixed.pdb') traj = traj.atom_slice(traj.top.select('(protein and not resname SAH) or resname ZN')) # implement changes necessary for the use of the dummy atom Zn2+ model # change residue name of the zincs from ZN to ZNB, and atom names from ZN to Zn for residue in traj.top.chain(1).residues: residue.name = 'ZNB' for atom in traj.top.chain(1).atoms: atom.name = 'Zn' # change name of cysteines coordinating zincs to CYM (deprotonated cysteine) for residue in traj.top.chain(0).residues: if residue.index in [86, 92, 82, 69, 54, 52, 73, 184, 233, 238, 231]:
def addSolvent(self, forcefield, model='tip3p', boxSize=None, padding=None, positiveIon='Na+', negativeIon='Cl-', ionicStrength=0*molar): """Add solvent (both water and ions) to the model to fill a rectangular box. The algorithm works as follows: 1. Water molecules are added to fill the box. 2. Water molecules are removed if their distance to any solute atom is less than the sum of their van der Waals radii. 3. If the solute is charged, enough positive or negative ions are added to neutralize it. Each ion is added by randomly selecting a water molecule and replacing it with the ion. 4. Ion pairs are added to give the requested total ionic strength. The box size can be specified in three ways. First, you can explicitly give a box size to use. Alternatively, you can give a padding distance. The largest dimension of the solute (along the x, y, or z axis) is determined, and a cubic box of size (largest dimension)+2*padding is used. Finally, if neither a box size nor a padding distance is specified, the existing Topology's unit cell dimensions are used. Parameters: - forcefield (ForceField) the ForceField to use for determining van der Waals radii and atomic charges - model (string='tip3p') the water model to use. Supported values are 'tip3p', 'spce', 'tip4pew', and 'tip5p'. - boxSize (Vec3=None) the size of the box to fill with water - padding (distance=None) the padding distance to use - positiveIon (string='Na+') the type of positive ion to add. Allowed values are 'Cs+', 'K+', 'Li+', 'Na+', and 'Rb+' - negativeIon (string='Cl-') the type of negative ion to add. Allowed values are 'Cl-', 'Br-', 'F-', and 'I-'. Be aware that not all force fields support all ion types. - ionicString (concentration=0*molar) the total concentration of ions (both positive and negative) to add. This does not include ions that are added to neutralize the system. """ # Pick a unit cell size. if boxSize is not None: if is_quantity(boxSize): boxSize = boxSize.value_in_unit(nanometer) box = Vec3(boxSize[0], boxSize[1], boxSize[2])*nanometer elif padding is not None: maxSize = max(max((pos[i] for pos in self.positions))-min((pos[i] for pos in self.positions)) for i in range(3)) box = (maxSize+2*padding)*Vec3(1, 1, 1) else: box = self.topology.getUnitCellDimensions() if box is None: raise ValueError('Neither the box size nor padding was specified, and the Topology does not define unit cell dimensions') box = box.value_in_unit(nanometer) invBox = Vec3(1.0/box[0], 1.0/box[1], 1.0/box[2]) # Identify the ion types. posIonElements = {'Cs+':elem.cesium, 'K+':elem.potassium, 'Li+':elem.lithium, 'Na+':elem.sodium, 'Rb+':elem.rubidium} negIonElements = {'Cl-':elem.chlorine, 'Br-':elem.bromine, 'F-':elem.fluorine, 'I-':elem.iodine} if positiveIon not in posIonElements: raise ValueError('Illegal value for positive ion: %s' % positiveIon) if negativeIon not in negIonElements: raise ValueError('Illegal value for negative ion: %s' % negativeIon) positiveElement = posIonElements[positiveIon] negativeElement = negIonElements[negativeIon] # Load the pre-equilibrated water box. vdwRadiusPerSigma = 0.5612310241546864907 if model == 'tip3p': waterRadius = 0.31507524065751241*vdwRadiusPerSigma elif model == 'spce': waterRadius = 0.31657195050398818*vdwRadiusPerSigma elif model == 'tip4pew': waterRadius = 0.315365*vdwRadiusPerSigma elif model == 'tip5p': waterRadius = 0.312*vdwRadiusPerSigma else: raise ValueError('Unknown water model: %s' % model) pdb = PDBFile(os.path.join(os.path.dirname(__file__), 'data', model+'.pdb')) pdbTopology = pdb.getTopology() pdbPositions = pdb.getPositions().value_in_unit(nanometer) pdbResidues = list(pdbTopology.residues()) pdbBoxSize = pdbTopology.getUnitCellDimensions().value_in_unit(nanometer) # Have the ForceField build a System for the solute from which we can determine van der Waals radii. system = forcefield.createSystem(self.topology) nonbonded = None for i in range(system.getNumForces()): if isinstance(system.getForce(i), NonbondedForce): nonbonded = system.getForce(i) if nonbonded is None: raise ValueError('The ForceField does not specify a NonbondedForce') cutoff = [nonbonded.getParticleParameters(i)[1].value_in_unit(nanometer)*vdwRadiusPerSigma+waterRadius for i in range(system.getNumParticles())] waterCutoff = waterRadius if len(cutoff) == 0: maxCutoff = waterCutoff else: maxCutoff = max(waterCutoff, max(cutoff)) # Copy the solute over. newTopology = Topology() newTopology.setUnitCellDimensions(box) newAtoms = {} newPositions = []*nanometer for chain in self.topology.chains(): newChain = newTopology.addChain() for residue in chain.residues(): newResidue = newTopology.addResidue(residue.name, newChain) for atom in residue.atoms(): newAtom = newTopology.addAtom(atom.name, atom.element, newResidue) newAtoms[atom] = newAtom newPositions.append(deepcopy(self.positions[atom.index])) for bond in self.topology.bonds(): newTopology.addBond(newAtoms[bond[0]], newAtoms[bond[1]]) # Sort the solute atoms into cells for fast lookup. if len(self.positions) == 0: positions = [] else: positions = self.positions.value_in_unit(nanometer) cells = {} numCells = tuple((max(1, int(floor(box[i]/maxCutoff))) for i in range(3))) cellSize = tuple((box[i]/numCells[i] for i in range(3))) for i in range(len(positions)): cell = tuple((int(floor(positions[i][j]/cellSize[j]))%numCells[j] for j in range(3))) if cell in cells: cells[cell].append(i) else: cells[cell] = [i] # Create a generator that loops over atoms close to a position. def neighbors(pos): centralCell = tuple((int(floor(pos[i]/cellSize[i])) for i in range(3))) offsets = (-1, 0, 1) for i in offsets: for j in offsets: for k in offsets: cell = ((centralCell[0]+i+numCells[0])%numCells[0], (centralCell[1]+j+numCells[1])%numCells[1], (centralCell[2]+k+numCells[2])%numCells[2]) if cell in cells: for atom in cells[cell]: yield atom # Define a function to compute the distance between two points, taking periodic boundary conditions into account. def periodicDistance(pos1, pos2): delta = pos1-pos2 delta = [delta[i]-floor(delta[i]*invBox[i]+0.5)*box[i] for i in range(3)] return norm(delta) # Find the list of water molecules to add. newChain = newTopology.addChain() if len(positions) == 0: center = Vec3(0, 0, 0) else: center = [(max((pos[i] for pos in positions))+min((pos[i] for pos in positions)))/2 for i in range(3)] center = Vec3(center[0], center[1], center[2]) numBoxes = [int(ceil(box[i]/pdbBoxSize[i])) for i in range(3)] addedWaters = [] for boxx in range(numBoxes[0]): for boxy in range(numBoxes[1]): for boxz in range(numBoxes[2]): offset = Vec3(boxx*pdbBoxSize[0], boxy*pdbBoxSize[1], boxz*pdbBoxSize[2]) for residue in pdbResidues: oxygen = [atom for atom in residue.atoms() if atom.element == elem.oxygen][0] atomPos = pdbPositions[oxygen.index]+offset if not any((atomPos[i] > box[i] for i in range(3))): # This molecule is inside the box, so see how close to it is to the solute. atomPos += center-box/2 for i in neighbors(atomPos): if periodicDistance(atomPos, positions[i]) < cutoff[i]: break else: # Record this water molecule as one to add. addedWaters.append((residue.index, atomPos)) # There could be clashes between water molecules at the box edges. Find ones to remove. upperCutoff = center+box/2-Vec3(waterCutoff, waterCutoff, waterCutoff) lowerCutoff = center-box/2+Vec3(waterCutoff, waterCutoff, waterCutoff) lowerSkinPositions = [pos for index, pos in addedWaters if pos[0] < lowerCutoff[0] or pos[1] < lowerCutoff[1] or pos[2] < lowerCutoff[2]] filteredWaters = [] cells = {} for i in range(len(lowerSkinPositions)): cell = tuple((int(floor(lowerSkinPositions[i][j]/cellSize[j]))%numCells[j] for j in range(3))) if cell in cells: cells[cell].append(i) else: cells[cell] = [i] for entry in addedWaters: pos = entry[1] if pos[0] < upperCutoff[0] and pos[1] < upperCutoff[1] and pos[2] < upperCutoff[2]: filteredWaters.append(entry) else: if not any((periodicDistance(lowerSkinPositions[i], pos) < waterCutoff and norm(lowerSkinPositions[i]-pos) > waterCutoff for i in neighbors(pos))): filteredWaters.append(entry) addedWaters = filteredWaters # Add ions to neutralize the system. totalCharge = int(floor(0.5+sum((nonbonded.getParticleParameters(i)[0].value_in_unit(elementary_charge) for i in range(system.getNumParticles()))))) if abs(totalCharge) > len(addedWaters): raise Exception('Cannot neutralize the system because the charge is greater than the number of available positions for ions') def addIon(element): # Replace a water by an ion. index = random.randint(0, len(addedWaters)-1) newResidue = newTopology.addResidue(element.symbol.upper(), newChain) newTopology.addAtom(element.symbol, element, newResidue) newPositions.append(addedWaters[index][1]*nanometer) del addedWaters[index] for i in range(abs(totalCharge)): addIon(positiveElement if totalCharge < 0 else negativeElement) # Add ions based on the desired ionic strength. numIons = len(addedWaters)*ionicStrength/(55.4*molar) # Pure water is about 55.4 molar (depending on temperature) numPairs = int(floor(numIons/2+0.5)) for i in range(numPairs): addIon(positiveElement) for i in range(numPairs): addIon(negativeElement) # Add the water molecules. for index, pos in addedWaters: newResidue = newTopology.addResidue(residue.name, newChain) residue = pdbResidues[index] oxygen = [atom for atom in residue.atoms() if atom.element == elem.oxygen][0] oPos = pdbPositions[oxygen.index] molAtoms = [] for atom in residue.atoms(): molAtoms.append(newTopology.addAtom(atom.name, atom.element, newResidue)) newPositions.append((pos+pdbPositions[atom.index]-oPos)*nanometer) for atom1 in molAtoms: if atom1.element == elem.oxygen: for atom2 in molAtoms: if atom2.element == elem.hydrogen: newTopology.addBond(atom1, atom2) newTopology.setUnitCellDimensions(deepcopy(box)*nanometer) self.topology = newTopology self.positions = newPositions
def run(options): fixer = PDBFixer(options['pdb']) fixer.addMissingHydrogens(7.0) fixer.addSolvent(boxSize=Vec3(2.62,2.62,2.62)*nanometers, padding=None, positiveIon='Na+', negativeIon='Cl-', ionicStrength=0.0*molar) PDBFile.writeFile(fixer.topology, fixer.positions, open(options['outfile'], 'w'))
def __del__(self): if self._topology is not None: PDBFile.writeFooter(self._topology, self._out) self._out.close()
def __init__(self, file, periodicBoxVectors=None, unitCellDimensions=None, includeDir=None, defines=None): """Load a top file. Parameters ---------- file : str the name of the file to load periodicBoxVectors : tuple of Vec3=None the vectors defining the periodic box unitCellDimensions : Vec3=None the dimensions of the crystallographic unit cell. For non-rectangular unit cells, specify periodicBoxVectors instead. includeDir : string=None A directory in which to look for other files included from the top file. If not specified, we will attempt to locate a gromacs installation on your system. When gromacs is installed in /usr/local, this will resolve to /usr/local/gromacs/share/gromacs/top defines : dict={} preprocessor definitions that should be predefined when parsing the file """ if includeDir is None: includeDir = _defaultGromacsIncludeDir() self._includeDirs = (os.path.dirname(file), includeDir) # Most of the gromacs water itp files for different forcefields, # unless the preprocessor #define FLEXIBLE is given, don't define # bonds between the water hydrogen and oxygens, but only give the # constraint distances and exclusions. self._defines = OrderedDict() self._defines['FLEXIBLE'] = True self._genpairs = True if defines is not None: for define, value in defines.iteritems(): self._defines[define] = value # Parse the file. self._currentCategory = None self._ifStack = [] self._elseStack = [] self._moleculeTypes = {} self._molecules = [] self._currentMoleculeType = None self._atomTypes = {} self._bondTypes= {} self._angleTypes = {} self._dihedralTypes = {} self._implicitTypes = {} self._pairTypes = {} self._cmapTypes = {} self._processFile(file) # Create the Topology from it. top = Topology() ## The Topology read from the prmtop file self.topology = top if periodicBoxVectors is not None: if unitCellDimensions is not None: raise ValueError("specify either periodicBoxVectors or unitCellDimensions, but not both") top.setPeriodicBoxVectors(periodicBoxVectors) else: top.setUnitCellDimensions(unitCellDimensions) PDBFile._loadNameReplacementTables() for moleculeName, moleculeCount in self._molecules: if moleculeName not in self._moleculeTypes: raise ValueError("Unknown molecule type: "+moleculeName) moleculeType = self._moleculeTypes[moleculeName] if moleculeCount > 0 and moleculeType.has_virtual_sites: raise ValueError('Virtual sites not yet supported by Gromacs parsers') # Create the specified number of molecules of this type. for i in range(moleculeCount): atoms = [] lastResidue = None c = top.addChain() for index, fields in enumerate(moleculeType.atoms): resNumber = fields[2] if resNumber != lastResidue: lastResidue = resNumber resName = fields[3] if resName in PDBFile._residueNameReplacements: resName = PDBFile._residueNameReplacements[resName] r = top.addResidue(resName, c) if resName in PDBFile._atomNameReplacements: atomReplacements = PDBFile._atomNameReplacements[resName] else: atomReplacements = {} atomName = fields[4] if atomName in atomReplacements: atomName = atomReplacements[atomName] # Try to guess the element. upper = atomName.upper() if upper.startswith('CL'): element = elem.chlorine elif upper.startswith('NA'): element = elem.sodium elif upper.startswith('MG'): element = elem.magnesium else: try: element = elem.get_by_symbol(atomName[0]) except KeyError: element = None atoms.append(top.addAtom(atomName, element, r)) # Add bonds to the topology for fields in moleculeType.bonds: top.addBond(atoms[int(fields[0])-1], atoms[int(fields[1])-1])
def __init__(self, **kwargs): super(LoopSoftening, self).__init__(**kwargs) self.description = 'Alchemical Loop Softening script' padding = 9.0*unit.angstrom explicit_solvent_model = 'tip3p' setup_path = 'data/mtor' # Create topology, positions, and system. from pkg_resources import resource_filename gaff_xml_filename = resource_filename('sams', 'data/gaff.xml') system_generators = dict() ffxmls = [gaff_xml_filename, 'amber99sbildn.xml', 'tip3p.xml'] forcefield_kwargs={ 'nonbondedMethod' : app.CutoffPeriodic, 'nonbondedCutoff' : 9.0 * unit.angstrom, 'implicitSolvent' : None, 'constraints' : app.HBonds, 'rigidWater' : True } # Load topologies and positions for all components print('Creating mTOR test system...') forcefield = app.ForceField(*ffxmls) from simtk.openmm.app import PDBFile, Modeller pdb_filename = resource_filename('sams', os.path.join(setup_path, 'mtor_pdbfixer_apo.pdb')) pdbfile = PDBFile(pdb_filename) modeller = app.Modeller(pdbfile.topology, pdbfile.positions) print('Adding solvent...') modeller.addSolvent(forcefield, model=explicit_solvent_model, padding=padding) self.topology = modeller.getTopology() self.positions = modeller.getPositions() print('Creating system...') self.system = forcefield.createSystem(self.topology, **forcefield_kwargs) # DEBUG: Write PDB outfile = open('initial.pdb', 'w') PDBFile.writeFile(self.topology, self.positions, outfile) outfile.close() # Atom Selection using MDtraj res_pairs = [[403, 483], [1052, 1109]] t = md.load(pdb_filename) alchemical_atoms = set() for x in res_pairs: start = min(t.top.select('residue %s' % min(x))) end = max(t.top.select('residue %s' % max(x))) + 1 alchemical_atoms.union(set(range(start, end))) # Create thermodynamic states. print('Creating alchemically-modified system...') temperature = 300 * unit.kelvin pressure = 1.0 * unit.atmospheres from alchemy import AbsoluteAlchemicalFactory factory = AbsoluteAlchemicalFactory(self.system, ligand_atoms=alchemical_atoms, annihilate_electrostatics=True, alchemical_torsions=True, annihilate_sterics=True, softcore_beta=0.0) # turn off softcore electrostatics self.system = factory.createPerturbedSystem() print('Setting up alchemical intermediates...') from sams import ThermodynamicState self.thermodynamic_states = list() for state in range(26): parameters = {'lambda_sterics' : 1.0, 'lambda_electrostatics' : (1.0 - float(state)/25.0) } self.thermodynamic_states.append( ThermodynamicState(system=self.system, temperature=temperature, parameters=parameters) ) for state in range(1,26): parameters = {'lambda_sterics' : (1.0 - float(state)/25.0), 'lambda_electrostatics' : 0.0 } self.thermodynamic_states.append( ThermodynamicState(system=self.system, temperature=temperature, parameters=parameters) ) #minimize(self.system, self.positions) minimize(self.system) # Create SAMS samplers print('Setting up samplers...') from sams.samplers import SamplerState, MCMCSampler, ExpandedEnsembleSampler, SAMSSampler thermodynamic_state_index = 0 # initial thermodynamic state index thermodynamic_state = self.thermodynamic_states[thermodynamic_state_index] sampler_state = SamplerState(positions=self.system.positions) self.mcmc_sampler = MCMCSampler(sampler_state=sampler_state, thermodynamic_state=thermodynamic_state, ncfile=self.ncfile) self.mcmc_sampler.pdbfile = open('output.pdb', 'w') self.mcmc_sampler.topology = self.topology self.mcmc_sampler.verbose = True self.exen_sampler = ExpandedEnsembleSampler(self.mcmc_sampler, self.thermodynamic_states) self.exen_sampler.verbose = True self.sams_sampler = SAMSSampler(self.exen_sampler) self.sams_sampler.verbose = True
nsteps = 5 # number of timesteps per iteration niterations = 500 # number of iterations mctrials = 10 # number of Monte Carlo trials per iteration nsalt = 0 # current number of salt pairs tol = 1e-6 # constraint tolerance # Determine number of molecules nmolecules = 0 for residue in topology.residues(): nmolecules += 1 print('system originally has %d water molecules' % nmolecules) # Open PDB file for writing. from simtk.openmm.app import PDBFile pdbfile = open('output.pdb', 'w') PDBFile.writeHeader(topology, file=pdbfile) PDBFile.writeModel(topology, positions, file=pdbfile, modelIndex=0) # Simulate for iteration in range(niterations): print('iteration %5d / %5d' % (iteration, niterations)) # Create a simulation from openmmtools.integrators import VelocityVerletIntegrator integrator = VelocityVerletIntegrator(timestep) integrator.setConstraintTolerance(tol) context = openmm.Context(system, integrator) context.setPositions(positions) # Propagate dynamics at constant counterion number. print('propagating dynamics for %d steps...' % nsteps)
def __init__(self, alchemical_protocol='two-phase', nlambda=50, **kwargs): """ Create an alchemical free energy calculation SAMS test system from the provided system. Parameters ---------- alchemical_protocol : str, optional, default='two-phase' Alchemical protocol scheme to use. ['two-phase', 'fused'] nlambda : int, optional, default=50 Number of alchemical states. """ super(AlchemicalSAMSTestSystem, self).__init__(**kwargs) self.description = 'Alchemical SAMS test system' self.alchemical_protocol = alchemical_protocol if not (hasattr(self, 'topology') and hasattr(self, 'system') and hasattr(self, 'positions') and hasattr(self, 'alchemical_atoms')): raise Exception("%s: 'topology', 'system', 'positions', and 'alchemical_atoms' properties must be defined!" % self.__class__.__name__) if not hasattr(self, 'temperature'): self.temperature = 300 * unit.kelvin if not hasattr(self, 'temperature'): self.temperature = 300 * unit.kelvin if not hasattr(self, 'pressure'): self.pressure = None # Add a MonteCarloBarostat if system does not have one has_barostat = False for force in self.system.getForces(): if force.__class__.__name__ in ['MonteCarloBarostat', 'MonteCarloAnisotropicBarostat']: has_barostat = True if (self.pressure is not None) and (not has_barostat): barostat = openmm.MonteCarloBarostat(self.pressure, self.temperature) self.system.addForce(barostat) # Create alchemically-modified system and populate thermodynamic states. from alchemy import AbsoluteAlchemicalFactory from sams import ThermodynamicState self.thermodynamic_states = list() if alchemical_protocol == 'fused': factory = AbsoluteAlchemicalFactory(self.system, ligand_atoms=self.alchemical_atoms, annihilate_electrostatics=True, annihilate_sterics=False) self.system = factory.createPerturbedSystem() from sams import ThermodynamicState alchemical_lambdas = np.linspace(1.0, 0.0, nlambda) for alchemical_lambda in alchemical_lambdas: parameters = {'lambda_sterics' : alchemical_lambda, 'lambda_electrostatics' : alchemical_lambda} self.thermodynamic_states.append( ThermodynamicState(system=self.system, temperature=self.temperature, pressure=self.pressure, parameters=parameters) ) elif alchemical_protocol == 'two-phase': factory = AbsoluteAlchemicalFactory(self.system, ligand_atoms=self.alchemical_atoms, annihilate_electrostatics=True, annihilate_sterics=False, softcore_beta=0.0) # turn off softcore electrostatics self.system = factory.createPerturbedSystem() nelec = int(nlambda/2.0) nvdw = nlambda - nelec for state in range(nelec+1): parameters = {'lambda_sterics' : 1.0, 'lambda_electrostatics' : (1.0 - float(state)/float(nelec)) } self.thermodynamic_states.append( ThermodynamicState(system=self.system, temperature=self.temperature, pressure=self.pressure, parameters=parameters) ) for state in range(1,nvdw+1): parameters = {'lambda_sterics' : (1.0 - float(state)/float(nvdw)), 'lambda_electrostatics' : 0.0 } self.thermodynamic_states.append( ThermodynamicState(system=self.system, temperature=self.temperature, pressure=self.pressure, parameters=parameters) ) else: raise Exception("'alchemical_protocol' must be one of ['two-phase', 'fused']; scheme '%s' unknown." % alchemical_protocol) # Create SAMS samplers print('Setting up samplers...') from sams.samplers import SamplerState, MCMCSampler, ExpandedEnsembleSampler, SAMSSampler thermodynamic_state_index = 0 # initial thermodynamic state index thermodynamic_state = self.thermodynamic_states[thermodynamic_state_index] sampler_state = SamplerState(positions=self.positions) self.mcmc_sampler = MCMCSampler(sampler_state=sampler_state, thermodynamic_state=thermodynamic_state, ncfile=self.ncfile) self.mcmc_sampler.timestep = 2.0 * unit.femtoseconds self.mcmc_sampler.nsteps = 500 #self.mcmc_sampler.pdbfile = open('output.pdb', 'w') self.mcmc_sampler.topology = self.topology self.mcmc_sampler.verbose = True self.exen_sampler = ExpandedEnsembleSampler(self.mcmc_sampler, self.thermodynamic_states) self.exen_sampler.verbose = True self.sams_sampler = SAMSSampler(self.exen_sampler) self.sams_sampler.verbose = True # DEBUG: Write PDB of initial frame from simtk.openmm.app import PDBFile outfile = open('initial.pdb', 'w') PDBFile.writeFile(self.topology, self.positions, outfile) outfile.close()
return # # TEST HARNESS # if __name__ == '__main__': # Read OpenMM System and coordinates. from simtk import openmm from simtk import unit system = openmm.XmlSerializer.deserialize(_readFileContents('explicit-system.xml')) state = openmm.XmlSerializer.deserialize(_readFileContents('explicit-state.xml')) # Read explicitly solvated PDB file. from simtk.openmm.app import PDBFile pdb_filename = 'explicit-refined.pdb' pdbfile = PDBFile(pdb_filename) topology = pdbfile.getTopology() # Create AMBER input files. prmtop_filename = 'amber.prmtop' inpcrd_filename = 'amber.crd' openmm_forcefields_to_use = ['amber99sbildn.xml', 'tip3p.xml'] amber_forcefield_to_use = '/mnt/b/projects/sciteam/jn6/GIT/amber-gnu/dat/leap/cmd/oldff/leaprc.ff99SBildn' createAmberInputFiles(topology, system, state, openmm_forcefields_to_use, amber_forcefield_to_use, prmtop_filename, inpcrd_filename, verbose=True) # Run dynamics. ntimesteps = 5000 run_pmemd_cuda(ntimesteps, verbose=True)
def __init__(self, file, unitCellDimensions=None, includeDir='/usr/local/gromacs/share/gromacs/top', defines={}): """Load a top file. Parameters: - file (string) the name of the file to load - unitCellDimensions (Vec3=None) the dimensions of the crystallographic unit cell - includeDir (string=/usr/local/gromacs/share/gromacs/top) a directory in which to look for other files included from the top file - defines (map={}) preprocessor definitions that should be predefined when parsing the file """ self._includeDirs = (os.path.dirname(file), includeDir) self._defines = defines # Parse the file. self._currentCategory = None self._ifStack = [] self._moleculeTypes = {} self._molecules = [] self._currentMoleculeType = None self._atomTypes = {} self._bondTypes= {} self._angleTypes = {} self._dihedralTypes = {} self._implicitTypes = {} self._pairTypes = {} self._cmapTypes = {} self._processFile(file) # Create the Topology from it. top = Topology() ## The Topology read from the prmtop file self.topology = top top.setUnitCellDimensions(unitCellDimensions) PDBFile._loadNameReplacementTables() for moleculeName, moleculeCount in self._molecules: if moleculeName not in self._moleculeTypes: raise ValueError("Unknown molecule type: "+moleculeName) moleculeType = self._moleculeTypes[moleculeName] # Create the specified number of molecules of this type. for i in range(moleculeCount): atoms = [] lastResidue = None c = top.addChain() for index, fields in enumerate(moleculeType.atoms): resNumber = fields[2] if resNumber != lastResidue: lastResidue = resNumber resName = fields[3] if resName in PDBFile._residueNameReplacements: resName = PDBFile._residueNameReplacements[resName] r = top.addResidue(resName, c) if resName in PDBFile._atomNameReplacements: atomReplacements = PDBFile._atomNameReplacements[resName] else: atomReplacements = {} atomName = fields[4] if atomName in atomReplacements: atomName = atomReplacements[atomName] # Try to guess the element. upper = atomName.upper() if upper.startswith('CL'): element = elem.chlorine elif upper.startswith('NA'): element = elem.sodium elif upper.startswith('MG'): element = elem.magnesium else: try: element = elem.get_by_symbol(atomName[0]) except KeyError: element = None atoms.append(top.addAtom(atomName, element, r)) # Add bonds to the topology for fields in moleculeType.bonds: top.addBond(atoms[int(fields[0])-1], atoms[int(fields[1])-1])
def __del__(self): PDBFile.writeFooter(self._topology, self._out) self._out.close()
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]