示例#1
0
def renumber_structure(target_structure: oechem.OEGraphMol,
                       residue_numbers: List[int]) -> oechem.OEGraphMol:
    """
    Renumber the residues of a protein structure according to the given list of residue numbers.
    Parameters
    ----------
    target_structure: oechem.OEGraphMol
        An OpenEye molecule holding the protein structure to renumber.
    residue_numbers: list of int
        A list of residue numbers matching the order of the target structure.
    Returns
    -------
    renumbered_structure: oechem.OEGraphMol
        An OpenEye molecule holding the cropped protein structure.
    """
    import copy

    renumbered_structure = copy.deepcopy(
        target_structure)  # don't touch input structure
    hierview = oechem.OEHierView(renumbered_structure)
    structure_residues = hierview.GetResidues()
    for residue_number, structure_residue in zip(residue_numbers,
                                                 structure_residues):
        structure_residue_mod = structure_residue.GetOEResidue()
        structure_residue_mod.SetResidueNumber(residue_number)
        for residue_atom in structure_residue.GetAtoms():
            oechem.OEAtomSetResidue(residue_atom, structure_residue_mod)

    return renumbered_structure
示例#2
0
def setup_yank_calculation(receptor_file_name,
                           ligand_file_name,
                           setup_directory_name,
                           solvate=False):

    # Cleanup setup directory
    if os.path.exists(setup_directory_name):
        shutil.rmtree(setup_directory_name)
    os.makedirs(setup_directory_name)

    # Read ligand and receptor molecule
    ifs_mol2 = oechem.oemolistream()
    ifs_mol2.open(ligand_file_name)
    ligand_oemol = oechem.OEGraphMol()
    oechem.OEReadMolecule(ifs_mol2, ligand_oemol)
    ifs_mol2.close()

    ifs_mol2 = oechem.oemolistream()
    ifs_mol2.open(receptor_file_name)
    receptor_oemol = oechem.OEGraphMol()
    oechem.OEReadMolecule(ifs_mol2, receptor_oemol)
    ifs_mol2.close()

    # Push ligand close to receptor
    pull_close(receptor_oemol, ligand_oemol, MIN_DISTANCE, MAX_DISTANCE)

    # Add residue name 'MOL'
    residue = oechem.OEResidue()
    residue.SetName('MOL')
    for atom in ligand_oemol.GetAtoms():
        oechem.OEAtomSetResidue(atom, residue)

    # Parametrize ligand
    with working_directory(setup_directory_name):

        # Save translated ligand
        ofs = oechem.oemolostream()
        ofs.open('ligand.mol2')
        oechem.OEWriteMolecule(ofs, ligand_oemol)
        ofs.close()

        # Parametrize ligand
        print "Parameterizing ligand with GAFF..."
        run_command(
            'antechamber -fi mol2 -i ligand.mol2 -fo mol2 -o ligand.gaff.mol2')
        run_command(
            'parmchk -i ligand.gaff.mol2 -o ligand.gaff.frcmod -f mol2')

        # Copy receptor so that leap will know the PDB file name
        shutil.copyfile(receptor_file_name, 'receptor.pdb')

        # Create AMBER prmtop/inpcrd files.
        print "Creating AMBER prmtop/inpcrd files..."
        cmd = 'tleap -f {!s} > setup.leap.out'
        if solvate:
            run_command(cmd.format(LEAP_IN_EXPLICIT))
        else:
            run_command(cmd.format(LEAP_IN_IMPLICIT))
示例#3
0
    def __iter__(self):
        max_idx = self.args.limit
        if max_idx is not None:
            max_idx = int(max_idx)
        count = 0
        self.config = config_from_env()
        in_orion = self.config is not None
        if not in_orion:
            with oechem.oemolistream(str(self.args.data_in)) as ifs:
                for mol in ifs.GetOEMols():
                    mol.SetData(oechem.OEGetTag('prefix'), self.opt['prefix'])
                    mol.SetData(oechem.OEGetTag('suffix'), self.opt['suffix'])

                    for at in mol.GetAtoms():
                        residue = oechem.OEAtomGetResidue(at)
                        residue.SetName(self.opt['type'])
                        oechem.OEAtomSetResidue(at, residue)

                    if self.opt['IDTag']:
                        mol.SetData(oechem.OEGetTag('IDTag'), 'l' + mol.GetTitle()[0:12] + '_' + str(count))
                    yield mol
                    count += 1
                    if max_idx is not None and count == max_idx:
                        break
        else:
            stream = StreamingDataset(self.args.data_in,
                                      input_format=self.args.download_format)
            for mol in stream:
                mol.SetData(oechem.OEGetTag('prefix'), self.opt['prefix'])
                mol.SetData(oechem.OEGetTag('suffix'), self.opt['suffix'])

                for at in mol.GetAtoms():
                    residue = oechem.OEAtomGetResidue(at)
                    residue.SetName(self.opt['type'])
                    oechem.OEAtomSetResidue(at, residue)

                if self.opt['IDTag']:
                    mol.SetData(oechem.OEGetTag('IDTag'), 'l' + mol.GetTitle()[0:12] + '_'+str(count))
                yield mol
                count += 1
                if max_idx is not None and count == max_idx:
                    break
示例#4
0
def update_residue_identifiers(
        structure: oechem.OEGraphMol,
        keep_protein_residue_ids: bool = True) -> oechem.OEGraphMol:
    """
    Updates the atom, residue and chain ids of the given molecular structure. All residues become part of chain A. Atom
    ids will start from 1. Residue will start from 1, except protein residue ids are fixed. This is especially useful,
    if molecules were merged, which can result in overlapping atom and residue ids as well as separate chains.

    Parameters
    ----------
    structure: oechem.OEGraphMol
        The OpenEye molecule structure for updating atom and residue ids.
    keep_protein_residue_ids: bool
        If the protein residues should be kept.

    Returns
    -------
    structure: oechem.OEGraphMol
        The OpenEye molecule structure with updated atom and residue ids.
    """
    # update residue ids
    residue_number = 0
    hierarchical_view = oechem.OEHierView(structure)
    for hv_residue in hierarchical_view.GetResidues():
        residue = hv_residue.GetOEResidue()
        residue.SetChainID("A")
        if not residue.IsHetAtom() and keep_protein_residue_ids:
            if residue.GetName() == "NME" and residue.GetResidueNumber(
            ) == residue_number:
                # NME residues may have same id as preceding residue
                residue_number += 1
            else:
                # catch protein residue id if those should not be touched
                residue_number = residue.GetResidueNumber()

        else:
            # change residue id
            residue_number += 1
        residue.SetResidueNumber(residue_number)
        for atom in hv_residue.GetAtoms():
            oechem.OEAtomSetResidue(atom, residue)

    # update residue identifiers, except atom names, residue ids,
    # residue names, fragment number, chain id and record type
    preserved_info = (oechem.OEPreserveResInfo_ResidueNumber
                      | oechem.OEPreserveResInfo_ResidueName
                      | oechem.OEPreserveResInfo_HetAtom
                      | oechem.OEPreserveResInfo_AtomName
                      | oechem.OEPreserveResInfo_FragmentNumber
                      | oechem.OEPreserveResInfo_ChainID)
    oechem.OEPerceiveResidues(structure, preserved_info)

    return structure
示例#5
0
    def process(self, initialRecord, port):
        try:
            if not initialRecord.has_value(Fields.primary_molecule):
                raise ValueError("Missing Primary Molecule field")

            ligand = initialRecord.get_value(Fields.primary_molecule)

            # place the entire initial record as a sub-record, to be restored when conformer runs are gathered
            record = OERecord()
            record.set_value(Fields.ligInit_rec, initialRecord)

            if oechem.OECalculateMolecularWeight(ligand) > 1500.0:  # Units are in Dalton
                raise ValueError("[{}] The molecule {} seems to have a large molecular weight for a "
                                 "ligand: {:.2f} Da)".format(self.title,
                                                             ligand.GetTitle(),
                                                             oechem.OECalculateMolecularWeight(ligand)))

            # Removing Interaction Hint Container, Style and PDB Data
            oechem.OEDeleteInteractionsHintSerializationData(ligand)
            oechem.OEDeleteInteractionsHintSerializationIds(ligand)
            oechem.OEClearStyle(ligand)
            oechem.OEClearPDBData(ligand)

            # Ligand sanitation
            ligand = oeommutils.sanitizeOEMolecule(ligand)

            lig_title = ligand.GetTitle()

            if lig_title == "":
                lig_title = 'LIG'

            record.set_value(Fields.ligand_name, lig_title)

            for at in ligand.GetAtoms():
                residue = oechem.OEAtomGetResidue(at)
                residue.SetName(self.args.lig_res_name)
                oechem.OEAtomSetResidue(at, residue)

            record.set_value(Fields.primary_molecule, ligand)
            record.set_value(Fields.ligid, self.ligand_count)

            self.success.emit(record)
            self.ligand_count += 1
            self.max_runs += ligand.NumConfs()

        except Exception as e:

            print("Failed to complete", str(e), flush=True)
            self.opt['Logger'].info('Exception {} {}'.format(str(e), self.title))
            self.log.error(traceback.format_exc())
            self.failure.emit(initialRecord)
示例#6
0
def _OEFixBuiltLoopFragmentNumbers(protein):
    """
    Temporary fix, thanks to Jesper!
    """
    prev_fn = -1
    # Checking for CA atoms, since this will avoid messing with the caps and built sidechains,
    # since this is only a built loop problem
    builtPred = oespruce.OEIsModeledAtom()
    for atom in protein.GetAtoms(oechem.OEIsCAlpha()):
        res = oechem.OEAtomGetResidue(atom)
        fn = res.GetFragmentNumber()
        if builtPred(atom) and prev_fn != -1:
            for ra in oechem.OEGetResidueAtoms(atom):
                r = oechem.OEAtomGetResidue(ra)
                r.SetFragmentNumber(prev_fn)
                oechem.OEAtomSetResidue(ra, r)
        else:
            prev_fn = fn
示例#7
0
def Rename(ims, oms):
    for mol in ims.GetOEGraphMols():
        # @ <SNIPPET-PERCEIVE-RES>
        if not oechem.OEHasResidues(mol):
            oechem.OEPerceiveResidues(mol, oechem.OEPreserveResInfo_All)
        # @ </SNIPPET-PERCEIVE-RES>
        # @ <SNIPPET-MSE-TO-MET-CORE>
        for atom in mol.GetAtoms():
            thisRes = oechem.OEAtomGetResidue(atom)
            if oechem.OEGetResidueIndex(thisRes) == oechem.OEResidueIndex_MSE:
                thisRes.SetName("MET")  # modify res properties
                thisRes.SetHetAtom(False)
                oechem.OEAtomSetResidue(atom, thisRes)  # store updated residue

                if atom.GetAtomicNum() == oechem.OEElemNo_Se:
                    atom.SetAtomicNum(
                        oechem.OEElemNo_S)  # fix atom type & name
                    atom.SetName(" SD ")
        # @ </SNIPPET-MSE-TO-MET-CORE>
        oechem.OEWriteMolecule(oms, mol)
示例#8
0
def _OEFixWaterFragmentNumbers(solvent):
    """
    Temporary fix, thanks to Jesper!
    """
    fragment_counter = {}
    for atom in solvent.GetAtoms(oechem.OEIsWater()):
        res = oechem.OEAtomGetResidue(atom)
        if res.GetInsertCode() != " ":
            continue
        if res.GetFragmentNumber() not in fragment_counter:
            fragment_counter[res.GetFragmentNumber()] = 0
        fragment_counter[res.GetFragmentNumber()] += 1
    largest_solvent_fn_count = -1
    largest_solvent_fn = -1
    for fn in fragment_counter:
        if fragment_counter[fn] > largest_solvent_fn_count:
            largest_solvent_fn_count = fragment_counter[fn]
            largest_solvent_fn = fn
    if largest_solvent_fn < 0:
        return
    for atom in solvent.GetAtoms(oechem.OEIsWater(True)):
        res = oechem.OEAtomGetResidue(atom)
        res.SetFragmentNumber(largest_solvent_fn)
        oechem.OEAtomSetResidue(atom, res)
def enumerate_conformations(name, smiles=None, pdbname=None):
    """Run Epik to get protonation states using PDB residue templates for naming.

    Parameters
    ----------
    name : str
       Common name of molecule (used to create subdirectory)
    smiles : str
       Isomeric SMILES string
    pdbname : str
       Three-letter PDB code (e.g. 'DB8')
    """
    # Create output subfolder
    output_basepath = os.path.join(output_dir, name)
    if not os.path.isdir(output_basepath):
        os.mkdir(output_basepath)
    output_basepath = os.path.join(output_basepath, name)

    if pdbname:
        # Make sure to only use one entry if there are mutliple
        if ' ' in pdbname:
            pdbnames = pdbname.split(' ')
            print("Splitting '%s' into first entry only: '%s'" % (pdbname, pdbnames[0]))
            pdbname = pdbnames[0]

        # Retrieve PDB (for atom names)
        url = 'http://ligand-expo.rcsb.org/reports/%s/%s/%s_model.pdb' % (pdbname[0], pdbname, pdbname)
        pdb_filename = output_basepath + '-input.pdb'
        retrieve_url(url, pdb_filename)
        pdb_molecule = read_molecule(pdb_filename)

        # Retrieve SDF (for everything else)
        url = 'http://ligand-expo.rcsb.org/reports/%s/%s/%s_model.sdf' % (pdbname[0], pdbname, pdbname)
        sdf_filename = output_basepath + '-input.sdf'
        retrieve_url(url, sdf_filename)
        sdf_molecule = read_molecule(sdf_filename)

        # Replace atom names in SDF
        for (sdf_atom, pdb_atom) in zip(sdf_molecule.GetAtoms(), pdb_molecule.GetAtoms()):
            sdf_atom.SetName(pdb_atom.GetName())
        # Assign Tripos atom types
        oechem.OETriposAtomTypeNames(sdf_molecule)
        oechem.OETriposBondTypeNames(sdf_molecule)

        oe_molecule = sdf_molecule

        # We already know the residue name
        residue_name = pdbname
    elif smiles:
        # Generate molecule geometry with OpenEye
        print("Generating molecule {}".format(name))
        oe_molecule = openeye.smiles_to_oemol(smiles)
        # Assign Tripos atom types
        oechem.OETriposAtomTypeNames(oe_molecule)
        oechem.OETriposBondTypeNames(oe_molecule)
        try:
            oe_molecule = openeye.get_charges(oe_molecule, keep_confs=1)
        except RuntimeError as e:
            traceback.print_exc()
            print("Skipping molecule " + name)
            return
        residue_name = re.sub('[^A-Za-z]+', '', name.upper())[:3]
    else:
        raise Exception('Must provide SMILES string or pdbname')

    # Save mol2 file, preserving atom names
    print("Running epik on molecule {}".format(name))
    mol2_file_path = output_basepath + '-input.mol2'
    write_mol2_preserving_atomnames(mol2_file_path, oe_molecule, residue_name)

    # Run epik on mol2 file
    mae_file_path = output_basepath + '-epik.mae'
    schrodinger.run_epik(mol2_file_path, mae_file_path, tautomerize=False,
                         max_structures=100, min_probability=np.exp(-MAX_ENERGY_PENALTY), ph=7.4)

    # Convert maestro file to sdf and mol2
    output_sdf_filename = output_basepath + '-epik.sdf'
    output_mol2_filename = output_basepath + '-epik.mol2'
    schrodinger.run_structconvert(mae_file_path, output_sdf_filename)
    schrodinger.run_structconvert(mae_file_path, output_mol2_filename)

    # Read SDF file.
    ifs_sdf = oechem.oemolistream()
    ifs_sdf.SetFormat(oechem.OEFormat_SDF)
    ifs_sdf.open(output_sdf_filename)
    sdf_molecule = oechem.OEGraphMol()

    # Read MOL2 file.
    ifs_mol2 = oechem.oemolistream()
    ifs_mol2.open(output_mol2_filename)
    mol2_molecule = oechem.OEMol()

    # Assign charges.
    charged_molecules = list()
    index = 0
    while oechem.OEReadMolecule(ifs_sdf, sdf_molecule):
        oechem.OEReadMolecule(ifs_mol2, mol2_molecule)

        index += 1
        print("Charging molecule %d" % (index))
        try:
            # Charge molecule.
            charged_molecule = openeye.get_charges(mol2_molecule, max_confs=800, strictStereo=False, normalize=True, keep_confs=None)
            # Assign Tripos types
            oechem.OETriposAtomTypeNames(charged_molecule)
            oechem.OETriposBondTypeNames(charged_molecule)
            # Store tags.
            oechem.OECopySDData(charged_molecule, sdf_molecule)
            # Store molecule
            charged_molecules.append(charged_molecule)
        except Exception as e:
            print(e)
            print("Skipping protomer/tautomer because of failed charging.")

    # Clean up
    ifs_sdf.close()
    ifs_mol2.close()

    # Write state penalites.
    outfile = open(output_basepath + '-state-penalties.out', 'w')
    for (index, charged_molecule) in enumerate(charged_molecules):
        # Get Epik data.
        epik_Ionization_Penalty = float(oechem.OEGetSDData(charged_molecule, "r_epik_Ionization_Penalty"))
        epik_Ionization_Penalty_Charging = float(oechem.OEGetSDData(charged_molecule, "r_epik_Ionization_Penalty_Charging"))
        epik_Ionization_Penalty_Neutral = float(oechem.OEGetSDData(charged_molecule, "r_epik_Ionization_Penalty_Neutral"))
        epik_State_Penalty = float(oechem.OEGetSDData(charged_molecule, "r_epik_State_Penalty"))
        epik_Tot_Q = int(oechem.OEGetSDData(charged_molecule, "i_epik_Tot_Q"))

        outfile.write('%16.8f\n' % epik_State_Penalty)
    outfile.close()

    # Write as PDB
    charged_pdb_filename = output_basepath + '-epik-charged.pdb'
    ofs = oechem.oemolostream(charged_pdb_filename)
    flavor = oechem.OEOFlavor_PDB_CurrentResidues | oechem.OEOFlavor_PDB_ELEMENT | oechem.OEOFlavor_PDB_BONDS | oechem.OEOFlavor_PDB_HETBONDS | oechem.OEOFlavor_PDB_BOTH
    ofs.SetFlavor(oechem.OEFormat_PDB, flavor)
    for (index, charged_molecule) in enumerate(charged_molecules):
        # Fix residue names
        for atom in charged_molecule.GetAtoms():
            residue = oechem.OEAtomGetResidue(atom)
            residue.SetName(residue_name)
            oechem.OEAtomSetResidue(atom, residue)

        #oechem.OEWritePDBFile(ofs, charged_molecule, flavor)
        oechem.OEWriteMolecule(ofs, charged_molecule)
    ofs.close()

    # Write molecules as mol2.
    charged_mol2_filename = output_basepath + '-epik-charged.mol2'
    write_mol2_preserving_atomnames(charged_mol2_filename, charged_molecules, residue_name)
示例#10
0
    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
示例#11
0
    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
示例#12
0
def openmmTop_to_oemol(topology, positions, verbose=False):
    """
    This function converts an OpenMM topology in an OEMol

    Parameters:
    -----------
    topology : OpenMM Topology
        The OpenMM topology
    positions : OpenMM Quantity
        The molecule atom positions associated with the
        topology

    Return:
    -------
    oe_mol : OEMol
        The generated OEMol molecule
    """

    # Create an empty OEMol
    oe_mol = oechem.OEMol()

    # Mapping dictionary between openmm atoms and oe atoms
    openmm_atom_to_oe_atom = {}

    # Python set used to identify atoms that are not in protein residues
    keep = set(proteinResidues).union(dnaResidues).union(rnaResidues)

    for chain in topology.chains():
        for res in chain.residues():
            # Create an OEResidue
            oe_res = oechem.OEResidue()
            # Set OEResidue name
            oe_res.SetName(res.name)
            # If the atom is not a protein atom then set its heteroatom
            # flag to True
            if res.name not in keep:
                oe_res.SetFragmentNumber(chain.index + 1)
                oe_res.SetHetAtom(True)
            # Set OEResidue Chain ID
            oe_res.SetChainID(chain.id)
            # res_idx = int(res.id) - chain.index * len(chain._residues)
            # Set OEResidue number
            oe_res.SetResidueNumber(int(res.id))

            for openmm_at in res.atoms():
                # Create an OEAtom  based on the atomic number
                oe_atom = oe_mol.NewAtom(openmm_at.element._atomic_number)
                # Set atom name
                oe_atom.SetName(openmm_at.name)
                # Set Symbol
                oe_atom.SetType(openmm_at.element.symbol)
                # Set Atom index
                oe_res.SetSerialNumber(openmm_at.index + 1)
                # Commit the changes
                oechem.OEAtomSetResidue(oe_atom, oe_res)
                # Update the dictionary OpenMM to OE
                openmm_atom_to_oe_atom[openmm_at] = oe_atom

    if topology.getNumAtoms() != oe_mol.NumAtoms():
        raise ValueError(
            "OpenMM topology and OEMol number of atoms mismatching: "
            "OpenMM = {} vs OEMol  = {}".format(topology.getNumAtoms(),
                                                oe_mol.NumAtoms()))

    # Count the number of bonds in the openmm topology
    omm_bond_count = 0

    # Create the bonds
    for omm_bond in topology.bonds():

        omm_bond_count += 1

        at0 = omm_bond[0]
        at1 = omm_bond[1]

        oe_bond_order = omm_bond.order

        # If bond order info are not present set the bond order temporary to one
        if not omm_bond.order:
            oe_bond_order = 1

        # OE atoms
        oe_atom0 = openmm_atom_to_oe_atom[at0]
        oe_atom1 = openmm_atom_to_oe_atom[at1]

        # Create the bond
        oe_bond = oe_mol.NewBond(oe_atom0, oe_atom1, oe_bond_order)

        if omm_bond.type:
            if omm_bond.type == 'Aromatic':
                oe_atom0.SetAromatic(True)
                oe_atom1.SetAromatic(True)
                oe_bond.SetAromatic(True)
                oe_bond.SetType("Aromatic")
            elif omm_bond.type in ["Single", "Double", "Triple", "Amide"]:
                oe_bond.SetType(omm_bond.type)
            else:
                oe_bond.SetType("")

    if omm_bond_count != oe_mol.NumBonds():
        raise ValueError(
            "OpenMM topology and OEMol number of bonds mismatching: "
            "OpenMM = {} vs OEMol  = {}".format(omm_bond_count,
                                                oe_mol.NumBonds()))

    # Set the OEMol positions
    pos = positions.in_units_of(unit.angstrom) / unit.angstrom
    pos = list(itertools.chain.from_iterable(pos))
    oe_mol.SetCoords(pos)
    oechem.OESetDimensionFromCoords(oe_mol)

    return oe_mol
