Bioisostere Examples

The following table lists the currently available Bioisostere TK examples:

Program

Description

brood_database

Building Brood Database (CHOMP)

brood_query

Creating Brood Query

brood_hitlist

Generating Brood Hits

brood_matches

Generating Brood Matches

fragment_overlay

Overlay between Fragments

replace_fragment

Replacing a Fragment in a Molecule

cpddb

Building and Using an External Compound Database

brood_cluster

Clustering Brood Hits

2molLinker_query

Creating a Brood Query for Linking Two Molecules (Bridging)

brood_combo_builder

Building Combined Hits from Different Queries of the Same Molecule

brood_score_hitlist

Curating Scored Fragment Hits with OEScoreHitlist

brood_connection_mol_builder

Comparing Connection and Molecule Building Outcomes

Building Brood Database (CHOMP)

The following code example shows how to build a Brood database from a library of molecules.

This example demonstrates end-to-end fragment database construction, including fragment generation, optional filtering, and writing the final Brood database.

Listing 1: Building Brood Database (CHOMP)

#!/usr/bin/env python
# (C) 2022 Cadence Design Systems, Inc. (Cadence) 
# All rights reserved.
# TERMS FOR USE OF SAMPLE CODE The software below ("Sample Code") is
# provided to current licensees or subscribers of Cadence products or
# SaaS offerings (each a "Customer").
# Customer is hereby permitted to use, copy, and modify the Sample Code,
# subject to these terms. Cadence claims no rights to Customer's
# modifications. Modification of Sample Code is at Customer's sole and
# exclusive risk. Sample Code may require Customer to have a then
# current license or subscription to the applicable Cadence offering.
# THE SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED.  OPENEYE DISCLAIMS ALL WARRANTIES, INCLUDING, BUT
# NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
# PARTICULAR PURPOSE AND NONINFRINGEMENT. In no event shall Cadence be
# liable for any damages or liability in connection with the Sample Code
# or its use.


from openeye import oechem
from openeye import oebioisostere

class ChompOptions(oechem.OEOptions):
    def __init__(self):
        oechem.OEOptions.__init__(self, "ChompOptions")

        dbParam = oechem.OEStringParameter("-out")
        dbParam.SetVisibility(oechem.OEParamVisibility_Simple)
        dbParam.SetBrief("Output database folder name")
        dbParam.SetRequired(True)
        dbParam.SetKeyless(2)
        self._dbParam = self.AddParameter(dbParam)

        self._fragOpts = oebioisostere.ToFragmentOptions(self.AddOption(oebioisostere.OEFragmentOptions()))
        self._screenOpts = oebioisostere.ToDBScreenOptions(self.AddOption(oebioisostere.OEDBScreenOptions()))
        pass

    def CreateCopy(self):
        return self

    def GetDBName(self):
        return self._dbParam.GetStringValue()

    def GetFragOpts(self):
        return self._fragOpts

    def GetScreenOpts(self):
        return self._screenOpts


def main(argv=[__name__]):
    chompOpts = ChompOptions()
    opts = oechem.OESimpleAppOptions(chompOpts, "BroodDataBase", oechem.OEFileStringType_Mol)
    if oechem.OEConfigureOpts(opts, argv, False) == oechem.OEOptsConfigureStatus_Help:
        return 0
    chompOpts.UpdateValues(opts)

    ifs = oechem.oemolistream()
    if not ifs.open(opts.GetInFile()):
        oechem.OEThrow.Fatal("Unable to open %s for reading" % opts.GetInFile())

    print("Generating fragments...")
    builder = oebioisostere.OEDBBuilder(chompOpts.GetFragOpts())
    for mol in ifs.GetOEMols():
        builder.Generate(mol)

    print("Building 2D fragments library...")
    builder.Filter(oebioisostere.OECreateFragFilter())
    builder.Expand(oebioisostere.OECreateFlipperOptions())
    builder.Screen(chompOpts.GetScreenOpts())

    print("Generating fragment conformers...")
    writer = oebioisostere.OEDBWriter()
    writer.Init(chompOpts.GetDBName())
    count = 0
    for frag in builder.GetFrags():
        if oebioisostere.OEGenerateConformers(frag):
            writer.Write(frag)
            count += 1
    writer.Finish()
    print("Generated fragments: %d" % count)


if __name__ == "__main__":
    import sys
    sys.exit(main(sys.argv))

Download code

brood_database.py

Creating Brood Query

The following code example shows how to create a Brood query from a molecule that could be used for bioisosteric fragment replacements using Brood.

See also

Listing 2: Creating Brood Query

#!/usr/bin/env python
# (C) 2022 Cadence Design Systems, Inc. (Cadence) 
# All rights reserved.
# TERMS FOR USE OF SAMPLE CODE The software below ("Sample Code") is
# provided to current licensees or subscribers of Cadence products or
# SaaS offerings (each a "Customer").
# Customer is hereby permitted to use, copy, and modify the Sample Code,
# subject to these terms. Cadence claims no rights to Customer's
# modifications. Modification of Sample Code is at Customer's sole and
# exclusive risk. Sample Code may require Customer to have a then
# current license or subscription to the applicable Cadence offering.
# THE SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED.  OPENEYE DISCLAIMS ALL WARRANTIES, INCLUDING, BUT
# NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
# PARTICULAR PURPOSE AND NONINFRINGEMENT. In no event shall Cadence be
# liable for any damages or liability in connection with the Sample Code
# or its use.


from openeye import oechem
from openeye import oebioisostere

class QueryOptions(oechem.OEOptions):
    def __init__(self):
        oechem.OEOptions.__init__(self, "QueryOptions")

        idxParam = oechem.OEUIntParameter("-atomIndices")
        idxParam.SetIsList(True)
        idxParam.SetRequired(True)
        idxParam.SetBrief("Index of atoms in fragment")
        self._idxParam = self.AddParameter(idxParam)

        duParam = oechem.OEFileStringParameter("-du", oechem.OEFileStringType_DU)
        duParam.SetBrief("Design unit containing protein target for bump check")
        self._duParam = self.AddParameter(duParam)

        maskParam = oechem.OEUIntParameter("-proteinMask", oechem.OEDesignUnitComponents_TargetComplexNoSolvent)
        maskParam.SetBrief("Design unit mask to identify protein target")
        self._maskParam = self.AddParameter(maskParam)
        
        selectDUParam = oechem.OEFileStringParameter("-selectDU", oechem.OEFileStringType_DU)
        selectDUParam.SetBrief("Design unit containing select protein for bump check")
        self._selectDUParam = self.AddParameter(selectDUParam)

        maskSelectParam = oechem.OEUIntParameter("-proteinSelectMask", oechem.OEDesignUnitComponents_TargetComplexNoSolvent)
        maskSelectParam.SetBrief("Design unit mask to identify select protein target")
        self._maskSelectParam = self.AddParameter(maskSelectParam)

    def CreateCopy(self):
        return self

    def GetIndices(self):
        indices = []
        for idx in self._idxParam.GetStringValues():
            indices.append(int(idx))
        return indices

    def GetDU(self):
        ifs = oechem.oeifstream()
        if not self._duParam.GetHasValue():
            return None
        if not ifs.open(self._duParam.GetStringValue()):
            oechem.OEThrow.Fatal("Unable to open %s for reading" % opts.GetInFile())
        du = oechem.OEDesignUnit()
        if not oechem.OEReadDesignUnit(ifs, du):
            oechem.OEThrow.Fatal("Unable to read design unit")
        return du

    def GetProteinMask(self):
        if self._maskParam.GetHasValue():
            return int(self._maskParam.GetStringValue())
        return int(self._maskParam.GetStringDefault())


    def GetSelectDU(self):
        ifs = oechem.oeifstream()
        if not self._selectDUParam.GetHasValue():
            return None
        if not ifs.open(self._selectDUParam.GetStringValue()):
            oechem.OEThrow.Fatal("Unable to open %s for reading" % opts.GetInFile())
        selectDU = oechem.OEDesignUnit()
        if not oechem.OEReadDesignUnit(ifs, selectDU):
            oechem.OEThrow.Fatal("Unable to read design unit")
        return selectDU

    def GetSelectProteinMask(self):
        if self._maskSelectParam.GetHasValue():
            return int(self._maskSelectParam.GetStringValue())
        return int(self._maskSelectParam.GetStringDefault())

def main(argv=[__name__]):
    queryOpts = QueryOptions()
    opts = oechem.OESimpleAppOptions(queryOpts, "BroodQuery", oechem.OEFileStringType_Mol3D, "oeb")
    if oechem.OEConfigureOpts(opts, argv, False) == oechem.OEOptsConfigureStatus_Help:
        return 0
    queryOpts.UpdateValues(opts)

    ifs = oechem.oemolistream()
    if not ifs.open(opts.GetInFile()):
        oechem.OEThrow.Fatal("Unable to open %s for reading" % opts.GetInFile())

    ofs = oechem.oemolostream()
    if not ofs.open(opts.GetOutFile()):
        oechem.OEThrow.Fatal("Unable to open %s for writing" % opts.GetOutFile())

    queryMol = oechem.OEMol()
    if not oechem.OEReadMolecule(ifs, queryMol):
        oechem.OEThrow.Fatal("Unable to load molecule")

    indices = queryOpts.GetIndices()
    atoms = []
    for idx in indices:
        atom = queryMol.GetAtom(oechem.OEHasAtomIdx(idx))
        if not atom:
            oechem.OEThrow.Fatal("Invalid atom index %d" % idx)
        atoms.append(atom)

    selection = oechem.OEAtomBondSet()
    selection.AddAtoms(atoms)

    query = oebioisostere.OEBroodQuery()
    du = queryOpts.GetDU()
    selectDU = queryOpts.GetSelectDU()
    
    if du is None and selectDU is None:
        retCode = oebioisostere.OECreateBroodQuery(query, queryMol, selection)
    elif du is None and selectDU is not None:
        passingProteinSelect = True
        retCode = oebioisostere.OECreateBroodQuery(query, queryMol, selection, selectDU, queryOpts.GetSelectProteinMask(), passingProteinSelect)
    elif selectDU is None:
        retCode = oebioisostere.OECreateBroodQuery(query, queryMol, selection, du, queryOpts.GetProteinMask())
    else:
        retCode = oebioisostere.OECreateBroodQuery(query, queryMol, selection, du, queryOpts.GetProteinMask(),selectDU,queryOpts.GetSelectProteinMask())
    
    if retCode != oebioisostere.OEBroodStatusCode_Success:
        oechem.OEThrow.Fatal("%s" % oebioisostere.OEGetBroodStatus(retCode))
    oebioisostere.OEWriteBroodQuery(ofs, query)

if __name__ == "__main__":
    import sys
    sys.exit(main(sys.argv))

Download code

brood_query.py

Generating Brood Hits

The following code example shows how to perform bioisosteric fragment replacements on a Brood query and generate a hit list.

Listing 3: Generating Brood Hits

#!/usr/bin/env python
# (C) 2022 Cadence Design Systems, Inc. (Cadence) 
# All rights reserved.
# TERMS FOR USE OF SAMPLE CODE The software below ("Sample Code") is
# provided to current licensees or subscribers of Cadence products or
# SaaS offerings (each a "Customer").
# Customer is hereby permitted to use, copy, and modify the Sample Code,
# subject to these terms. Cadence claims no rights to Customer's
# modifications. Modification of Sample Code is at Customer's sole and
# exclusive risk. Sample Code may require Customer to have a then
# current license or subscription to the applicable Cadence offering.
# THE SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED.  OPENEYE DISCLAIMS ALL WARRANTIES, INCLUDING, BUT
# NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
# PARTICULAR PURPOSE AND NONINFRINGEMENT. In no event shall Cadence be
# liable for any damages or liability in connection with the Sample Code
# or its use.


from openeye import oechem
from openeye import oebioisostere

class BroodOptions(oechem.OEOptions):
    def __init__(self):
        oechem.OEOptions.__init__(self, "BroodOptions")

        dbParam = oechem.OEStringParameter("-db")
        dbParam.SetRequired(True)
        dbParam.SetVisibility(oechem.OEParamVisibility_Simple)
        dbParam.SetBrief("Database folder")
        self._dbParam = self.AddParameter(dbParam)

        self._genOpts = oebioisostere.ToGeneralOptions(self.AddOption(oebioisostere.OEBroodGeneralOptions()))
        self._scoreOpts = oebioisostere.ToScoreOptions(self.AddOption(oebioisostere.OEBroodScoreOptions()))
        self._hitOpts = oebioisostere.ToHitlistOptions(self.AddOption(oebioisostere.OEBroodHitlistOptions()))
        pass

    def CreateCopy(self):
        return self

    def GetDatabase(self):
        return self._dbParam.GetStringValue()

    def GetGenOpts(self):
        return self._genOpts

    def GetScoreOpts(self):
        return self._scoreOpts

    def GetHitlistOpts(self):
        return self._hitOpts


def main(argv=[__name__]):
    broodOpts = BroodOptions()
    opts = oechem.OESimpleAppOptions(broodOpts, "BroodHitlist", oechem.OEFileStringType_Mol3D,
                                     oechem.OEFileStringType_Mol3D)
    if oechem.OEConfigureOpts(opts, argv, False) == oechem.OEOptsConfigureStatus_Help:
        return 0
    broodOpts.UpdateValues(opts)

    ifs = oechem.oemolistream()
    if not ifs.open(opts.GetInFile()):
        oechem.OEThrow.Fatal("Unable to open %s for reading" % opts.GetInFile())

    ofs = oechem.oemolostream()
    if not ofs.open(opts.GetOutFile()):
        oechem.OEThrow.Fatal("Unable to open %s for writing" % opts.GetOutFile())

    query = oebioisostere.OEBroodQuery()
    retCode = oebioisostere.OEReadBroodQuery(ifs, query)
    if retCode != oebioisostere.OEBroodStatusCode_Success:
        oechem.OEThrow.Fatal("Failed: %s" % oebioisostere.OEGetBroodStatus(retCode))
        
    reader = oebioisostere.OEDBReader()
    retCode = reader.Init(broodOpts.GetDatabase(), query, broodOpts.GetGenOpts())
    if retCode != oebioisostere.OEBroodStatusCode_Success:
        oechem.OEThrow.Fatal("Unable to load Brood database")

    overlay = oebioisostere.OEBroodOverlay(broodOpts.GetGenOpts(), broodOpts.GetScoreOpts())
    overlay.SetupRef(query)

    hlist = oebioisostere.OEHitlistBuilder(query, broodOpts.GetGenOpts(), broodOpts.GetHitlistOpts())

    packetCount = 0
    packet = oebioisostere.OEBroodDBPacket()
    while reader.GetNextPacket(packet):
        packetCount += 1
        print("Processing packet %d with %d fragments" % (packetCount, packet.GetFragCount()))
        vecScores = overlay.Overlay(packet)
        hlist.AddScores(vecScores)
    print("Generating hitlist...")
    hlist.Build()
    print("Total number of fragments overlayed: %d" % hlist.GetAddCount())
    print("Number of final hits: %d" % hlist.GetHitCount())

    for idx, hit in enumerate(hlist.GetHits()):
        oechem.OEWriteMolecule(ofs, hit.GetMol())
        comboScore = hit.GetComboScore()
        print("Hit: %d %s Combo Score: %2f Belief Score: %.2f Complexity: %2f"
              % (idx+1, hit.GetMol().GetTitle(), comboScore, hit.GetBeliefScore(), hit.GetComplexity()))


if __name__ == "__main__":
    import sys
    sys.exit(main(sys.argv))

Download code

brood_hitlist.py

Generating Brood Matches

The following code example shows how to perform bioisosteric fragment replacements on a Brood query and generate all possible matches. This could be a use case when generating all possible design ideas using BROOD and postprocessing them with other tools.

Listing 4: Generating Brood Matches

#!/usr/bin/env python
# (C) 2022 Cadence Design Systems, Inc. (Cadence) 
# All rights reserved.
# TERMS FOR USE OF SAMPLE CODE The software below ("Sample Code") is
# provided to current licensees or subscribers of Cadence products or
# SaaS offerings (each a "Customer").
# Customer is hereby permitted to use, copy, and modify the Sample Code,
# subject to these terms. Cadence claims no rights to Customer's
# modifications. Modification of Sample Code is at Customer's sole and
# exclusive risk. Sample Code may require Customer to have a then
# current license or subscription to the applicable Cadence offering.
# THE SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED.  OPENEYE DISCLAIMS ALL WARRANTIES, INCLUDING, BUT
# NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
# PARTICULAR PURPOSE AND NONINFRINGEMENT. In no event shall Cadence be
# liable for any damages or liability in connection with the Sample Code
# or its use.

import sys

from openeye import oechem
from openeye import oebioisostere

class BroodOptions(oechem.OEOptions):
    def __init__(self):
        oechem.OEOptions.__init__(self, "BroodOptions")

        dbParam = oechem.OEStringParameter("-db")
        dbParam.SetRequired(True)
        dbParam.SetVisibility(oechem.OEParamVisibility_Simple)
        dbParam.SetBrief("Database folder")
        self._dbParam = self.AddParameter(dbParam)

        self._genOpts = oebioisostere.ToGeneralOptions(self.AddOption(oebioisostere.OEBroodGeneralOptions()))
        self._scoreOpts = oebioisostere.ToScoreOptions(self.AddOption(oebioisostere.OEBroodScoreOptions()))
        self._buildOpts = oebioisostere.ToBuildOptions(self.AddOption(oebioisostere.OEBroodBuildOptions()))
        pass

    def CreateCopy(self):
        return self

    def GetDatabase(self):
        return self._dbParam.GetStringValue()

    def GetGenOpts(self):
        return self._genOpts

    def GetScoreOpts(self):
        return self._scoreOpts

    def GetBuildOpts(self):
        return self._buildOpts


def main(argv=[__name__]):
    broodOpts = BroodOptions()
    opts = oechem.OESimpleAppOptions(broodOpts, "BroodMatching", oechem.OEFileStringType_Mol3D,
                                     oechem.OEFileStringType_Mol3D)
    if oechem.OEConfigureOpts(opts, argv, False) == oechem.OEOptsConfigureStatus_Help:
        return 0
    broodOpts.UpdateValues(opts)

    ifs = oechem.oemolistream()
    if not ifs.open(opts.GetInFile()):
        oechem.OEThrow.Fatal("Unable to open %s for reading" % opts.GetInFile())

    ofs = oechem.oemolostream()
    if not ofs.open(opts.GetOutFile()):
        oechem.OEThrow.Fatal("Unable to open %s for writing" % opts.GetOutFile())

    query = oebioisostere.OEBroodQuery()
    retCode = oebioisostere.OEReadBroodQuery(ifs, query)
    if retCode != oebioisostere.OEBroodStatusCode_Success:
        oechem.OEThrow.Fatal("Failed: %s" % oebioisostere.OEGetBroodStatus(retCode))

    reader = oebioisostere.OEDBReader()
    retCode = reader.Init(broodOpts.GetDatabase(), query, broodOpts.GetGenOpts())
    if retCode != oebioisostere.OEBroodStatusCode_Success:
        oechem.OEThrow.Fatal("Unable to load Brood database")

    overlay = oebioisostere.OEBroodOverlay(broodOpts.GetGenOpts(), broodOpts.GetScoreOpts())
    overlay.SetupRef(query)
    builder = oebioisostere.OEBroodMolBuilder(query, broodOpts.GetGenOpts(), broodOpts.GetBuildOpts())

    packetCount = 0
    packet = oebioisostere.OEBroodDBPacket()
    totalCount = 0
    successCount = 0
    while reader.GetNextPacket(packet):
        packetCount += 1
        print("Processing packet %d with %d fragments" % (packetCount, packet.GetFragCount()))
        vecScores = overlay.Overlay(packet)
        for score in vecScores:
            totalCount += 1
            if score.GetStatus() == oebioisostere.OEBroodStatusCode_Success:
                hit = oebioisostere.OEBroodHit()
                if builder.Build(score, hit) == oebioisostere.OEBroodStatusCode_Success:
                    oechem.OEWriteMolecule(ofs, hit.GetMol())
                    successCount += 1

    print("Total number of fragments overlayed: %d" % totalCount)
    print("Number of successful matches: %d" % successCount)


if __name__ == "__main__":
    import sys
    sys.exit(main(sys.argv))

Download code

brood_matching.py

Overlay between Fragments

The following code example shows how to overlay a fragment against a query fragment. This could be a use case when working with synthons and trying to find similar synthons based on 3D similarity.

Listing 5: Overlay between Fragments

#!/usr/bin/env python
# (C) 2022 Cadence Design Systems, Inc. (Cadence) 
# All rights reserved.
# TERMS FOR USE OF SAMPLE CODE The software below ("Sample Code") is
# provided to current licensees or subscribers of Cadence products or
# SaaS offerings (each a "Customer").
# Customer is hereby permitted to use, copy, and modify the Sample Code,
# subject to these terms. Cadence claims no rights to Customer's
# modifications. Modification of Sample Code is at Customer's sole and
# exclusive risk. Sample Code may require Customer to have a then
# current license or subscription to the applicable Cadence offering.
# THE SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED.  OPENEYE DISCLAIMS ALL WARRANTIES, INCLUDING, BUT
# NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
# PARTICULAR PURPOSE AND NONINFRINGEMENT. In no event shall Cadence be
# liable for any damages or liability in connection with the Sample Code
# or its use.

import sys

from openeye import oechem
from openeye import oebioisostere

def main(argv=[__name__]):
    genOpts = oebioisostere.OEBroodGeneralOptions()
    opts = oechem.OERefInputAppOptions(genOpts, "FragmentOverlay", oechem.OEFileStringType_Mol3D,
                                     oechem.OEFileStringType_Mol3D, oechem.OEFileStringType_Mol3D, "-queryFrag")
    if oechem.OEConfigureOpts(opts, argv, False) == oechem.OEOptsConfigureStatus_Help:
        return 0
    genOpts.UpdateValues(opts)

    ifs = oechem.oemolistream()
    if not ifs.open(opts.GetInFile()):
        oechem.OEThrow.Fatal("Unable to open %s for reading" % opts.GetInFile())

    rfs = oechem.oemolistream()
    if not rfs.open(opts.GetRefFile()):
        oechem.OEThrow.Fatal("Unable to open %s for reading" % opts.GetRefFile())

    ofs = oechem.oemolostream()
    if not ofs.open(opts.GetOutFile()):
        oechem.OEThrow.Fatal("Unable to open %s for writing" % opts.GetOutFile())

    query = oechem.OEMol()
    if not oechem.OEReadMolecule(rfs, query):
        oechem.OEThrow.Fatal("Failed to read Query fragment")

    prep = oebioisostere.OEBroodFragPrep()
    overlay = oebioisostere.OEFragOverlay(genOpts, oebioisostere.OEBroodScoreOptions())
    overlay.SetupRef(query)

    for frag in ifs.GetOEMols():
        print("Overlaying %s" % frag.GetTitle())
        prep.Prep(frag)
        score = oebioisostere.OEBroodScore()
        ret_code = overlay.Overlay(frag, score)
        if ret_code == oebioisostere.OEBroodStatusCode_Success:
            outmol = oechem.OEGraphMol()
            score.GetFragMol(outmol)
            overlay.Transform(outmol)
            comboScore = score.GetComboScore()
            print("Fragment: %s Combo Score: %2f" % (frag.GetTitle(), comboScore))
            oechem.OEWriteMolecule(ofs, outmol)
        else:
            errMsg = oebioisostere.OEGetBroodStatus(ret_code)
            print("%s: %s" % (frag.GetTitle(), errMsg))
    return 0


if __name__ == "__main__":
    import sys
    sys.exit(main(sys.argv))

Download code

fragment_overlay.py

Replacing a Fragment in a Molecule

The following code example shows how to replace a fragment in a molecule defined in the form of a BROOD query.

Listing 6: Replacing a Fragment in a Molecule

#!/usr/bin/env python
# (C) 2022 Cadence Design Systems, Inc. (Cadence) 
# All rights reserved.
# TERMS FOR USE OF SAMPLE CODE The software below ("Sample Code") is
# provided to current licensees or subscribers of Cadence products or
# SaaS offerings (each a "Customer").
# Customer is hereby permitted to use, copy, and modify the Sample Code,
# subject to these terms. Cadence claims no rights to Customer's
# modifications. Modification of Sample Code is at Customer's sole and
# exclusive risk. Sample Code may require Customer to have a then
# current license or subscription to the applicable Cadence offering.
# THE SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED.  OPENEYE DISCLAIMS ALL WARRANTIES, INCLUDING, BUT
# NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
# PARTICULAR PURPOSE AND NONINFRINGEMENT. In no event shall Cadence be
# liable for any damages or liability in connection with the Sample Code
# or its use.

import sys

from openeye import oechem
from openeye import oebioisostere

class BroodOptions(oechem.OEOptions):
    def __init__(self):
        oechem.OEOptions.__init__(self, "BroodOptions")
        self._genOpts = oebioisostere.ToGeneralOptions(self.AddOption(oebioisostere.OEBroodGeneralOptions()))
        self._scoreOpts = oebioisostere.ToScoreOptions(self.AddOption(oebioisostere.OEBroodScoreOptions()))
        self._buildOpts = oebioisostere.ToBuildOptions(self.AddOption(oebioisostere.OEBroodBuildOptions()))
        pass

    def CreateCopy(self):
        return self

    def GetGenOpts(self):
        return self._genOpts

    def GetScoreOpts(self):
        return self._scoreOpts

    def GetBuildOpts(self):
        return self._buildOpts


