$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.Arrays;
8import java.util.HashMap;
9import java.util.HashSet;
10import java.util.List;
11import java.util.Map;
12import java.util.Set;
13import java.util.logging.Level;
14import java.util.logging.Logger;
15
16import javax.vecmath.Point3d;
17import javax.vecmath.Vector3d;
18
19import org.openscience.cdk.Atom;
20import org.openscience.cdk.Bond;
21import org.openscience.cdk.DefaultChemObjectBuilder;
22import org.openscience.cdk.PseudoAtom;
23import org.openscience.cdk.config.Isotopes;
24import org.openscience.cdk.exception.CDKException;
25import org.openscience.cdk.geometry.alignment.KabschAlignment;
26import org.openscience.cdk.interfaces.IAtom;
27import org.openscience.cdk.interfaces.IAtomContainer;
28import org.openscience.cdk.interfaces.IBond;
29import org.openscience.cdk.interfaces.IIsotope;
30import org.openscience.cdk.io.iterator.IteratingSDFReader;
31import org.openscience.cdk.isomorphism.Mappings;
32import org.openscience.cdk.isomorphism.Pattern;
33import org.openscience.cdk.silent.SilentChemObjectBuilder;
34import org.openscience.cdk.tools.manipulator.AtomContainerManipulator;
35
36import denoptim.constants.DENOPTIMConstants;
37import denoptim.exception.DENOPTIMException;
38import denoptim.files.FileFormat;
39import denoptim.files.UndetectedFileFormatException;
40import denoptim.graph.APClass;
41import denoptim.graph.AttachmentPoint;
42import denoptim.graph.DGraph;
43import denoptim.graph.Edge;
44import denoptim.graph.Fragment;
45import denoptim.graph.Template;
46import denoptim.graph.Vertex;
47import denoptim.graph.Vertex.BBType;
48import denoptim.graph.rings.RingClosingAttractor;
49import denoptim.io.DenoptimIO;
50import denoptim.io.IteratingAtomContainerReader;
51import denoptim.molecularmodeling.ThreeDimTreeBuilder;
52import denoptim.programs.RunTimeParameters.ParametersType;
53import denoptim.programs.fragmenter.CuttingRule;
54import denoptim.programs.fragmenter.FragmenterParameters;
55import denoptim.programs.fragmenter.MatchedBond;
56import denoptim.utils.CartesianSpaceUtils;
57import denoptim.utils.DummyAtomHandler;
58import denoptim.utils.FormulaUtils;
59import denoptim.utils.ManySMARTSQuery;
60import denoptim.utils.MathUtils;
61import denoptim.utils.MoleculeUtils;
62import denoptim.utils.Randomizer;
63
64public class FragmenterTools
65{
66
67//------------------------------------------------------------------------------
68
83 public static void checkElementalAnalysisAgainstFormula(File input,
84 File output, Logger logger)
85 throws DENOPTIMException, IOException
86 {
87 FileInputStream fis = new FileInputStream(input);
88 IteratingSDFReader reader = new IteratingSDFReader(fis,
89 DefaultChemObjectBuilder.getInstance());
90
91 int index = -1;
92 int maxBufferSize = 2000;
93 ArrayList<IAtomContainer> buffer = new ArrayList<IAtomContainer>(500);
94 try {
95 while (reader.hasNext())
96 {
97 index++;
98 if (logger!=null)
99 {
100 logger.log(Level.FINE,"Checking elemental analysis of "
101 + "structure " + index);
102 }
103 IAtomContainer mol = reader.next();
104 if (mol.getProperty(DENOPTIMConstants.FORMULASTR)==null)
105 {
106 throw new Error("Property '" + DENOPTIMConstants.FORMULASTR
107 + "' not found in molecule " + index + " in file "
108 + input + ". Cannot compare formula with elemental"
109 + "analysis.");
110 }
111 String formula = mol.getProperty(DENOPTIMConstants.FORMULASTR)
112 .toString();
113
115 mol, logger))
116 {
117 buffer.add(mol);
118 } else {
119 if (logger!=null)
120 {
121 logger.log(Level.INFO,"Inconsistency between elemental "
122 + "analysis of structure and molecular formula."
123 + " Rejecting structure " + index + ": "
124 + mol.getTitle());
125 }
126 }
127
128 // If max buffer size is reached, then bump to file
129 if (buffer.size() >= maxBufferSize)
130 {
131 DenoptimIO.writeSDFFile(output.getAbsolutePath(), buffer,
132 true);
133 buffer.clear();
134 }
135 }
136 }
137 finally {
138 reader.close();
139 }
140 if (buffer.size() < maxBufferSize)
141 {
142 DenoptimIO.writeSDFFile(output.getAbsolutePath(), buffer, true);
143 buffer.clear();
144 }
145 }
146
147//------------------------------------------------------------------------------
148
149
163 public static boolean prepareMolToFragmentation(IAtomContainer mol,
164 FragmenterParameters settings, int index)
165 {
166 try
167 {
168 if (settings.addExplicitH())
169 {
171 } else {
173 }
175 } catch (CDKException e)
176 {
177 if (e.getMessage().contains("Cannot assign Kekulé structure"))
178 {
179 if (!settings.acceptUnsetToSingeBO())
180 {
181 settings.getLogger().log(Level.WARNING,"Some bond order "
182 + "are unset and attempt to kekulize the "
183 + "system has failed "
184 + "for structure " + index + ". "
185 + "This hampers use of SMARTS queries, which "
186 + "may very well "
187 + "not work as expected. Structure " + index
188 + " will be rejected. "
189 + "You can avoid rejection by using "
190 + "keyword "
191 + ParametersType.FRG_PARAMS.getKeywordRoot()
192 + "UNSETTOSINGLEBO, but you'll "
193 + "still be using a peculiar connectivity "
194 + "table were "
195 + "many bonds are artificially marked as "
196 + "single to "
197 + "avoid use of 'UNSET' bond order. "
198 + "Further details on the problem: "
199 + e.getMessage());
200 return false;
201 } else {
202 settings.getLogger().log(Level.WARNING,"Failed "
203 + "kekulization "
204 + "for structure " + index
205 + " but UNSETTOSINGLEBO "
206 + "keyword used. Forcing use of single bonds to "
207 + "replace bonds with unset order.");
208 for (IBond bnd : mol.bonds())
209 {
210 if (bnd.getOrder().equals(IBond.Order.UNSET))
211 {
212 bnd.setOrder(IBond.Order.SINGLE);
213 }
214 }
215 }
216 }
217 }
218 return true;
219 }
220
221//------------------------------------------------------------------------------
222
234 public static void filterStrucutresBySMARTS(File input, Set<String> smarts,
235 File output, Logger logger)
236 throws DENOPTIMException, IOException
237 {
238 FileInputStream fis = new FileInputStream(input);
239 IteratingSDFReader reader = new IteratingSDFReader(fis,
240 DefaultChemObjectBuilder.getInstance());
241
242 int i = -1;
243 Map<String, String> smartsMap = new HashMap<String, String>();
244 for (String s : smarts)
245 {
246 i++;
247 smartsMap.put("prefilter-"+i, s);
248 }
249
250 int index = -1;
251 int maxBufferSize = 2000;
252 ArrayList<IAtomContainer> buffer = new ArrayList<IAtomContainer>(500);
253 try {
254 while (reader.hasNext())
255 {
256 index++;
257 if (logger!=null)
258 {
259 logger.log(Level.FINE,"Prefiltering structure " + index);
260 }
261 IAtomContainer mol = reader.next();
262
263 ManySMARTSQuery msq = new ManySMARTSQuery(mol, smartsMap);
264 if (msq.hasProblems())
265 {
266 String msg = "WARNING! Problems while searching for "
267 + "specific atoms/bonds using SMARTS: "
268 + msq.getMessage();
269 throw new DENOPTIMException(msg,msq.getProblem());
270 }
271 Map<String, Mappings> allMatches = msq.getAllMatches();
272
273 if (allMatches.size()==0)
274 {
275 buffer.add(mol);
276 } else {
277 String hits = "";
278 for (String s : allMatches.keySet())
279 hits = hits + DenoptimIO.NL + smartsMap.get(s);
280 if (logger!=null)
281 {
282 logger.log(Level.INFO,"Found match for " + hits
283 + "Rejecting structure " + index + ": "
284 + mol.getTitle());
285 }
286 }
287
288 // If max buffer size is reached, then bump to file
289 if (buffer.size() >= maxBufferSize)
290 {
291 DenoptimIO.writeSDFFile(output.getAbsolutePath(), buffer,
292 true);
293 buffer.clear();
294 }
295 }
296 } finally {
297 reader.close();
298 }
299 if (buffer.size() < maxBufferSize)
300 {
301 DenoptimIO.writeSDFFile(output.getAbsolutePath(), buffer, true);
302 buffer.clear();
303 }
304 }
305
306//------------------------------------------------------------------------------
307
323 public static boolean fragmentationFromGraphs(File input,
324 FragmenterParameters settings, File output, Logger logger)
325 throws DENOPTIMException, Exception
326 {
327 int totalProd = 0;
328 for (DGraph graph : DenoptimIO.readDENOPTIMGraphsFromFile(input))
329 {
330 List<Vertex> fragments = graph.getVertexList();
331
332 // Post-fragmentation processing of fragments
333 List<Vertex> keptFragments = new ArrayList<Vertex>();
334 int fragCounter = 0;
335 for (Vertex frag : fragments)
336 {
337 // Add metadata
338 fragCounter++;
339 manageFragmentCollection(frag, fragCounter, settings,
340 keptFragments, logger);
341 }
342 if (logger!=null)
343 {
344 logger.log(Level.FINE,"Fragments surviving post-"
345 + "processing: " + keptFragments.size());
346 }
347
348 if (keptFragments.size()>0)
349 {
350 totalProd += keptFragments.size();
352 keptFragments, true);
353 }
354 }
355 return totalProd>0;
356 }
357
358//------------------------------------------------------------------------------
359
367 public static List<Vertex> fragmentation(IAtomContainer mol,
368 FragmenterParameters settings)
369 throws DENOPTIMException
370 {
371 List<Vertex> fragments = new ArrayList<Vertex>();
372 if (settings.getFragmentationTmpls().size()>0)
373 {
374 fragments = fragmentation(mol, settings.getFragmentationTmpls(),
375 settings.getMaxBufferShellSize(),
376 settings.getRandomizer(), settings.getLogger());
377 } else {
378 fragments = fragmentation(mol, settings.getCuttingRules(),
379 settings.getLogger());
380 }
381 return fragments;
382 }
383
384//-----------------------------------------------------------------------------
385
405 public static boolean fragmentation(File input, FragmenterParameters settings,
406 File output, Logger logger) throws CDKException, IOException,
407 DENOPTIMException, IllegalArgumentException, UndetectedFileFormatException
408 {
411
412 int totalProd = 0;
413 int totalKept = 0;
414 int index = -1;
415 try {
416 while (iterator.hasNext())
417 {
418 index++;
419 if (logger!=null)
420 {
421 logger.log(Level.FINE,"Fragmenting structure " + index);
422 }
423 IAtomContainer mol = iterator.next();
424 String molName = "noname-mol" + index;
425 if (mol.getTitle()!=null && !mol.getTitle().isBlank())
426 molName = mol.getTitle();
427
428 // Generate the fragments
429 List<Vertex> fragments = fragmentation(mol, settings);
430 if (logger!=null)
431 {
432 logger.log(Level.FINE,"Fragmentation produced "
433 + fragments.size() + " fragments.");
434 }
435 totalProd += fragments.size();
436
437 // Post-fragmentation processing of fragments
438 List<Vertex> keptFragments = new ArrayList<Vertex>();
439 int fragCounter = 0;
440 for (Vertex frag : fragments)
441 {
442 // Add metadata
443 String fragIdStr = "From_" + molName + "_" + fragCounter;
444 frag.setProperty("cdk:Title", fragIdStr);
445 fragCounter++;
446 manageFragmentCollection(frag, fragCounter, settings,
447 keptFragments, logger);
448 }
449 if (logger!=null)
450 {
451 logger.log(Level.FINE,"Fragments surviving post-"
452 + "processing: " + keptFragments.size());
453 }
454 totalKept += keptFragments.size();
455 if (!settings.doManageIsomorphicFamilies() && totalKept>0)
456 {
458 keptFragments,true);
459 }
460 }
461 } finally {
462 iterator.close();
463 }
464
465 // Did we actually produce anything? We might not...
466 if (totalProd==0)
467 {
468 if (logger!=null)
469 {
470 logger.log(Level.WARNING,"No fragment produced. Cutting rules "
471 + "were ineffective on the given structures.");
472 }
473 return false;
474 } else if (totalKept==0)
475 {
476 if (logger!=null)
477 {
478 logger.log(Level.WARNING,"No fragment kept out of " + totalProd
479 + " produced fragments. Filtering criteria might be "
480 + "too restrictive.");
481 }
482 return false;
483 }
484 return true;
485 }
486
487//------------------------------------------------------------------------------
488
502 public static List<Vertex> fragmentation(IAtomContainer mol,
503 List<DGraph> templates, int maxBufferShellSize,
504 Randomizer randomizer, Logger logger)
505 throws DENOPTIMException
506 {
507 // The best set of fragments is the one with as many fragments that are isomorfic
508 // // with the template
509 List<Vertex> fragments = new ArrayList<Vertex>();
510 int maxNumIsomorfFrgs = -1;
511 for (DGraph templateGraph : templates)
512 {
513 // Check if the template is connected
514 if (!templateGraph.isConnected())
515 {
516 // This limitation derives from the fact that to speed up substructure
517 // search we reduce the template graph to the smallest set of atoms that
518 // identify the topology of APs. The
519 // can identify the matching topology. This is not possible for disconnected
520 // graphs.
521 throw new DENOPTIMException("Template graph is not connected. "
522 + "Cannot use it for fragmentation. Please check the "
523 + "template graph and make sure it is connected.");
524 }
525
526 ThreeDimTreeBuilder tb = new ThreeDimTreeBuilder(logger, randomizer);
527 IAtomContainer templateMol = tb.convertGraphTo3DAtomContainer(templateGraph,
528 true, true, true);
529
530 //TODO: consider splitting the template into connected components and
531 // reduce each component independently.
532
533 // Optimized creation of a reduced templateMol for faster isomorphism search
534 // Keep full templateMol for exploreDGraphForMappings
535 TopoTemplateProducer ttp = new TopoTemplateProducer(templateMol);
536
537 // The best set of fragments from this template may not be perfect, so we
538 // consider a score that consists of how many vertexes are isomorphic
539 // to the template graph vertexes.
540 List<Vertex> fragsFromThisTemplate = new ArrayList<>();
541 int maxNumIsomorfFrgsFromThisTmpl = -1;
542
543 // Define the maximum theoretical score considering that the template may
544 // be matched multiple times in the molecule
545 List<Vertex> templateVertexes = templateGraph.getVertexAnyLevel();
546 int maxTheoreticalScorePerMapping = 0;
547 for (int iva=0; iva<templateVertexes.size(); iva++)
548 {
549 Vertex va = templateVertexes.get(iva);
550 if (!(va instanceof Fragment))
551 {
552 continue;
553 }
554 for (int ivb=0; ivb<templateVertexes.size(); ivb++)
555 {
556 Vertex vb = templateVertexes.get(ivb);
557 if (!(vb instanceof Fragment))
558 {
559 continue;
560 }
561 if (iva == ivb)
562 {
563 // we know that the same
564 maxTheoreticalScorePerMapping++;
565 } else if (((Fragment) va).isIsomorphicTo(vb))
566 {
567 maxTheoreticalScorePerMapping++;
568 }
569 }
570 }
571
572 // Use the smallest possible template needed to identify the graph topology
573 // in the molecular structure.
574 for (int bufferShellSize = 0; bufferShellSize < maxBufferShellSize; bufferShellSize++)
575 {
576 // Use a reduced templateMol for isomorphism search (faster)
577 IAtomContainer reducedTemplateMol = ttp.getTemplateWithBufferShell(bufferShellSize);
578 List<Map<IAtom,IAtom>> reducedAtomMappings = MoleculeUtils.findUniqueAtomMappings(
579 reducedTemplateMol, mol, logger);
580
581 // Convert mappings from reducedTemplateMol:mol to templateMol:mol using stored indices
582 List<Map<IAtom,IAtom>> atomMappings = new ArrayList<>();
583 for (Map<IAtom,IAtom> reducedMapping : reducedAtomMappings)
584 {
585 Map<IAtom,IAtom> fullMapping = new HashMap<>();
586 for (Map.Entry<IAtom,IAtom> entry : reducedMapping.entrySet())
587 {
588 IAtom reducedAtom = entry.getKey();
589 IAtom molAtom = entry.getValue();
590
591 // Get original atom index from reduced atom property
592 Object indexObj = reducedAtom.getProperty("DENOPTIM_ORIGINAL_ATOM_INDEX");
593 if (indexObj != null)
594 {
595 int originalIndex = ((Number) indexObj).intValue();
596 IAtom originalAtom = templateMol.getAtom(originalIndex);
597 if (originalAtom != null)
598 {
599 fullMapping.put(originalAtom, molAtom);
600 }
601 }
602 }
603 if (!fullMapping.isEmpty())
604 {
605 atomMappings.add(fullMapping);
606 }
607 }
608
609 Fragment masterFrag = new Fragment(mol, BBType.UNDEFINED);
610 IAtomContainer masterFragIAC = masterFrag.getIAtomContainer();
611
612 // For each atom mapping, replace bonds corresponding to edges
613 // (at any embedding level) with attachment points and annotate embedding level.
614 // In case of symmetry, multiple mappings will effectivly cut the same bonds,
615 // and produce the same fragments of the template graph only for
616 // symmetry-redundant mappings.
617 for (Map<IAtom,IAtom> atomMapping : atomMappings)
618 {
619 try {
620 exploreDGraphForMappings(templateGraph, templateMol,
621 masterFrag, masterFragIAC, mol, atomMapping);
622 } catch (Throwable e) {
623 e.printStackTrace();
624 logger.log(Level.WARNING, "Error while exploring the template graph: " + e.getMessage());
625 continue;
626 }
627 }
628
629 // Extract isolated fragments
630 List<Vertex> locfragments = new ArrayList<Vertex>();
631 Set<Integer> doneAlready = new HashSet<Integer>();
632 for (int idx=0 ; idx<masterFrag.getAtomCount(); idx++)
633 {
634 if (doneAlready.contains(idx))
635 continue;
636
637 Fragment cloneOfMaster = masterFrag.clone();
638 IAtomContainer iac = cloneOfMaster.getIAtomContainer();
639 Set<IAtom> atmsToKeep = exploreConnectivity(iac.getAtom(idx), iac);
640 atmsToKeep.stream().forEach(atm -> doneAlready.add(iac.indexOf(atm)));
641
642 Set<IAtom> atmsToRemove = new HashSet<IAtom>();
643 for (IAtom atm : cloneOfMaster.atoms())
644 {
645 if (!atmsToKeep.contains(atm))
646 {
647 atmsToRemove.add(atm);
648 }
649 }
650 cloneOfMaster.removeAtoms(atmsToRemove);
651 if (cloneOfMaster.getAttachmentPoints().size()>0)
652 locfragments.add(cloneOfMaster);
653 }
654
655 int localScore = 0;
656 for (Vertex frag : locfragments)
657 {
658 if (!(frag instanceof Fragment))
659 {
660 continue;
661 }
662 for (Vertex templateVertex : templateGraph.getVertexList())
663 {
664 if (((Fragment) frag).isIsomorphicTo(templateVertex))
665 {
666 localScore++;
667 }
668 }
669 }
670
671 if (localScore > maxNumIsomorfFrgsFromThisTmpl)
672 {
673 maxNumIsomorfFrgsFromThisTmpl = localScore;
674 fragsFromThisTemplate = locfragments;
675 if (maxNumIsomorfFrgsFromThisTmpl ==
676 maxTheoreticalScorePerMapping*atomMappings.size())
677 {
678 break;
679 }
680 }
681 }
682
683 if (maxNumIsomorfFrgsFromThisTmpl > maxNumIsomorfFrgs)
684 {
685 maxNumIsomorfFrgs = maxNumIsomorfFrgsFromThisTmpl;
686 fragments = fragsFromThisTemplate;
687 }
688 }
689 return fragments;
690 }
691
692//------------------------------------------------------------------------------
693
702 private static void exploreDGraphForMappings(DGraph graph, IAtomContainer graphIAC,
703 Fragment masterFrag, IAtomContainer masterFragIAC, IAtomContainer mol,
704 Map<IAtom,IAtom> graphToMolMapping)
705 throws DENOPTIMException
706 {
707 int cutId = -1;
708 for (Edge edge : graph.getEdgeList())
709 {
710 cutId++;
711 int srcAPIdx = edge.getSrcAP().getAtomPositionNumberInMol();
712 int trgAPIdx = edge.getTrgAP().getAtomPositionNumberInMol();
713 IAtom graphAtmSrc = graphIAC.getAtom(srcAPIdx);
714 IAtom graphAtmTrg = graphIAC.getAtom(trgAPIdx);
715
716 IAtom masterFragAtmSrc = masterFragIAC.getAtom(mol.indexOf(graphToMolMapping.get(graphAtmSrc)));
717 IAtom masterFragAtmTrg = masterFragIAC.getAtom(mol.indexOf(graphToMolMapping.get(graphAtmTrg)));
718
719 IBond bnd = masterFragIAC.getBond(masterFragAtmSrc, masterFragAtmTrg);
720 if (bnd != null)
721 {
722 masterFragIAC.removeBond(bnd);
723 }
724
725 AttachmentPoint srcAP = masterFrag.addAPOnAtom(masterFragAtmSrc,
726 edge.getSrcAP().getAPClass(),
727 MoleculeUtils.getPoint3d(masterFragAtmTrg));
728 srcAP.setCutId(cutId);
729 AttachmentPoint trgAP = masterFrag.addAPOnAtom(masterFragAtmTrg,
730 edge.getTrgAP().getAPClass(),
731 MoleculeUtils.getPoint3d(masterFragAtmSrc));
732 trgAP.setCutId(cutId);
733 }
734
735 for (Vertex v : graph.getVertexList())
736 {
737 if (v instanceof Template)
738 {
739 DGraph innerGraph = ((Template) v).getInnerGraph();
740 exploreDGraphForMappings(innerGraph, graphIAC, masterFrag, masterFragIAC, mol, graphToMolMapping);
741 continue;
742 }
743
744 // Deal with vertexes without edges, i.e., free APs
745 for (AttachmentPoint ap : v.getAttachmentPoints())
746 {
747 if (!ap.isAvailable())
748 {
749 continue;
750 }
751 IAtom graphAtmSrc = graphIAC.getAtom(ap.getAtomPositionNumberInMol());
752 IAtom masterFragAtmSrc = masterFragIAC.getAtom(mol.indexOf(graphToMolMapping.get(graphAtmSrc)));
753
754 // find the position of the AP head in 3D space by aligning geoetry to template geometry
755 Point3d apHead = findPointAlignedWithTmpl(ap, graphAtmSrc,
756 graphIAC, masterFragIAC, mol, graphToMolMapping);
757
758 // Make the free AP on the master
759 masterFrag.addAPOnAtom(masterFragAtmSrc, ap.getAPClass(), apHead);
760 }
761 }
762 }
763
764//------------------------------------------------------------------------------
765
782 private static Point3d findPointAlignedWithTmpl(AttachmentPoint ap,
783 IAtom graphAtmSrc, IAtomContainer graphIAC, IAtomContainer masterFragIAC,
784 IAtomContainer mol,
785 Map<IAtom,IAtom> graphToMolMapping) throws DENOPTIMException
786 {
787 // Ensure assumptions
788 if (!graphIAC.contains(graphAtmSrc))
789 {
790 throw new DENOPTIMException("The atom " + graphAtmSrc.getSymbol() + " is not in the graphIAC.");
791 }
792 if (mol.getAtomCount() != masterFragIAC.getAtomCount())
793 {
794 throw new DENOPTIMException("The number of atoms in the molecule and the master fragment are not the same.");
795 }
796
797 // Work with cloned molecules
798 IAtomContainer molA = MoleculeUtils.makeSameAs(masterFragIAC);
799 IAtomContainer molB = MoleculeUtils.makeSameAs(graphIAC);
800
801 // Analogue of graphAtmSrc (belongs to graphIAC) on molA, which is a clone of masterFragIAC
802 // which is consistent with mol.
803 IAtom graphAtmSrcOnMolA = molA.getAtom(mol.indexOf(graphToMolMapping.get(graphAtmSrc)));
804
805 // Add the AP as a dummy atom to the molecule that will be rototranslated
806 IAtom dummyAtm = new Atom("He"); //element is irrelevant
807 dummyAtm.setPoint3d(ap.getDirectionVector());
808 molB.addAtom(dummyAtm);
809
810 List<IAtom> closestNeighborsOnGrphIAC = graphIAC.getConnectedAtomsList(graphAtmSrc);
811 List<IAtom> closestNeighborsOnMolA = molA.getConnectedAtomsList(graphAtmSrcOnMolA);
812 int minNumClosestNeighbors = Math.min(closestNeighborsOnGrphIAC.size(), closestNeighborsOnMolA.size());
813 if (minNumClosestNeighbors < 1)
814 {
815 // We do not have enough atoms to do an alignement: use only distance
816 // NB: the case of a biatomic fragment is handles below.
817 double dist = graphAtmSrc.getPoint3d().distance(ap.getDirectionVector());
818 Point3d apHead = new Point3d(
819 graphAtmSrcOnMolA.getPoint3d().x + dist,
820 graphAtmSrcOnMolA.getPoint3d().y,
821 graphAtmSrcOnMolA.getPoint3d().z);
822 return apHead;
823 } else if (minNumClosestNeighbors == 1) {
824 // For biatomic fragments
825 double dist = graphAtmSrc.getPoint3d().distance(ap.getDirectionVector());
826 double angle = MathUtils.angle(
827 closestNeighborsOnGrphIAC.get(0).getPoint3d(),
828 graphAtmSrc.getPoint3d(),
829 ap.getDirectionVector());
830 IAtom nbrAtmOnMolA = molA.getAtom(mol.indexOf(graphToMolMapping.get(closestNeighborsOnGrphIAC.get(0))));
831 Point3d pSrc = graphAtmSrcOnMolA.getPoint3d();
832 Point3d pNbr = nbrAtmOnMolA.getPoint3d();
833 Point3d pSrcTmpl = graphAtmSrc.getPoint3d();
834 Point3d pNbrTmpl = closestNeighborsOnGrphIAC.get(0).getPoint3d();
835 Point3d pApTmpl = ap.getDirectionVector();
836
837 Vector3d uMol = CartesianSpaceUtils.getVectorFromTo(pSrc, pNbr);
838 uMol.normalize();
839 Vector3d uTmpl = CartesianSpaceUtils.getVectorFromTo(pSrcTmpl, pNbrTmpl);
840 uTmpl.normalize();
841 Vector3d wTmpl = CartesianSpaceUtils.getVectorFromTo(pSrcTmpl, pApTmpl);
842 wTmpl.normalize();
843
844 double angleRad = Math.toRadians(angle);
845 Vector3d apDir = new Vector3d();
846 apDir.scale(Math.cos(angleRad), uMol);
847 double sinAngle = Math.sin(angleRad);
849 {
850 Vector3d vRefTmpl = new Vector3d(wTmpl);
851 vRefTmpl.scaleAdd(-uTmpl.dot(wTmpl), uTmpl, vRefTmpl);
852 vRefTmpl.normalize();
853 Vector3d rotAxis = new Vector3d();
854 rotAxis.cross(uTmpl, uMol);
855 double sinRot = rotAxis.length();
856 double cosRot = uTmpl.dot(uMol);
858 {
859 rotAxis.normalize();
860 double rotAng = Math.toDegrees(Math.atan2(sinRot, cosRot));
861 CartesianSpaceUtils.rotatedVectorWAxisAngle(vRefTmpl, rotAxis, rotAng);
862 }
863 else if (cosRot < 0.0)
864 {
865 Vector3d flipAxis = CartesianSpaceUtils.getNormalDirection(uMol);
866 CartesianSpaceUtils.rotatedVectorWAxisAngle(vRefTmpl, flipAxis, 180.0);
867 }
868 vRefTmpl.scale(sinAngle);
869 apDir.add(vRefTmpl);
870 }
871
872 Point3d apHead = new Point3d(
873 pSrc.x + dist * apDir.x,
874 pSrc.y + dist * apDir.y,
875 pSrc.z + dist * apDir.z);
876 return apHead;
877 } else {
878 // Try to alogn atoms around AP sourc to get the position of the AP head
879 Map<IAtom,IAtom> molToGraphMappingAroundAPSrc = new HashMap<>();
880 Set<IAtom> atomsAlreadyUsed = new HashSet<>();
881 for (Map.Entry<IAtom,IAtom> pair : graphToMolMapping.entrySet())
882 {
883 IAtom atmGraphIAC = pair.getKey();
884 if (graphIAC.getConnectedAtomsList(graphAtmSrc).contains(atmGraphIAC)
885 || atmGraphIAC == graphAtmSrc)
886 {
887 atomsAlreadyUsed.add(atmGraphIAC);
888 molToGraphMappingAroundAPSrc.put(
889 molA.getAtom(mol.indexOf(pair.getValue())),
890 molB.getAtom(graphIAC.indexOf(atmGraphIAC)));
891 }
892 }
893 // When we have few atoms we take also the second shell of neighbors
894 if (minNumClosestNeighbors<3)
895 {
896 for (Map.Entry<IAtom,IAtom> pair : graphToMolMapping.entrySet())
897 {
898 IAtom atmGraphIAC = pair.getKey();
899 if (closestNeighborsOnGrphIAC.contains(atmGraphIAC)
900 && !atomsAlreadyUsed.contains(atmGraphIAC))
901 {
902 atomsAlreadyUsed.add(atmGraphIAC);
903 molToGraphMappingAroundAPSrc.put(
904 molA.getAtom(mol.indexOf(pair.getValue())),
905 molB.getAtom(graphIAC.indexOf(atmGraphIAC)));
906 }
907 }
908 }
909
910 // Make mapping in a format suitable for KabschAlignment
911 IAtom[] lstA = new IAtom[molToGraphMappingAroundAPSrc.size()];
912 IAtom[] lstB = new IAtom[molToGraphMappingAroundAPSrc.size()];
913 int k = 0;
914 for (Map.Entry<IAtom,IAtom> pair : molToGraphMappingAroundAPSrc.entrySet())
915 {
916 lstA[k] = pair.getKey();
917 lstB[k] = pair.getValue();
918 k++;
919 }
920
921 //Calculate rotational matrix and align
922 KabschAlignment sa;
923 try
924 {
925 sa = new KabschAlignment(lstA, lstB);
926 sa.align();
927 } catch (Throwable t) {
928 throw new DENOPTIMException("KabschAlignment failed.", t);
929 }
930
931 //Rototranslation of molecule B (origin is in A's center of mass!!!)
932 sa.rotateAtomContainer(molB);
933
934 // Translate B to to actual coordinates of A (instead of c.o.m.)
935 Point3d cm = sa.getCenterOfMass();
936 for (int ib = 0; ib < molB.getAtomCount(); ib++)
937 {
938 IAtom a = molB.getAtom(ib);
939 Point3d oldPlace = MoleculeUtils.getPoint3d(a);
940 Point3d newPlace = new Point3d(oldPlace.x + cm.x,
941 oldPlace.y + cm.y,
942 oldPlace.z + cm.z);
943 a.setPoint3d(newPlace);
944 }
945 }
946 return MoleculeUtils.getPoint3d(dummyAtm);
947 }
948
949//------------------------------------------------------------------------------
950
959 public static List<Vertex> fragmentation(IAtomContainer mol,
960 List<CuttingRule> rules, Logger logger) throws DENOPTIMException
961 {
962 Fragment masterFrag = new Fragment(mol,BBType.UNDEFINED);
963 IAtomContainer fragsMol = masterFrag.getIAtomContainer();
964
965 // Identify bonds
966 Map<String, List<MatchedBond>> matchingbonds =
967 FragmenterTools.getMatchingBondsAllInOne(fragsMol,rules,logger);
968
969 // Select bonds to cut and what rule to use for cutting them
970 int cutId = -1;
971 for (CuttingRule rule : rules) // NB: iterator follows rule's priority
972 {
973 String ruleName = rule.getName();
974
975 // Skip unmatched rules
976 if (!matchingbonds.keySet().contains(ruleName))
977 continue;
978
979 for (MatchedBond tb: matchingbonds.get(ruleName))
980 {
981 IAtom atmA = tb.getAtmSubClass0();
982 IAtom atmB = tb.getAtmSubClass1();
983
984 //ignore if bond already broken
985 if (!fragsMol.getConnectedAtomsList(atmA).contains(atmB))
986 {
987 continue;
988 }
989
990 // Initialize the source atoms for the attachment points
991 // but may be changed to handle hapticity
992 IAtom srcAPA = atmA;
993 IAtom srcAPB = atmB;
994
995 //treatment of n-hapto ligands
996 if (rule.isHAPTO())
997 {
998 // Get central atom (i.e., the "mono-hapto" side,
999 // typically the metal)
1000 // As a convention the central atom has subclass '0'
1001 IAtom centralAtm = atmA;
1002
1003 // Get list of candidates for hapto-system:
1004 // they have same cutting Rule and central metal
1005 ArrayList<IAtom> candidatesForHapto = new ArrayList<IAtom>();
1006 for (MatchedBond tbForHapto : matchingbonds.get(ruleName))
1007 {
1008 //Consider only bond involving same central atom
1009 if (tbForHapto.getAtmSubClass0() == centralAtm)
1010 candidatesForHapto.add(tbForHapto.getAtmSubClass1());
1011 }
1012
1013 // Select atoms in n-hapto system: contiguous neighbors with
1014 // same type of bond with the same central atom.
1015 Set<IAtom> atmsInHapto = new HashSet<IAtom>();
1016 atmsInHapto.add(tb.getAtmSubClass1());
1017 atmsInHapto = exploreHapticity(tb.getAtmSubClass1(),
1018 centralAtm, candidatesForHapto, fragsMol);
1019 if (atmsInHapto.size() == 1)
1020 {
1021 logger.log(Level.WARNING,"Unable to find more than one "
1022 + "bond involved in high-hapticity ligand! "
1023 + "Bond ignored.");
1024 continue;
1025 }
1026
1027 // Check existence of all bonds involved in multi-hapto system
1028 boolean isSystemIntact = true;
1029 for (IAtom ligAtm : atmsInHapto)
1030 {
1031 List<IAtom> nbrsOfLigAtm =
1032 fragsMol.getConnectedAtomsList(ligAtm);
1033 if (!nbrsOfLigAtm.contains(centralAtm))
1034 {
1035 isSystemIntact = false;
1036 break;
1037 }
1038 }
1039
1040 // If not, it means that another rule already acted on the
1041 // system thus kill this attempt without generating du-atom
1042 if (!isSystemIntact)
1043 continue;
1044
1045 srcAPA = centralAtm;
1046 srcAPB = getAPSourceAtom(masterFrag, new ArrayList<>(atmsInHapto), Arrays.asList(centralAtm));
1047 }
1048
1049 // Storage of info for AP creation
1050 List<List<List<IAtom>>> listOfAtomPairs = new ArrayList<>();
1051 List<List<APClass>> listOfAPClasses = new ArrayList<>();
1052 listOfAtomPairs.add(Arrays.asList(Arrays.asList(srcAPA), Arrays.asList(srcAPB)));
1053 listOfAPClasses.add(Arrays.asList(rule.getAPClass0(), rule.getAPClass1()));
1054 cutId++;
1055 makeAPPairs(masterFrag, listOfAtomPairs, listOfAPClasses, cutId);
1056 } //end of loop over matching bonds
1057 } //end of loop over rules
1058
1059 return isolateFragments(masterFrag);
1060 }
1061
1062//------------------------------------------------------------------------------
1063
1078 public static List<Vertex> fragmentation(IAtomContainer mol,
1079 List<List<List<IAtom>>> atomPairs, List<List<APClass>> apClasses)
1080 throws DENOPTIMException
1081 {
1082 Fragment masterFrag = new Fragment(mol,BBType.UNDEFINED);
1083 IAtomContainer fragsMol = masterFrag.getIAtomContainer();
1084
1085 // Project the list of atoms to the new atom container
1086 List<List<List<IAtom>>> projectedAtomPairs = new ArrayList<>();
1087 for (List<List<IAtom>> atomPair : atomPairs)
1088 {
1089 List<List<IAtom>> projectedAtomPair = new ArrayList<>();
1090 for (List<IAtom> leftOrRightMembers : atomPair)
1091 {
1092 List<IAtom> projectedLeftOrRightMembers = new ArrayList<>();
1093 for (IAtom atm : leftOrRightMembers)
1094 {
1095 projectedLeftOrRightMembers.add(fragsMol.getAtom(mol.indexOf(atm)));
1096 }
1097 projectedAtomPair.add(projectedLeftOrRightMembers);
1098 }
1099 projectedAtomPairs.add(projectedAtomPair);
1100 }
1101 makeAPPairs(masterFrag, projectedAtomPairs, apClasses, 1);
1102 return isolateFragments(masterFrag);
1103 }
1104
1105//------------------------------------------------------------------------------
1106
1107 private static List<Vertex> isolateFragments(Fragment masterFrag)
1108 throws DENOPTIMException
1109 {
1110 List<Vertex> fragments = new ArrayList<Vertex>();
1111 Set<Integer> doneAlready = new HashSet<Integer>();
1112 for (int idx=0 ; idx<masterFrag.getAtomCount(); idx++)
1113 {
1114 if (doneAlready.contains(idx))
1115 continue;
1116
1117 Fragment cloneOfMaster = masterFrag.clone();
1118 IAtomContainer iac = cloneOfMaster.getIAtomContainer();
1119 Set<IAtom> atmsToKeep = exploreConnectivity(iac.getAtom(idx), iac);
1120 atmsToKeep.stream().forEach(atm -> doneAlready.add(iac.indexOf(atm)));
1121
1122 Set<IAtom> atmsToRemove = new HashSet<IAtom>();
1123 for (IAtom atm : cloneOfMaster.atoms())
1124 {
1125 if (!atmsToKeep.contains(atm))
1126 {
1127 atmsToRemove.add(atm);
1128 }
1129 }
1130 cloneOfMaster.removeAtoms(atmsToRemove);
1131 if (cloneOfMaster.getAttachmentPoints().size()>0)
1132 fragments.add(cloneOfMaster);
1133 }
1134 return fragments;
1135 }
1136
1137//------------------------------------------------------------------------------
1138
1152 private static void makeAPPairs(Fragment masterFrag,
1153 List<List<List<IAtom>>> atomPairs, List<List<APClass>> apClasses, Integer cutId)
1154 throws DENOPTIMException
1155 {
1156 for (int i = 0; i < atomPairs.size(); i++)
1157 {
1158 // "pair" in the sense that the list must have two items, but each
1159 // item may actually be a list of atoms in case of hapticity>1.
1160 List<List<IAtom>> atomPair = atomPairs.get(i);
1161 APClass apClassA = apClasses.get(i).get(0);
1162 APClass apClassB = apClasses.get(i).get(1);
1163
1164 IAtom srcAPA = getAPSourceAtom(masterFrag, atomPair.get(0), atomPair.get(1));
1165 IAtom srcAPB = getAPSourceAtom(masterFrag, atomPair.get(1), atomPair.get(0));
1166
1167 //treatment of mono-hapto ligands
1168 IBond bnd = masterFrag.getIAtomContainer().getBond(srcAPA,srcAPB);
1169 if (bnd != null)
1170 {
1171 masterFrag.removeBond(bnd);
1172 }
1173
1174 AttachmentPoint apA = masterFrag.addAPOnAtom(srcAPA, apClassA,
1175 MoleculeUtils.getPoint3d(srcAPB));
1176 AttachmentPoint apB = masterFrag.addAPOnAtom(srcAPB, apClassB,
1177 MoleculeUtils.getPoint3d(srcAPA));
1178
1179 cutId++;
1180 apA.setCutId(cutId);
1181 apB.setCutId(cutId);
1182 }
1183 }
1184
1185//------------------------------------------------------------------------------
1186
1198 private static IAtom getAPSourceAtom(Fragment masterFrag, List<IAtom> atmsInHapto,
1199 List<IAtom> atmsOutsideHapto) throws DENOPTIMException
1200 {
1201 // Not really a multihapto system: return the single atoms
1202 if (atmsInHapto.size() == 1)
1203 {
1204 return atmsInHapto.get(0);
1205 }
1206
1207 IAtomContainer fragsMol = masterFrag.getIAtomContainer();
1208 // A dummy atom will be used to define attachment point of
1209 // ligand with high hapticity
1210 Point3d dummyP3d = new Point3d(); //Used also for 2D
1211 for (IAtom ligAtm : atmsInHapto)
1212 {
1213 Point3d ligP3d = MoleculeUtils.getPoint3d(ligAtm);
1214 dummyP3d.x = dummyP3d.x + ligP3d.x;
1215 dummyP3d.y = dummyP3d.y + ligP3d.y;
1216 dummyP3d.z = dummyP3d.z + ligP3d.z;
1217 }
1218
1219 dummyP3d.x = dummyP3d.x / (double) atmsInHapto.size();
1220 dummyP3d.y = dummyP3d.y / (double) atmsInHapto.size();
1221 dummyP3d.z = dummyP3d.z / (double) atmsInHapto.size();
1222
1223 //Add Dummy atom to molecular object
1224 //if no other Du is already in the same position
1225 IAtom dummyAtm = null;
1226 for (IAtom oldDu : fragsMol.atoms())
1227 {
1230 {
1231 Point3d oldDuP3d = oldDu.getPoint3d();
1232 if (oldDuP3d.distance(dummyP3d) < 0.002)
1233 {
1234 dummyAtm = oldDu;
1235 break;
1236 }
1237 }
1238 }
1239
1240 // Make the dummy, since it is not there already
1241 if (dummyAtm==null)
1242 {
1243 dummyAtm = new PseudoAtom(DENOPTIMConstants.DUMMYATMSYMBOL);
1244 dummyAtm.setPoint3d(dummyP3d);
1245 fragsMol.addAtom(dummyAtm);
1246 }
1247
1248 // Modify connectivity of atoms involved in high-hapticity
1249 // coordination creation of Du-to-ATM bonds
1250 // By internal convention the bond order is "SINGLE".
1251 IBond.Order border = IBond.Order.valueOf("SINGLE");
1252
1253 for (IAtom ligAtm : atmsInHapto)
1254 {
1255 List<IAtom> nbrsOfDu = fragsMol.getConnectedAtomsList(
1256 dummyAtm);
1257 if (!nbrsOfDu.contains(ligAtm))
1258 {
1259 // Add bond with dummy
1260 Bond bnd = new Bond(dummyAtm,ligAtm,border);
1261 fragsMol.addBond(bnd);
1262 }
1263 // Remove bonds between central and coordinating atoms
1264 for (IAtom atmOutsideHapto : atmsOutsideHapto)
1265 {
1266 IBond oldBnd = fragsMol.getBond(atmOutsideHapto,ligAtm);
1267 fragsMol.removeBond(oldBnd);
1268 }
1269 }
1270 return dummyAtm;
1271 }
1272
1273//------------------------------------------------------------------------------
1287 static Set<IAtom> exploreHapticity(IAtom seed, IAtom centralAtom,
1288 ArrayList<IAtom> candidates, IAtomContainer mol)
1289 {
1290 Set<IAtom> atmsInHapto = new HashSet<IAtom>();
1291 atmsInHapto.add(seed);
1292 ArrayList<IAtom> toVisitAtoms = new ArrayList<IAtom>();
1293 toVisitAtoms.add(seed);
1294 ArrayList<IAtom> visitedAtoms = new ArrayList<IAtom>();
1295 while (toVisitAtoms.size()>0)
1296 {
1297 ArrayList<IAtom> toVisitLater = new ArrayList<IAtom>();
1298 for (IAtom atomInFocus : toVisitAtoms)
1299 {
1300 if (visitedAtoms.contains(atomInFocus)
1301 || atomInFocus==centralAtom)
1302 continue;
1303 else
1304 visitedAtoms.add(atomInFocus);
1305
1306 if (candidates.contains(atomInFocus))
1307 {
1308 atmsInHapto.add(atomInFocus);
1309 toVisitLater.addAll(mol.getConnectedAtomsList(atomInFocus));
1310 }
1311 }
1312 toVisitAtoms.clear();
1313 toVisitAtoms.addAll(toVisitLater);
1314 }
1315 return atmsInHapto;
1316 }
1317
1318//------------------------------------------------------------------------------
1327 static Set<IAtom> exploreConnectivity(IAtom seed, IAtomContainer mol)
1328 {
1329 Set<IAtom> atmsReachableFromSeed = new HashSet<IAtom>();
1330 ArrayList<IAtom> toVisitAtoms = new ArrayList<IAtom>();
1331 toVisitAtoms.add(seed);
1332 ArrayList<IAtom> visitedAtoms = new ArrayList<IAtom>();
1333 while (toVisitAtoms.size()>0)
1334 {
1335 ArrayList<IAtom> toVisitLater = new ArrayList<IAtom>();
1336 for (IAtom atomInFocus : toVisitAtoms)
1337 {
1338 if (visitedAtoms.contains(atomInFocus))
1339 continue;
1340 else
1341 visitedAtoms.add(atomInFocus);
1342
1343 atmsReachableFromSeed.add(atomInFocus);
1344 toVisitLater.addAll(mol.getConnectedAtomsList(atomInFocus));
1345 }
1346 toVisitAtoms.clear();
1347 toVisitAtoms.addAll(toVisitLater);
1348 }
1349 return atmsReachableFromSeed;
1350 }
1351
1352//-----------------------------------------------------------------------------
1353
1362 static Map<String, List<MatchedBond>> getMatchingBondsAllInOne(
1363 IAtomContainer mol, List<CuttingRule> rules, Logger logger)
1364 {
1365 // Collect all SMARTS queries
1366 Map<String,String> smarts = new HashMap<String,String>();
1367 for (CuttingRule rule : rules)
1368 {
1369 smarts.put(rule.getName(),rule.getWholeSMARTSRule());
1370 }
1371
1372 // Prepare a data structure for the return value
1373 Map<String, List<MatchedBond>> bondsMatchingRules =
1374 new HashMap<String, List<MatchedBond>>();
1375
1376 // Get all the matches to the SMARTS queries
1377 ManySMARTSQuery msq = new ManySMARTSQuery(mol, smarts);
1378 if (msq.hasProblems())
1379 {
1380 if (logger!=null)
1381 {
1382 logger.log(Level.WARNING, "Problem matching SMARTS: "
1383 + msq.getMessage());
1384 }
1385 return bondsMatchingRules;
1386 }
1387
1388 for (CuttingRule rule : rules)
1389 {
1390 String ruleName = rule.getName();
1391
1392 if (msq.getNumMatchesOfQuery(ruleName) == 0)
1393 {
1394 continue;
1395 }
1396
1397 // Get atoms matching cutting rule queries
1398 Mappings purgedPairs = msq.getMatchesOfSMARTS(ruleName);
1399
1400 // Evaluate subclass membership and eventually store target bonds
1401 ArrayList<MatchedBond> bondsMatched = new ArrayList<MatchedBond>();
1402 for (int[] pair : purgedPairs)
1403 {
1404 if (pair.length!=2)
1405 {
1406 throw new Error("Cutting rule: " + ruleName
1407 + " has identified " + pair.length + " atoms "
1408 + "instead of 2. Modify rule to make it find a "
1409 + "pair of atoms.");
1410 }
1411 MatchedBond tb = new MatchedBond(mol.getAtom(pair[0]),
1412 mol.getAtom(pair[1]), rule);
1413
1414 // Apply any further option of the cutting rule
1415 if (tb.satisfiesRuleOptions(logger))
1416 bondsMatched.add(tb);
1417 }
1418
1419 if (!bondsMatched.isEmpty())
1420 bondsMatchingRules.put(ruleName, bondsMatched);
1421 }
1422
1423 return bondsMatchingRules;
1424 }
1425
1426//------------------------------------------------------------------------------
1427
1433 public static void manageFragmentCollection(File input,
1434 FragmenterParameters settings,
1435 File output, Logger logger) throws DENOPTIMException, IOException,
1436 IllegalArgumentException, UndetectedFileFormatException
1437 {
1438 FileInputStream fis = new FileInputStream(input);
1439 IteratingSDFReader reader = new IteratingSDFReader(fis,
1440 DefaultChemObjectBuilder.getInstance());
1441
1442 int index = -1;
1443 int maxBufferSize = 2000;
1444 ArrayList<Vertex> buffer = new ArrayList<Vertex>(500);
1445 try {
1446 while (reader.hasNext())
1447 {
1448 index++;
1449 if (logger!=null)
1450 {
1451 logger.log(Level.FINE,"Processing fragment " + index);
1452 }
1453 Vertex frag = new Fragment(reader.next(), BBType.UNDEFINED);
1454 manageFragmentCollection(frag, index, settings,
1455 buffer, logger);
1456
1457 // If max buffer size is reached, then bump to file
1458 if (buffer.size() >= maxBufferSize)
1459 {
1461 buffer, true);
1462 buffer.clear();
1463 }
1464 }
1465 } finally {
1466 reader.close();
1467 }
1468 if (buffer.size() < maxBufferSize)
1469 {
1471 buffer, true);
1472 buffer.clear();
1473 }
1474 }
1475
1476//------------------------------------------------------------------------------
1477
1495 public static void manageFragmentCollection(Vertex frag, int fragCounter,
1496 FragmenterParameters settings,
1497 List<Vertex> collector, Logger logger)
1498 throws DENOPTIMException, IllegalArgumentException,
1500 {
1501
1502 if (!filterFragment((Fragment) frag, settings, logger))
1503 {
1504 return;
1505 }
1506
1507 //Compare with list of fragments to ignore
1508 if (settings.getIgnorableFragments().size() > 0)
1509 {
1510 if (settings.getIgnorableFragments().stream()
1511 .anyMatch(ignorable -> ((Fragment)frag)
1512 .isIsomorphicTo(ignorable)))
1513 {
1514 if (logger!=null)
1515 {
1516 logger.log(Level.FINE,"Fragment " + fragCounter
1517 + " is ignorable.");
1518 }
1519 return;
1520 }
1521 }
1522
1523 //Compare with list of fragments to retain
1524 if (settings.getTargetFragments().size() > 0)
1525 {
1526 if (!settings.getTargetFragments().stream()
1527 .anyMatch(ignorable -> ((Fragment)frag)
1528 .isIsomorphicTo(ignorable)))
1529 {
1530 if (logger!=null)
1531 {
1532 logger.log(Level.FINE,"Fragment " + fragCounter
1533 + " doesn't match any target: rejected.");
1534 }
1535 return;
1536 }
1537 }
1538
1539 // Add dummy atoms on linearities
1541 && settings.doAddDuOnLinearity())
1542 {
1544 settings.getLinearAngleLimit());
1545 }
1546
1547 // Management of duplicate fragments:
1548 // -> identify duplicates (isomorphic fragments),
1549 // -> keep one (or more, if we want to sample the isomorphs),
1550 // -> reject the rest.
1551 if (settings.doManageIsomorphicFamilies())
1552 {
1553 synchronized (settings.MANAGEMWSLOTSSLOCK)
1554 {
1555 String mwSlotID = getMWSlotIdentifier(frag,
1556 settings.getMWSlotSize());
1557
1558 File mwFileUnq = settings.getMWSlotFileNameUnqFrags(
1559 mwSlotID);
1560 File mwFileAll = settings.getMWSlotFileNameAllFrags(
1561 mwSlotID);
1562
1563 // Compare this fragment with previously seen ones
1564 Vertex unqVersion = null;
1565 if (mwFileUnq.exists())
1566 {
1567 ArrayList<Vertex> knownFrags =
1569 unqVersion = knownFrags.stream()
1570 .filter(knownFrag ->
1571 ((Fragment)frag).isIsomorphicTo(knownFrag))
1572 .findAny()
1573 .orElse(null);
1574 }
1575 if (unqVersion!=null)
1576 {
1577 // Identify this unique fragment
1578 String isoFamID = unqVersion.getProperty(
1580 .toString();
1581
1582 // Do we already have enough isomorphic family members
1583 // for this fragment?
1584 int sampleSize = settings.getIsomorphsCount()
1585 .get(isoFamID);
1586 if (sampleSize < settings.getIsomorphicSampleSize())
1587 {
1588 // Add this isomorphic version to the sample
1589 frag.setProperty(
1591 isoFamID);
1592 settings.getIsomorphsCount().put(isoFamID,
1593 sampleSize+1);
1594 DenoptimIO.writeVertexToFile(mwFileAll,
1595 FileFormat.VRTXSDF, frag, true);
1596 collector.add(frag);
1597 } else {
1598 // This would be inefficient in the long run
1599 // because it by-passes the splitting by MW.
1600 // Do not do it!
1601 /*
1602 if (logger!=null)
1603 {
1604 logger.log(Level.FINE,"Fragment "
1605 + fragCounter
1606 + " is isomorphic to unique fragment "
1607 + unqVersionID + ", but we already "
1608 + "have a sample of " + sampleSize
1609 + ": ignoring this fragment from now "
1610 + "on.");
1611 }
1612 settings.getIgnorableFragments().add(frag);
1613 */
1614 }
1615 } else {
1616 // This is a never-seen fragment
1617 String isoFamID = settings.newIsomorphicFamilyID();
1618 frag.setProperty(
1620 isoFamID);
1621 settings.getIsomorphsCount().put(isoFamID, 1);
1622 DenoptimIO.writeVertexToFile(mwFileUnq,
1623 FileFormat.VRTXSDF, frag, true);
1624 DenoptimIO.writeVertexToFile(mwFileAll,
1625 FileFormat.VRTXSDF, frag, true);
1626 collector.add(frag);
1627 }
1628 } // end synchronized block
1629 } else {
1630 //If we are here, we did not ask to remove duplicates
1631 collector.add(frag);
1632 }
1633 }
1634
1635//------------------------------------------------------------------------------
1636
1646 public static boolean filterFragment(Fragment frag,
1647 FragmenterParameters settings)
1648 {
1649 return filterFragment(frag, settings, settings.getLogger());
1650 }
1651
1652//------------------------------------------------------------------------------
1653
1665 public static boolean filterFragment(Fragment frag,
1666 FragmenterParameters settings, Logger logger)
1667 {
1668 // Default filtering criteria: get ring of R/*/X/Xx
1669 for (IAtom atm : frag.atoms())
1670 {
1671 if (MoleculeUtils.isElement(atm))
1672 {
1673 continue;
1674 }
1675 String smb = MoleculeUtils.getSymbolOrLabel(atm);
1676 if (DENOPTIMConstants.DUMMYATMSYMBOL.equals(smb))
1677 {
1678 continue;
1679 }
1680 logger.log(Level.FINE,"Removing fragment contains non-element '"
1681 + smb + "'");
1682 return false;
1683 }
1684
1685 if (settings.isWorkingIn3D())
1686 {
1687 // Incomplete 3D fragmentation: an atom has the same coords of an AP.
1688 for (AttachmentPoint ap : frag.getAttachmentPoints())
1689 {
1690 Point3d ap3d = ap.getDirectionVector();
1691 if (ap3d!=null)
1692 {
1693 for (IAtom atm : frag.atoms())
1694 {
1695 Point3d atm3d = MoleculeUtils.getPoint3d(atm);
1696 double dist = ap3d.distance(atm3d);
1697 if (dist < 0.0002)
1698 {
1699 logger.log(Level.FINE,"Removing fragment with AP"
1700 + frag.getIAtomContainer().indexOf(atm)
1701 + " and atom " + MoleculeUtils.getSymbolOrLabel(atm)
1702 + " coincide.");
1703 return false;
1704 }
1705 }
1706 }
1707 }
1708 }
1709 if (settings.doRejectWeirdIsotopes())
1710 {
1711 for (IAtom atm : frag.atoms())
1712 {
1713 if (MoleculeUtils.isElement(atm))
1714 {
1715 // Unconfigured isotope has null mass number
1716 if (atm.getMassNumber() == null)
1717 continue;
1718
1719 String symb = MoleculeUtils.getSymbolOrLabel(atm);
1720 int a = atm.getMassNumber();
1721 try {
1722 IIsotope major = Isotopes.getInstance().getMajorIsotope(symb);
1723 if (a != major.getMassNumber())
1724 {
1725 logger.log(Level.FINE,"Removing fragment containing "
1726 + "isotope "+symb+a+".");
1727 return false;
1728 }
1729 } catch (Throwable t) {
1730 logger.log(Level.WARNING,"Not able to perform Isotope"
1731 + "detection.");
1732 }
1733 }
1734
1735 }
1736 }
1737
1738 // User-controlled filtering criteria
1739
1740 if (settings.getRejectedElements().size() > 0)
1741 {
1742 for (IAtom atm : frag.atoms())
1743 {
1744 String symb = MoleculeUtils.getSymbolOrLabel(atm);
1745 if (settings.getRejectedElements().contains(symb))
1746 {
1747 logger.log(Level.FINE,"Removing fragment containing '"
1748 + symb + "'.");
1749 return false;
1750 }
1751 }
1752 }
1753
1754 if (settings.getRejectedFormulaLessThan().size() > 0
1755 || settings.getRejectedFormulaMoreThan().size() > 0)
1756 {
1757 Map<String,Double> eaMol = FormulaUtils.getElementalanalysis(
1758 frag.getIAtomContainer());
1759
1760 for (Map<String,Double> criterion :
1761 settings.getRejectedFormulaMoreThan())
1762 {
1763 for (String el : criterion.keySet())
1764 {
1765 if (eaMol.containsKey(el))
1766 {
1767 // -0.5 to make it strictly less-than
1768 if (eaMol.get(el) - criterion.get(el) > 0.5)
1769 {
1770 logger.log(Level.FINE,"Removing fragment that "
1771 + "contains too much '" + el + "' "
1772 + "as requested by formula"
1773 + "-based (more-than) settings (" + el
1774 + eaMol.get(el) + " > " + criterion + ").");
1775 return false;
1776 }
1777 }
1778 }
1779 }
1780
1781 Map<String,Double> criterion = settings.getRejectedFormulaLessThan();
1782 for (String el : criterion.keySet())
1783 {
1784 if (!eaMol.containsKey(el))
1785 {
1786 logger.log(Level.FINE,"Removing fragment that does not "
1787 + "contain '" + el + "' as requested by formula"
1788 + "-based (less-than) settings.");
1789 return false;
1790 } else {
1791 // 0.5 to make it strictly more-than
1792 if (eaMol.get(el) - criterion.get(el) < -0.5)
1793 {
1794 logger.log(Level.FINE,"Removing fragment that "
1795 + "contains too little '" + el + "' "
1796 + "as requested by formula"
1797 + "-based settings (" + el
1798 + eaMol.get(el) + " < " + criterion + ").");
1799 return false;
1800 }
1801 }
1802 }
1803
1804 }
1805
1806 if (settings.getRejectedAPClasses().size() > 0)
1807 {
1808 for (APClass apc : frag.getAllAPClasses())
1809 {
1810 for (String s : settings.getRejectedAPClasses())
1811 {
1812 if (apc.toString().startsWith(s))
1813 {
1814 logger.log(Level.FINE,"Removing fragment with APClass "
1815 + apc);
1816 return false;
1817 }
1818 }
1819 }
1820 }
1821
1822 if (settings.getRejectedAPClassCombinations().size() > 0)
1823 {
1824 loopOverCombinations:
1825 for (String[] conditions : settings.getRejectedAPClassCombinations())
1826 {
1827 for (int ip=0; ip<conditions.length; ip++)
1828 {
1829 String condition = conditions[ip];
1830 boolean found = false;
1831 for (APClass apc : frag.getAllAPClasses())
1832 {
1833 if (apc.toString().startsWith(condition))
1834 {
1835 found = true;
1836 continue;
1837 }
1838 }
1839 if (!found)
1840 continue loopOverCombinations;
1841 // Here we do have at least one AP satisfying the condition.
1842 }
1843 // Here we manage or satisfy all conditions. Therefore, we can
1844 // reject this fragment
1845
1846 String allCondsAsString = "";
1847 for (int i=0; i<conditions.length; i++)
1848 allCondsAsString = allCondsAsString + " " + conditions[i];
1849
1850 logger.log(Level.FINE,"Removing fragment with combination of "
1851 + "APClasses matching '" + allCondsAsString + "'.");
1852 return false;
1853 }
1854 }
1855
1856 if (settings.getMaxFragHeavyAtomCount()>0
1857 || settings.getMinFragHeavyAtomCount()>0)
1858 {
1859 int totHeavyAtm = 0;
1860 for (IAtom atm : frag.atoms())
1861 {
1862 if (MoleculeUtils.isElement(atm))
1863 {
1864 String symb = MoleculeUtils.getSymbolOrLabel(atm);
1865 if ((!symb.equals("H")) && (!symb.equals(
1867 totHeavyAtm++;
1868 }
1869 }
1870 if (settings.getMaxFragHeavyAtomCount() > 0
1871 && totHeavyAtm > settings.getMaxFragHeavyAtomCount())
1872 {
1873 logger.log(Level.FINE,"Removing fragment with too many atoms ("
1874 + totHeavyAtm + " < "
1875 + settings.getMaxFragHeavyAtomCount()
1876 + ")");
1877 return false;
1878 }
1879 if (settings.getMinFragHeavyAtomCount() > 0
1880 && totHeavyAtm < settings.getMinFragHeavyAtomCount())
1881 {
1882 logger.log(Level.FINE,"Removing fragment with too few atoms ("
1883 + totHeavyAtm + " < "
1884 + settings.getMinFragHeavyAtomCount()
1885 + ")");
1886 return false;
1887 }
1888 }
1889
1890 if (settings.getFragRejectionSMARTS().size() > 0)
1891 {
1893 settings.getFragRejectionSMARTS());
1894 if (msq.hasProblems())
1895 {
1896 logger.log(Level.WARNING,"Problems evaluating SMARTS-based "
1897 + "rejection criteria. " + msq.getMessage());
1898 }
1899
1900 for (String criterion : settings.getFragRejectionSMARTS().keySet())
1901 {
1902 if (msq.getNumMatchesOfQuery(criterion)>0)
1903 {
1904 logger.log(Level.FINE,"Removing fragment that matches "
1905 + "SMARTS-based rejection criteria '" + criterion
1906 + "'.");
1907 return false;
1908 }
1909 }
1910 }
1911
1912 if (settings.getFragRetentionSMARTS().size() > 0)
1913 {
1915 settings.getFragRetentionSMARTS());
1916 if (msq.hasProblems())
1917 {
1918 logger.log(Level.WARNING,"Problems evaluating SMARTS-based "
1919 + "rejection criteria. " + msq.getMessage());
1920 }
1921
1922 boolean matchesAny = false;
1923 for (String criterion : settings.getFragRetentionSMARTS().keySet())
1924 {
1925 if (msq.getNumMatchesOfQuery(criterion) > 0)
1926 {
1927 matchesAny = true;
1928 break;
1929 }
1930 }
1931 if (!matchesAny)
1932 {
1933 logger.log(Level.FINE,"Removing fragment that does not "
1934 + "match any SMARTS-based retention criteria.");
1935 return false;
1936 }
1937 }
1938 return true;
1939 }
1940
1941//------------------------------------------------------------------------------
1942
1950 public static String getMWSlotIdentifier(Vertex frag, int slotSize)
1951 {
1952 for (IAtom a : frag.getIAtomContainer().atoms())
1953 {
1954 if (a.getImplicitHydrogenCount()==null)
1955 a.setImplicitHydrogenCount(0);
1956 }
1957 double mw = AtomContainerManipulator.getMass(frag.getIAtomContainer());
1958 int slotNum = (int) (mw / (Double.valueOf(slotSize)));
1959 return slotNum*slotSize + "-" + (slotNum+1)*slotSize;
1960 }
1961
1962//------------------------------------------------------------------------------
1963
1964 public static Vertex getRCVForAP(AttachmentPoint ap, APClass rcvApClass)
1965 throws DENOPTIMException
1966 {
1967 IAtomContainer mol = SilentChemObjectBuilder.getInstance()
1968 .newAtomContainer();
1969 Point3d apv = ap.getDirectionVector();
1970 mol.addAtom(new PseudoAtom(RingClosingAttractor.RCALABELPERAPCLASS.get(
1971 rcvApClass),
1972 new Point3d(
1973 Double.valueOf(apv.x),
1974 Double.valueOf(apv.y),
1975 Double.valueOf(apv.z))));
1976
1977 Fragment rcv = new Fragment(mol, BBType.FRAGMENT);
1978 rcv.setAsRCV(true);
1979
1980 Point3d aps = MoleculeUtils.getPoint3d(
1981 ap.getOwner().getIAtomContainer().getAtom(
1982 ap.getAtomPositionNumber()));
1983 rcv.addAP(0, rcvApClass, new Point3d(
1984 Double.valueOf(aps.x),
1985 Double.valueOf(aps.y),
1986 Double.valueOf(aps.z)));
1987 return rcv;
1988 }
1989
1990//------------------------------------------------------------------------------
1991
1992}
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 double FLOATCOMPARISONTOLERANCE
Smallest difference for comparison of double and float numbers.
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 IAtom getAPSourceAtom(Fragment masterFrag, List< IAtom > atmsInHapto, List< IAtom > atmsOutsideHapto)
Returns the atom that will be used as the source of the attachment point possibly handling a multy-ha...
static List< Vertex > fragmentation(IAtomContainer mol, List< List< List< IAtom > > > atomPairs, List< List< APClass > > apClasses)
Chops one chemical structure by converting the given structure of atoms into attachment points anre r...
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 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 > isolateFragments(Fragment masterFrag)
static List< Vertex > fragmentation(IAtomContainer mol, List< CuttingRule > rules, Logger logger)
Chops one chemical structure by applying the given cutting rules.
static List< Vertex > fragmentation(IAtomContainer mol, List< DGraph > templates, int maxBufferShellSize, Randomizer randomizer, Logger logger)
Chops one chemical structure by applying the given fragmentation templates.
static boolean filterFragment(Fragment frag, FragmenterParameters settings)
Filter fragments according to the criteria defined in the settings.
static void makeAPPairs(Fragment masterFrag, List< List< List< IAtom > > > atomPairs, List< List< APClass > > apClasses, Integer cutId)
Makes attachment points for the given atom pairs and AP classes.
static Set< IAtom > exploreConnectivity(IAtom seed, IAtomContainer mol)
Explores the connectivity annotating which atoms have been visited.
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 List< Vertex > fragmentation(IAtomContainer mol, FragmenterParameters settings)
Performs fragmentation according to the given settings.
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 Point3d findPointAlignedWithTmpl(AttachmentPoint ap, IAtom graphAtmSrc, IAtomContainer graphIAC, IAtomContainer masterFragIAC, IAtomContainer mol, Map< IAtom, IAtom > graphToMolMapping)
Finds the point on the master fragment that is aligned with the template geometry.
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 fragmentationFromGraphs(File input, FragmenterParameters settings, File output, Logger logger)
Performs fragmentation from graphs, i.e., extracts existing fragments from graphs (stored in a file).
static boolean fragmentation(File input, FragmenterParameters settings, File output, Logger logger)
Performs fragmentation according to the given cutting rules.
IAtomContainer getTemplateWithBufferShell(int bufferShellSize)
Produced a new IAtomContainer containing all the atoms needed to define the topology of the original ...
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:104
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
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< DGraph > readDENOPTIMGraphsFromFile(File inFile)
Reads a list of DGraphs from 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...
Utilities for working in the Cartesian space.
static Vector3d getNormalDirection(Vector3d dir)
Generate a vector that is perpendicular to the given one.
static Vector3d getVectorFromTo(Point3d a, Point3d b)
Creates an object Vector3d that originates from point a and goes to point b.
static void rotatedVectorWAxisAngle(Vector3d v, Vector3d axis, double ang)
Rotate a vector according to a given rotation axis and angle.
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)
Some useful math operations.
Definition: MathUtils.java:39
static double angle(Point3d a, Point3d b, Point3d c)
Calculate the angle between the 3 points.
Definition: MathUtils.java:284
Utilities for molecule conversion.
static void setZeroImplicitHydrogensToAllAtoms(IAtomContainer iac)
Sets zero implicit hydrogen count to all atoms.
static IAtomContainer makeSameAs(IAtomContainer mol)
Constructs a copy of an atom container, i.e., a molecule that reflects the one given in the input arg...
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 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.