示例#13
0
def enumerate_conformations(name,
                            pdbfile=None,
                            smiles=None,
                            pdbname=None,
                            pH=7.4):
    """Run Epik to get protonation states using PDB residue templates for naming.

    Parameters
    ----------
    name : str
       Common name of molecule (used to create subdirectory)
    smiles : str
       Isomeric SMILES string
    pdbname : str
       Three-letter PDB code (e.g. 'DB8')
    """
    # Create output subfolder
    # output_basepath = os.path.join(output_dir, name)
    # if not os.path.isdir(output_basepath):
    #     os.mkdir(output_basepath)
    # output_basepath = os.path.join(output_basepath, name)

    oehandler = openeye.oechem.OEThrow
    # String stream output
    oss = oechem.oeosstream()
    oehandler.SetOutputStream(oss)
    log = "New run:\nPDB code: {pdbname}; Molecule: {name}; pH {pH}\n".format(
        **locals())
    success_status = True

    if pdbname:
        # Make sure to only use one entry if there are multiple
        if ' ' in pdbname:
            pdbnames = pdbname.split(' ')
            log += "Splitting '%s' into first entry only: '%s'" % (pdbname,
                                                                   pdbnames[0])
            pdbname = pdbnames[0]

        # Retrieve PDB (for atom names)
        url = 'http://ligand-expo.rcsb.org/reports/%s/%s/%s_model.pdb' % (
            pdbname[0], pdbname, pdbname)
        pdb_filename = name + '-rcsb_download.pdb'
        log += "Retrieving PDB structure from RCSB ligand expo: {}.\n".format(
            pdb_filename)
        retrieve_url(url, pdb_filename)
        log += "Parsing PDB file.\n"
        pdb_molecule = read_molecule(pdb_filename)

        # Retrieve SDF (for everything else)
        url = 'http://ligand-expo.rcsb.org/reports/%s/%s/%s_model.sdf' % (
            pdbname[0], pdbname, pdbname)
        sdf_filename = name + '-rcsb_download.sdf'
        log += "Retrieving SDF structure from RCSB ligand expo: {}.\n".format(
            sdf_filename)
        retrieve_url(url, sdf_filename)
        log += "Parsing SDF file.\n"
        sdf_molecule = read_molecule(sdf_filename)

        # Replace atom names in SDF
        log += "Canonicalizing atom names.\n"
        for (sdf_atom, pdb_atom) in zip(sdf_molecule.GetAtoms(),
                                        pdb_molecule.GetAtoms()):
            sdf_atom.SetName(pdb_atom.GetName())
        # Assign Tripos atom types
        log += "Assign atom type names.\n"
        oechem.OETriposAtomTypeNames(sdf_molecule)
        oechem.OETriposBondTypeNames(sdf_molecule)

        oe_molecule = sdf_molecule

        # We already know the residue name
        residue_name = pdbname

    # For the moment, disabling these two types of input
    # elif smiles:
    #     # Generate molecule geometry with OpenEye
    #     logging.info(("Generating molecule {}".format(name)))
    #     oe_molecule = openeye.smiles_to_oemol(smiles)
    #     # Assign Tripos atom types
    #     oechem.OETriposAtomTypeNames(oe_molecule)
    #     oechem.OETriposBondTypeNames(oe_molecule)
    #     try:
    #         logging.info("Charging initial")
    #         write_mol2_preserving_atomnames(name + '-debug.mol2', oe_molecule, 'debug')
    #         oe_molecule = openeye.get_charges(oe_molecule, keep_confs=1)
    #     except RuntimeError as e:
    #         traceback.print_exc()
    #         logging.info(("Skipping molecule " + name))
    #         return
    #     residue_name = re.sub('[^A-Za-z]+', '', name.upper())[:3]
    #     logging.info("resname = %s", residue_name)
    #     oe_molecule.SetTitle(residue_name) # fix iupac name issue with mol2convert
    # elif pdbfile:
    #     residue_name = re.sub('[^A-Za-z]+', '', name.upper())[:3]
    #     logging.info("Loading molecule molecule {0} from {1}".format(name, pdbfile))
    #     oe_molecule = read_molecule(pdbfile)
    #     # Assign Tripos atom types
    #     oechem.OETriposAtomTypeNames(oe_molecule)
    #     oechem.OETriposBondTypeNames(oe_molecule)
    #     try:
    #         logging.info("Charging initial")
    #         write_mol2_preserving_atomnames(name + '-debug.mol2', oe_molecule, 'debug')
    #         oe_molecule = openeye.get_charges(oe_molecule, keep_confs=1)
    #     except RuntimeError as e:
    #         traceback.print_exc()
    #         logging.info(("Skipping molecule " + name))
    #         return
    else:
        raise Exception('Must provide SMILES string or pdbname, or pdbfile')

    # Save mol2 file, preserving atom names
    log += "Running Epik.\n"
    mol2_file_path = name + '-before_epik.mol2'
    write_mol2_preserving_atomnames(mol2_file_path, oe_molecule, residue_name)

    # Run epik on mol2 file
    mae_file_path = name + '-epik.mae'
    schrodinger.run_epik(mol2_file_path,
                         mae_file_path,
                         tautomerize=False,
                         max_structures=50,
                         min_probability=np.exp(-MAX_ENERGY_PENALTY),
                         ph=pH)

    log += "Epik run completed.\n"
    # Convert maestro file to sdf and mol2
    output_sdf_filename = name + '-after_epik.sdf'
    output_mol2_filename = name + '-after_epik.mol2'
    # logging.info("Creating sdf")
    schrodinger.run_structconvert(mae_file_path, output_sdf_filename)
    # logging.info("Creating mol2")
    schrodinger.run_structconvert(mae_file_path, output_mol2_filename)

    # Read SDF file.
    ifs_sdf = oechem.oemolistream()
    ifs_sdf.SetFormat(oechem.OEFormat_SDF)
    ifs_sdf.open(output_sdf_filename)
    sdf_molecule = oechem.OEGraphMol()

    # Read MOL2 file.
    ifs_mol2 = oechem.oemolistream()
    ifs_mol2.open(output_mol2_filename)
    mol2_molecule = oechem.OEMol()

    # Assign charges.

    # reset count of error handler
    oehandler.Clear()
    log += "Assigning charges to protonation states.\n"
    charged_molecules = list()
    index = 0
    failed_states = set()
    while oechem.OEReadMolecule(ifs_sdf, sdf_molecule):
        oechem.OEReadMolecule(ifs_mol2, mol2_molecule)

        index += 1
        log += "State {0:d}\n".format(index)
        try:
            # Charge molecule.
            charged_molecule_conformers = omtoe.get_charges(mol2_molecule,
                                                            max_confs=800,
                                                            strictStereo=False,
                                                            normalize=True,
                                                            keep_confs=-1)

            log += "Charging stage output:\n"
            OEOutput = str(oss)
            log += OEOutput
            log += "\nCharging state completed.\n"

            # Restore coordinates to original
            charged_molecule = select_conformers(charged_molecule_conformers,
                                                 mol2_molecule,
                                                 keep_confs=None)

            # Assign Tripos types
            oechem.OETriposAtomTypeNames(charged_molecule)
            oechem.OETriposBondTypeNames(charged_molecule)
            # Store tags.
            oechem.OECopySDData(charged_molecule, sdf_molecule)
            # Store molecule
            charged_molecules.append(charged_molecule)

            # Check for failure in the log
            openeye_charge_log_parser(OEOutput, True)

            oehandler.Clear()

        except Exception as e:
            failed_states.add(index)
            logging.info(e)
            log += "State failed charging.\n"
            log += str(e)
            log += "\n"

            filename_failure = name + '-conformers-failed-state-{}-.mol2'.format(
                index)
            try:
                write_mol2_preserving_atomnames(filename_failure,
                                                charged_molecule_conformers,
                                                residue_name)
            except:
                log += "Could not store result, most likely failed during Omega step!\n"

            success_status = False
            oehandler.Clear()

    # Clean up
    ifs_sdf.close()
    ifs_mol2.close()

    # Write state penalties.
    outfile = open(name + '-state-penalties.out', 'w')
    for (index, charged_molecule) in enumerate(charged_molecules):
        # Get Epik data.
        log += "Writing Epik data for state {:d}\n".format(index + 1)
        epik_Ionization_Penalty = float(
            oechem.OEGetSDData(charged_molecule, "r_epik_Ionization_Penalty"))
        epik_Ionization_Penalty_Charging = float(
            oechem.OEGetSDData(charged_molecule,
                               "r_epik_Ionization_Penalty_Charging"))
        epik_Ionization_Penalty_Neutral = float(
            oechem.OEGetSDData(charged_molecule,
                               "r_epik_Ionization_Penalty_Neutral"))
        epik_State_Penalty = float(
            oechem.OEGetSDData(charged_molecule, "r_epik_State_Penalty"))
        epik_Tot_Q = int(oechem.OEGetSDData(charged_molecule, "i_epik_Tot_Q"))

        outfile.write('%16.8f\n' % epik_State_Penalty)
    outfile.close()

    # Write as PDB
    charged_pdb_filename = name + '-charged_output.pdb'
    ofs = oechem.oemolostream(charged_pdb_filename)
    flavor = oechem.OEOFlavor_PDB_CurrentResidues | oechem.OEOFlavor_PDB_ELEMENT | oechem.OEOFlavor_PDB_BONDS | oechem.OEOFlavor_PDB_HETBONDS | oechem.OEOFlavor_PDB_BOTH
    ofs.SetFlavor(oechem.OEFormat_PDB, flavor)
    for (index, charged_molecule) in enumerate(charged_molecules):
        # Fix residue names
        for atom in charged_molecule.GetAtoms():
            residue = oechem.OEAtomGetResidue(atom)
            residue.SetName(residue_name)
            oechem.OEAtomSetResidue(atom, residue)
        oechem.OEWriteMolecule(ofs, charged_molecule)
    ofs.close()

    # Write molecules as mol2.
    charged_mol2_filename = name + '-charged_output.mol2'
    write_mol2_preserving_atomnames(charged_mol2_filename, charged_molecules,
                                    residue_name)
    log += "Run completed.\n"
    if success_status:
        log += "Status: Success\n"
    else:
        log += "Status: Failure\n"
        log += "Failed states: {}\n".format(" ".join(
            [str(state) for state in sorted(list(failed_states))]))

    with open("log.txt", 'w') as logfile:
        logfile.write(log)

    return log, success_status