def main(argv=[__name__]):
    broodOpts = BroodOptions()
    opts = oechem.OERefInputAppOptions(broodOpts, "ReplaceFragment", oechem.OEFileStringType_Mol3D,
                                     oechem.OEFileStringType_Mol3D, oechem.OEFileStringType_Mol3D, "-query")
    if oechem.OEConfigureOpts(opts, argv, False) == oechem.OEOptsConfigureStatus_Help:
        return 0
    broodOpts.UpdateValues(opts)

    ifs = oechem.oemolistream()
    if not ifs.open(opts.GetInFile()):
        oechem.OEThrow.Fatal("Unable to open %s for reading" % opts.GetInFile())

    rfs = oechem.oemolistream()
    if not rfs.open(opts.GetRefFile()):
        oechem.OEThrow.Fatal("Unable to open %s for reading" % opts.GetRefFile())

    ofs = oechem.oemolostream()
    if not ofs.open(opts.GetOutFile()):
        oechem.OEThrow.Fatal("Unable to open %s for writing" % opts.GetOutFile())

    query = oebioisostere.OEBroodQuery()
    retCode = oebioisostere.OEReadBroodQuery(rfs, query)
    if retCode != oebioisostere.OEBroodStatusCode_Success:
        oechem.OEThrow.Fatal("Failed: %s" % oebioisostere.OEGetBroodStatus(retCode))

    prep = oebioisostere.OEBroodFragPrep()
    overlay = oebioisostere.OEBroodOverlay(broodOpts.GetGenOpts(), broodOpts.GetScoreOpts())
    overlay.SetupRef(query)
    builder = oebioisostere.OEBroodMolBuilder(query, broodOpts.GetGenOpts(), broodOpts.GetBuildOpts())

    for frag in ifs.GetOEMols():
        print("Replacement fragment %s" % frag.GetTitle())
        prep.Prep(frag)
        score = oebioisostere.OEBroodScore()
        ret_code = overlay.Overlay(frag, score)
        if ret_code == oebioisostere.OEBroodStatusCode_Success:
            comboScore = score.GetComboScore()
            print("Fragment: %s Combo Score: %2f" % (frag.GetTitle(), comboScore))
            hit = oebioisostere.OEBroodHit()
            if builder.Build(score, hit) == oebioisostere.OEBroodStatusCode_Success:
                oechem.OEWriteMolecule(ofs, hit.GetMol())
        else:
            errMsg = oebioisostere.OEGetBroodStatus(ret_code)
            print("%s: %s" % (frag.GetTitle(), errMsg))
    return 0


if __name__ == "__main__":
    import sys
    sys.exit(main(sys.argv))

Download code

replace_fragment.py

Building and Using an External Compound Database

The following code example shows how to build and use an external (in-house) compound database to find similar 2D compounds to a generated hit list. It demonstrates optional analog lookup against a user-provided compound database to annotate Brood hits with similar known molecules.

Listing 7: Building and using an external compound database

#!/usr/bin/env python
# (C) 2022 Cadence Design Systems, Inc. (Cadence) 
# All rights reserved.
# TERMS FOR USE OF SAMPLE CODE The software below ("Sample Code") is
# provided to current licensees or subscribers of Cadence products or
# SaaS offerings (each a "Customer").
# Customer is hereby permitted to use, copy, and modify the Sample Code,
# subject to these terms. Cadence claims no rights to Customer's
# modifications. Modification of Sample Code is at Customer's sole and
# exclusive risk. Sample Code may require Customer to have a then
# current license or subscription to the applicable Cadence offering.
# THE SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED.  OPENEYE DISCLAIMS ALL WARRANTIES, INCLUDING, BUT
# NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
# PARTICULAR PURPOSE AND NONINFRINGEMENT. In no event shall Cadence be
# liable for any damages or liability in connection with the Sample Code
# or its use.


from openeye import oechem
from openeye import oebioisostere

class BroodOptions(oechem.OEOptions):
    def __init__(self):
        oechem.OEOptions.__init__(self, "BroodOptions")

        dbParam = oechem.OEStringParameter("-db")
        dbParam.SetRequired(True)
        dbParam.SetVisibility(oechem.OEParamVisibility_Simple)
        dbParam.SetBrief("Brood Database Directory")
        self._dbParam = self.AddParameter(dbParam)
        
        cpddbParam = oechem.OEStringParameter("-cpddb")
        cpddbParam.SetRequired(True)
        cpddbParam.SetVisibility(oechem.OEParamVisibility_Simple)
        cpddbParam.SetBrief("CPDDatabase File (database of known compounds to identify available compounds similar to hits)")
        self._cpddbParam = self.AddParameter(cpddbParam)

        self._genOpts = oebioisostere.ToGeneralOptions(self.AddOption(oebioisostere.OEBroodGeneralOptions()))
        self._scoreOpts = oebioisostere.ToScoreOptions(self.AddOption(oebioisostere.OEBroodScoreOptions()))
        self._hitOpts = oebioisostere.ToHitlistOptions(self.AddOption(oebioisostere.OEBroodHitlistOptions()))
        pass

    def CreateCopy(self):
        return self

    def GetDatabase(self):
        return self._dbParam.GetStringValue()
        
    def GetCPDDatabase(self):
        return self._cpddbParam.GetStringValue()

    def GetGenOpts(self):
        return self._genOpts

    def GetScoreOpts(self):
        return self._scoreOpts

    def GetHitlistOpts(self):
        return self._hitOpts


def main(argv=[__name__]):
    broodOpts = BroodOptions()
    opts = oechem.OESimpleAppOptions(broodOpts, "BroodCPDDB", oechem.OEFileStringType_Mol3D,
                                     "oeb")
    if oechem.OEConfigureOpts(opts, argv, False) == oechem.OEOptsConfigureStatus_Help:
        return 0
    broodOpts.UpdateValues(opts)

    ifs = oechem.oemolistream()
    if not ifs.open(opts.GetInFile()):
        oechem.OEThrow.Fatal("Unable to open %s for reading" % opts.GetInFile())
        
    query = oebioisostere.OEBroodQuery()
    retCode = oebioisostere.OEReadBroodQuery(ifs, query)
    if retCode != oebioisostere.OEBroodStatusCode_Success:
        oechem.OEThrow.Fatal("Failed: %s" % oebioisostere.OEGetBroodStatus(retCode))

    reader = oebioisostere.OEDBReader()
    retCode = reader.Init(broodOpts.GetDatabase(), query, broodOpts.GetGenOpts())
    if retCode != oebioisostere.OEBroodStatusCode_Success:
        oechem.OEThrow.Fatal("Unable to load Brood database")
        
    ifsCPDDB = oechem.oemolistream()
    if not ifsCPDDB.open(broodOpts.GetCPDDatabase()):
        oechem.OEThrow.Fatal("Unable to load Brood CPDDatabase file")
        
    cpddb = oebioisostere.OECPDDatabase()
    retValue = cpddb.Prep(ifsCPDDB)
    print("Prepration Mode: %s" % oebioisostere.OEGetBroodStatus(retValue))
    
    numOfCPDDBMolecule = cpddb.GetNumMolecules()
    print("Number of molecules in CPDDatabse: %d" % numOfCPDDBMolecule)
    
    if retValue == oebioisostere.OEBroodStatusCode_NewFPGenerated:
      ofsCPDDB = oechem.oemolostream()
      if not ofsCPDDB.open(opts.GetOutFile()):
        oechem.OEThrow.Fatal("Unable to open %s for writing the newly generated finegrprint" , opts.GetOutFile())
      
      if cpddb.Write(ifsCPDDB, ofsCPDDB):
        print("Fingerprints are written in the %s. You can use this file in the future with -cpddb for improved efficiency over current useage." % ofsCPDDB.GetFileName())
      else:
        oechem.OEThrow.Warning("Fingerprint mismatch in writing updated -cpddb file." % ofsCPDDB.GetFileName())

    overlay = oebioisostere.OEBroodOverlay(broodOpts.GetGenOpts(), broodOpts.GetScoreOpts())
    overlay.SetupRef(query)

    hlist = oebioisostere.OEHitlistBuilder(query, broodOpts.GetGenOpts(), broodOpts.GetHitlistOpts())

    packet = oebioisostere.OEBroodDBPacket()
    while reader.GetNextPacket(packet):
        vecScores = overlay.Overlay(packet)
        hlist.AddScores(vecScores)
    print("Generating hitlist...")
    hlist.Build()
    print("Number of final hits: %d \n" % hlist.GetHitCount())

    for idx, hit in enumerate(hlist.GetHits()):
        vecCpddbValues = cpddb.GetSimilarMolecules(hit.GetMol())
        if not len(vecCpddbValues) == 0:
          print("Hit Mol (SMILES): %s \nAnalog Mol (SMILES): %s \nAnalog Mol Label: %s \n \n"
              % (oechem.OEMolToSmiles(hit.GetMol()),vecCpddbValues[0], vecCpddbValues[1]))

if __name__ == "__main__":
    import sys
    sys.exit(main(sys.argv))

Download code

brood_cpddb.py

Creating Brood Query by Linking Two Molecules (Bridging)

The following code example shows how to create a Brood query from two molecules to find a suitable linker between the two using bioisosteric fragment replacements using Brood.

See also

Listing 8: Creating Brood Query by Linking Two Molecules (Bridging)

