def _pmdStructureToOEMol(self): top = self.structure.topology pos = self.structure.positions molecule = oeommtools.openmmTop_to_oemol(top, pos, verbose=False) oechem.OEPerceiveResidues(molecule) oechem.OEFindRingAtomsAndBonds(molecule) return molecule
def _pmdStructureToOEMol(self): """Helper function for converting the parmed structure into an OEMolecule.""" top = self.structure.topology pos = self.structure.positions molecule = oeommtools.openmmTop_to_oemol(top, pos, verbose=False) oechem.OEPerceiveResidues(molecule) oechem.OEFindRingAtomsAndBonds(molecule) return molecule
def _pmdStructureToOEMol(self): from oeommtools.utils import openmmTop_to_oemol top = self.structure.topology pos = self.structure.positions molecule = openmmTop_to_oemol(top, pos, verbose=False) OEPerceiveResidues(molecule, OEPreserveResInfo_All) OEPerceiveResidues(molecule) OEFindRingAtomsAndBonds(molecule) return molecule
def test_improper_pyramidal(verbose=False): """Test implement of impropers on ammonia.""" from openeye import oeomega from openeye import oequacpac from oeommtools.utils import oemol_to_openmmTop, openmmTop_to_oemol from openforcefield.utils import get_data_file_path, extractPositionsFromOEMol # Prepare ammonia mol = oechem.OEMol() oechem.OESmilesToMol(mol, 'N') oechem.OEAddExplicitHydrogens(mol) omega = oeomega.OEOmega() omega.SetMaxConfs(100) omega.SetIncludeInput(False) omega.SetStrictStereo(False) # Generate charges chargeEngine = oequacpac.OEAM1BCCCharges() status = omega(mol) oequacpac.OEAssignCharges(mol, chargeEngine) # Assign atom names oechem.OETriposAtomTypes(mol) oechem.OETriposAtomNames(mol) # Set up minimization ff = ForceField('test_forcefields/ammonia_minimal.offxml') topology, positions = oemol_to_openmmTop(mol) system = ff.createSystem(topology, [mol], verbose=verbose) positions = extractPositionsFromOEMol(mol) integrator = openmm.VerletIntegrator(2.0 * unit.femtoseconds) simulation = app.Simulation(topology, system, integrator) simulation.context.setPositions(positions) # Minimize energy simulation.minimizeEnergy() state = simulation.context.getState(getEnergy=True, getPositions=True) energy = state.getPotentialEnergy() / unit.kilocalories_per_mole newpositions = state.getPositions() outmol = openmmTop_to_oemol(topology, state.getPositions()) # Sum angles around the N atom for atom in outmol.GetAtoms(oechem.OEIsInvertibleNitrogen()): aidx = atom.GetIdx() nbors = list(atom.GetAtoms()) ang1 = math.degrees(oechem.OEGetAngle(outmol, nbors[0], atom, nbors[1])) ang2 = math.degrees(oechem.OEGetAngle(outmol, nbors[1], atom, nbors[2])) ang3 = math.degrees(oechem.OEGetAngle(outmol, nbors[2], atom, nbors[0])) ang_sum = math.fsum([ang1, ang2, ang3]) # Check that sum of angles around N is within 1 degree of 353.5 if abs(ang_sum - 353.5) > 1.0: raise Exception( "Improper torsion for ammonia differs too much from reference partly pyramidal geometry." "The reference case has a sum of H-N-H angles (3x) at 353.5 degrees; the test case has a sum of {}." .format(ang_sum))
def pmdStructureToOEMol(parm, resname): from oeommtools.utils import openmmTop_to_oemol mask = "!(:%s)" % resname structure_LIG = parmed.load_file( '../2gmx_wat.prmtop', xyz='../equilibration/rst/step8.rst.125000') structure_LIG.strip(mask) pos = structure_LIG.positions top = structure_LIG.topology molecule = openmmTop_to_oemol(top, pos, verbose=False) OEPerceiveBondOrders(molecule) OEAssignAromaticFlags(molecule) OEFindRingAtomsAndBonds(molecule) return molecule
def test_openmmTop_to_oemol(self): protein = ommutils.get_data_filename('examples', 'data/T4-protein.pdb') pdb = app.PDBFile(protein) oe_mol = oeommtools.openmmTop_to_oemol(pdb.topology, pdb.positions) # Assert self.assertEqual(pdb.topology.getNumAtoms(), oe_mol.NumAtoms()) for (op_at, oe_at) in zip(pdb.topology.atoms(), oe_mol.GetAtoms()): self.assertEqual(op_at.index, oe_at.GetIdx()) oe_pos = [v for k, v in oe_mol.GetCoords().items()] np.testing.assert_almost_equal( pdb.getPositions(asNumpy=True).in_units_of(unit.angstrom) / unit.angstrom, np.array(oe_pos), decimal=2)
def process(self, mol, port): try: # Split the complex in components in order to apply the FF protein, ligand, water, excipients = utils.split(mol) # Unique prefix name used to output parametrization files self.opt['prefix_name'] = mol.GetTitle() # Apply FF to the Protein protein_structure = utils.applyffProtein(protein, self.opt) # Apply FF to water molecules water_structure = utils.applyffWater(water, self.opt) # Apply FF to the excipients if excipients.NumAtoms() > 0: excipient_structure = utils.applyffExcipients(excipients, self.opt) # The excipient order is set equal to the order in related # parmed structure to avoid possible atom index mismatching excipients = oeommutils.openmmTop_to_oemol(excipient_structure.topology, excipient_structure.positions, verbose=False) # Apply FF to the ligand ligand_structure = utils.applyffLigand(ligand, self.opt) # Build the Parmed structure if excipients.NumAtoms() > 0: complex_structure = protein_structure + ligand_structure + \ excipient_structure + water_structure else: complex_structure = protein_structure + ligand_structure + water_structure num_atom_system = protein.NumAtoms() + ligand.NumAtoms() + excipients.NumAtoms() + water.NumAtoms() if not num_atom_system == complex_structure.topology.getNumAtoms(): oechem.OEThrow.Fatal("Parmed and OE topologies mismatch atom number error") # Assemble a new OEMol complex in a specific order # to match the defined Parmed structure complex complx = protein.CreateCopy() oechem.OEAddMols(complx, ligand) oechem.OEAddMols(complx, excipients) oechem.OEAddMols(complx, water) complx.SetTitle(mol.GetTitle()) # Set Parmed structure box_vectors vec_data = pack_utils.PackageOEMol.getData(complx, tag='box_vectors') vec = pack_utils.PackageOEMol.decodePyObj(vec_data) complex_structure.box_vectors = vec # Attach the Parmed structure to the complex packed_complex = pack_utils.PackageOEMol.pack(complx, complex_structure) # Attach the reference positions to the complex ref_positions = complex_structure.positions packedpos = pack_utils.PackageOEMol.encodePyObj(ref_positions) packed_complex.SetData(oechem.OEGetTag('OEMDDataRefPositions'), packedpos) # Set atom serial numbers, Ligand name and HETATM flag # oechem.OEPerceiveResidues(packed_complex, oechem.OEPreserveResInfo_SerialNumber) for at in packed_complex.GetAtoms(): thisRes = oechem.OEAtomGetResidue(at) thisRes.SetSerialNumber(at.GetIdx()) if thisRes.GetName() == 'UNL': thisRes.SetName("LIG") thisRes.SetHetAtom(True) oechem.OEAtomSetResidue(at, thisRes) if packed_complex.GetMaxAtomIdx() != complex_structure.topology.getNumAtoms(): raise ValueError("OEMol complex and Parmed structure mismatch atom numbers") # Check if it is possible to create the OpenMM System system = complex_structure.createSystem(nonbondedMethod=app.CutoffPeriodic, nonbondedCutoff=10.0 * unit.angstroms, constraints=app.HBonds, removeCMMotion=False) self.success.emit(packed_complex) except Exception as e: # Attach error message to the molecule that failed self.log.error(traceback.format_exc()) mol.SetData('error', str(e)) # Return failed mol self.failure.emit(mol) return
def process(self, mol, port): try: # Split the complex in components in order to apply the FF protein, ligand, water, excipients = oeommutils.split( mol, ligand_res_name=self.opt['ligand_res_name']) self.log.info( "\nComplex name: {}\nProtein atom numbers = {}\nLigand atom numbers = {}\n" "Water atom numbers = {}\nExcipients atom numbers = {}".format( mol.GetTitle(), protein.NumAtoms(), ligand.NumAtoms(), water.NumAtoms(), excipients.NumAtoms())) # Unique prefix name used to output parametrization files self.opt['prefix_name'] = mol.GetTitle() oe_mol_list = [] par_mol_list = [] # Apply FF to the Protein if protein.NumAtoms(): oe_mol_list.append(protein) protein_structure = utils.applyffProtein(protein, self.opt) par_mol_list.append(protein_structure) # Apply FF to the ligand if ligand.NumAtoms(): oe_mol_list.append(ligand) ligand_structure = utils.applyffLigand(ligand, self.opt) par_mol_list.append(ligand_structure) # Apply FF to water molecules if water.NumAtoms(): oe_mol_list.append(water) water_structure = utils.applyffWater(water, self.opt) par_mol_list.append(water_structure) # Apply FF to the excipients if excipients.NumAtoms(): excipient_structure = utils.applyffExcipients( excipients, self.opt) par_mol_list.append(excipient_structure) # The excipient order is set equal to the order in related # parmed structure to avoid possible atom index mismatching excipients = oeommutils.openmmTop_to_oemol( excipient_structure.topology, excipient_structure.positions, verbose=False) oechem.OEPerceiveBondOrders(excipients) oe_mol_list.append(excipients) # Build the overall Parmed structure complex_structure = parmed.Structure() for struc in par_mol_list: complex_structure = complex_structure + struc complx = oe_mol_list[0].CreateCopy() num_atom_system = complx.NumAtoms() for idx in range(1, len(oe_mol_list)): oechem.OEAddMols(complx, oe_mol_list[idx]) num_atom_system += oe_mol_list[idx].NumAtoms() if not num_atom_system == complex_structure.topology.getNumAtoms(): oechem.OEThrow.Fatal( "Parmed and OE topologies mismatch atom number error") complx.SetTitle(mol.GetTitle()) # Set Parmed structure box_vectors is_periodic = True try: vec_data = pack_utils.PackageOEMol.getData(complx, tag='box_vectors') vec = pack_utils.PackageOEMol.decodePyObj(vec_data) complex_structure.box_vectors = vec except: is_periodic = False self.log.warn( "System has been parametrize without periodic box vectors for vacuum simulation" ) # Attach the Parmed structure to the complex packed_complex = pack_utils.PackageOEMol.pack( complx, complex_structure) # Attach the reference positions to the complex ref_positions = complex_structure.positions packedpos = pack_utils.PackageOEMol.encodePyObj(ref_positions) packed_complex.SetData(oechem.OEGetTag('OEMDDataRefPositions'), packedpos) # Set atom serial numbers, Ligand name and HETATM flag # oechem.OEPerceiveResidues(packed_complex, oechem.OEPreserveResInfo_SerialNumber) for at in packed_complex.GetAtoms(): thisRes = oechem.OEAtomGetResidue(at) thisRes.SetSerialNumber(at.GetIdx()) if thisRes.GetName() == 'UNL': # thisRes.SetName("LIG") thisRes.SetHetAtom(True) oechem.OEAtomSetResidue(at, thisRes) if packed_complex.GetMaxAtomIdx( ) != complex_structure.topology.getNumAtoms(): raise ValueError( "OEMol complex and Parmed structure mismatch atom numbers") # Check if it is possible to create the OpenMM System if is_periodic: complex_structure.createSystem( nonbondedMethod=app.CutoffPeriodic, nonbondedCutoff=10.0 * unit.angstroms, constraints=app.HBonds, removeCMMotion=False) else: complex_structure.createSystem(nonbondedMethod=app.NoCutoff, constraints=app.HBonds, removeCMMotion=False) self.success.emit(packed_complex) except Exception as e: # Attach error message to the molecule that failed self.log.error(traceback.format_exc()) mol.SetData('error', str(e)) # Return failed mol self.failure.emit(mol) return
def hydrate(system, opt): """ This function solvates the system by using PDBFixer Parameters: ----------- system: OEMol molecule The system to solvate opt: python dictionary The parameters used to solvate the system Return: ------- oe_mol: OEMol The solvated system """ def BoundingBox(molecule): """ This function calculates the Bounding Box of the passed molecule molecule: OEMol return: bb (numpy array) the calculated bounding box is returned as numpy array: [(xmin,ymin,zmin), (xmax,ymax,zmax)] """ coords = [v for k, v in molecule.GetCoords().items()] np_coords = np.array(coords) min_coord = np_coords.min(axis=0) max_coord = np_coords.max(axis=0) bb = np.array([min_coord, max_coord]) return bb # Create a system copy sol_system = system.CreateCopy() # Calculate system BoundingBox (Angstrom units) BB = BoundingBox(sol_system) # Estimation of the box cube length in A box_edge = 2.0 * opt['solvent_padding'] + np.max(BB[1] - BB[0]) # BB center xc = (BB[0][0]+BB[1][0])/2. yc = (BB[0][1]+BB[1][1])/2. zc = (BB[0][2]+BB[1][2])/2. delta = np.array([box_edge/2., box_edge/2., box_edge/2.]) - np.array([xc, yc, zc]) sys_coord_dic = {k: (v+delta) for k, v in sol_system.GetCoords().items()} sol_system.SetCoords(sys_coord_dic) # Load a fake system to initialize PDBfixer filename = resource_filename('pdbfixer', 'tests/data/test.pdb') fixer = PDBFixer(filename=filename) # Convert between OE and OpenMM topology omm_top, omm_pos = oeommutils.oemol_to_openmmTop(sol_system) chain_names = [] for chain in omm_top.chains(): chain_names.append(chain.id) # Set the correct topology to the fake system fixer.topology = omm_top fixer.positions = omm_pos # Solvate the system fixer.addSolvent(padding=unit.Quantity(opt['solvent_padding'], unit.angstroms), ionicStrength=unit.Quantity(opt['salt_concentration'], unit.millimolar)) # The OpenMM topology produced by the solvation fixer has missing bond # orders and aromaticity. The following section is creating a new openmm # topology made of just water molecules and ions. The new topology is then # converted in an OEMol and added to the passed molecule to produce the # solvated system wat_ion_top = app.Topology() # Atom dictionary between the the PDBfixer topology and the water_ion topology fixer_atom_to_wat_ion_atom = {} for chain in fixer.topology.chains(): if chain.id not in chain_names: n_chain = wat_ion_top.addChain(chain.id) for res in chain.residues(): n_res = wat_ion_top.addResidue(res.name, n_chain) for at in res.atoms(): n_at = wat_ion_top.addAtom(at.name, at.element, n_res) fixer_atom_to_wat_ion_atom[at] = n_at for bond in fixer.topology.bonds(): at0 = bond[0] at1 = bond[1] try: wat_ion_top.addBond(fixer_atom_to_wat_ion_atom[at0], fixer_atom_to_wat_ion_atom[at1], type=None, order=1) except: pass wat_ion_pos = fixer.positions[len(omm_pos):] oe_mol = oeommutils.openmmTop_to_oemol(wat_ion_top, wat_ion_pos) # Setting the box vectors omm_box_vectors = fixer.topology.getPeriodicBoxVectors() box_vectors = utils.PackageOEMol.encodePyObj(omm_box_vectors) oe_mol.SetData(oechem.OEGetTag('box_vectors'), box_vectors) oechem.OEAddMols(oe_mol, sol_system) return oe_mol
def simulation(mdData, **opt): """ This supporting function performs: OpenMM Minimization, NVT and NPT Molecular Dynamics (MD) simulations Parameters ---------- mdData : MDData data object The object which recovers the relevant Parmed structure data to perform MD opt: python dictionary A dictionary containing all the MD setting info """ if opt['Logger'] is None: printfile = sys.stdout else: printfile = opt['Logger'].file # MD data extracted from Parmed structure = mdData.structure topology = mdData.topology positions = mdData.positions velocities = mdData.velocities box = mdData.box # Time step in ps stepLen = 0.002 * unit.picoseconds # Centering the system to the OpenMM Unit Cell if opt['center'] and box is not None: opt['Logger'].info("Centering is On") # Numpy array in A coords = structure.coordinates # System Center of Geometry cog = np.mean(coords, axis=0) # System box vectors box_v = structure.box_vectors.in_units_of(unit.angstrom)/unit.angstrom box_v = np.array([box_v[0][0], box_v[1][1], box_v[2][2]]) # Translation vector delta = box_v/2 - cog # New Coordinates new_coords = coords + delta structure.coordinates = new_coords positions = structure.positions # OpenMM system if box is not None: system = structure.createSystem(nonbondedMethod=eval("app.%s" % opt['nonbondedMethod']), nonbondedCutoff=opt['nonbondedCutoff']*unit.angstroms, constraints=eval("app.%s" % opt['constraints']), removeCMMotion=False) else: # Vacuum system = structure.createSystem(nonbondedMethod=app.NoCutoff, constraints=eval("app.%s" % opt['constraints']), removeCMMotion=False) # OpenMM Integrator integrator = openmm.LangevinIntegrator(opt['temperature']*unit.kelvin, 1/unit.picoseconds, stepLen) if opt['SimType'] == 'npt': if box is None: oechem.OEThrow.Fatal("NPT simulation without box vector") # Add Force Barostat to the system system.addForce(openmm.MonteCarloBarostat(opt['pressure']*unit.atmospheres, opt['temperature']*unit.kelvin, 25)) # Apply restraints if opt['restraints']: opt['Logger'].info("RESTRAINT mask applied to: {}" "\tRestraint weight: {}".format(opt['restraints'], opt['restraintWt'] * unit.kilocalories_per_mole/unit.angstroms**2)) # Select atom to restraint res_atom_set = oeommutils.select_oemol_atom_idx_by_language(opt['molecule'], mask=opt['restraints']) opt['Logger'].info("Number of restraint atoms: {}".format(len(res_atom_set))) # define the custom force to restrain atoms to their starting positions force_restr = openmm.CustomExternalForce('k_restr*periodicdistance(x, y, z, x0, y0, z0)^2') # Add the restraint weight as a global parameter in kcal/mol/A^2 force_restr.addGlobalParameter("k_restr", opt['restraintWt']*unit.kilocalories_per_mole/unit.angstroms**2) # Define the target xyz coords for the restraint as per-atom (per-particle) parameters force_restr.addPerParticleParameter("x0") force_restr.addPerParticleParameter("y0") force_restr.addPerParticleParameter("z0") for idx in range(0, len(positions)): if idx in res_atom_set: xyz = positions[idx].in_units_of(unit.nanometers)/unit.nanometers force_restr.addParticle(idx, xyz) system.addForce(force_restr) # Freeze atoms if opt['freeze']: opt['Logger'].info("FREEZE mask applied to: {}".format(opt['freeze'])) freeze_atom_set = oeommutils.select_oemol_atom_idx_by_language(opt['molecule'], mask=opt['freeze']) opt['Logger'].info("Number of frozen atoms: {}".format(len(freeze_atom_set))) # Set atom masses to zero for idx in range(0, len(positions)): if idx in freeze_atom_set: system.setParticleMass(idx, 0.0) if opt['platform'] == 'Auto': simulation = app.Simulation(topology, system, integrator) else: try: platform = openmm.Platform.getPlatformByName(opt['platform']) except Exception as e: oechem.OEThrow.Fatal('The selected platform is not supported: {}'.format(str(e))) if opt['platform'] in ['CUDA', 'OpenCL']: try: # Set platform CUDA or OpenCL precision properties = {'Precision': opt['cuda_opencl_precision']} simulation = app.Simulation(topology, system, integrator, platform=platform, platformProperties=properties) except Exception: oechem.OEThrow.Fatal('It was not possible to set the {} precision for the {} platform' .format(opt['cuda_opencl_precision'], opt['platform'])) else: simulation = app.Simulation(topology, system, integrator, platform=platform) # Set starting positions and velocities simulation.context.setPositions(positions) # Set Box dimensions if box is not None: simulation.context.setPeriodicBoxVectors(box[0], box[1], box[2]) # If the velocities are not present in the Parmed structure # new velocity vectors are generated otherwise the system is # restarted from the previous State if opt['SimType'] in ['nvt', 'npt']: if opt['trajectory_interval']: structure.save(opt['outfname']+'.pdb', overwrite=True) # GAC ADDED - TESTING # Preserve original pdb file residue numbers pdbfname_test = opt['outfname'] + '_ordering_test' + '.pdb' ofs = oechem.oemolostream(pdbfname_test) flavor = ofs.GetFlavor(oechem.OEFormat_PDB) ^ oechem.OEOFlavor_PDB_OrderAtoms ofs.SetFlavor(oechem.OEFormat_PDB, flavor) new_temp_mol = oeommutils.openmmTop_to_oemol(structure.topology, structure.positions, verbose=False) new_pos = new_temp_mol.GetCoords() opt['molecule'].SetCoords(new_pos) oechem.OEWriteConstMolecule(ofs, opt['molecule']) if velocities is not None: opt['Logger'].info('RESTARTING simulation from a previous State') simulation.context.setVelocities(velocities) else: # Set the velocities drawing from the Boltzmann distribution at the selected temperature opt['Logger'].info('GENERATING a new starting State') simulation.context.setVelocitiesToTemperature(opt['temperature']*unit.kelvin) # Convert simulation time in steps opt['steps'] = int(round(opt['time']/(stepLen.in_units_of(unit.picoseconds)/unit.picoseconds))) # Set Reporters for rep in getReporters(**opt): simulation.reporters.append(rep) # OpenMM platform information mmver = openmm.version.version mmplat = simulation.context.getPlatform() if opt['verbose']: # Host information from platform import uname for k, v in uname()._asdict().items(): print(k, ':', v, file=printfile) # Platform properties for prop in mmplat.getPropertyNames(): val = mmplat.getPropertyValue(simulation.context, prop) print(prop, ':', val, file=printfile) print('OpenMM({}) simulation generated for {} platform'.format(mmver, mmplat.getName()), file=printfile) if opt['SimType'] in ['nvt', 'npt']: opt['Logger'].info('Running {time} ps = {steps} steps of {SimType} at {temperature} K'.format(**opt)) # Start Simulation simulation.step(opt['steps']) if box is not None: state = simulation.context.getState(getPositions=True, getVelocities=True, getEnergy=True, enforcePeriodicBox=True) else: state = simulation.context.getState(getPositions=True, getVelocities=True, getEnergy=True, enforcePeriodicBox=False) elif opt['SimType'] == 'min': # Start Simulation opt['Logger'].info('Minimization steps: {steps}'.format(**opt)) state = simulation.context.getState(getEnergy=True) print('Initial energy = {}'.format(state.getPotentialEnergy().in_units_of(unit.kilocalorie_per_mole)), file=printfile) simulation.minimizeEnergy(maxIterations=opt['steps']) state = simulation.context.getState(getPositions=True, getEnergy=True) print('Minimized energy = {}'.format(state.getPotentialEnergy().in_units_of(unit.kilocalorie_per_mole)), file=printfile) # OpenMM Quantity object structure.positions = state.getPositions(asNumpy=False) # OpenMM Quantity object if box is not None: structure.box_vectors = state.getPeriodicBoxVectors() if opt['SimType'] in ['nvt', 'npt']: # numpy array in units of angstrom/picosecond structure.velocities = state.getVelocities(asNumpy=False) # If required uploading files to Orion _file_processing(**opt) # Update the OEMol complex positions to match the new # Parmed structure after the simulation new_temp_mol = oeommutils.openmmTop_to_oemol(structure.topology, structure.positions, verbose=False) new_pos = new_temp_mol.GetCoords() opt['molecule'].SetCoords(new_pos) return
def solvate(system, opt): """ This function solvates the system by using PDBFixer Parameters: ----------- system: OEMol molecule The system to solvate opt: python dictionary The parameters used to solvate the system Return: ------- oe_mol: OEMol The solvated system """ # Load a fake system to initialize PDBfixer filename = resource_filename('pdbfixer', 'tests/data/test.pdb') fixer = PDBFixer(filename=filename) # Convert between OE and OpenMM topology omm_top, omm_pos = oeommutils.oemol_to_openmmTop(system) chain_names = [] for chain in omm_top.chains(): chain_names.append(chain.id) # Set the correct topology to the fake system fixer.topology = omm_top fixer.positions = omm_pos # Solvate the system fixer.addSolvent(padding=unit.Quantity(opt['solvent_padding'], unit.angstroms), ionicStrength=unit.Quantity(opt['salt_concentration'], unit.millimolar)) # The OpenMM topology produced by the solvation fixer has missing bond # orders and aromaticity. The following section is creating a new openmm # topology made of just water molecules and ions. The new topology is then # converted in an OEMol and added to the passed molecule to produce the # solvated system wat_ion_top = app.Topology() # Atom dictionary between the the PDBfixer topology and the water_ion topology fixer_atom_to_wat_ion_atom = {} for chain in fixer.topology.chains(): if chain.id not in chain_names: n_chain = wat_ion_top.addChain(chain.id) for res in chain.residues(): n_res = wat_ion_top.addResidue(res.name, n_chain) for at in res.atoms(): n_at = wat_ion_top.addAtom(at.name, at.element, n_res) fixer_atom_to_wat_ion_atom[at] = n_at for bond in fixer.topology.bonds(): at0 = bond[0] at1 = bond[1] try: wat_ion_top.addBond(fixer_atom_to_wat_ion_atom[at0], fixer_atom_to_wat_ion_atom[at1], type=None, order=1) except: pass wat_ion_pos = fixer.positions[len(omm_pos):] oe_mol = oeommutils.openmmTop_to_oemol(wat_ion_top, wat_ion_pos) # Setting the box vectors omm_box_vectors = fixer.topology.getPeriodicBoxVectors() box_vectors = utils.PackageOEMol.encodePyObj(omm_box_vectors) oe_mol.SetData(oechem.OEGetTag('box_vectors'), box_vectors) oechem.OEAddMols(oe_mol, system) return oe_mol