示例#14
0
def pack_box(molecules,
             n_copies,
             tolerance=2.0,
             box_size=None,
             mass_density=None,
             verbose=False):
    """Run packmol to generate a box containing a mixture of molecules.

    Parameters
    ----------
    molecules : list of OEMol
        Molecules in the system (with 3D geometries)
    n_copies : list of int (same length as 'molecules')
        Number of copies of the molecules
    tolerance : float, optional, default=2.0
        The mininum spacing between molecules during packing.  In ANGSTROMS!
    box_size : simtk.unit.Quantity in units compatible with angstroms
        The size of the box to generate.
        Default generates boxes that are very large for increased stability.
        May require extra time for energy minimization and equilibration.
    mass_density : simtk.unit.Quantity with units compatible with grams/milliliters, optional, default = 1.0*grams/milliliters
        Target mass density for final system, if available.
    verbose : bool, optional, default=False
        If True, verbose output is written.

    Returns
    -------
    topology : simtk.openmm.Topology
        Topology of the resulting system
    positions : simtk.unit.Quantity wrapped [natoms,3] numpy array with units compatible with angstroms
        Single frame trajectory with mixture box.

    """
    assert len(molecules) == len(
        n_copies), "Length of 'molecules' and 'n_copies' must be identical"

    # Create PDB files for all components
    pdb_filenames = list()
    pdb_flavor = oechem.OEOFlavor_PDB_CurrentResidues | oechem.OEOFlavor_PDB_ELEMENT | oechem.OEOFlavor_PDB_BONDS | oechem.OEOFlavor_PDB_HETBONDS | oechem.OEOFlavor_PDB_BOTH
    for molecule in molecules:
        tmp_filename = tempfile.mktemp(suffix=".pdb")
        pdb_filenames.append(tmp_filename)
        # Write PDB file
        mol_copy = copy.deepcopy(molecule)
        ofs = oechem.oemolostream(tmp_filename)
        ofs.SetFlavor(oechem.OEFormat_PDB, pdb_flavor)
        # Fix residue names
        residue_name = "".join(
            [random.choice(string.ascii_uppercase) for i in range(3)])
        for atom in mol_copy.GetAtoms():
            residue = oechem.OEAtomGetResidue(atom)
            residue.SetName(residue_name)
            oechem.OEAtomSetResidue(atom, residue)
        oechem.OEWriteMolecule(ofs, mol_copy)
        ofs.close()

    # Run packmol
    PACKMOL_PATH = find_executable("packmol")
    if PACKMOL_PATH is None:
        raise (IOError("Packmol not found, cannot run pack_box()"))

    output_filename = tempfile.mktemp(suffix=".pdb")

    # Approximate volume to initialize box
    if (box_size is None):
        if (mass_density is not None):
            # Estimate box_size from mass density.
            box_size = approximate_volume_by_density(molecules,
                                                     n_copies,
                                                     mass_density=mass_density)
        else:
            # Use vdW radii to estimate box_size
            box_size = approximate_volume(molecules, n_copies)

    header = HEADER_TEMPLATE % (tolerance, output_filename)
    for (pdb_filename, molecule, count) in zip(pdb_filenames, molecules,
                                               n_copies):
        header = header + BOX_TEMPLATE % (
            pdb_filename, count, box_size / unit.angstroms,
            box_size / unit.angstroms, box_size / unit.angstroms)

    pwd = os.getcwd()

    if verbose: print(header)

    # Write packmol input
    packmol_filename = "packmol_input.txt"
    packmol_filename = tempfile.mktemp(suffix=".txt")
    file_handle = open(packmol_filename, 'w')
    file_handle.write(header)
    file_handle.close()

    os.system("%s < %s" % (PACKMOL_PATH, packmol_filename))

    # Read the resulting PDB file.
    pdbfile = app.PDBFile(output_filename)

    # Extract topology and positions
    topology = pdbfile.getTopology()
    positions = pdbfile.getPositions()

    return [topology, positions]