#!/usr/bin/env python
# (C) 2022 Cadence Design Systems, Inc. (Cadence) 
# All rights reserved.
# TERMS FOR USE OF SAMPLE CODE The software below ("Sample Code") is
# provided to current licensees or subscribers of Cadence products or
# SaaS offerings (each a "Customer").
# Customer is hereby permitted to use, copy, and modify the Sample Code,
# subject to these terms. Cadence claims no rights to Customer's
# modifications. Modification of Sample Code is at Customer's sole and
# exclusive risk. Sample Code may require Customer to have a then
# current license or subscription to the applicable Cadence offering.
# THE SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED.  OPENEYE DISCLAIMS ALL WARRANTIES, INCLUDING, BUT
# NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
# PARTICULAR PURPOSE AND NONINFRINGEMENT. In no event shall Cadence be
# liable for any damages or liability in connection with the Sample Code
# or its use.


from openeye import oechem
from openeye import oebioisostere

class QueryOptions(oechem.OEOptions):
    def __init__(self):
        oechem.OEOptions.__init__(self, "2MolLinkerQueryOptions")

        idxNo1Param = oechem.OEUIntParameter("-atomIndicesNo1")
        idxNo1Param.SetIsList(True)
        idxNo1Param.SetRequired(True)
        idxNo1Param.SetBrief("Index of atoms in fragment No.1")
        self._idxNo1Param = self.AddParameter(idxNo1Param)
        
        idxNo2Param = oechem.OEUIntParameter("-atomIndicesNo2")
        idxNo2Param.SetIsList(True)
        idxNo2Param.SetRequired(True)
        idxNo2Param.SetBrief("Index of atoms in fragment No.2")
        self._idxNo2Param = self.AddParameter(idxNo2Param)

        duParam = oechem.OEFileStringParameter("-du", oechem.OEFileStringType_DU)
        duParam.SetBrief("Design unit containing protein target for bump check")
        self._duParam = self.AddParameter(duParam)

        maskParam = oechem.OEUIntParameter("-proteinMask", oechem.OEDesignUnitComponents_TargetComplexNoSolvent)
        maskParam.SetBrief("Design unit mask to identify protein target")
        self._maskParam = self.AddParameter(maskParam)
        
        selectDUParam = oechem.OEFileStringParameter("-selectDU", oechem.OEFileStringType_DU)
        selectDUParam.SetBrief("Design unit containing select protein for bump check")
        self._selectDUParam = self.AddParameter(selectDUParam)

        maskSelectParam = oechem.OEUIntParameter("-proteinSelectMask", oechem.OEDesignUnitComponents_TargetComplexNoSolvent)
        maskSelectParam.SetBrief("Design unit mask to identify select protein target")
        self._maskSelectParam = self.AddParameter(maskSelectParam)

    def CreateCopy(self):
        return self

    def GetFirstMolIndices(self):
        indices = []
        for idx in self._idxNo1Param.GetStringValues():
            indices.append(int(idx))
        return indices

    def GetSecondMolIndices(self):
        indices = []
        for idx in self._idxNo2Param.GetStringValues():
            indices.append(int(idx))
        return indices

    def GetDU(self):
        ifs = oechem.oeifstream()
        if not self._duParam.GetHasValue():
            return None
        if not ifs.open(self._duParam.GetStringValue()):
            oechem.OEThrow.Fatal("Unable to open %s for reading" % opts.GetInFile())
        du = oechem.OEDesignUnit()
        if not oechem.OEReadDesignUnit(ifs, du):
            oechem.OEThrow.Fatal("Unable to read design unit")
        return du

    def GetProteinMask(self):
        if self._maskParam.GetHasValue():
            return int(self._maskParam.GetStringValue())
        return int(self._maskParam.GetStringDefault())


    def GetSelectDU(self):
        ifs = oechem.oeifstream()
        if not self._selectDUParam.GetHasValue():
            return None
        if not ifs.open(self._selectDUParam.GetStringValue()):
            oechem.OEThrow.Fatal("Unable to open %s for reading" % opts.GetInFile())
        selectDU = oechem.OEDesignUnit()
        if not oechem.OEReadDesignUnit(ifs, selectDU):
            oechem.OEThrow.Fatal("Unable to read design unit")
        return selectDU

    def GetSelectProteinMask(self):
        if self._maskSelectParam.GetHasValue():
            return int(self._maskSelectParam.GetStringValue())
        return int(self._maskSelectParam.GetStringDefault())

def main(argv=[__name__]):
    queryOpts = QueryOptions()
    opts = oechem.OERefInputAppOptions(queryOpts, "2MolLinkerBroodQuery", oechem.OEFileStringType_Mol3D, oechem.OEFileStringType_Mol3D, oechem.OEFileStringType_Mol3D , "-in2")
    if oechem.OEConfigureOpts(opts, argv, False) == oechem.OEOptsConfigureStatus_Help:
        return 0
    queryOpts.UpdateValues(opts)

    ifs = oechem.oemolistream()
    if not ifs.open(opts.GetInFile()):
        oechem.OEThrow.Fatal("Unable to open %s for reading" % opts.GetInFile())
        
    rfs = oechem.oemolistream()
    if not rfs.open(opts.GetRefFile()):
        oechem.OEThrow.Fatal("Unable to open %s for reading" % opts.GetRefFile())
        
    ofs = oechem.oemolostream()
    if not ofs.open(opts.GetOutFile()):
        oechem.OEThrow.Fatal("Unable to open %s for writing" % opts.GetOutFile())

    firstMol = oechem.OEMol()
    if not oechem.OEReadMolecule(ifs, firstMol):
        oechem.OEThrow.Fatal("Unable to load molecule 1")
        
    secondMol = oechem.OEMol()
    if not oechem.OEReadMolecule(rfs, secondMol):
        oechem.OEThrow.Fatal("Unable to load molecule 2")

    firstMolIndices = queryOpts.GetFirstMolIndices()
    firstMolAtoms = []
    for idx in firstMolIndices:
        firstMolAtom = firstMol.GetAtom(oechem.OEHasAtomIdx(idx))
        if not firstMolAtom:
            oechem.OEThrow.Fatal("Invalid atom index %d" % idx)
        firstMolAtoms.append(firstMolAtom)
  
    secondMolIndices = queryOpts.GetSecondMolIndices()
    secondMolAtoms = []
    for idx in secondMolIndices:
        secondMolAtom = secondMol.GetAtom(oechem.OEHasAtomIdx(idx))
        if not secondMolAtom:
            oechem.OEThrow.Fatal("Invalid atom index %d" % idx)
        secondMolAtoms.append(secondMolAtom)

    firstMolSelection = oechem.OEAtomBondSet()
    firstMolSelection.AddAtoms(firstMolAtoms)

    secondMolSelection = oechem.OEAtomBondSet()
    secondMolSelection.AddAtoms(secondMolAtoms)
    
    query = oebioisostere.OEBroodQuery()
    du = queryOpts.GetDU()
    selectDU = queryOpts.GetSelectDU()
    
    if du is None and selectDU is None:
        retCode = oebioisostere.OECreateBroodQuery(query, firstMol, secondMol, firstMolSelection, secondMolSelection)
    elif du is None and selectDU is not None:
        passingProteinSelect = True
        retCode = oebioisostere.OECreateBroodQuery(query, firstMol, secondMol, firstMolSelection, secondMolSelection, selectDU, queryOpts.GetSelectProteinMask(), passingProteinSelect)
    elif selectDU is None:
        retCode = oebioisostere.OECreateBroodQuery(query, firstMol, secondMol, firstMolSelection, secondMolSelection, du, queryOpts.GetProteinMask())
    else:
        retCode = oebioisostere.OECreateBroodQuery(query, firstMol, secondMol, firstMolSelection, secondMolSelection, du, queryOpts.GetProteinMask(), selectDU, queryOpts.GetSelectProteinMask())
    
    if retCode != oebioisostere.OEBroodStatusCode_Success:
        oechem.OEThrow.Fatal("%s" % oebioisostere.OEGetBroodStatus(retCode))
    oebioisostere.OEWriteBroodQuery(ofs, query)

if __name__ == "__main__":
    import sys
    sys.exit(main(sys.argv))

Clustering Brood Hits

The following code example shows how to cluster a generated Brood hit list using OEBroodClusterBuilder and OEBroodCluster. It demonstrates post-processing of hits into similarity clusters and cluster-level prioritization.

Listing 9: Clustering Brood Hits

#!/usr/bin/env python
# (C) 2022 Cadence Design Systems, Inc. (Cadence)
# All rights reserved.
# TERMS FOR USE OF SAMPLE CODE The software below ("Sample Code") is
# provided to current licensees or subscribers of Cadence products or
# SaaS offerings (each a "Customer").
# Customer is hereby permitted to use, copy, and modify the Sample Code,
# subject to these terms. Cadence claims no rights to Customer's
# modifications. Modification of Sample Code is at Customer's sole and
# exclusive risk. Sample Code may require Customer to have a then
# current license or subscription to the applicable Cadence offering.
# THE SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED. OPENEYE DISCLAIMS ALL WARRANTIES, INCLUDING, BUT
# NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
# PARTICULAR PURPOSE AND NONINFRINGEMENT. In no event shall Cadence be
# liable for any damages or liability in connection with the Sample Code
# or its use.

