/** * Performs the pharmacophore matching. * * @param atomContainer The target molecule. Must have 3D coordinates * @param initializeTarget If <i>true</i>, the target molecule specified in the first argument * will be analyzed to identify matching pharmacophore groups. If <i>false</i> this is not * performed. The latter case is only useful when dealing with conformers since for a given * molecule, all conformers will have the same pharmacophore groups and only the constraints * will change from one conformer to another. * @return true is the target molecule contains the query pharmacophore * @throws org.openscience.cdk.exception.CDKException if the query pharmacophore was not set or * the query is invalid or if the molecule does not have 3D coordinates */ public boolean matches(IAtomContainer atomContainer, boolean initializeTarget) throws CDKException { if (!GeometryUtil.has3DCoordinates(atomContainer)) throw new CDKException("Molecule must have 3D coordinates"); if (pharmacophoreQuery == null) throw new CDKException("Must set the query pharmacophore before matching"); if (!checkQuery(pharmacophoreQuery)) throw new CDKException( "A problem in the query. Make sure all pharmacophore groups of the same symbol have the same same SMARTS"); String title = (String) atomContainer.getProperty(CDKConstants.TITLE); if (initializeTarget) pharmacophoreMolecule = getPharmacophoreMolecule(atomContainer); else { // even though the atoms comprising the pcore groups are // constant, their coords will differ, so we need to make // sure we get the latest set of effective coordinates for (IAtom iAtom : pharmacophoreMolecule.atoms()) { PharmacophoreAtom patom = (PharmacophoreAtom) iAtom; List<Integer> tmpList = new ArrayList<Integer>(); for (int idx : patom.getMatchingAtoms()) tmpList.add(idx); Point3d coords = getEffectiveCoordinates(atomContainer, tmpList); patom.setPoint3d(coords); } } if (pharmacophoreMolecule.getAtomCount() < pharmacophoreQuery.getAtomCount()) { logger.debug("Target [" + title + "] did not match the query SMARTS. Skipping constraints"); return false; } mappings = Pattern.findSubstructure(pharmacophoreQuery).matchAll(pharmacophoreMolecule); // XXX: doing one search then discarding return mappings.atLeast(1); }
/** * Convert the input into a pcore molecule. * * @param input the compound being converted from * @return pcore molecule * @throws CDKException match failed */ private IAtomContainer getPharmacophoreMolecule(IAtomContainer input) throws CDKException { // XXX: prepare query, to be moved prepareInput(input); IAtomContainer pharmacophoreMolecule = input.getBuilder().newInstance(IAtomContainer.class, 0, 0, 0, 0); final Set<String> matched = new HashSet<>(); final Set<PharmacophoreAtom> uniqueAtoms = new LinkedHashSet<>(); logger.debug("Converting [" + input.getProperty(CDKConstants.TITLE) + "] to a pcore molecule"); // lets loop over each pcore query atom for (IAtom atom : pharmacophoreQuery.atoms()) { final PharmacophoreQueryAtom qatom = (PharmacophoreQueryAtom) atom; final String smarts = qatom.getSmarts(); // a pcore query might have multiple instances of a given pcore atom (say // 2 hydrophobic groups separated by X unit). In such a case we want to find // the atoms matching the pgroup SMARTS just once, rather than redoing the // matching for each instance of the pcore query atom. if (!matched.add(qatom.getSymbol())) continue; // see if the smarts for this pcore query atom gets any matches // in our query molecule. If so, then collect each set of // matching atoms and for each set make a new pcore atom and // add it to the pcore atom container object int count = 0; for (final IQueryAtomContainer query : qatom.getCompiledSmarts()) { // create the lazy mappings iterator final Mappings mappings = Pattern.findSubstructure(query).matchAll(input).uniqueAtoms(); for (final int[] mapping : mappings) { uniqueAtoms.add(newPCoreAtom(input, qatom, smarts, mapping)); count++; } } logger.debug("\tFound " + count + " unique matches for " + smarts); } pharmacophoreMolecule.setAtoms(uniqueAtoms.toArray(new IAtom[uniqueAtoms.size()])); // now that we have added all the pcore atoms to the container // we need to join all atoms with pcore bonds (i.e. distance constraints) if (hasDistanceConstraints(pharmacophoreQuery)) { int npatom = pharmacophoreMolecule.getAtomCount(); for (int i = 0; i < npatom - 1; i++) { for (int j = i + 1; j < npatom; j++) { PharmacophoreAtom atom1 = (PharmacophoreAtom) pharmacophoreMolecule.getAtom(i); PharmacophoreAtom atom2 = (PharmacophoreAtom) pharmacophoreMolecule.getAtom(j); PharmacophoreBond bond = new PharmacophoreBond(atom1, atom2); pharmacophoreMolecule.addBond(bond); } } } // if we have angle constraints, generate only the valid // possible angle relationships, rather than all possible if (hasAngleConstraints(pharmacophoreQuery)) { int nangleDefs = 0; for (IBond bond : pharmacophoreQuery.bonds()) { if (!(bond instanceof PharmacophoreQueryAngleBond)) continue; IAtom startQAtom = bond.getAtom(0); IAtom middleQAtom = bond.getAtom(1); IAtom endQAtom = bond.getAtom(2); // make a list of the patoms in the target that match // each type of angle atom List<IAtom> startl = new ArrayList<IAtom>(); List<IAtom> middlel = new ArrayList<IAtom>(); List<IAtom> endl = new ArrayList<IAtom>(); for (IAtom tatom : pharmacophoreMolecule.atoms()) { if (tatom.getSymbol().equals(startQAtom.getSymbol())) startl.add(tatom); if (tatom.getSymbol().equals(middleQAtom.getSymbol())) middlel.add(tatom); if (tatom.getSymbol().equals(endQAtom.getSymbol())) endl.add(tatom); } // now we form the relevant angles, but we will // have reversed repeats List<IAtom[]> tmpl = new ArrayList<IAtom[]>(); for (IAtom middle : middlel) { for (IAtom start : startl) { if (middle.equals(start)) continue; for (IAtom end : endl) { if (start.equals(end) || middle.equals(end)) continue; tmpl.add(new IAtom[] {start, middle, end}); } } } // now clean up reversed repeats List<IAtom[]> unique = new ArrayList<IAtom[]>(); for (int i = 0; i < tmpl.size(); i++) { IAtom[] seq1 = tmpl.get(i); boolean isRepeat = false; for (int j = 0; j < unique.size(); j++) { if (i == j) continue; IAtom[] seq2 = unique.get(j); if (seq1[1] == seq2[1] && seq1[0] == seq2[2] && seq1[2] == seq2[0]) { isRepeat = true; } } if (!isRepeat) unique.add(seq1); } // finally we can add the unique angle to the target for (IAtom[] seq : unique) { PharmacophoreAngleBond pbond = new PharmacophoreAngleBond( (PharmacophoreAtom) seq[0], (PharmacophoreAtom) seq[1], (PharmacophoreAtom) seq[2]); pharmacophoreMolecule.addBond(pbond); nangleDefs++; } } logger.debug("Added " + nangleDefs + " defs to the target pcore molecule"); } return pharmacophoreMolecule; }