示例#15
0
def oesolvate(solute,
              density=1.0,
              padding_distance=10.0,
              distance_between_atoms=2.5,
              solvents='tip3p',
              molar_fractions='1.0',
              geometry='box',
              close_solvent=True,
              salt='[Na+], [Cl-]',
              salt_concentration=0.0,
              neutralize_solute=True,
              verbose=False,
              return_components=False,
              **kargs):
    """
    This function solvates the passed solute in a cubic box or a sphere by using Packmol. Packmol
    creates an initial point for molecular dynamics simulations by packing molecule in defined regions
    of space. For additional info:
    http://www.ime.unicamp.br/~martinez/packmol/home.shtml

    The geometry volume is estimated by the using the padding parameter and the solute size.
    The number of solvent molecules is calculated by using the specified density and volume.
    Solvent molecules are specified as comma separated smiles strings. The molar fractions
    of each solvent molecule are specified in a similar fashion. By default if the solute is
    charged counter ions are added to neutralize it

    Parameters:
    -----------
    solute: OEMol molecule
        The solute to solvate
    density: float
        The solution density in g/ml
    padding_distance: float
        The largest dimension of the solute (along the x, y, or z axis) is determined (in A), 
        and a cubic box of size (largest dimension)+2*padding is used
    distance_between_atoms: float
        The minimum distance between atoms in A
    solvents: python string
        A comma separated smiles string or keywords for the solvent molecules.
        Special water models can be selected by using the keywords:
        tip3p for TIP3P water model geometry
    molar_fractions: python string
        A comma separated molar fraction string of the solvent molecules
    close_solvent: boolean
        If True solvent molecules will be placed very close to the solute
    salt: python string
        A comma separated string of the dissociated salt in solution
    salt_concentration: float
        Salt concentration in millimolar
    neutralize_solute: boolean
        If True counter-ions will be added to the solution to neutralize the solute
    verbose: Bool
        If True verbose mode is enabled
    return_components: Bool
        If True the added solvent molecules are also returned as OEMol

    Return:
    -------
    oe_mol: OEMol
        The solvated system. If the selected geometry is a box a SD tag with
        name 'box_vector' is attached the output molecule containing
        the system box vectors.
    oe_mol_components: OEMol
        If the return_components flag is True the added solvent molecules are
        returned as an additional OEMol
    """
    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

    if shutil.which("packmol") is None:
        raise (IOError("Packmol executable not found"))

    # Extract solvent smiles strings and mole fractions
    solvents = [sm.strip() for sm in solvents.split(',')]
    fractions = [float(mf) for mf in molar_fractions.split(',')]

    # If the smiles string and mole fractions lists have different lengths raise an error
    if len(solvents) != len(fractions):
        raise ValueError(
            "Selected solvent number and selected molar fraction number mismatch: {} vs {}"
            .format(len(solvents), len(fractions)))

    # Remove smiles string with 0.0 mole fraction
    solvent_smiles = [
        solvents[i] for i, v in enumerate(fractions) if fractions[i]
    ]
    mol_fractions = [mf for mf in fractions if mf]

    # Mole fractions are non-negative numbers
    if any([v < 0.0 for v in mol_fractions]):
        raise ValueError("Error: Mole fractions are non-negative real numbers")

    # Mole fractions must sum up to 1.0
    if abs(sum(mol_fractions) - 1.0) > 0.001:
        oechem.OEThrow.Error("Error: Mole fractions do not sum up to 1.0")

    if geometry not in ['box', 'sphere']:
        raise ValueError(
            "Error geometry: the supported geometries are box and sphere not {}"
            .format(geometry))

    # Set Units
    density = density * unit.grams / unit.milliliter
    padding_distance = padding_distance * unit.angstrom
    salt_concentration = salt_concentration * unit.millimolar

    # Calculate the Solute Bounding Box
    BB_solute = BoundingBox(solute)

    # Estimate of the box cube length
    box_edge = 2.0 * padding_distance + np.max(BB_solute[1] -
                                               BB_solute[0]) * unit.angstrom

    if geometry == 'box':
        # Box Volume
        Volume = box_edge**3
    if geometry == 'sphere':
        Volume = (4.0 / 3.0) * 3.14159265 * (0.5 * box_edge)**3

    # Omega engine is used to generate conformations
    omegaOpts = oeomega.OEOmegaOptions()
    omegaOpts.SetMaxConfs(1)
    omegaOpts.SetStrictStereo(False)
    omega = oeomega.OEOmega(omegaOpts)

    # Create a string code to identify the solute residues. The code ID used is based
    # on the residue number id, the residue name and the chain id:
    # id+resname+chainID
    hv_solute = oechem.OEHierView(
        solute,
        oechem.OEAssumption_BondedResidue + oechem.OEAssumption_ResPerceived)
    solute_resid_list = []
    for chain in hv_solute.GetChains():
        for frag in chain.GetFragments():
            for hres in frag.GetResidues():
                oe_res = hres.GetOEResidue()
                solute_resid_list.append(
                    str(oe_res.GetResidueNumber()) + oe_res.GetName() +
                    chain.GetChainID())

    # Solvent component list_names
    solvent_resid_dic_names = dict()

    # Neutralize solute
    ion_sum_wgt_n_ions = 0.0 * unit.grams / unit.mole
    if neutralize_solute:
        # Container for the counter-ions
        oe_ions = []
        # Container for the ion smiles strings
        ions_smiles = []
        solute_formal_charge = 0
        for at in solute.GetAtoms():
            solute_formal_charge += at.GetFormalCharge()
        if solute_formal_charge > 0:
            ions_smiles.append("[Cl-]")
        elif solute_formal_charge < 0:
            ions_smiles.append("[Na+]")
        else:
            pass

        # Total number of counter-ions to neutralize the solute
        n_ions = abs(solute_formal_charge)

        # print("Counter ions to add = {} of {}".format(n_ions, ions_smiles[0]))

        # Ions
        if n_ions >= 1:
            for sm in ions_smiles:
                mol = oechem.OEMol()
                if not oechem.OESmilesToMol(mol, sm):
                    raise ValueError(
                        "Error counter ions: SMILES string parsing fails for the string: {}"
                        .format(sm))

                # Generate conformer
                if not omega(mol):
                    raise ValueError(
                        "Error counter ions: Conformer generation fails for the molecule with "
                        "smiles string: {}".format(sm))

                oe_ions.append(mol)

                if sm == '[Na+]':
                    solvent_resid_dic_names[' NA'] = mol
                else:
                    solvent_resid_dic_names[' CL'] = mol

            ion_sum_wgt = 0.0 * unit.grams / unit.mole
            for ion in oe_ions:
                # Molecular weight
                ion_sum_wgt += oechem.OECalculateMolecularWeight(
                    ion) * unit.grams / unit.mole

            ion_sum_wgt_n_ions = ion_sum_wgt * n_ions

            # Create ions .pdb files
            ions_smiles_pdbs = []
            for i in range(0, len(ions_smiles)):
                pdb_name = os.path.basename(tempfile.mktemp(suffix='.pdb'))
                pdb_name = ions_smiles[i] + '_' + pdb_name
                ions_smiles_pdbs.append(pdb_name)

            for i in range(0, len(ions_smiles)):
                ofs = oechem.oemolostream(ions_smiles_pdbs[i])
                oechem.OEWriteConstMolecule(ofs, oe_ions[i])

    # Add salts to the solution

    # Solvent smiles string parsing
    char_set = string.ascii_uppercase
    salt_sum_wgt_n_salt = 0.0 * unit.grams / unit.mole
    if salt_concentration > 0.0 * unit.millimolar:

        salt_smiles = [sm.strip() for sm in salt.split(',')]

        # Container list of oemol salt molecules generated by using smiles strings
        oe_salt = []

        for sm in salt_smiles:
            mol_salt = oechem.OEMol()
            if not oechem.OESmilesToMol(mol_salt, sm):
                raise ValueError(
                    "Error salt: SMILES string parsing fails for the string: {}"
                    .format(sm))

            # Generate conformer
            if not omega(mol_salt):
                raise ValueError(
                    "Error salt: Conformer generation fails for the "
                    "molecule with smiles string: {}".format(sm))

            # Unique 3 code letter are set as solvent residue names
            solv_id = ''.join(random.sample(char_set * 3, 3))

            # Try to recognize the residue name
            oechem.OEPerceiveResidues(mol_salt)

            for atmol in mol_salt.GetAtoms():
                res = oechem.OEAtomGetResidue(atmol)
                if res.GetName() == 'UNL':
                    res.SetName(solv_id)
                    oechem.OEAtomSetResidue(atmol, res)
                    if solv_id not in solvent_resid_dic_names:
                        solvent_resid_dic_names[solv_id] = mol_salt
                else:
                    if res.GetName() not in solvent_resid_dic_names:
                        solvent_resid_dic_names[res.GetName()] = mol_salt
                    break

            oe_salt.append(mol_salt)

        n_salt = int(
            round(unit.AVOGADRO_CONSTANT_NA * salt_concentration *
                  Volume.in_units_of(unit.liter)))

        # for i in range(0, len(salt_smiles)):
        #     print("Number of molecules for the salt component {} = {}".format(salt_smiles[i], n_salt))

        salt_sum_wgt = 0.0 * unit.grams / unit.mole
        for salt in oe_salt:
            # Molecular weight
            salt_sum_wgt += oechem.OECalculateMolecularWeight(
                salt) * unit.grams / unit.mole

        salt_sum_wgt_n_salt = salt_sum_wgt * n_salt

        # Create salt .pdb files
        if n_salt >= 1:
            salt_pdbs = []
            for i in range(0, len(salt_smiles)):
                pdb_name = os.path.basename(tempfile.mktemp(suffix='.pdb'))
                # pdb_name = salt_smiles[i] + '_' + pdb_name
                salt_pdbs.append(pdb_name)

            for i in range(0, len(salt_smiles)):
                ofs = oechem.oemolostream(salt_pdbs[i])
                oechem.OEWriteConstMolecule(ofs, oe_salt[i])

    # Container list of oemol solvent molecules generated by using smiles strings
    oe_solvents = []

    for sm in solvent_smiles:

        if sm == 'tip3p':
            tip3p_fn = os.path.join(PACKAGE_DIR, 'oeommtools', 'data',
                                    'tip3p.pdb')
            ifs = oechem.oemolistream(tip3p_fn)
            mol_sol = oechem.OEMol()

            if not oechem.OEReadMolecule(ifs, mol_sol):
                raise IOError(
                    "It was not possible to read the tip3p molecule file")
        else:

            mol_sol = oechem.OEMol()

            if not oechem.OESmilesToMol(mol_sol, sm):
                raise ValueError(
                    "Error solvent: SMILES string parsing fails for the string: {}"
                    .format(sm))

            # Generate conformer
            if not omega(mol_sol):
                raise ValueError(
                    "Error solvent: Conformer generation fails for "
                    "the molecule with smiles string: {}".format(sm))

        # Unique 3 code letter are set as solvent residue names
        solv_id = ''.join(random.sample(char_set * 3, 3))

        # Try to recognize the residue name
        oechem.OEPerceiveResidues(mol_sol)

        for atmol in mol_sol.GetAtoms():
            res = oechem.OEAtomGetResidue(atmol)
            if res.GetName() == 'UNL':
                res.SetName(solv_id)
                oechem.OEAtomSetResidue(atmol, res)
                if solv_id not in solvent_resid_dic_names:
                    solvent_resid_dic_names[solv_id] = mol_sol
            else:
                if res.GetName() not in solvent_resid_dic_names:
                    solvent_resid_dic_names[res.GetName()] = mol_sol
                break

        oe_solvents.append(mol_sol)

    # Sum of the solvent molecular weights
    solvent_sum_wgt_frac = 0.0 * unit.grams / unit.mole

    for idx in range(0, len(oe_solvents)):
        # Molecular weight
        wgt = oechem.OECalculateMolecularWeight(
            oe_solvents[idx]) * unit.grams / unit.mole
        solvent_sum_wgt_frac += wgt * mol_fractions[idx]

    # Solute molecular weight
    solute_wgt = oechem.OECalculateMolecularWeight(
        solute) * unit.gram / unit.mole

    # Estimate of the number of each molecular species present in the solution accordingly
    # to their molar fraction fi:
    #
    # ni = fi*(density*volume*NA - wgt_solute - sum_k(wgt_salt_k*nk) - wgt_ion*n_ion)/sum_j(wgt_nj * fj)
    #
    # where ni is the number of molecule of specie i, density the mixture density, volume the
    # mixture volume, wgt_solute the molecular weight of the solute, wgt_salt_k the molecular
    # weight of the salt component k, nk the number of molecule of salt component k, wgt_ion
    # the counter ion molecular weight, n_ions the number of counter ions and wgt_nj the molecular
    # weight of the molecule specie j with molar fraction fj

    div = (unit.AVOGADRO_CONSTANT_NA * density * Volume -
           (solute_wgt + salt_sum_wgt_n_salt +
            ion_sum_wgt_n_ions)) / solvent_sum_wgt_frac

    # Solvent number of monomers
    n_monomers = [int(round(mf * div)) for mf in mol_fractions]

    if not all([nm > 0 for nm in n_monomers]):
        raise ValueError(
            "Error negative number of solvent components: the density could be too low"
        )

    # for i in range(0, len(solvent_smiles)):
    #     print("Number of molecules for the component {} = {}".format(solvent_smiles[i], n_monomers[i]))

    # Packmol Configuration file setting
    if close_solvent:
        header_template = """\n# Mixture\ntolerance {}\nfiletype pdb\noutput {}\nadd_amber_ter\navoid_overlap no"""
    else:
        header_template = """\n# Mixture\ntolerance {}\nfiletype pdb\noutput {}\nadd_amber_ter\navoid_overlap yes"""

    # Templates strings
    solute_template = """\n\n# Solute\nstructure {}\nnumber 1\nfixed 0. 0. 0. 0. 0. 0.\nresnumbers 1\nend structure"""

    if geometry == 'box':
        solvent_template = """\nstructure {}\nnumber {}\ninside box {:0.3f} {:0.3f} {:0.3f} {:0.3f} {:0.3f} {:0.3f}\
        \nchain !\nresnumbers 3\nend structure"""
    if geometry == 'sphere':
        solvent_template = """\nstructure {}\nnumber {}\ninside sphere {:0.3f} {:0.3f} {:0.3f} {:0.3f}\
        \nchain !\nresnumbers 3\nend structure"""

    # Create solvents .pdb files
    solvent_pdbs = []
    for i in range(0, len(solvent_smiles)):
        pdb_name = os.path.basename(tempfile.mktemp(suffix='.pdb'))
        solvent_pdbs.append(pdb_name)

    for i in range(0, len(solvent_smiles)):
        ofs = oechem.oemolostream(solvent_pdbs[i])
        oechem.OEWriteConstMolecule(ofs, oe_solvents[i])

    solute_pdb = 'solute' + '_' + os.path.basename(
        tempfile.mktemp(suffix='.pdb'))
    ofs = oechem.oemolostream(solute_pdb)

    if solute.GetMaxConfIdx() > 1:
        raise ValueError("Solutes with multiple conformers are not supported")
    else:
        oechem.OEWriteConstMolecule(ofs, solute)

    # Write Packmol header section
    mixture_pdb = 'mixture' + '_' + os.path.basename(
        tempfile.mktemp(suffix='.pdb'))
    body = header_template.format(distance_between_atoms, mixture_pdb)
    # Write Packmol configuration file solute section
    body += solute_template.format(solute_pdb)

    # The solute is centered inside the box
    xc = (BB_solute[0][0] + BB_solute[1][0]) / 2.
    yc = (BB_solute[0][1] + BB_solute[1][1]) / 2.
    zc = (BB_solute[0][2] + BB_solute[1][2]) / 2.

    # Correct for periodic box conditions to avoid
    # steric clashes at the box edges
    pbc_correction = 1.0 * unit.angstrom

    xmin = xc - ((box_edge - pbc_correction) / 2.) / unit.angstrom
    xmax = xc + ((box_edge - pbc_correction) / 2.) / unit.angstrom
    ymin = yc - ((box_edge - pbc_correction) / 2.) / unit.angstrom
    ymax = yc + ((box_edge - pbc_correction) / 2.) / unit.angstrom
    zmin = zc - ((box_edge - pbc_correction) / 2.) / unit.angstrom
    zmax = zc + ((box_edge - pbc_correction) / 2.) / unit.angstrom

    # Packmol setting for the solvent section
    body += '\n\n# Solvent'
    for i in range(0, len(solvent_smiles)):
        if geometry == 'box':
            body += solvent_template.format(solvent_pdbs[i], n_monomers[i],
                                            xmin, ymin, zmin, xmax, ymax, zmax)
        if geometry == 'sphere':
            body += solvent_template.format(solvent_pdbs[i], n_monomers[i], xc,
                                            yc, zc,
                                            0.5 * box_edge / unit.angstrom)

    # Packmol setting for the salt section
    if salt_concentration > 0.0 * unit.millimolar and n_salt >= 1:
        body += '\n\n# Salt'
        for i in range(0, len(salt_smiles)):
            if geometry == 'box':
                body += solvent_template.format(salt_pdbs[i],
                                                int(round(n_salt)), xmin, ymin,
                                                zmin, xmax, ymax, zmax)
            if geometry == 'sphere':
                body += solvent_template.format(salt_pdbs[i],
                                                int(round(n_salt)), xc, yc, zc,
                                                0.5 * box_edge / unit.angstrom)

    # Packmol setting for the ions section
    if neutralize_solute and n_ions >= 1:
        body += '\n\n# Counter Ions'
        for i in range(0, len(ions_smiles)):
            if geometry == 'box':
                body += solvent_template.format(ions_smiles_pdbs[i], n_ions,
                                                xmin, ymin, zmin, xmax, ymax,
                                                zmax)
            if geometry == 'sphere':
                body += solvent_template.format(ions_smiles_pdbs[i], n_ions,
                                                xc, yc, zc,
                                                0.5 * box_edge / unit.angstrom)

    # Packmol configuration file
    packmol_filename = os.path.basename(tempfile.mktemp(suffix='.inp'))

    with open(packmol_filename, 'w') as file_handle:
        file_handle.write(body)

    # Call Packmol
    if not verbose:
        mute_output = open(os.devnull, 'w')
        with open(packmol_filename, 'r') as file_handle:
            subprocess.check_call(['packmol'],
                                  stdin=file_handle,
                                  stdout=mute_output,
                                  stderr=mute_output)
    else:
        with open(packmol_filename, 'r') as file_handle:
            subprocess.check_call(['packmol'], stdin=file_handle)

    # Read in the Packmol solvated system
    solvated = oechem.OEMol()

    if os.path.exists(mixture_pdb + '_FORCED'):
        os.rename(mixture_pdb + '_FORCED', mixture_pdb)
        print("Warning: Packing solution is not optimal")

    ifs = oechem.oemolistream(mixture_pdb)
    oechem.OEReadMolecule(ifs, solvated)

    # To avoid to change the user oemol starting solute by reading in
    # the generated mixture pdb file and loosing molecule info, the
    # solvent molecules are extracted from the mixture system and
    # added back to the starting solute

    # Extract from the solution system the solvent molecules
    # by checking the previous solute generated ID: id+resname+chainID
    hv_solvated = oechem.OEHierView(
        solvated,
        oechem.OEAssumption_BondedResidue + oechem.OEAssumption_ResPerceived)

    # This molecule will hold the solvent molecules generated directly from
    # the omega conformers. This is useful to avoid problems related to read in
    # the solvent molecules from pdb files and triggering unwanted perceiving actions
    new_components = oechem.OEMol()

    bv = oechem.OEBitVector(solvated.GetMaxAtomIdx())
    for chain in hv_solvated.GetChains():
        for frag in chain.GetFragments():
            for hres in frag.GetResidues():
                oe_res = hres.GetOEResidue()
                if str(oe_res.GetResidueNumber()) + oe_res.GetName(
                ) + chain.GetChainID() not in solute_resid_list:
                    oechem.OEAddMols(new_components,
                                     solvent_resid_dic_names[oe_res.GetName()])
                    atms = hres.GetAtoms()
                    for at in atms:
                        bv.SetBitOn(at.GetIdx())

    pred = oechem.OEAtomIdxSelected(bv)
    components = oechem.OEMol()
    oechem.OESubsetMol(components, solvated, pred)

    new_components.SetCoords(components.GetCoords())

    # This is necessary otherwise just one big residue is created
    oechem.OEPerceiveResidues(new_components)

    # Add the solvent molecules to the solute copy
    solvated_system = solute.CreateCopy()
    oechem.OEAddMols(solvated_system, new_components)

    # Set Title
    solvated_system.SetTitle(solute.GetTitle())

    # Set ions resname to Na+ and Cl-
    for at in solvated_system.GetAtoms():
        res = oechem.OEAtomGetResidue(at)
        if res.GetName() == ' NA':
            res.SetName("Na+")
            oechem.OEAtomSetResidue(atmol, res)
        elif res.GetName() == ' CL':
            res.SetName("Cl-")
            oechem.OEAtomSetResidue(atmol, res)
        else:
            pass

    # Cleaning
    to_delete = solvent_pdbs + [packmol_filename, solute_pdb, mixture_pdb]

    if salt_concentration > 0.0 * unit.millimolar and n_salt >= 1:
        to_delete += salt_pdbs
    if neutralize_solute and n_ions >= 1:
        to_delete += ions_smiles_pdbs

    for fn in to_delete:
        try:
            os.remove(fn)
        except:
            pass

    # Calculate the solution total density
    total_wgt = oechem.OECalculateMolecularWeight(
        solvated_system) * unit.gram / unit.mole
    density_mix = (1 / unit.AVOGADRO_CONSTANT_NA) * total_wgt / Volume
    print("Computed Solution Density = {}".format(
        density_mix.in_units_of(unit.gram / unit.milliliter)))
    # Threshold checking
    ths = 0.1 * unit.gram / unit.milliliter
    if not abs(density -
               density_mix.in_units_of(unit.gram / unit.milliliter)) < ths:
        raise ValueError(
            "Error: the computed density for the solute {} does not match the selected density {} vs {}"
            .format(solute.GetTitle(), density_mix, density))

    if geometry == 'box':
        # Define the box vector and attached it as SD tag to the solvated system
        # with ID tag: 'box_vectors'
        box_vectors = (Vec3(box_edge / unit.angstrom, 0.0,
                            0.0), Vec3(0.0, box_edge / unit.angstrom, 0.0),
                       Vec3(0.0, 0.0,
                            box_edge / unit.angstrom)) * unit.angstrom

        box_vectors = data_utils.encodePyObj(box_vectors)
        solvated_system.SetData(oechem.OEGetTag('box_vectors'), box_vectors)

    if return_components:
        new_components.SetTitle(solute.GetTitle() + '_solvent_comp')
        return solvated_system, new_components
    else:
        return solvated_system