from openeye import oechem
from openeye import oebioisostere


class BroodOptions(oechem.OEOptions):
    def __init__(self):
        oechem.OEOptions.__init__(self, "BroodOptions")

        dbParam = oechem.OEStringParameter("-db")
        dbParam.SetRequired(True)
        dbParam.SetVisibility(oechem.OEParamVisibility_Simple)
        dbParam.SetBrief("Database folder")
        self._dbParam = self.AddParameter(dbParam)

        self._genOpts = oebioisostere.ToGeneralOptions(self.AddOption(oebioisostere.OEBroodGeneralOptions()))
        self._scoreOpts = oebioisostere.ToScoreOptions(self.AddOption(oebioisostere.OEBroodScoreOptions()))
        self._hitOpts = oebioisostere.ToHitlistOptions(self.AddOption(oebioisostere.OEBroodHitlistOptions()))

    def CreateCopy(self):
        return self

    def GetDatabase(self):
        return self._dbParam.GetStringValue()

    def GetGenOpts(self):
        return self._genOpts

    def GetScoreOpts(self):
        return self._scoreOpts

    def GetHitlistOpts(self):
        return self._hitOpts


def main(argv=[__name__]):
    broodOpts = BroodOptions()
    opts = oechem.OESimpleAppOptions(broodOpts, "BroodCluster", oechem.OEFileStringType_Mol3D,
                                     oechem.OEFileStringType_Mol3D)
    if oechem.OEConfigureOpts(opts, argv, False) == oechem.OEOptsConfigureStatus_Help:
        return 0
    broodOpts.UpdateValues(opts)

    ifs = oechem.oemolistream()
    if not ifs.open(opts.GetInFile()):
        oechem.OEThrow.Fatal("Unable to open %s for reading" % opts.GetInFile())

    ofs = oechem.oemolostream()
    if not ofs.open(opts.GetOutFile()):
        oechem.OEThrow.Fatal("Unable to open %s for writing" % opts.GetOutFile())

    query = oebioisostere.OEBroodQuery()
    retCode = oebioisostere.OEReadBroodQuery(ifs, query)
    if retCode != oebioisostere.OEBroodStatusCode_Success:
        oechem.OEThrow.Fatal("Failed to read query: %s" % oebioisostere.OEGetBroodStatus(retCode))

    reader = oebioisostere.OEDBReader()
    retCode = reader.Init(broodOpts.GetDatabase(), query, broodOpts.GetGenOpts())
    if retCode != oebioisostere.OEBroodStatusCode_Success:
        oechem.OEThrow.Fatal("Unable to load Brood database")

    overlay = oebioisostere.OEBroodOverlay(broodOpts.GetGenOpts(), broodOpts.GetScoreOpts())
    overlay.SetupRef(query)

    hlist = oebioisostere.OEHitlistBuilder(query, broodOpts.GetGenOpts(), broodOpts.GetHitlistOpts())

    packetCount = 0
    packet = oebioisostere.OEBroodDBPacket()
    while reader.GetNextPacket(packet):
        packetCount += 1
        print("Processing packet %d with %d fragments" % (packetCount, packet.GetFragCount()))
        vecScores = overlay.Overlay(packet)
        hlist.AddScores(vecScores)

    print("Generating hitlist...")
    hlist.Build()
    vecHits = list(hlist.GetHits())
    print("Initial hit count: %d" % hlist.GetHitCount())
    
    clBuilder = oebioisostere.OEBroodClusterBuilder(query)
    
    if not clBuilder.Add(vecHits):
        oechem.OEThrow.Fatal("Unable to add hits to cluster builder")

    if not clBuilder.Rank():
        oechem.OEThrow.Fatal("Unable to build hit clusters")

    vecClusters = clBuilder.GetClusters()
    print("Number of clusters: %d" % len(vecClusters))

    written = 0
    for cluster in vecClusters:
        print("Cluster %d: head=%s members=%d interest=%.3f"
              % (cluster.GetRank(),
                 cluster.GetHead().GetFragSmiles(),
                 cluster.Count(),
                 cluster.GetInterest()))

    for cluster in vecClusters:
        oechem.OEWriteMolecule(ofs, cluster.GetHead().GetMol())
        written += 1

    for cluster in vecClusters:
        for hit in cluster.GetMembers():
            oechem.OEWriteMolecule(ofs, hit.GetMol())
            written += 1

    print("Wrote %d clustered molecules to %s" % (written, opts.GetOutFile()))
    return 0


if __name__ == "__main__":
    import sys
    sys.exit(main(sys.argv))

Download code

brood_cluster.py

Building Combined Hits from Different Queries of the Same Molecule with OEBroodComboBuilder

The following code example shows how to use two Brood queries for the same molecule, where the first query is used to select a limited set of primary hits (controlled by -primaryQueryMaxHits) and the second query uses OEBroodComboBuilder to generate all possible secondary (combo) hits from those selected primary hits. The second stage is bounded by -secondaryQueryMaxHits.

This example demonstrates a two-query combo workflow using -in and -in2: it builds primary hits from the first query, expands secondary combo hits from the second query, then clusters and ranks the resulting combo hits before writing the final output molecules.

Listing 10: Building Combined Hits from Different Queries of the Same Molecule with OEBroodComboBuilder

#!/usr/bin/env python
# (C) 2022 Cadence Design Systems, Inc. (Cadence)
# All rights reserved.
# TERMS FOR USE OF SAMPLE CODE The software below ("Sample Code") is
# provided to current licensees or subscribers of Cadence products or
# SaaS offerings (each a "Customer").
# Customer is hereby permitted to use, copy, and modify the Sample Code,
# subject to these terms. Cadence claims no rights to Customer's
# modifications. Modification of Sample Code is at Customer's sole and
# exclusive risk. Sample Code may require Customer to have a then
# current license or subscription to the applicable Cadence offering.
# THE SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED. OPENEYE DISCLAIMS ALL WARRANTIES, INCLUDING, BUT
# NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
# PARTICULAR PURPOSE AND NONINFRINGEMENT. In no event shall Cadence be
# liable for any damages or liability in connection with the Sample Code
# or its use.

from openeye import oechem
from openeye import oebioisostere


class BroodComboOptions(oechem.OEOptions):
    def __init__(self):
        oechem.OEOptions.__init__(self, "BroodComboOptions")

        dbParam = oechem.OEStringParameter("-db")
        dbParam.SetRequired(True)
        dbParam.SetVisibility(oechem.OEParamVisibility_Simple)
        dbParam.SetBrief("Database folder")
        self._dbParam = self.AddParameter(dbParam)

        maxHitsParam = oechem.OEUIntParameter("-primaryQueryMaxHits", 15)
        maxHitsParam.SetVisibility(oechem.OEParamVisibility_Simple)
        maxHitsParam.SetBrief("Maximum number of primary hits collected from the first query")
        self._maxHitsParam = self.AddParameter(maxHitsParam)

        secondaryMaxHitsParam = oechem.OEUIntParameter("-secondaryQueryMaxHits", 15)
        secondaryMaxHitsParam.SetVisibility(oechem.OEParamVisibility_Simple)
        secondaryMaxHitsParam.SetBrief("Maximum number of secondary combo hits collected for each primary hit")
        self._secondaryMaxHitsParam = self.AddParameter(secondaryMaxHitsParam)

    def CreateCopy(self):
        return self

    def GetDatabase(self):
        return self._dbParam.GetStringValue()

    def GetPrimaryQueryMaxHits(self):
        if self._maxHitsParam.GetHasValue():
            return int(self._maxHitsParam.GetStringValue())
        return int(self._maxHitsParam.GetStringDefault())

    def GetSecondaryQueryMaxHits(self):
        if self._secondaryMaxHitsParam.GetHasValue():
            return int(self._secondaryMaxHitsParam.GetStringValue())
        return int(self._secondaryMaxHitsParam.GetStringDefault())

