def _prep_sim(self, coords, external_forces=[]): try: from simtk.openmm import Platform, LangevinIntegrator, Vec3 from simtk.openmm.app import Modeller, ForceField, \ CutoffNonPeriodic, PME, Simulation, HBonds from simtk.unit import angstrom, nanometers, picosecond, \ kelvin, Quantity, molar except ImportError: raise ImportError( 'Please install PDBFixer and OpenMM in order to use ClustENM.') positions = Quantity([Vec3(*xyz) for xyz in coords], angstrom) modeller = Modeller(self._topology, positions) if self._sol == 'imp': forcefield = ForceField(*self._force_field) system = forcefield.createSystem(modeller.topology, nonbondedMethod=CutoffNonPeriodic, nonbondedCutoff=1.0 * nanometers, constraints=HBonds) if self._sol == 'exp': forcefield = ForceField(*self._force_field) modeller.addSolvent(forcefield, padding=self._padding * nanometers, ionicStrength=self._ionicStrength * molar) system = forcefield.createSystem(modeller.topology, nonbondedMethod=PME, nonbondedCutoff=1.0 * nanometers, constraints=HBonds) for force in external_forces: system.addForce(force) integrator = LangevinIntegrator(self._temp * kelvin, 1 / picosecond, 0.002 * picosecond) # precision could be mixed, but single is okay. platform = self._platform if self._platform is None else Platform.getPlatformByName( self._platform) properties = None if self._platform is None: properties = {'Precision': 'single'} elif self._platform in ['CUDA', 'OpenCL']: properties = {'Precision': 'single'} simulation = Simulation(modeller.topology, system, integrator, platform, properties) simulation.context.setPositions(modeller.positions) return simulation
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 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 __init__(self, pdb_path, offset_size=2): # OpenMM init self.pdb_path = pdb_path self.pdb = PDBFile(self.pdb_path) self.forcefield = ForceField('amber14-all.xml', 'amber14/tip3pfb.xml') self.modeller = Modeller(self.pdb.topology, self.pdb.positions) # Remove any water that might be present in the PDB file self.modeller.deleteWater() # Add any hydrogens not present self.modeller.addHydrogens(self.forcefield) self.system = self.forcefield.createSystem(self.modeller.topology, nonbondedMethod=PME, nonbondedCutoff=1 * u.nanometer, constraints=HBonds) self.integrator = LangevinIntegrator(300 * u.kelvin, 1 / u.picosecond, 0.002 * u.picoseconds) self.simulation = Simulation(self.modeller.topology, self.system, self.integrator) self.pdb_positions = self.modeller.getPositions() # Initialize bond dictionary and positions for chemcoord self.cc_bonds = {} self.offset_size = offset_size self._init_pdb_bonds() self.set_cc_positions(self.pdb_positions) # Perform initial minimization, which updates self.pdb_positions min_energy, min_positions = self.run_simulation() # Reset the positions after the minimization self.set_cc_positions(self.pdb_positions) self.torsion_indices = self._get_torsion_indices() self.starting_positions = min_positions self.starting_torsions = np.array([ self.zmat.loc[self.torsion_indices[:, 0], 'dihedral'], self.zmat.loc[self.torsion_indices[:, 1], 'dihedral'] ]).T self.seed_offsets()
def test_add_solvent(self): """Test using simtk.opnmm.app.Modeller to add solvent to a small molecule parameterized by template generator""" # Select a molecule to add solvent around from simtk.openmm.app import NoCutoff, Modeller from simtk import unit molecule = self.molecules[0] openmm_topology = molecule.to_topology().to_openmm() openmm_positions = molecule.conformers[0] # Try adding solvent without residue template generator; this will fail from simtk.openmm.app import ForceField forcefield = ForceField('tip3p.xml') # Add solvent to a system containing a small molecule modeller = Modeller(openmm_topology, openmm_positions) try: modeller.addSolvent(forcefield, model='tip3p', padding=6.0*unit.angstroms) except ValueError as e: pass # Create a generator that knows about a few molecules generator = self.TEMPLATE_GENERATOR(molecules=self.molecules) # Add to the forcefield object forcefield.registerTemplateGenerator(generator.generator) # Add solvent to a system containing a small molecule # This should succeed modeller.addSolvent(forcefield, model='tip3p', padding=6.0*unit.angstroms)
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'))
---SYSTEM PREPARATION--- setup AM1-BCC charges for the solute, add solvent, set non-bonded method etc ''' ligand_mol = Molecule.from_file('ethanol.sdf', file_format='sdf') forcefield_kwargs = {'constraints': app.HBonds, 'rigidWater': True, 'removeCMMotion': True, 'hydrogenMass': 4 * unit.amu } system_generator = SystemGenerator( forcefields=['amber/ff14SB.xml', 'amber/tip4pew_standard.xml'], small_molecule_forcefield='gaff-2.11', molecules=[ligand_mol], forcefield_kwargs=forcefield_kwargs) ligand_pdb = PDBFile('ethanol.pdb') modeller = Modeller(ligand_pdb.topology, ligand_pdb.positions) modeller.addSolvent(system_generator.forcefield, model='tip4pew', padding=12.0 * unit.angstroms) system = system_generator.forcefield.createSystem(modeller.topology, nonbondedMethod=PME, nonbondedCutoff=9.0 * unit.angstroms, constraints=HBonds) ''' ---FINISHED SYSTEM PREPARATION--- ''' ''' ---ALCHEMICAL CONFIGURATION--- define solute indexes, set up the alchemical region + factory, and specify steric/electrostatic lambda coupling for solvation ''' # determines solute indexes
'rigidWater': True, 'removeCMMotion': False, 'hydrogenMass': 4 * unit.amu } system_generator = SystemGenerator( forcefields=['amber/ff14SB.xml', 'amber/tip3p_standard.xml'], small_molecule_forcefield='gaff-2.11', molecules=[ligand_mol], forcefield_kwargs=forcefield_kwargs) # Use Modeller to combine the protein and ligand into a complex print('Reading protein') protein_pdb = PDBFile(pdb_in) print('Preparing complex') modeller = Modeller(protein_pdb.topology, protein_pdb.positions) print('System has %d atoms' % modeller.topology.getNumAtoms()) # This next bit is black magic. # Modeller needs topology and positions. Lots of trial and error found that this is what works to get these from # an openforcefield Molecule object that was created from a RDKit molecule. # The topology part is described in the openforcefield API but the positions part grabs the first (and only) # conformer and passes it to Modeller. It works. Don't ask why! modeller.add(ligand_mol.to_topology().to_openmm(), ligand_mol.conformers[0]) print('System has %d atoms' % modeller.topology.getNumAtoms()) # Solvate print('Adding solvent...') # we use the 'padding' option to define the periodic box. The PDB file does not contain any # unit cell information so we just create a box that has a 10A padding around the complex.
if chain.get_id() == self.chain: return 1 else: return 0 p = PDBParser(PERMISSIVE=1) structure = p.get_structure(f'{pdbid}', f'{pdbid}_fixed.pdb') pdb_chain_file = f'chain_{chain}.pdb' io_w_no_h = PDBIO() io_w_no_h.set_structure(structure) io_w_no_h.save(f'{pdbid}_chain{chain}.pdb', ChainSelect(chain)) print("The fixed.pdb file with selected chain is ready.") # load pdb to Modeller pdb = PDBFile(f'{pdbid}_chain{chain}.pdb') molecule = Modeller(pdb.topology, pdb.positions) print("Done loading pdb to Modeller.") # load force field forcefield = ForceField('amber14-all.xml', 'amber14/tip3pfb.xml') print("Done loading force field.") print("OpenMM version:", version.version) # prepare system molecule.addSolvent(forcefield, padding=12 * unit.angstrom, model='tip3p', positiveIon='Na+', negativeIon='Cl-', ionicStrength=0 * unit.molar) print("Done adding solvent.") PDBxFile.writeFile(molecule.topology, molecule.positions,
'removeCMMotion': False, 'hydrogenMass': 4 * unit.amu } system_generator = SystemGenerator( forcefields=['amber/ff14SB.xml', 'amber/tip3p_standard.xml'], small_molecule_forcefield='openff_unconstrained-1.2.1.offxml', # small_molecule_forcefield='gaff-2.11', molecules=other_mols, forcefield_kwargs=forcefield_kwargs) # Use Modeller to combine the protein and ligand into a complex print('Reading protein') protein_pdb = PDBFile(opt.receptor) print('Preparing complex') modeller = Modeller(protein_pdb.topology, protein_pdb.positions) print('System has %d atoms' % modeller.topology.getNumAtoms()) # This next bit is black magic. # Modeller needs topology and positions. Lots of trial and error found that this is what works to get these from # an openforcefield Molecule object that was created from a RDKit molecule. # The topology part is described in the openforcefield API but the positions part grabs the first (and only) # conformer and passes it to Modeller. It works. Don't ask why! if len(other_mols) != 0: for other_mol in other_mols: modeller.add(other_mol.to_topology().to_openmm(), other_mol.conformers[0]) #modeller.add(ligand_mol.to_topology().to_openmm(), ligand_mol.conformers[0]) # Generate ligand with solvent for FEP if opt.ligand:
pdbid = '2JIU' chain = 'B' experiment = '2_dimensional' work_dir = f'/home/guoj1/data_projects/cv_selection/metadynamics/{experiment}/{pdbid}' temperature = 310.15 * unit.kelvin pressure = 1.0 * unit.atmospheres cv_min_1 = -19.44 cv_max_1 = -0.82 cv_std_1 = 4.24 cv_min_2 = 0.48 cv_max_2 = 13.63 cv_std_2 = 2.30 # load pdb to Modeller pdb = PDBFile(f'{pdbid}_chain{chain}_minequi.pdb') molecule = Modeller(pdb.topology, pdb.positions) print("Done loading pdb to Modeller.") # load force field forcefield = ForceField('amber14-all.xml', 'amber14/tip3pfb.xml') print("Done loading force field.") print("OpenMM version:", version.version) # prepare the system (using heavy hydrogens and constrain all hygrogen atom-involved bonds) system = forcefield.createSystem(pdb.topology, nonbondedMethod=PME, rigidWater=True, nonbondedCutoff=1 * unit.nanometer, hydrogenMass=4 * unit.amu, constraints=HBonds) # specify the set of key atoms and calculate key dihedrals and distances
ligand_mol = Molecule.from_file('ligand1.sdf', file_format='sdf') print(ligand_mol) # can't read as PDB as "No toolkits in registry can read file" #complex_pdb = Molecule(open('complex1.pdb', 'rb'), file_format='pdb') # Use Modeller to combine the protein and ligand into a complex print('Reading protein') protein_pdb = PDBFile('protein.pdb') # reading the ligand gives lots of warnings about "duplicate atom" but this seems incorrect print('Reading ligand') ligand_pdb = PDBFile('ligand1.pdb') print('Preparing complex') # Approach 1 modeller = Modeller(protein_pdb.topology, protein_pdb.positions) modeller.add(ligand_pdb.topology, ligand_pdb.positions) PDBFile.writeFile(modeller.topology, modeller.positions, open('complex1.pdb', 'w')) # Approach 2 # protein_pdb = parmed.load_file('protein.pdb') # ligand_pdb = parmed.load_file('ligand1.pdb') # complex = protein_pdb + ligand_pdb # complex.save('complex1.pdb') complex_pdb = PDBFile('complex1.pdb') print('Preparing system') forcefield_kwargs = { 'constraints': app.HBonds,
def calculate_protein_energetics(): """ * Create an OpenMM system using the first fragment. * Add each fragment into the system. * Calculate the energy of the system and print. """ os.chdir('group2') # Necessary due to size of calculation sys.setrecursionlimit(15000) frag1 = PDBFile('frag1/no_QUP_frag1.pdb') forcefield = ForceField( 'frag1/QUBE_pro_frag1.xml', 'frag2/QUBE_pro_frag2_plus.xml', 'frag3/QUBE_pro_frag3_plus.xml', 'frag4/QUBE_pro_frag4_plus.xml', ) modeller = Modeller(frag1.topology, frag1.positions) frag2 = PDBFile('frag2/no_QUP_frag2.pdb') modeller.add(frag2.topology, frag2.positions) frag3 = PDBFile('frag3/no_QUP_frag3.pdb') modeller.add(frag3.topology, frag3.positions) frag4 = PDBFile('frag4/no_QUP_frag4.pdb') modeller.add(frag4.topology, frag4.positions) system = forcefield.createSystem( modeller.topology, nonbondedMethod=NoCutoff, ) system = apply_opls_combo(system) integrator = LangevinIntegrator( 298.15 * unit.kelvin, # Temperature of heat bath 1.0 / unit.picoseconds, # Friction coefficient 2.0 * unit.femtoseconds, # Time step ) platform = Platform.getPlatformByName('CPU') simulation = Simulation(modeller.topology, system, integrator, platform) simulation.context.setPositions(modeller.positions) print('energy from openmm library') print(simulation.context.getState(getEnergy=True).getPotentialEnergy()) positions = simulation.context.getState(getPositions=True).getPositions() with open('output.pdb', 'w') as out_file: PDBFile.writeFile(simulation.topology, positions, out_file) structure = parmed.load_file('output.pdb') energy_comps = parmed.openmm.energy_decomposition_system(structure, system) total_energy = 0.0 for comp in energy_comps: total_energy += comp[1] print(*comp) print(f'Total energy {total_energy: 6.6f}')
system_generator = SystemGenerator( forcefields=['amber/ff14SB.xml', 'amber/tip3p_standard.xml'], small_molecule_forcefield='gaff-2.11', molecules=[ligand_mol], forcefield_kwargs=forcefield_kwargs) # Use Modeller to combine the protein and ligand into a complex print('Reading protein') protein_pdb = PDBFile('protein.pdb') # reading the ligand gives lots of warnings about "duplicate atom" but this seems incorrect print('Reading ligand') ligand_pdb = PDBFile('ligand1.pdb') print('Preparing complex') modeller = Modeller(protein_pdb.topology, protein_pdb.positions) print('System has %d atoms' % modeller.topology.getNumAtoms()) modeller.add(ligand_pdb.topology, ligand_pdb.positions) print('System has %d atoms' % modeller.topology.getNumAtoms()) box_vectors = unit.Quantity(np.diag([100, 100, 100]), unit.angstrom) modeller.topology.setPeriodicBoxVectors(box_vectors) # Solvate print('Adding solvent...') modeller.addSolvent(system_generator.forcefield, model='tip3p') #, padding=5.0*unit.angstroms) print('System has %d atoms' % modeller.topology.getNumAtoms()) PDBFile.writeFile(modeller.topology, modeller.positions, open('complex1.pdb', 'w'))
""" Testing the funnel hypothesis """ from math import pi from simtk.openmm.app import PDBFile, ForceField, Modeller, PME, HBonds from simtk.openmm import LangevinIntegrator from simtk.unit import kelvin, nanometer, picosecond, picoseconds import chemcoord as cc import numpy as np import pandas as pd import util pdb = PDBFile('1ubq.pdb') forcefield = ForceField('amber14-all.xml', 'amber14/tip3pfb.xml') modeller = Modeller(pdb.topology, pdb.positions) modeller.addHydrogens(forcefield) system = forcefield.createSystem(modeller.topology, nonbondedMethod=PME, nonbondedCutoff=1 * nanometer, constraints=HBonds) integrator = LangevinIntegrator(300 * kelvin, 1 / picosecond, 0.002 * picoseconds) pdb_bonds = modeller.topology.bonds() atoms = modeller.topology.atoms() positions = modeller.getPositions() cc_bonds = {} cc_positions = np.zeros((3, modeller.topology.getNumAtoms())) atom_names = []
class MoleculeUtil(object): """ A class for managing a molecule defined by a PDB file """ np.random.seed(20) def __init__(self, pdb_path, offset_size=2): # OpenMM init self.pdb_path = pdb_path self.pdb = PDBFile(self.pdb_path) self.forcefield = ForceField('amber14-all.xml', 'amber14/tip3pfb.xml') self.modeller = Modeller(self.pdb.topology, self.pdb.positions) # Remove any water that might be present in the PDB file self.modeller.deleteWater() # Add any hydrogens not present self.modeller.addHydrogens(self.forcefield) self.system = self.forcefield.createSystem(self.modeller.topology, nonbondedMethod=PME, nonbondedCutoff=1 * u.nanometer, constraints=HBonds) self.integrator = LangevinIntegrator(300 * u.kelvin, 1 / u.picosecond, 0.002 * u.picoseconds) self.simulation = Simulation(self.modeller.topology, self.system, self.integrator) self.pdb_positions = self.modeller.getPositions() # Initialize bond dictionary and positions for chemcoord self.cc_bonds = {} self.offset_size = offset_size self._init_pdb_bonds() self.set_cc_positions(self.pdb_positions) # Perform initial minimization, which updates self.pdb_positions min_energy, min_positions = self.run_simulation() # Reset the positions after the minimization self.set_cc_positions(self.pdb_positions) self.torsion_indices = self._get_torsion_indices() self.starting_positions = min_positions self.starting_torsions = np.array([ self.zmat.loc[self.torsion_indices[:, 0], 'dihedral'], self.zmat.loc[self.torsion_indices[:, 1], 'dihedral'] ]).T self.seed_offsets() def _add_backbone_restraint(self): # https://github.com/ParmEd/ParmEd/wiki/OpenMM-Tricks-and-Recipes#positional-restraints positions = self.modeller.getPositions() force = CustomExternalForce('k*((x-x0)^2+(y-y0)^2+(z-z0)^2)') force.addGlobalParameter( 'k', 5.0 * u.kilocalories_per_mole / u.angstroms**2) force.addPerParticleParameter('x0') force.addPerParticleParameter('y0') force.addPerParticleParameter('z0') for index, atom in enumerate(self.modeller.topology.atoms()): if atom.name in ('CA', 'C', 'N'): coord = positions[index] force.addParticle(index, coord.value_in_unit(u.nanometers)) self.restraint_force_id = self.system.addForce(force) def _remove_backbone_restraint(self): self.system.removeForce(self.restraint_force_id) def _fix_backbone(self): for index, atom in enumerate(self.modeller.topology.atoms()): if atom.name in ('CA', 'C', 'N'): self.system.setParticleMass(index, 0) def seed_offsets(self): self.offsets = np.random.choice([0, 0, -1, 1], self.starting_torsions.shape) def get_torsions(self): return np.array([ self.zmat.loc[self.torsion_indices[:, 0], 'dihedral'], self.zmat.loc[self.torsion_indices[:, 1], 'dihedral'] ]).T def set_torsions(self, new_torsions): self.zmat.safe_loc[self.torsion_indices[:, 0], 'dihedral'] = new_torsions[:, 0] self.zmat.safe_loc[self.torsion_indices[:, 1], 'dihedral'] = new_torsions[:, 1] def get_offset_torsions(self, scale_factor): """ Calculates and returns new torsion angles based on randomly generated offsets. Args: scale_factor: the relative scale of the offset relative to self.offset_size Returns: The new torsion angles """ total_offset = self.offset_size * scale_factor new_torsions = np.zeros(shape=self.starting_torsions.shape) new_torsions[:, 0] = self.starting_torsions[:, 0] + \ (self.offsets[:, 0] * total_offset) new_torsions[:, 1] = self.starting_torsions[:, 1] + \ (self.offsets[:, 1] * total_offset) return new_torsions def run_simulation(self): """ Run a simulation to calculate the current configuration's energy level. Note that the atoms will likely move somewhat during the calculation, since energy minimization is used. Returns: A tuple of the form (potential_energy, updated_positions) """ # Delete solvent that's based on previous positions cartesian = self.zmat.get_cartesian().sort_index() self.simulation.context.setPositions([ Vec3(x, y, z) for x, y, z in zip(cartesian['x'], cartesian['y'], cartesian['z']) ]) # self._add_backbone_restraint() # self._fix_backbone() self.modeller.addSolvent(self.forcefield, padding=1.0 * u.nanometer) self.simulation.minimizeEnergy(maxIterations=200) state = self.simulation.context.getState(getEnergy=True, getPositions=True) p_energy = state.getPotentialEnergy() positions = state.getPositions(asNumpy=True) # Clean up - remove solvent and backbone restraint (for next iteration) self.modeller.deleteWater() # self._remove_backbone_restraint() return p_energy, positions def _init_pdb_bonds(self): """Construct a dictionary describing the PDB's bonds for chemcoord use""" for index in range(self.modeller.topology.getNumAtoms()): self.cc_bonds[index] = set() for bond in self.modeller.topology.bonds(): self.cc_bonds[bond[0].index].add(bond[1].index) self.cc_bonds[bond[1].index].add(bond[0].index) def set_cc_positions(self, positions): """ Calculates the zmat from an OpenMM modeller Args: positions (list): A list """ cc_df = self._get_cartesian_df(positions) self.cartesian = cc.Cartesian(cc_df) self.cartesian.set_bonds(self.cc_bonds) self.cartesian._give_val_sorted_bond_dict(use_lookup=True) self.zmat = self.cartesian.get_zmat(use_lookup=True) def _get_cartesian_df(self, positions): cc_positions = np.zeros((3, self.modeller.topology.getNumAtoms())) atom_names = [] for index, atom in enumerate(self.modeller.topology.atoms()): pos = positions[index] / u.nanometer atom_names.append(atom.name) cc_positions[:, index] = pos cc_df = pd.DataFrame({ 'atom': atom_names, 'x': cc_positions[0, :], 'y': cc_positions[1, :], 'z': cc_positions[2, :] }) return cc_df def _get_torsion_indices(self): """ Calculates indices into the zmatrix which correspond to phi and psi angles. Args: zmat: the zmatrix specifying the molecule Returns: a numpy.array, with first column as phi_indices, second column as psi_indices """ phi_indices = [] psi_indices = [] for i in range(len(self.zmat.index)): b_index = self.zmat.loc[i, 'b'] a_index = self.zmat.loc[i, 'a'] d_index = self.zmat.loc[i, 'd'] # If this molecule references a magic string (origin, e_x, e_y, e_z, etc) if isinstance(b_index, str) or isinstance( a_index, str) or isinstance(d_index, str): continue # Psi angles if (self.zmat.loc[i, 'atom'] == 'N') & \ (self.zmat.loc[b_index, 'atom'] == 'CA') & \ (self.zmat.loc[a_index, 'atom'] == 'C') & \ (self.zmat.loc[d_index, 'atom'] == 'N'): psi_indices.append(i) elif (self.zmat.loc[i, 'atom'] == 'N') & \ (self.zmat.loc[b_index, 'atom'] == 'C') & \ (self.zmat.loc[a_index, 'atom'] == 'CA') & \ (self.zmat.loc[d_index, 'atom'] == 'N'): psi_indices.append(i) elif (self.zmat.loc[i, 'atom'] == 'C') & \ (self.zmat.loc[b_index, 'atom'] == 'N') & \ (self.zmat.loc[a_index, 'atom'] == 'CA') & \ (self.zmat.loc[d_index, 'atom'] == 'C'): phi_indices.append(i) elif (self.zmat.loc[i, 'atom'] == 'C') & \ (self.zmat.loc[b_index, 'atom'] == 'CA') & \ (self.zmat.loc[a_index, 'atom'] == 'N') & \ (self.zmat.loc[d_index, 'atom'] == 'C'): phi_indices.append(i) return np.array([phi_indices, psi_indices]).T
# Read the molfile into RDKit, add Hs and create an openforcefield Molecule object print('Reading ligand') rdkitmol = Chem.MolFromMolFile(mol_in) print('Adding hydrogens') rdkitmolh = Chem.AddHs(rdkitmol, addCoords=True) # ensure the chiral centers are all defined Chem.AssignAtomChiralTagsFromStructure(rdkitmolh) ligand_mol = Molecule(rdkitmolh) print('Reading protein') protein_pdb = PDBFile(pdb_in) # Use Modeller to combine the protein and ligand into a complex print('Preparing complex') modeller = Modeller(protein_pdb.topology, protein_pdb.positions) # This next bit is black magic. # Modeller needs topology and positions. Lots of trial and error found that this is what works to get these from # an openforcefield Molecule object that was created from a RDKit molecule. # The topology part is described in the openforcefield API but the positions part grabs the first (and only) # conformer and passes it to Modeller. It works. Don't ask why! modeller.add(ligand_mol.to_topology().to_openmm(), ligand_mol.conformers[0]) # modeller.topology.setPeriodicBoxVectors( # [Vec3(x=8.461, y=0.0, z=0.0), # Vec3(x=0.0, y=8.461, z=0.0), # Vec3(x=0.0, y=0.0, z=8.461)]) print('System has %d atoms' % modeller.topology.getNumAtoms()) with open(output_complex, 'w') as outfile: