$darkmode
DENOPTIM
FragmenterTools.java
Go to the documentation of this file.
1package denoptim.fragmenter;
2
3import java.io.File;
4import java.io.FileInputStream;
5import java.io.IOException;
6import java.util.ArrayList;
7import java.util.HashMap;
8import java.util.HashSet;
9import java.util.List;
10import java.util.Map;
11import java.util.Set;
12import java.util.logging.Level;
13import java.util.logging.Logger;
14
15import javax.vecmath.Point3d;
16
17import org.openscience.cdk.Bond;
18import org.openscience.cdk.DefaultChemObjectBuilder;
19import org.openscience.cdk.PseudoAtom;
20import org.openscience.cdk.config.Isotopes;
21import org.openscience.cdk.exception.CDKException;
22import org.openscience.cdk.interfaces.IAtom;
23import org.openscience.cdk.interfaces.IAtomContainer;
24import org.openscience.cdk.interfaces.IBond;
25import org.openscience.cdk.interfaces.IIsotope;
26import org.openscience.cdk.io.iterator.IteratingSDFReader;
27import org.openscience.cdk.isomorphism.Mappings;
28import org.openscience.cdk.isomorphism.Pattern;
29import org.openscience.cdk.silent.SilentChemObjectBuilder;
30import org.openscience.cdk.tools.manipulator.AtomContainerManipulator;
31
32import denoptim.constants.DENOPTIMConstants;
33import denoptim.exception.DENOPTIMException;
34import denoptim.files.FileFormat;
35import denoptim.files.UndetectedFileFormatException;
36import denoptim.graph.APClass;
37import denoptim.graph.AttachmentPoint;
38import denoptim.graph.DGraph;
39import denoptim.graph.Edge;
40import denoptim.graph.Fragment;
41import denoptim.graph.Template;
42import denoptim.graph.Vertex;
43import denoptim.graph.Vertex.BBType;
44import denoptim.graph.rings.RingClosingAttractor;
45import denoptim.io.DenoptimIO;
46import denoptim.io.IteratingAtomContainerReader;
47import denoptim.molecularmodeling.ThreeDimTreeBuilder;
48import denoptim.programs.RunTimeParameters.ParametersType;
49import denoptim.programs.fragmenter.CuttingRule;
50import denoptim.programs.fragmenter.FragmenterParameters;
51import denoptim.programs.fragmenter.MatchedBond;
52import denoptim.utils.DummyAtomHandler;
53import denoptim.utils.FormulaUtils;
54import denoptim.utils.ManySMARTSQuery;
55import denoptim.utils.MoleculeUtils;
56import denoptim.utils.Randomizer;
57
58public class FragmenterTools
59{
60
61//------------------------------------------------------------------------------
62
77 public static void checkElementalAnalysisAgainstFormula(File input,
78 File output, Logger logger)
79 throws DENOPTIMException, IOException
80 {
81 FileInputStream fis = new FileInputStream(input);
82 IteratingSDFReader reader = new IteratingSDFReader(fis,
83 DefaultChemObjectBuilder.getInstance());
84
85 int index = -1;
86 int maxBufferSize = 2000;
87 ArrayList<IAtomContainer> buffer = new ArrayList<IAtomContainer>(500);
88 try {
89 while (reader.hasNext())
90 {
91 index++;
92 if (logger!=null)
93 {
94 logger.log(Level.FINE,"Checking elemental analysis of "
95 + "structure " + index);
96 }
97 IAtomContainer mol = reader.next();
98 if (mol.getProperty(DENOPTIMConstants.FORMULASTR)==null)
99 {
100 throw new Error("Property '" + DENOPTIMConstants.FORMULASTR
101 + "' not found in molecule " + index + " in file "
102 + input + ". Cannot compare formula with elemental"
103 + "analysis.");
104 }
105 String formula = mol.getProperty(DENOPTIMConstants.FORMULASTR)
106 .toString();
107
109 mol, logger))
110 {
111 buffer.add(mol);
112 } else {
113 if (logger!=null)
114 {
115 logger.log(Level.INFO,"Inconsistency between elemental "
116 + "analysis of structure and molecular formula."
117 + " Rejecting structure " + index + ": "
118 + mol.getTitle());
119 }
120 }
121
122 // If max buffer size is reached, then bump to file
123 if (buffer.size() >= maxBufferSize)
124 {
125 DenoptimIO.writeSDFFile(output.getAbsolutePath(), buffer,
126 true);
127 buffer.clear();
128 }
129 }
130 }
131 finally {
132 reader.close();
133 }
134 if (buffer.size() < maxBufferSize)
135 {
136 DenoptimIO.writeSDFFile(output.getAbsolutePath(), buffer, true);
137 buffer.clear();
138 }
139 }
140
141//------------------------------------------------------------------------------
142
143
157 public static boolean prepareMolToFragmentation(IAtomContainer mol,
158 FragmenterParameters settings, int index)
159 {
160 try
161 {
162 if (settings.addExplicitH())
163 {
165 } else {
167 }
169 } catch (CDKException e)
170 {
171 if (e.getMessage().contains("Cannot assign Kekulé structure"))
172 {
173 if (!settings.acceptUnsetToSingeBO())
174 {
175 settings.getLogger().log(Level.WARNING,"Some bond order "
176 + "are unset and attempt to kekulize the "
177 + "system has failed "
178 + "for structure " + index + ". "
179 + "This hampers use of SMARTS queries, which "
180 + "may very well "
181 + "not work as expected. Structure " + index
182 + " will be rejected. "
183 + "You can avoid rejection by using "
184 + "keyword "
185 + ParametersType.FRG_PARAMS.getKeywordRoot()
186 + "UNSETTOSINGLEBO, but you'll "
187 + "still be using a peculiar connectivity "
188 + "table were "
189 + "many bonds are artificially marked as "
190 + "single to "
191 + "avoid use of 'UNSET' bond order. "
192 + "Further details on the problem: "
193 + e.getMessage());
194 return false;
195 } else {
196 settings.getLogger().log(Level.WARNING,"Failed "
197 + "kekulization "
198 + "for structure " + index
199 + " but UNSETTOSINGLEBO "
200 + "keyword used. Forcing use of single bonds to "
201 + "replace bonds with unset order.");
202 for (IBond bnd : mol.bonds())
203 {
204 if (bnd.getOrder().equals(IBond.Order.UNSET))
205 {
206 bnd.setOrder(IBond.Order.SINGLE);
207 }
208 }
209 }
210 }
211 }
212 return true;
213 }
214
215//------------------------------------------------------------------------------
216
228 public static void filterStrucutresBySMARTS(File input, Set<String> smarts,
229 File output, Logger logger)
230 throws DENOPTIMException, IOException
231 {
232 FileInputStream fis = new FileInputStream(input);
233 IteratingSDFReader reader = new IteratingSDFReader(fis,
234 DefaultChemObjectBuilder.getInstance());
235
236 int i = -1;
237 Map<String, String> smartsMap = new HashMap<String, String>();
238 for (String s : smarts)
239 {
240 i++;
241 smartsMap.put("prefilter-"+i, s);
242 }
243
244 int index = -1;
245 int maxBufferSize = 2000;
246 ArrayList<IAtomContainer> buffer = new ArrayList<IAtomContainer>(500);
247 try {
248 while (reader.hasNext())
249 {
250 index++;
251 if (logger!=null)
252 {
253 logger.log(Level.FINE,"Prefiltering structure " + index);
254 }
255 IAtomContainer mol = reader.next();
256
257 ManySMARTSQuery msq = new ManySMARTSQuery(mol, smartsMap);
258 if (msq.hasProblems())
259 {
260 String msg = "WARNING! Problems while searching for "
261 + "specific atoms/bonds using SMARTS: "
262 + msq.getMessage();
263 throw new DENOPTIMException(msg,msq.getProblem());
264 }
265 Map<String, Mappings> allMatches = msq.getAllMatches();
266
267 if (allMatches.size()==0)
268 {
269 buffer.add(mol);
270 } else {
271 String hits = "";
272 for (String s : allMatches.keySet())
273 hits = hits + DenoptimIO.NL + smartsMap.get(s);
274 if (logger!=null)
275 {
276 logger.log(Level.INFO,"Found match for " + hits
277 + "Rejecting structure " + index + ": "
278 + mol.getTitle());
279 }
280 }
281
282 // If max buffer size is reached, then bump to file
283 if (buffer.size() >= maxBufferSize)
284 {
285 DenoptimIO.writeSDFFile(output.getAbsolutePath(), buffer,
286 true);
287 buffer.clear();
288 }
289 }
290 } finally {
291 reader.close();
292 }
293 if (buffer.size() < maxBufferSize)
294 {
295 DenoptimIO.writeSDFFile(output.getAbsolutePath(), buffer, true);
296 buffer.clear();
297 }
298 }
299
300//-----------------------------------------------------------------------------
301
321 public static boolean fragmentation(File input, FragmenterParameters settings,
322 File output, Logger logger) throws CDKException, IOException,
323 DENOPTIMException, IllegalArgumentException, UndetectedFileFormatException
324 {
327
328 int totalProd = 0;
329 int totalKept = 0;
330 int index = -1;
331 try {
332 while (iterator.hasNext())
333 {
334 index++;
335 if (logger!=null)
336 {
337 logger.log(Level.FINE,"Fragmenting structure " + index);
338 }
339 IAtomContainer mol = iterator.next();
340 String molName = "noname-mol" + index;
341 if (mol.getTitle()!=null && !mol.getTitle().isBlank())
342 molName = mol.getTitle();
343
344 // Generate the fragments
345 List<Vertex> fragments = new ArrayList<Vertex>();
346 if (settings.getFragmentationTmpls().size()>0)
347 {
348 fragments = fragmentation(mol, settings.getFragmentationTmpls(),
349 settings.getRandomizer(), logger);
350 } else {
351 fragments = fragmentation(mol, settings.getCuttingRules(),
352 logger);
353 }
354 if (logger!=null)
355 {
356 logger.log(Level.FINE,"Fragmentation produced "
357 + fragments.size() + " fragments.");
358 }
359 totalProd += fragments.size();
360
361 // Post-fragmentation processing of fragments
362 List<Vertex> keptFragments = new ArrayList<Vertex>();
363 int fragCounter = 0;
364 for (Vertex frag : fragments)
365 {
366 // Add metadata
367 String fragIdStr = "From_" + molName + "_" + fragCounter;
368 frag.setProperty("cdk:Title", fragIdStr);
369 fragCounter++;
370 manageFragmentCollection(frag, fragCounter, settings,
371 keptFragments, logger);
372 }
373 if (logger!=null)
374 {
375 logger.log(Level.FINE,"Fragments surviving post-"
376 + "processing: " + keptFragments.size());
377 }
378 totalKept += keptFragments.size();
379 if (!settings.doManageIsomorphicFamilies() && totalKept>0)
380 {
382 keptFragments,true);
383 }
384 }
385 } finally {
386 iterator.close();
387 }
388
389 // Did we actually produce anything? We might not...
390 if (totalProd==0)
391 {
392 if (logger!=null)
393 {
394 logger.log(Level.WARNING,"No fragment produced. Cutting rules "
395 + "were ineffective on the given structures.");
396 }
397 return false;
398 } else if (totalKept==0)
399 {
400 if (logger!=null)
401 {
402 logger.log(Level.WARNING,"No fragment kept out of " + totalProd
403 + " produced fragments. Filtering criteria might be "
404 + "too restrictive.");
405 }
406 return false;
407 }
408 return true;
409 }
410
411//------------------------------------------------------------------------------
412
423 private static IAtomContainer reduceTemplateToVertexBoundaries(IAtomContainer templateMol)
424 {
425 // Find all boundary atoms: atoms connected to atoms with different vertex IDs
426 Set<IAtom> boundaryAtoms = new HashSet<>();
427 Map<IAtom, Long> atomToVertexId = new HashMap<>();
428
429 // First pass: collect vertex IDs and identify boundary atoms
430 for (IAtom atom : templateMol.atoms())
431 {
432 Object vidProp = atom.getProperty(DENOPTIMConstants.ATMPROPVERTEXID);
433 if (vidProp == null)
434 {
435 // If no vertex ID, keep all atoms (can't optimize)
436 return templateMol;
437 }
438 Long vid = Long.parseLong(vidProp.toString());
439 atomToVertexId.put(atom, vid);
440
441 // Check neighbors for different vertex IDs
442 List<IAtom> neighbors = templateMol.getConnectedAtomsList(atom);
443 for (IAtom neighbor : neighbors)
444 {
445 Object nbrVidProp = neighbor.getProperty(DENOPTIMConstants.ATMPROPVERTEXID);
446 if (nbrVidProp != null)
447 {
448 Long nbrVid = Long.parseLong(nbrVidProp.toString());
449 if (!vid.equals(nbrVid))
450 {
451 boundaryAtoms.add(atom);
452 boundaryAtoms.add(neighbor);
453 break;
454 }
455 }
456 }
457 }
458
459 // If no boundary atoms found, return original (all atoms have same vertex ID)
460 if (boundaryAtoms.isEmpty())
461 {
462 return templateMol;
463 }
464
465 // Find minimal chains connecting boundary atoms using BFS
466 Set<IAtom> atomsToKeep = new HashSet<>(boundaryAtoms);
467
468 // For each pair of boundary atoms, find shortest path
469 List<IAtom> boundaryList = new ArrayList<>(boundaryAtoms);
470 for (int i = 0; i < boundaryList.size(); i++)
471 {
472 for (int j = i + 1; j < boundaryList.size(); j++)
473 {
474 IAtom start = boundaryList.get(i);
475 IAtom end = boundaryList.get(j);
476
477 // Find shortest path between these boundary atoms
478 List<IAtom> path = MoleculeUtils.findShortestPath(templateMol, start, end, atomToVertexId);
479 if (path != null)
480 {
481 atomsToKeep.addAll(path);
482 }
483 }
484 }
485
486 // Create reduced molecule with only atoms to keep
487 IAtomContainer reduced = SilentChemObjectBuilder.getInstance().newAtomContainer();
488 Map<IAtom, IAtom> originalToReduced = new HashMap<>(); // original atom -> reduced atom
489
490 // Add atoms with original index stored as property
491 for (IAtom originalAtom : atomsToKeep)
492 {
493 IAtom reducedAtom = originalAtom.getBuilder().newInstance(IAtom.class, originalAtom);
494 reduced.addAtom(reducedAtom);
495 originalToReduced.put(originalAtom, reducedAtom);
496
497 // Store the original atom index as a property
498 int originalIndex = templateMol.indexOf(originalAtom);
499 reducedAtom.setProperty("DENOPTIM_ORIGINAL_ATOM_INDEX", originalIndex);
500
501 // Copy all other properties
502 for (Object key : originalAtom.getProperties().keySet())
503 {
504 if (!key.equals("DENOPTIM_ORIGINAL_ATOM_INDEX"))
505 {
506 reducedAtom.setProperty(key, originalAtom.getProperty(key));
507 }
508 }
509 }
510
511 // Add bonds between kept atoms
512 for (IBond bond : templateMol.bonds())
513 {
514 IAtom atom1 = bond.getAtom(0);
515 IAtom atom2 = bond.getAtom(1);
516
517 if (atomsToKeep.contains(atom1) && atomsToKeep.contains(atom2))
518 {
519 IBond newBond = bond.getBuilder().newInstance(IBond.class,
520 originalToReduced.get(atom1), originalToReduced.get(atom2), bond.getOrder());
521 reduced.addBond(newBond);
522 }
523 }
524
525 return reduced;
526 }
527
528
529//------------------------------------------------------------------------------
530
539 public static List<Vertex> fragmentation(IAtomContainer mol,
540 List<DGraph> templates, Randomizer randomizer, Logger logger)
541 throws DENOPTIMException
542 {
543 List<Vertex> fragments = new ArrayList<Vertex>();
544 int bestMatch = -1;
545 for (DGraph template : templates)
546 {
547 ThreeDimTreeBuilder tb = new ThreeDimTreeBuilder(logger, randomizer);
548 IAtomContainer templateMol = tb.convertGraphTo3DAtomContainer(template,
549 false, true, true);
550
551 // Optimize: create reduced templateMol for faster isomorphism search
552 // Keep full templateMol for exploreDGraphForMappings
553 IAtomContainer reducedTemplateMol = reduceTemplateToVertexBoundaries(templateMol);
554
555 List<Map<IAtom,IAtom>> atomMappings;
556
557 // Check if reduction actually happened
558 if (reducedTemplateMol == templateMol)
559 {
560 // No reduction possible, use full template directly
561 atomMappings = MoleculeUtils.findUniqueAtomMappings(templateMol, mol, logger);
562 }
563 else
564 {
565 // Use reduced template for isomorphism search (faster)
566 List<Map<IAtom,IAtom>> reducedAtomMappings = MoleculeUtils.findUniqueAtomMappings(
567 reducedTemplateMol, mol, logger);
568
569 // Convert mappings from reducedTemplateMol:mol to templateMol:mol using stored indices
570 atomMappings = new ArrayList<>();
571 for (Map<IAtom,IAtom> reducedMapping : reducedAtomMappings)
572 {
573 Map<IAtom,IAtom> fullMapping = new HashMap<>();
574 for (Map.Entry<IAtom,IAtom> entry : reducedMapping.entrySet())
575 {
576 IAtom reducedAtom = entry.getKey();
577 IAtom molAtom = entry.getValue();
578
579 // Get original atom index from reduced atom property
580 Object indexObj = reducedAtom.getProperty("DENOPTIM_ORIGINAL_ATOM_INDEX");
581 if (indexObj != null)
582 {
583 int originalIndex = ((Number) indexObj).intValue();
584 IAtom originalAtom = templateMol.getAtom(originalIndex);
585 if (originalAtom != null)
586 {
587 fullMapping.put(originalAtom, molAtom);
588 }
589 }
590 }
591 if (!fullMapping.isEmpty())
592 {
593 atomMappings.add(fullMapping);
594 }
595 }
596 }
597
598 Fragment masterFrag = new Fragment(mol, BBType.UNDEFINED);
599 IAtomContainer masterFragIAC = masterFrag.getIAtomContainer();
600
601 // For each atom mapping, replace bonds corresponding to edges
602 // (at any embedding level) with attachment pointsand annotate embedding level
603 for (Map<IAtom,IAtom> atomMapping : atomMappings)
604 {
605 try {
606 exploreDGraphForMappings(template, templateMol,
607 masterFrag, masterFragIAC, mol, atomMapping);
608 } catch (Throwable e) {
609 e.printStackTrace();
610 logger.log(Level.WARNING, "Error while exploring the template graph: " + e.getMessage());
611 continue;
612 }
613 }
614
615 // Extract isolated fragments
616 List<Vertex> locfragments = new ArrayList<Vertex>();
617 Set<Integer> doneAlready = new HashSet<Integer>();
618 for (int idx=0 ; idx<masterFrag.getAtomCount(); idx++)
619 {
620 if (doneAlready.contains(idx))
621 continue;
622
623 Fragment cloneOfMaster = masterFrag.clone();
624 IAtomContainer iac = cloneOfMaster.getIAtomContainer();
625 Set<IAtom> atmsToKeep = exploreConnectivity(iac.getAtom(idx), iac);
626 atmsToKeep.stream().forEach(atm -> doneAlready.add(iac.indexOf(atm)));
627
628 Set<IAtom> atmsToRemove = new HashSet<IAtom>();
629 for (IAtom atm : cloneOfMaster.atoms())
630 {
631 if (!atmsToKeep.contains(atm))
632 {
633 atmsToRemove.add(atm);
634 }
635 }
636 cloneOfMaster.removeAtoms(atmsToRemove);
637 if (cloneOfMaster.getAttachmentPoints().size()>0)
638 locfragments.add(cloneOfMaster);
639 }
640
641 if (locfragments.size() > bestMatch)
642 {
643 bestMatch = locfragments.size();
644 fragments = locfragments;
645 }
646 }
647 return fragments;
648 }
649
650//------------------------------------------------------------------------------
651
656 private static void exploreDGraphForMappings(DGraph graph, IAtomContainer graphIAC,
657 Fragment masterFrag, IAtomContainer masterFragIAC, IAtomContainer mol,
658 Map<IAtom,IAtom> graphToMolMapping)
659 {
660 for (Edge edge : graph.getEdgeList())
661 {
662 //TODO: what about RCVs?
663 IAtom graphAtmSrc = graphIAC.getAtom(edge.getSrcAP().getAtomPositionNumberInMol());
664 IAtom graphAtmTrg = graphIAC.getAtom(edge.getTrgAP().getAtomPositionNumberInMol());
665
666 IAtom masterFragAtmSrc = masterFragIAC.getAtom(mol.indexOf(graphToMolMapping.get(graphAtmSrc)));
667 IAtom masterFragAtmTrg = masterFragIAC.getAtom(mol.indexOf(graphToMolMapping.get(graphAtmTrg)));
668
669 IBond bnd = masterFragIAC.getBond(masterFragAtmSrc, masterFragAtmTrg);
670 if (bnd != null)
671 {
672 masterFragIAC.removeBond(bnd);
673 }
674
675 masterFrag.addAPOnAtom(masterFragAtmSrc,
676 edge.getSrcAP().getAPClass(),
677 MoleculeUtils.getPoint3d(masterFragAtmTrg));
678 masterFrag.addAPOnAtom(masterFragAtmTrg,
679 edge.getTrgAP().getAPClass(),
680 MoleculeUtils.getPoint3d(masterFragAtmSrc));
681 }
682
683 for (Vertex v : graph.getVertexList())
684 {
685 if (v instanceof Template)
686 {
687 DGraph innerGraph = ((Template) v).getInnerGraph();
688 exploreDGraphForMappings(innerGraph, graphIAC, masterFrag, masterFragIAC, mol, graphToMolMapping);
689 }
690 }
691 }
692
693//------------------------------------------------------------------------------
694
703 public static List<Vertex> fragmentation(IAtomContainer mol,
704 List<CuttingRule> rules, Logger logger) throws DENOPTIMException
705 {
706 Fragment masterFrag = new Fragment(mol,BBType.UNDEFINED);
707 IAtomContainer fragsMol = masterFrag.getIAtomContainer();
708
709 // Identify bonds
710 Map<String, List<MatchedBond>> matchingbonds =
711 FragmenterTools.getMatchingBondsAllInOne(fragsMol,rules,logger);
712
713 // Select bonds to cut and what rule to use for cutting them
714 int cutId = -1;
715 for (CuttingRule rule : rules) // NB: iterator follows rule's priority
716 {
717 String ruleName = rule.getName();
718
719 // Skip unmatched rules
720 if (!matchingbonds.keySet().contains(ruleName))
721 continue;
722
723 for (MatchedBond tb: matchingbonds.get(ruleName))
724 {
725 IAtom atmA = tb.getAtmSubClass0();
726 IAtom atmB = tb.getAtmSubClass1();
727
728 //ignore if bond already broken
729 if (!fragsMol.getConnectedAtomsList(atmA).contains(atmB))
730 {
731 continue;
732 }
733
734 //treatment of n-hapto ligands
735 if (rule.isHAPTO())
736 {
737 // Get central atom (i.e., the "mono-hapto" side,
738 // typically the metal)
739 // As a convention the central atom has subclass '0'
740 IAtom centralAtm = atmA;
741
742 // Get list of candidates for hapto-system:
743 // they have same cutting Rule and central metal
744 ArrayList<IAtom> candidatesForHapto = new ArrayList<IAtom>();
745 for (MatchedBond tbForHapto : matchingbonds.get(ruleName))
746 {
747 //Consider only bond involving same central atom
748 if (tbForHapto.getAtmSubClass0() == centralAtm)
749 candidatesForHapto.add(tbForHapto.getAtmSubClass1());
750 }
751
752 // Select atoms in n-hapto system: contiguous neighbors with
753 // same type of bond with the same central atom.
754 Set<IAtom> atmsInHapto = new HashSet<IAtom>();
755 atmsInHapto.add(tb.getAtmSubClass1());
756 atmsInHapto = exploreHapticity(tb.getAtmSubClass1(),
757 centralAtm, candidatesForHapto, fragsMol);
758 if (atmsInHapto.size() == 1)
759 {
760 logger.log(Level.WARNING,"Unable to find more than one "
761 + "bond involved in high-hapticity ligand! "
762 + "Bond ignored.");
763 continue;
764 }
765
766 // Check existence of all bonds involved in multi-hapto system
767 boolean isSystemIntact = true;
768 for (IAtom ligAtm : atmsInHapto)
769 {
770 List<IAtom> nbrsOfLigAtm =
771 fragsMol.getConnectedAtomsList(ligAtm);
772 if (!nbrsOfLigAtm.contains(centralAtm))
773 {
774 isSystemIntact = false;
775 break;
776 }
777 }
778
779 // If not, it means that another rule already acted on the
780 // system thus kill this attempt without generating du-atom
781 if (!isSystemIntact)
782 continue;
783
784 // A dummy atom will be used to define attachment point of
785 // ligand with high hapticity
786 Point3d dummyP3d = new Point3d(); //Used also for 2D
787 for (IAtom ligAtm : atmsInHapto)
788 {
789 Point3d ligP3d = MoleculeUtils.getPoint3d(ligAtm);
790 dummyP3d.x = dummyP3d.x + ligP3d.x;
791 dummyP3d.y = dummyP3d.y + ligP3d.y;
792 dummyP3d.z = dummyP3d.z + ligP3d.z;
793 }
794
795 dummyP3d.x = dummyP3d.x / (double) atmsInHapto.size();
796 dummyP3d.y = dummyP3d.y / (double) atmsInHapto.size();
797 dummyP3d.z = dummyP3d.z / (double) atmsInHapto.size();
798
799 //Add Dummy atom to molecular object
800 //if no other Du is already in the same position
801 IAtom dummyAtm = null;
802 for (IAtom oldDu : fragsMol.atoms())
803 {
806 {
807 Point3d oldDuP3d = oldDu.getPoint3d();
808 if (oldDuP3d.distance(dummyP3d) < 0.002)
809 {
810 dummyAtm = oldDu;
811 break;
812 }
813 }
814 }
815
816 if (dummyAtm==null)
817 {
818 dummyAtm = new PseudoAtom(DENOPTIMConstants.DUMMYATMSYMBOL);
819 dummyAtm.setPoint3d(dummyP3d);
820 fragsMol.addAtom(dummyAtm);
821 }
822
823 // Modify connectivity of atoms involved in high-hapticity
824 // coordination creation of Du-to-ATM bonds
825 // By internal convention the bond order is "SINGLE".
826 IBond.Order border = IBond.Order.valueOf("SINGLE");
827
828 for (IAtom ligAtm : atmsInHapto)
829 {
830 List<IAtom> nbrsOfDu = fragsMol.getConnectedAtomsList(
831 dummyAtm);
832 if (!nbrsOfDu.contains(ligAtm))
833 {
834 // Add bond with dummy
835 Bond bnd = new Bond(dummyAtm,ligAtm,border);
836 fragsMol.addBond(bnd);
837 }
838 // Remove bonds between central and coordinating atoms
839 IBond oldBnd = fragsMol.getBond(centralAtm,ligAtm);
840 fragsMol.removeBond(oldBnd);
841 }
842
843 // NB: by convention the "first" class (i.e., the ???:0 class)
844 // is always on the central atom.
845 AttachmentPoint apA = masterFrag.addAPOnAtom(centralAtm,
846 rule.getAPClass0(),
847 MoleculeUtils.getPoint3d(dummyAtm));
848 AttachmentPoint apB = masterFrag.addAPOnAtom(dummyAtm,
849 rule.getAPClass1(),
850 MoleculeUtils.getPoint3d(centralAtm));
851
852 cutId++;
853 apA.setCutId(cutId);
854 apB.setCutId(cutId);
855 } else {
856 //treatment of mono-hapto ligands
857 IBond bnd = fragsMol.getBond(atmA,atmB);
858 fragsMol.removeBond(bnd);
859
860 AttachmentPoint apA = masterFrag.addAPOnAtom(atmA,
861 rule.getAPClass0(),
863 AttachmentPoint apB = masterFrag.addAPOnAtom(atmB,
864 rule.getAPClass1(),
866
867 cutId++;
868 apA.setCutId(cutId);
869 apB.setCutId(cutId);
870 } //end of if (hapticity>1)
871 } //end of loop over matching bonds
872 } //end of loop over rules
873
874 // Extract isolated fragments
875 List<Vertex> fragments = new ArrayList<Vertex>();
876 Set<Integer> doneAlready = new HashSet<Integer>();
877 for (int idx=0 ; idx<masterFrag.getAtomCount(); idx++)
878 {
879 if (doneAlready.contains(idx))
880 continue;
881
882 Fragment cloneOfMaster = masterFrag.clone();
883 IAtomContainer iac = cloneOfMaster.getIAtomContainer();
884 Set<IAtom> atmsToKeep = exploreConnectivity(iac.getAtom(idx), iac);
885 atmsToKeep.stream().forEach(atm -> doneAlready.add(iac.indexOf(atm)));
886
887 Set<IAtom> atmsToRemove = new HashSet<IAtom>();
888 for (IAtom atm : cloneOfMaster.atoms())
889 {
890 if (!atmsToKeep.contains(atm))
891 {
892 atmsToRemove.add(atm);
893 }
894 }
895 cloneOfMaster.removeAtoms(atmsToRemove);
896 if (cloneOfMaster.getAttachmentPoints().size()>0)
897 fragments.add(cloneOfMaster);
898 }
899
900 return fragments;
901 }
902
903//------------------------------------------------------------------------------
917 static Set<IAtom> exploreHapticity(IAtom seed, IAtom centralAtom,
918 ArrayList<IAtom> candidates, IAtomContainer mol)
919 {
920 Set<IAtom> atmsInHapto = new HashSet<IAtom>();
921 atmsInHapto.add(seed);
922 ArrayList<IAtom> toVisitAtoms = new ArrayList<IAtom>();
923 toVisitAtoms.add(seed);
924 ArrayList<IAtom> visitedAtoms = new ArrayList<IAtom>();
925 while (toVisitAtoms.size()>0)
926 {
927 ArrayList<IAtom> toVisitLater = new ArrayList<IAtom>();
928 for (IAtom atomInFocus : toVisitAtoms)
929 {
930 if (visitedAtoms.contains(atomInFocus)
931 || atomInFocus==centralAtom)
932 continue;
933 else
934 visitedAtoms.add(atomInFocus);
935
936 if (candidates.contains(atomInFocus))
937 {
938 atmsInHapto.add(atomInFocus);
939 toVisitLater.addAll(mol.getConnectedAtomsList(atomInFocus));
940 }
941 }
942 toVisitAtoms.clear();
943 toVisitAtoms.addAll(toVisitLater);
944 }
945 return atmsInHapto;
946 }
947
948//------------------------------------------------------------------------------
957 static Set<IAtom> exploreConnectivity(IAtom seed, IAtomContainer mol)
958 {
959 Set<IAtom> atmsReachableFromSeed = new HashSet<IAtom>();
960 ArrayList<IAtom> toVisitAtoms = new ArrayList<IAtom>();
961 toVisitAtoms.add(seed);
962 ArrayList<IAtom> visitedAtoms = new ArrayList<IAtom>();
963 while (toVisitAtoms.size()>0)
964 {
965 ArrayList<IAtom> toVisitLater = new ArrayList<IAtom>();
966 for (IAtom atomInFocus : toVisitAtoms)
967 {
968 if (visitedAtoms.contains(atomInFocus))
969 continue;
970 else
971 visitedAtoms.add(atomInFocus);
972
973 atmsReachableFromSeed.add(atomInFocus);
974 toVisitLater.addAll(mol.getConnectedAtomsList(atomInFocus));
975 }
976 toVisitAtoms.clear();
977 toVisitAtoms.addAll(toVisitLater);
978 }
979 return atmsReachableFromSeed;
980 }
981
982//-----------------------------------------------------------------------------
983
992 static Map<String, List<MatchedBond>> getMatchingBondsAllInOne(
993 IAtomContainer mol, List<CuttingRule> rules, Logger logger)
994 {
995 // Collect all SMARTS queries
996 Map<String,String> smarts = new HashMap<String,String>();
997 for (CuttingRule rule : rules)
998 {
999 smarts.put(rule.getName(),rule.getWholeSMARTSRule());
1000 }
1001
1002 // Prepare a data structure for the return value
1003 Map<String, List<MatchedBond>> bondsMatchingRules =
1004 new HashMap<String, List<MatchedBond>>();
1005
1006 // Get all the matches to the SMARTS queries
1007 ManySMARTSQuery msq = new ManySMARTSQuery(mol, smarts);
1008 if (msq.hasProblems())
1009 {
1010 if (logger!=null)
1011 {
1012 logger.log(Level.WARNING, "Problem matching SMARTS: "
1013 + msq.getMessage());
1014 }
1015 return bondsMatchingRules;
1016 }
1017
1018 for (CuttingRule rule : rules)
1019 {
1020 String ruleName = rule.getName();
1021
1022 if (msq.getNumMatchesOfQuery(ruleName) == 0)
1023 {
1024 continue;
1025 }
1026
1027 // Get atoms matching cutting rule queries
1028 Mappings purgedPairs = msq.getMatchesOfSMARTS(ruleName);
1029
1030 // Evaluate subclass membership and eventually store target bonds
1031 ArrayList<MatchedBond> bondsMatched = new ArrayList<MatchedBond>();
1032 for (int[] pair : purgedPairs)
1033 {
1034 if (pair.length!=2)
1035 {
1036 throw new Error("Cutting rule: " + ruleName
1037 + " has identified " + pair.length + " atoms "
1038 + "instead of 2. Modify rule to make it find a "
1039 + "pair of atoms.");
1040 }
1041 MatchedBond tb = new MatchedBond(mol.getAtom(pair[0]),
1042 mol.getAtom(pair[1]), rule);
1043
1044 // Apply any further option of the cutting rule
1045 if (tb.satisfiesRuleOptions(logger))
1046 bondsMatched.add(tb);
1047 }
1048
1049 if (!bondsMatched.isEmpty())
1050 bondsMatchingRules.put(ruleName, bondsMatched);
1051 }
1052
1053 return bondsMatchingRules;
1054 }
1055
1056//------------------------------------------------------------------------------
1057
1063 public static void manageFragmentCollection(File input,
1064 FragmenterParameters settings,
1065 File output, Logger logger) throws DENOPTIMException, IOException,
1066 IllegalArgumentException, UndetectedFileFormatException
1067 {
1068 FileInputStream fis = new FileInputStream(input);
1069 IteratingSDFReader reader = new IteratingSDFReader(fis,
1070 DefaultChemObjectBuilder.getInstance());
1071
1072 int index = -1;
1073 int maxBufferSize = 2000;
1074 ArrayList<Vertex> buffer = new ArrayList<Vertex>(500);
1075 try {
1076 while (reader.hasNext())
1077 {
1078 index++;
1079 if (logger!=null)
1080 {
1081 logger.log(Level.FINE,"Processing fragment " + index);
1082 }
1083 Vertex frag = new Fragment(reader.next(), BBType.UNDEFINED);
1084 manageFragmentCollection(frag, index, settings,
1085 buffer, logger);
1086
1087 // If max buffer size is reached, then bump to file
1088 if (buffer.size() >= maxBufferSize)
1089 {
1091 buffer, true);
1092 buffer.clear();
1093 }
1094 }
1095 } finally {
1096 reader.close();
1097 }
1098 if (buffer.size() < maxBufferSize)
1099 {
1101 buffer, true);
1102 buffer.clear();
1103 }
1104 }
1105
1106//------------------------------------------------------------------------------
1107
1125 public static void manageFragmentCollection(Vertex frag, int fragCounter,
1126 FragmenterParameters settings,
1127 List<Vertex> collector, Logger logger)
1128 throws DENOPTIMException, IllegalArgumentException,
1130 {
1131
1132 if (!filterFragment((Fragment) frag, settings, logger))
1133 {
1134 return;
1135 }
1136
1137 //Compare with list of fragments to ignore
1138 if (settings.getIgnorableFragments().size() > 0)
1139 {
1140 if (settings.getIgnorableFragments().stream()
1141 .anyMatch(ignorable -> ((Fragment)frag)
1142 .isIsomorphicTo(ignorable)))
1143 {
1144 if (logger!=null)
1145 {
1146 logger.log(Level.FINE,"Fragment " + fragCounter
1147 + " is ignorable.");
1148 }
1149 return;
1150 }
1151 }
1152
1153 //Compare with list of fragments to retain
1154 if (settings.getTargetFragments().size() > 0)
1155 {
1156 if (!settings.getTargetFragments().stream()
1157 .anyMatch(ignorable -> ((Fragment)frag)
1158 .isIsomorphicTo(ignorable)))
1159 {
1160 if (logger!=null)
1161 {
1162 logger.log(Level.FINE,"Fragment " + fragCounter
1163 + " doesn't match any target: rejected.");
1164 }
1165 return;
1166 }
1167 }
1168
1169 // Add dummy atoms on linearities
1171 && settings.doAddDuOnLinearity())
1172 {
1174 settings.getLinearAngleLimit());
1175 }
1176
1177 // Management of duplicate fragments:
1178 // -> identify duplicates (isomorphic fragments),
1179 // -> keep one (or more, if we want to sample the isomorphs),
1180 // -> reject the rest.
1181 if (settings.doManageIsomorphicFamilies())
1182 {
1183 synchronized (settings.MANAGEMWSLOTSSLOCK)
1184 {
1185 String mwSlotID = getMWSlotIdentifier(frag,
1186 settings.getMWSlotSize());
1187
1188 File mwFileUnq = settings.getMWSlotFileNameUnqFrags(
1189 mwSlotID);
1190 File mwFileAll = settings.getMWSlotFileNameAllFrags(
1191 mwSlotID);
1192
1193 // Compare this fragment with previously seen ones
1194 Vertex unqVersion = null;
1195 if (mwFileUnq.exists())
1196 {
1197 ArrayList<Vertex> knownFrags =
1199 unqVersion = knownFrags.stream()
1200 .filter(knownFrag ->
1201 ((Fragment)frag).isIsomorphicTo(knownFrag))
1202 .findAny()
1203 .orElse(null);
1204 }
1205 if (unqVersion!=null)
1206 {
1207 // Identify this unique fragment
1208 String isoFamID = unqVersion.getProperty(
1210 .toString();
1211
1212 // Do we already have enough isomorphic family members
1213 // for this fragment?
1214 int sampleSize = settings.getIsomorphsCount()
1215 .get(isoFamID);
1216 if (sampleSize < settings.getIsomorphicSampleSize())
1217 {
1218 // Add this isomorphic version to the sample
1219 frag.setProperty(
1221 isoFamID);
1222 settings.getIsomorphsCount().put(isoFamID,
1223 sampleSize+1);
1224 DenoptimIO.writeVertexToFile(mwFileAll,
1225 FileFormat.VRTXSDF, frag, true);
1226 collector.add(frag);
1227 } else {
1228 // This would be inefficient in the long run
1229 // because it by-passes the splitting by MW.
1230 // Do not do it!
1231 /*
1232 if (logger!=null)
1233 {
1234 logger.log(Level.FINE,"Fragment "
1235 + fragCounter
1236 + " is isomorphic to unique fragment "
1237 + unqVersionID + ", but we already "
1238 + "have a sample of " + sampleSize
1239 + ": ignoring this fragment from now "
1240 + "on.");
1241 }
1242 settings.getIgnorableFragments().add(frag);
1243 */
1244 }
1245 } else {
1246 // This is a never-seen fragment
1247 String isoFamID = settings.newIsomorphicFamilyID();
1248 frag.setProperty(
1250 isoFamID);
1251 settings.getIsomorphsCount().put(isoFamID, 1);
1252 DenoptimIO.writeVertexToFile(mwFileUnq,
1253 FileFormat.VRTXSDF, frag, true);
1254 DenoptimIO.writeVertexToFile(mwFileAll,
1255 FileFormat.VRTXSDF, frag, true);
1256 collector.add(frag);
1257 }
1258 } // end synchronized block
1259 } else {
1260 //If we are here, we did not ask to remove duplicates
1261 collector.add(frag);
1262 }
1263 }
1264
1265//------------------------------------------------------------------------------
1266
1276 public static boolean filterFragment(Fragment frag,
1277 FragmenterParameters settings)
1278 {
1279 return filterFragment(frag, settings, settings.getLogger());
1280 }
1281
1282//------------------------------------------------------------------------------
1283
1295 public static boolean filterFragment(Fragment frag,
1296 FragmenterParameters settings, Logger logger)
1297 {
1298 // Default filtering criteria: get ring of R/*/X/Xx
1299 for (IAtom atm : frag.atoms())
1300 {
1301 if (MoleculeUtils.isElement(atm))
1302 {
1303 continue;
1304 }
1305 String smb = MoleculeUtils.getSymbolOrLabel(atm);
1306 if (DENOPTIMConstants.DUMMYATMSYMBOL.equals(smb))
1307 {
1308 continue;
1309 }
1310 logger.log(Level.FINE,"Removing fragment contains non-element '"
1311 + smb + "'");
1312 return false;
1313 }
1314
1315 if (settings.isWorkingIn3D())
1316 {
1317 // Incomplete 3D fragmentation: an atom has the same coords of an AP.
1318 for (AttachmentPoint ap : frag.getAttachmentPoints())
1319 {
1320 Point3d ap3d = ap.getDirectionVector();
1321 if (ap3d!=null)
1322 {
1323 for (IAtom atm : frag.atoms())
1324 {
1325 Point3d atm3d = MoleculeUtils.getPoint3d(atm);
1326 double dist = ap3d.distance(atm3d);
1327 if (dist < 0.0002)
1328 {
1329 logger.log(Level.FINE,"Removing fragment with AP"
1330 + frag.getIAtomContainer().indexOf(atm)
1331 + " and atom " + MoleculeUtils.getSymbolOrLabel(atm)
1332 + " coincide.");
1333 return false;
1334 }
1335 }
1336 }
1337 }
1338 }
1339 if (settings.doRejectWeirdIsotopes())
1340 {
1341 for (IAtom atm : frag.atoms())
1342 {
1343 if (MoleculeUtils.isElement(atm))
1344 {
1345 // Unconfigured isotope has null mass number
1346 if (atm.getMassNumber() == null)
1347 continue;
1348
1349 String symb = MoleculeUtils.getSymbolOrLabel(atm);
1350 int a = atm.getMassNumber();
1351 try {
1352 IIsotope major = Isotopes.getInstance().getMajorIsotope(symb);
1353 if (a != major.getMassNumber())
1354 {
1355 logger.log(Level.FINE,"Removing fragment containing "
1356 + "isotope "+symb+a+".");
1357 return false;
1358 }
1359 } catch (Throwable t) {
1360 logger.log(Level.WARNING,"Not able to perform Isotope"
1361 + "detection.");
1362 }
1363 }
1364
1365 }
1366 }
1367
1368 // User-controlled filtering criteria
1369
1370 if (settings.getRejectedElements().size() > 0)
1371 {
1372 for (IAtom atm : frag.atoms())
1373 {
1374 String symb = MoleculeUtils.getSymbolOrLabel(atm);
1375 if (settings.getRejectedElements().contains(symb))
1376 {
1377 logger.log(Level.FINE,"Removing fragment containing '"
1378 + symb + "'.");
1379 return false;
1380 }
1381 }
1382 }
1383
1384 if (settings.getRejectedFormulaLessThan().size() > 0
1385 || settings.getRejectedFormulaMoreThan().size() > 0)
1386 {
1387 Map<String,Double> eaMol = FormulaUtils.getElementalanalysis(
1388 frag.getIAtomContainer());
1389
1390 for (Map<String,Double> criterion :
1391 settings.getRejectedFormulaMoreThan())
1392 {
1393 for (String el : criterion.keySet())
1394 {
1395 if (eaMol.containsKey(el))
1396 {
1397 // -0.5 to make it strictly less-than
1398 if (eaMol.get(el) - criterion.get(el) > 0.5)
1399 {
1400 logger.log(Level.FINE,"Removing fragment that "
1401 + "contains too much '" + el + "' "
1402 + "as requested by formula"
1403 + "-based (more-than) settings (" + el
1404 + eaMol.get(el) + " > " + criterion + ").");
1405 return false;
1406 }
1407 }
1408 }
1409 }
1410
1411 Map<String,Double> criterion = settings.getRejectedFormulaLessThan();
1412 for (String el : criterion.keySet())
1413 {
1414 if (!eaMol.containsKey(el))
1415 {
1416 logger.log(Level.FINE,"Removing fragment that does not "
1417 + "contain '" + el + "' as requested by formula"
1418 + "-based (less-than) settings.");
1419 return false;
1420 } else {
1421 // 0.5 to make it strictly more-than
1422 if (eaMol.get(el) - criterion.get(el) < -0.5)
1423 {
1424 logger.log(Level.FINE,"Removing fragment that "
1425 + "contains too little '" + el + "' "
1426 + "as requested by formula"
1427 + "-based settings (" + el
1428 + eaMol.get(el) + " < " + criterion + ").");
1429 return false;
1430 }
1431 }
1432 }
1433
1434 }
1435
1436 if (settings.getRejectedAPClasses().size() > 0)
1437 {
1438 for (APClass apc : frag.getAllAPClasses())
1439 {
1440 for (String s : settings.getRejectedAPClasses())
1441 {
1442 if (apc.toString().startsWith(s))
1443 {
1444 logger.log(Level.FINE,"Removing fragment with APClass "
1445 + apc);
1446 return false;
1447 }
1448 }
1449 }
1450 }
1451
1452 if (settings.getRejectedAPClassCombinations().size() > 0)
1453 {
1454 loopOverCombinations:
1455 for (String[] conditions : settings.getRejectedAPClassCombinations())
1456 {
1457 for (int ip=0; ip<conditions.length; ip++)
1458 {
1459 String condition = conditions[ip];
1460 boolean found = false;
1461 for (APClass apc : frag.getAllAPClasses())
1462 {
1463 if (apc.toString().startsWith(condition))
1464 {
1465 found = true;
1466 continue;
1467 }
1468 }
1469 if (!found)
1470 continue loopOverCombinations;
1471 // Here we do have at least one AP satisfying the condition.
1472 }
1473 // Here we manage or satisfy all conditions. Therefore, we can
1474 // reject this fragment
1475
1476 String allCondsAsString = "";
1477 for (int i=0; i<conditions.length; i++)
1478 allCondsAsString = allCondsAsString + " " + conditions[i];
1479
1480 logger.log(Level.FINE,"Removing fragment with combination of "
1481 + "APClasses matching '" + allCondsAsString + "'.");
1482 return false;
1483 }
1484 }
1485
1486 if (settings.getMaxFragHeavyAtomCount()>0
1487 || settings.getMinFragHeavyAtomCount()>0)
1488 {
1489 int totHeavyAtm = 0;
1490 for (IAtom atm : frag.atoms())
1491 {
1492 if (MoleculeUtils.isElement(atm))
1493 {
1494 String symb = MoleculeUtils.getSymbolOrLabel(atm);
1495 if ((!symb.equals("H")) && (!symb.equals(
1497 totHeavyAtm++;
1498 }
1499 }
1500 if (settings.getMaxFragHeavyAtomCount() > 0
1501 && totHeavyAtm > settings.getMaxFragHeavyAtomCount())
1502 {
1503 logger.log(Level.FINE,"Removing fragment with too many atoms ("
1504 + totHeavyAtm + " < "
1505 + settings.getMaxFragHeavyAtomCount()
1506 + ")");
1507 return false;
1508 }
1509 if (settings.getMinFragHeavyAtomCount() > 0
1510 && totHeavyAtm < settings.getMinFragHeavyAtomCount())
1511 {
1512 logger.log(Level.FINE,"Removing fragment with too few atoms ("
1513 + totHeavyAtm + " < "
1514 + settings.getMinFragHeavyAtomCount()
1515 + ")");
1516 return false;
1517 }
1518 }
1519
1520 if (settings.getFragRejectionSMARTS().size() > 0)
1521 {
1523 settings.getFragRejectionSMARTS());
1524 if (msq.hasProblems())
1525 {
1526 logger.log(Level.WARNING,"Problems evaluating SMARTS-based "
1527 + "rejection criteria. " + msq.getMessage());
1528 }
1529
1530 for (String criterion : settings.getFragRejectionSMARTS().keySet())
1531 {
1532 if (msq.getNumMatchesOfQuery(criterion)>0)
1533 {
1534 logger.log(Level.FINE,"Removing fragment that matches "
1535 + "SMARTS-based rejection criteria '" + criterion
1536 + "'.");
1537 return false;
1538 }
1539 }
1540 }
1541
1542 if (settings.getFragRetentionSMARTS().size() > 0)
1543 {
1545 settings.getFragRetentionSMARTS());
1546 if (msq.hasProblems())
1547 {
1548 logger.log(Level.WARNING,"Problems evaluating SMARTS-based "
1549 + "rejection criteria. " + msq.getMessage());
1550 }
1551
1552 boolean matchesAny = false;
1553 for (String criterion : settings.getFragRetentionSMARTS().keySet())
1554 {
1555 if (msq.getNumMatchesOfQuery(criterion) > 0)
1556 {
1557 matchesAny = true;
1558 break;
1559 }
1560 }
1561 if (!matchesAny)
1562 {
1563 logger.log(Level.FINE,"Removing fragment that does not "
1564 + "match any SMARTS-based retention criteria.");
1565 return false;
1566 }
1567 }
1568 return true;
1569 }
1570
1571//------------------------------------------------------------------------------
1572
1580 public static String getMWSlotIdentifier(Vertex frag, int slotSize)
1581 {
1582 for (IAtom a : frag.getIAtomContainer().atoms())
1583 {
1584 if (a.getImplicitHydrogenCount()==null)
1585 a.setImplicitHydrogenCount(0);
1586 }
1587 double mw = AtomContainerManipulator.getMass(frag.getIAtomContainer());
1588 int slotNum = (int) (mw / (Double.valueOf(slotSize)));
1589 return slotNum*slotSize + "-" + (slotNum+1)*slotSize;
1590 }
1591
1592//------------------------------------------------------------------------------
1593
1594 public static Vertex getRCVForAP(AttachmentPoint ap, APClass rcvApClass)
1595 throws DENOPTIMException
1596 {
1597 IAtomContainer mol = SilentChemObjectBuilder.getInstance()
1598 .newAtomContainer();
1599 Point3d apv = ap.getDirectionVector();
1600 mol.addAtom(new PseudoAtom(RingClosingAttractor.RCALABELPERAPCLASS.get(
1601 rcvApClass),
1602 new Point3d(
1603 Double.valueOf(apv.x),
1604 Double.valueOf(apv.y),
1605 Double.valueOf(apv.z))));
1606
1607 Fragment rcv = new Fragment(mol, BBType.FRAGMENT);
1608 rcv.setAsRCV(true);
1609
1610 Point3d aps = MoleculeUtils.getPoint3d(
1611 ap.getOwner().getIAtomContainer().getAtom(
1612 ap.getAtomPositionNumber()));
1613 rcv.addAP(0, rcvApClass, new Point3d(
1614 Double.valueOf(aps.x),
1615 Double.valueOf(aps.y),
1616 Double.valueOf(aps.z)));
1617 return rcv;
1618 }
1619
1620//------------------------------------------------------------------------------
1621
1622}
General set of constants used in DENOPTIM.
static final Object FORMULASTR
Property name used to store molecular formula as string in an atom container.
static final String ATMPROPVERTEXID
String tag of Atom property used to store the unique ID of the Vertex corresponding to the molecular ...
static final String DUMMYATMSYMBOL
Symbol of dummy atom.
static final Object ISOMORPHICFAMILYID
Property used to store the identifier of the family of isomorphic fragments that owns a fragment.
Exception thrown when the format of a file is not recognized.
static void exploreDGraphForMappings(DGraph graph, IAtomContainer graphIAC, Fragment masterFrag, IAtomContainer masterFragIAC, IAtomContainer mol, Map< IAtom, IAtom > graphToMolMapping)
Recursive function to process edges at any level of embedding to remove the corresponding bonds and c...
static List< Vertex > fragmentation(IAtomContainer mol, List< DGraph > templates, Randomizer randomizer, Logger logger)
Chops one chemical structure by applying the given fragmentation templates.
static Map< String, List< MatchedBond > > getMatchingBondsAllInOne(IAtomContainer mol, List< CuttingRule > rules, Logger logger)
Identification of the bonds matching a list of SMARTS queries.
static void checkElementalAnalysisAgainstFormula(File input, File output, Logger logger)
Processes all molecules analyzing the composition of the structure in the chemical representation as ...
static List< Vertex > fragmentation(IAtomContainer mol, List< CuttingRule > rules, Logger logger)
Chops one chemical structure by applying the given cutting rules.
static boolean filterFragment(Fragment frag, FragmenterParameters settings)
Filter fragments according to the criteria defined in the settings.
static Set< IAtom > exploreConnectivity(IAtom seed, IAtomContainer mol)
Explores the connectivity annotating which atoms have been visited.
static IAtomContainer reduceTemplateToVertexBoundaries(IAtomContainer templateMol)
Reduces a template molecule to keep only atoms at vertex boundaries (atoms connected to atoms with di...
static boolean prepareMolToFragmentation(IAtomContainer mol, FragmenterParameters settings, int index)
Do any pre-processing on a IAtomContainer meant to be fragmented.
static void manageFragmentCollection(Vertex frag, int fragCounter, FragmenterParameters settings, List< Vertex > collector, Logger logger)
Management of fragments: includes application of fragment filters, rejection rules,...
static Set< IAtom > exploreHapticity(IAtom seed, IAtom centralAtom, ArrayList< IAtom > candidates, IAtomContainer mol)
Identifies non-central atoms involved in the same n-hapto ligand as the seed atom.
static boolean filterFragment(Fragment frag, FragmenterParameters settings, Logger logger)
Filter fragments according to the criteria defined in the settings.
static void filterStrucutresBySMARTS(File input, Set< String > smarts, File output, Logger logger)
Removes from the structures anyone that matches any of the given SMARTS queries.
static String getMWSlotIdentifier(Vertex frag, int slotSize)
Determines the name of the MW slot to use when comparing the given fragment with previously stored fr...
static void manageFragmentCollection(File input, FragmenterParameters settings, File output, Logger logger)
Management of fragments: includes application of fragment filters, rejection rules,...
static Vertex getRCVForAP(AttachmentPoint ap, APClass rcvApClass)
static boolean fragmentation(File input, FragmenterParameters settings, File output, Logger logger)
Performs fragmentation according to the given cutting rules.
An attachment point (AP) is a possibility to attach a Vertex onto the vertex holding the AP (i....
Container for the list of vertices and the edges that connect them.
Definition: DGraph.java:102
List< Vertex > getVertexList()
Definition: DGraph.java:947
List< Edge > getEdgeList()
Definition: DGraph.java:992
This class represents the edge between two vertices.
Definition: Edge.java:38
Class representing a continuously connected portion of chemical object holding attachment points.
Definition: Fragment.java:61
void addAP(int atomPositionNumber)
Adds an attachment point with a dummy APClass.
Definition: Fragment.java:343
AttachmentPoint addAPOnAtom(IAtom srcAtm, APClass apc, Point3d vector)
Add an attachment point to the specifies atom.
Definition: Fragment.java:424
List< AttachmentPoint > getAttachmentPoints()
Definition: Fragment.java:1141
Fragment clone()
Returns a deep copy of this fragments.
Definition: Fragment.java:733
Iterable< IAtom > atoms()
Definition: Fragment.java:822
IAtomContainer getIAtomContainer()
Definition: Fragment.java:788
void removeAtoms(Collection< IAtom > atoms)
Removes a list of atoms and updates the list of attachment points.
Definition: Fragment.java:913
A vertex is a data structure that has an identity and holds a list of AttachmentPoints.
Definition: Vertex.java:61
ArrayList< APClass > getAllAPClasses()
Returns the list of all APClasses present on this vertex.
Definition: Vertex.java:792
void setAsRCV(boolean isRCV)
Definition: Vertex.java:274
Object getProperty(Object property)
Definition: Vertex.java:1223
abstract IAtomContainer getIAtomContainer()
void setProperty(Object key, Object property)
Definition: Vertex.java:1235
The RingClosingAttractor represent the available valence/connection that allows to close a ring.
static final HashMap< APClass, String > RCALABELPERAPCLASS
Conventional labels for attractor pseudoatom.
Utility methods for input/output.
static File writeVertexesToFile(File file, FileFormat format, List< Vertex > vertexes)
Writes vertexes to file.
static void writeSDFFile(String fileName, IAtomContainer mol)
Writes IAtomContainer to SDF file.
static File writeVertexToFile(File file, FileFormat format, Vertex vertex, boolean append)
Writes vertexes to file.
static ArrayList< Vertex > readVertexes(File file, Vertex.BBType bbt)
Reads Vertexes from any file that can contain such items.
An iterator that take IAtomContainers from a file, possibly using an available iterating reader,...
void close()
Close the memory-efficient iterator if any is open.
Tool to build build three-dimensional (3D) tree-like molecular structures from DGraph.
IAtomContainer convertGraphTo3DAtomContainer(DGraph graph)
Created a three-dimensional molecular representation from a given DGraph.
Logger getLogger()
Get the name of the program specific logger.
A cutting rule with three SMARTS queries (atom 1, bond, atom2) and options.
Parameters controlling execution of the fragmenter.
boolean doRejectWeirdIsotopes
Flag requesting to reject fragments with minor isotopes.
boolean addExplicitH
Flag requesting to add explicit H atoms.
Boolean satisfiesRuleOptions
Flag indicating that we have checked the additional option from the cutting rule (otherwise this flag...
Toll to add/remove dummy atoms from linearities or multi-hapto sites.
static void addDummiesOnLinearities(Fragment frag, double angLim)
Append dummy atoms on otherwise linear arrangements of atoms.
Utilities for manipulating molecular formulas.
static boolean compareFormulaAndElementalAnalysis(String formula, IAtomContainer mol)
Compares the molecular formula formatted as from the Cambridge Structural Database (CSD) against the ...
static Map< String, Double > getElementalanalysis(IAtomContainer mol)
Threads Deuterium as a different element than Hydrogen.
Container of lists of atoms matching a list of SMARTS.
Map< String, Mappings > getAllMatches()
int getNumMatchesOfQuery(String query)
Utilities for molecule conversion.
static void setZeroImplicitHydrogensToAllAtoms(IAtomContainer iac)
Sets zero implicit hydrogen count to all atoms.
static int getDimensions(IAtomContainer mol)
Determines the dimensionality of the given chemical object.
static String getSymbolOrLabel(IAtom atm)
Gets either the elemental symbol (for standard atoms) of the label (for pseudo-atoms).
static List< Map< IAtom, IAtom > > findUniqueAtomMappings(IAtomContainer substructure, IAtomContainer mol, Logger logger)
Finds the maximum common substructure (MCS) between two molecules.
static void ensureNoUnsetBondOrders(IAtomContainer iac)
Sets bond order = single to all otherwise unset bonds.
static void explicitHydrogens(IAtomContainer mol)
Converts all the implicit hydrogens to explicit.
static List< IAtom > findShortestPath(IAtomContainer mol, IAtom start, IAtom end, Map< IAtom, Long > atomToVertexId)
Finds the shortest path between two atoms in a molecule using BFS.
static Point3d getPoint3d(IAtom atm)
Return the 3D coordinates, if present.
static boolean isElement(IAtom atom)
Check element symbol corresponds to real element of Periodic Table.
Tool to generate random numbers and random decisions.
Definition: Randomizer.java:35
File formats identified by DENOPTIM.
Definition: FileFormat.java:32
The type of building block.
Definition: Vertex.java:86
FRG_PARAMS
Parameters controlling the fragmenter.