def main(argv=[__name__]):
    broodOpts = BroodComboOptions()
    opts = oechem.OERefInputAppOptions(
        broodOpts,
        "BroodComboBuilder",
        oechem.OEFileStringType_Mol3D,
        oechem.OEFileStringType_Mol3D,
        oechem.OEFileStringType_Mol3D,
        "-in2",
    )
    if oechem.OEConfigureOpts(opts, argv, False) == oechem.OEOptsConfigureStatus_Help:
        return 0
    broodOpts.UpdateValues(opts)

    ifs1 = oechem.oemolistream()
    if not ifs1.open(opts.GetInFile()):
        oechem.OEThrow.Fatal("Unable to open %s for reading" % opts.GetInFile())

    ifs2 = oechem.oemolistream()
    if not ifs2.open(opts.GetRefFile()):
        oechem.OEThrow.Fatal("Unable to open %s for reading" % opts.GetRefFile())

    ofs = oechem.oemolostream()
    if not ofs.open(opts.GetOutFile()):
        oechem.OEThrow.Fatal("Unable to open %s for writing" % opts.GetOutFile())

    query1 = oebioisostere.OEBroodQuery()
    retCode = oebioisostere.OEReadBroodQuery(ifs1, query1)
    if retCode != oebioisostere.OEBroodStatusCode_Success:
        oechem.OEThrow.Fatal("Failed to read -in1 query: %s" % oebioisostere.OEGetBroodStatus(retCode))

    query2 = oebioisostere.OEBroodQuery()
    retCode = oebioisostere.OEReadBroodQuery(ifs2, query2)
    if retCode != oebioisostere.OEBroodStatusCode_Success:
        oechem.OEThrow.Fatal("Failed to read -in2 query: %s" % oebioisostere.OEGetBroodStatus(retCode))

    # Build primary hits from the first query.
    vecPrimaryHits = []
    primaryReader = oebioisostere.OEDBReader()
    if primaryReader.Init(broodOpts.GetDatabase()) != oebioisostere.OEBroodStatusCode_Success:
        oechem.OEThrow.Fatal("Unable to open database for primary query hits")
    primaryOverlay = oebioisostere.OEBroodOverlay()
    primaryOverlay.SetupRef(query1)
    primaryBuilder = oebioisostere.OEBroodMolBuilder(query1)
    primaryPacket = oebioisostere.OEBroodDBPacket()
    while primaryReader.GetNextPacket(primaryPacket) and len(vecPrimaryHits) < broodOpts.GetPrimaryQueryMaxHits():
        for match in primaryOverlay.Overlay(primaryPacket):
            hit = oebioisostere.OEBroodHit()
            if primaryBuilder.Build(match, hit) == oebioisostere.OEBroodStatusCode_Success:
                vecPrimaryHits.append(hit)
                if len(vecPrimaryHits) >= broodOpts.GetPrimaryQueryMaxHits():
                    break

    if len(vecPrimaryHits) == 0:
        oechem.OEThrow.Fatal("Unable to build primary hits from -in1")

    totalSecondaryHits = 0
    written = 0
    allSecondaryHits = []

    # The attempt budget below bounds Build() calls per primary hit
    # regardless of success rate.
    maxBuildAttempts = broodOpts.GetSecondaryQueryMaxHits() * 20

    # For each primary hit from query1, run query2 as the reference query in combo mode.
    # The per-primary cap keeps each combo expansion bounded by -secondaryQueryMaxHits.
    for primaryHit in vecPrimaryHits:
        secondaryReader = oebioisostere.OEDBReader()
        if secondaryReader.Init(broodOpts.GetDatabase()) != oebioisostere.OEBroodStatusCode_Success:
            oechem.OEThrow.Fatal("Unable to build secondary combo hits from -in2")
        # Build an overlay pipeline for query2, then combine each score with the current
        # primary hit via OEBroodComboBuilder.
        secondaryOverlay = oebioisostere.OEBroodOverlay()
        secondaryOverlay.SetupRef(query2)
        secondaryBuilder = oebioisostere.OEBroodComboBuilder(query2, primaryHit)
        secondaryHits = []
        secondaryPacket = oebioisostere.OEBroodDBPacket()
        buildAttempts = 0
        done = False
        # Stream the database packets; stop once this primary hit reaches its cap
        # or exhausts the attempt budget.
        while secondaryReader.GetNextPacket(secondaryPacket) and not done:
            for match in secondaryOverlay.Overlay(secondaryPacket):
                hit = oebioisostere.OEBroodHit()
                # Combo build succeeds only when the secondary replacement can be merged with
                # the primary context for the current query2 reference.
                if secondaryBuilder.Build(match, hit) == oebioisostere.OEBroodStatusCode_Success:
                    secondaryHits.append(hit)
                    if len(secondaryHits) >= broodOpts.GetSecondaryQueryMaxHits():
                        done = True
                        break
                buildAttempts += 1
                if buildAttempts >= maxBuildAttempts:
                    done = True
                    break
        allSecondaryHits.extend(secondaryHits)

    if len(allSecondaryHits) > 0:
        # Cluster and rank all secondary hits before writing output molecules.
        clBuilder = oebioisostere.OEBroodClusterBuilder(query1)
        vecHits = []
        for hit in allSecondaryHits:
            vecHits.append(hit)

        if not clBuilder.Add(vecHits):
            oechem.OEThrow.Fatal("Unable to cluster secondary combo hits")
        if not clBuilder.Rank():
            oechem.OEThrow.Fatal("Unable to rank secondary combo clusters")

        vecClusters = clBuilder.GetClusters()

        for cluster in vecClusters:
            oechem.OEWriteMolecule(ofs, cluster.GetHead().GetMol())
            written += 1
            totalSecondaryHits += 1

        for cluster in vecClusters:
            for hit in cluster.GetMembers():
                oechem.OEWriteMolecule(ofs, hit.GetMol())
                written += 1
                totalSecondaryHits += 1

    print("Primary hits: %d" % len(vecPrimaryHits))
    print("Secondary combo hits: %d" % totalSecondaryHits)
    print("Wrote %d molecules to %s" % (written, opts.GetOutFile()))
    return 0


if __name__ == "__main__":
    import sys

    sys.exit(main(sys.argv))

Download code

brood_combo_builder.py

Curating Scored Fragment Hits with OEScoreHitlist

The following code example shows how to collect scored fragment matches for a query and curate them with OEScoreHitlist.

This example demonstrates score-level deduplication and ranking prior to full molecule building.

See also

Listing 11: Curating Scored Fragment Hits with OEScoreHitlist

#!/usr/bin/env python
# (C) 2022 Cadence Design Systems, Inc. (Cadence)
# All rights reserved.
# TERMS FOR USE OF SAMPLE CODE The software below ("Sample Code") is
# provided to current licensees or subscribers of Cadence products or
# SaaS offerings (each a "Customer").
# Customer is hereby permitted to use, copy, and modify the Sample Code,
# subject to these terms. Cadence claims no rights to Customer's
# modifications. Modification of Sample Code is at Customer's sole and
# exclusive risk. Sample Code may require Customer to have a then
# current license or subscription to the applicable Cadence offering.
# THE SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED. OPENEYE DISCLAIMS ALL WARRANTIES, INCLUDING, BUT
# NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
# PARTICULAR PURPOSE AND NONINFRINGEMENT. In no event shall Cadence be
# liable for any damages or liability in connection with the Sample Code
# or its use.

from openeye import oechem
from openeye import oebioisostere

# Demonstrates how to collect, curate, and write scored Brood fragment hits
# with OEScoreHitlist starting from a query and fragment database.

class BroodScoreHitlistOptions(oechem.OEOptions):
    def __init__(self):
        oechem.OEOptions.__init__(self, "BroodScoreHitlistOptions")

        dbParam = oechem.OEStringParameter("-db")
        dbParam.SetRequired(True)
        dbParam.SetVisibility(oechem.OEParamVisibility_Simple)
        dbParam.SetBrief("Brood database folder")
        self._dbParam = self.AddParameter(dbParam)

        maxHitsParam = oechem.OEUIntParameter("-maxScoreHits", 100)
        maxHitsParam.SetVisibility(oechem.OEParamVisibility_Simple)
        maxHitsParam.SetBrief("Maximum number of unique scored fragments curated by OEScoreHitlist")
        self._maxHitsParam = self.AddParameter(maxHitsParam)

    def CreateCopy(self):
        return self

    def GetDatabase(self):
        return self._dbParam.GetStringValue()

    def GetMaxScoreHits(self):
        if self._maxHitsParam.GetHasValue():
            return int(self._maxHitsParam.GetStringValue())
        return int(self._maxHitsParam.GetStringDefault())


def main(argv=[__name__]):
    broodOpts = BroodScoreHitlistOptions()
    opts = oechem.OESimpleAppOptions(
        broodOpts,
        "BroodScoreHitlist",
        oechem.OEFileStringType_Mol3D,
        oechem.OEFileStringType_Mol3D,
    )
    if oechem.OEConfigureOpts(opts, argv, False) == oechem.OEOptsConfigureStatus_Help:
        return 0
    broodOpts.UpdateValues(opts)

    ifs = oechem.oemolistream()
    if not ifs.open(opts.GetInFile()):
        oechem.OEThrow.Fatal("Unable to open %s for reading" % opts.GetInFile())

    ofs = oechem.oemolostream()
    if not ofs.open(opts.GetOutFile()):
        oechem.OEThrow.Fatal("Unable to open %s for writing" % opts.GetOutFile())

    query = oebioisostere.OEBroodQuery()
    retCode = oebioisostere.OEReadBroodQuery(ifs, query)
    if retCode != oebioisostere.OEBroodStatusCode_Success:
        oechem.OEThrow.Fatal("Failed to read query: %s" % oebioisostere.OEGetBroodStatus(retCode))

    # Read database packets and overlay them against the query.
    reader = oebioisostere.OEDBReader()
    if reader.Init(broodOpts.GetDatabase(), query) != oebioisostere.OEBroodStatusCode_Success:
        oechem.OEThrow.Fatal("Unable to open Brood database '%s'" % broodOpts.GetDatabase())

    overlay = oebioisostere.OEBroodOverlay()
    overlay.SetupRef(query)

    # Use OEScoreHitlist to deduplicate and rank scored fragment matches.
    scoreHitlist = oebioisostere.OEScoreHitlist(broodOpts.GetMaxScoreHits())

    packetCount = 0
    packet = oebioisostere.OEBroodDBPacket()
    while reader.GetNextPacket(packet):
        packetCount += 1
        scoreHitlist.AddScores(overlay.Overlay(packet))

    scoreHitlist.Build()

    print("Database packets processed:    %d" % packetCount)
    print("Total scores added:            %d" % scoreHitlist.GetAddCount())
    print("Unique fragment matches:       %d" % scoreHitlist.GetMatchCount())
    print("Duplicate scores filtered:     %d" % scoreHitlist.GetDuplicateCount())
    print("Curated score hits:            %d" % scoreHitlist.GetHitCount())

    # Write the curated fragment hits selected by the score hitlist.
    for score in scoreHitlist.GetHits():
        oechem.OEWriteMolecule(ofs, score.GetFrag())

    print("Wrote %d molecules to %s" % (scoreHitlist.GetHitCount(), opts.GetOutFile()))
    return 0


if __name__ == "__main__":
    import sys

    sys.exit(main(sys.argv))


Download code

brood_score_hitlist.py

Comparing Connection and Molecule Building Outcomes

The following code example shows how to compare connection-table construction and full molecule-building outcomes from curated scored fragments.

This example demonstrates use of OEMolCTBuilder, OEBroodMolBuilder, and OEBroodBuildResult for build diagnostics.

Listing 12: Comparing Connection and Molecule Building Outcomes

#!/usr/bin/env python
# (C) 2022 Cadence Design Systems, Inc. (Cadence)
# All rights reserved.
# TERMS FOR USE OF SAMPLE CODE The software below ("Sample Code") is
# provided to current licensees or subscribers of Cadence products or
# SaaS offerings (each a "Customer").
# Customer is hereby permitted to use, copy, and modify the Sample Code,
# subject to these terms. Cadence claims no rights to Customer's
# modifications. Modification of Sample Code is at Customer's sole and
# exclusive risk. Sample Code may require Customer to have a then
# current license or subscription to the applicable Cadence offering.
# THE SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED. OPENEYE DISCLAIMS ALL WARRANTIES, INCLUDING, BUT
# NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
# PARTICULAR PURPOSE AND NONINFRINGEMENT. In no event shall Cadence be
# liable for any damages or liability in connection with the Sample Code
# or its use.

from openeye import oechem
from openeye import oebioisostere

# Demonstrates how to take curated Brood scores and inspect connection-table
# and molecule-building outcomes with OEMolCTBuilder, OEBroodMolBuilder, and
# OEBroodBuildResult.

class BroodConnectionMolBuilderOptions(oechem.OEOptions):
    def __init__(self):
        oechem.OEOptions.__init__(self, "BroodConnectionMolBuilderOptions")

        dbParam = oechem.OEStringParameter("-db")
        dbParam.SetRequired(True)
        dbParam.SetVisibility(oechem.OEParamVisibility_Simple)
        dbParam.SetBrief("Brood database folder")
        self._dbParam = self.AddParameter(dbParam)

        maxHitsParam = oechem.OEUIntParameter("-maxScoreHits", 100)
        maxHitsParam.SetVisibility(oechem.OEParamVisibility_Simple)
        maxHitsParam.SetBrief("Maximum number of curated scores used for CT and molecule building")
        self._maxHitsParam = self.AddParameter(maxHitsParam)

    def CreateCopy(self):
        return self

    def GetDatabase(self):
        return self._dbParam.GetStringValue()

    def GetMaxScoreHits(self):
        if self._maxHitsParam.GetHasValue():
            return int(self._maxHitsParam.GetStringValue())
        return int(self._maxHitsParam.GetStringDefault())

def main(argv=[__name__]):
    broodOpts = BroodConnectionMolBuilderOptions()
    opts = oechem.OESimpleAppOptions(
        broodOpts,
        "BroodConnectionMolBuilder",
        oechem.OEFileStringType_Mol3D,
        oechem.OEFileStringType_Mol3D,
    )
    if oechem.OEConfigureOpts(opts, argv, False) == oechem.OEOptsConfigureStatus_Help:
        return 0
    broodOpts.UpdateValues(opts)

    ifs = oechem.oemolistream()
    if not ifs.open(opts.GetInFile()):
        oechem.OEThrow.Fatal("Unable to open %s for reading" % opts.GetInFile())

    ofs = oechem.oemolostream()
    if not ofs.open(opts.GetOutFile()):
        oechem.OEThrow.Fatal("Unable to open %s for writing" % opts.GetOutFile())

    query = oebioisostere.OEBroodQuery()
    retCode = oebioisostere.OEReadBroodQuery(ifs, query)
    if retCode != oebioisostere.OEBroodStatusCode_Success:
        oechem.OEThrow.Fatal("Failed to read query: %s" % oebioisostere.OEGetBroodStatus(retCode))

    # Gather scored fragment matches for the query from the Brood database.
    reader = oebioisostere.OEDBReader()
    if reader.Init(broodOpts.GetDatabase(), query) != oebioisostere.OEBroodStatusCode_Success:
        oechem.OEThrow.Fatal("Unable to open Brood database '%s'" % broodOpts.GetDatabase())

    overlay = oebioisostere.OEBroodOverlay()
    overlay.SetupRef(query)

    # Curate the raw scores before running CT and molecule building.
    scoreHitlist = oebioisostere.OEScoreHitlist(broodOpts.GetMaxScoreHits())
    packet = oebioisostere.OEBroodDBPacket()
    while reader.GetNextPacket(packet):
        scoreHitlist.AddScores(overlay.Overlay(packet))
    scoreHitlist.Build()

    if scoreHitlist.GetHitCount() == 0:
        oechem.OEThrow.Warning("No fragments survived overlay scoring; nothing to build.")
        return 0

    # Compare connection building and molecule building on the curated scores.
    ctBuilder = oebioisostere.OEMolCTBuilder(query)
    molBuilder = oebioisostere.OEBroodMolBuilder(query)

    written = 0
    failedBuild = 0
    failedCT = 0
    ctAttempts = 0

    for idx, score in enumerate(scoreHitlist.GetHits(), start=1):
        # Rebuild the candidate connection table from the scored fragment.
        ctMol = oechem.OEGraphMol(score.GetFrag())
        ctOk = ctBuilder.Build(ctMol)
        ctAttempts += 1
        if not ctOk:
            failedCT += 1

        # Build the full Brood hit directly from the same scored fragment.
        hit = oebioisostere.OEBroodHit()
        status = molBuilder.Build(score, hit)
        if status != oebioisostere.OEBroodStatusCode_Success:
            failedBuild += 1
            print("[%4d] Build(score) failed: status=%s, ct=%s"
                  % (idx, oebioisostere.OEGetBroodStatus(status), "pass" if ctOk else "fail"))
            continue

        # Collect build diagnostics for reporting with OEBroodBuildResult.
        diagMol = oechem.OEGraphMol(score.GetFrag())
        result = oebioisostere.OEBroodBuildResult()
        diagStatus = molBuilder.Build(diagMol, result)

        oechem.OEWriteMolecule(ofs, hit.GetMol())
        written += 1

        print("[%4d] ct=%s diagStatus=%-14s buildStatus=%-14s molTC=%.3f strain=%6.2f dStrain=%6.2f"
              % (idx,
                 "pass" if ctOk else "fail",
                 oebioisostere.OEGetBroodStatus(diagStatus),
                 oebioisostere.OEGetBroodStatus(result.GetBuildStatus()),
                 result.GetMolTanimotoCombo(),
                 result.GetLocalStrain(),
                 result.GetDeltaLocalStrain()))

    print("---- Summary ----")
    print("Curated scores:          %d" % scoreHitlist.GetHitCount())
    print("Hits written:            %d" % written)
    print("Mol build failures:      %d" % failedBuild)
    print("CT checks attempted:     %d" % ctAttempts)
    print("CT build failures:       %d" % failedCT)
    return 0


if __name__ == "__main__":
    import sys

    sys.exit(main(sys.argv))