$darkmode
DENOPTIM
ThreeDimTreeBuilder.java
Go to the documentation of this file.
1/*
2 * DENOPTIM
3 * Copyright (C) 2019 Marco Foscato <marco.foscato@uib.no>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Affero General Public License as published
7 * by the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19package denoptim.molecularmodeling;
20
21import java.io.File;
22import java.util.ArrayList;
23import java.util.HashMap;
24import java.util.LinkedHashMap;
25import java.util.List;
26import java.util.Map;
27import java.util.logging.Level;
28import java.util.logging.Logger;
29
30import javax.vecmath.AxisAngle4d;
31import javax.vecmath.Matrix3d;
32import javax.vecmath.Point3d;
33import javax.vecmath.Vector3d;
34
35import org.openscience.cdk.Bond;
36import org.openscience.cdk.PseudoAtom;
37import org.openscience.cdk.interfaces.IAtom;
38import org.openscience.cdk.interfaces.IAtomContainer;
39import org.openscience.cdk.interfaces.IBond;
40import org.openscience.cdk.interfaces.IChemObjectBuilder;
41import org.openscience.cdk.silent.SilentChemObjectBuilder;
42
43import denoptim.constants.DENOPTIMConstants;
44import denoptim.exception.DENOPTIMException;
45import denoptim.graph.AttachmentPoint;
46import denoptim.graph.DGraph;
47import denoptim.graph.Edge;
48import denoptim.graph.Edge.BondType;
49import denoptim.graph.Ring;
50import denoptim.graph.Vertex;
51import denoptim.io.DenoptimIO;
52import denoptim.utils.GraphConversionTool;
53import denoptim.utils.GraphUtils;
54import denoptim.utils.MathUtils;
55import denoptim.utils.MoleculeUtils;
56import denoptim.utils.Randomizer;
57
58
76{
81 private double maxCoord = 20.0;
82
87 private boolean alignIn3D = true;
88
92 private IChemObjectBuilder builder = SilentChemObjectBuilder.getInstance();
93
97 private Logger logger = Logger.getLogger("3dTreeBuilderLogger");
98
103
104 private static final String NL = DENOPTIMConstants.EOL;
105
106//------------------------------------------------------------------------------
107
113 {
114 this.logger = logger;
115 this.randomizer = randomizer;
116 }
117
118//------------------------------------------------------------------------------
119
126 public void setAlignBBsIn3D(boolean align)
127 {
128 this.alignIn3D = align;
129 }
130
131//------------------------------------------------------------------------------
132
158 public IAtomContainer convertGraphTo3DAtomContainer(DGraph graph)
159 throws DENOPTIMException
160 {
161 return convertGraphTo3DAtomContainer(graph, false);
162 }
163
164//------------------------------------------------------------------------------
165
194 public IAtomContainer convertGraphTo3DAtomContainer(DGraph graph,
195 boolean removeUsedRCAs) throws DENOPTIMException
196 {
197 return convertGraphTo3DAtomContainer(graph,removeUsedRCAs,true, false);
198 }
199
200//------------------------------------------------------------------------------
201
236 //NB: there is unused code that could turn out useful in debugging.
237 @SuppressWarnings("unused")
238 public IAtomContainer convertGraphTo3DAtomContainer(DGraph graph,
239 boolean removeUsedRCAs, boolean setCDKRequirements, boolean rebuild)
240 throws DENOPTIMException
241 {
242 // initialize data structures for storing logging info
243 Map<Long,ArrayList<AttachmentPoint>> apsPerVertexId = new HashMap<>();
244 Map<Edge,ArrayList<AttachmentPoint>> apsPerEdge = new HashMap<>();
245 Map<IAtom,ArrayList<AttachmentPoint>> apsPerAtom = new HashMap<>();
246 Map<IBond,ArrayList<AttachmentPoint>> apsPerBond = new HashMap<>();
247
248 // initialize the atom container to be returned
249 IAtomContainer mol = builder.newAtomContainer();
250
251 // We start from the scallof, if any, otherwise we start from the first vertex
252 Vertex rootVrtx = graph.getSourceVertex();
253 append3DFragmentsDisconnected(rootVrtx, mol, graph, removeUsedRCAs, rebuild,
254 apsPerVertexId, apsPerEdge, apsPerAtom, apsPerBond);
255
256 // Deal with disconnected graphs: not all vertexes are reachable from the root
257 for (Vertex v : graph.getVertexList())
258 {
259 Boolean visited = (Boolean) v.getProperty(DENOPTIMConstants.VISITEDBY3DBUILDER);
260 if (visited != null && visited == true)
261 {
262 continue;
263 }
264
265 Vertex nextBranchRoot = v;
266 List<Vertex> parentTree = new ArrayList<Vertex>();
267 graph.getParentTree(v, parentTree);
268 if (parentTree.size() > 0)
269 {
270 nextBranchRoot = parentTree.get(parentTree.size()-1);
271 }
272
273 append3DFragmentsDisconnected(nextBranchRoot,
274 mol, graph, removeUsedRCAs, rebuild,
275 apsPerVertexId, apsPerEdge, apsPerAtom, apsPerBond);
276 }
277 for (Vertex v : graph.getVertexList())
278 {
279 v.removeProperty(DENOPTIMConstants.VISITEDBY3DBUILDER);
280 }
281
282 if (removeUsedRCAs)
283 {
284 // This is where we make the rings-closing bonds.
285 // Unused RCAs should have been already replaced by capping groups
286 // (or removed, if no capping needed), by changing the graph with
287 // GraphConversionTool.removeUnusedRCVs
288 // So, this will deal only with used RCAs.
289
290 // WARNING: since we are changing the atom list, we need to make
291 // sure we retail consistency between the changing atom list of
292 // the atom indexes stored in the APs, and in particular, the
293 // index returned by getAtomPositionNumberInMol.
295 }
296
297 if (setCDKRequirements)
298 {
301 }
302
303 // Code that may turn out useful for deep level debugging
304 if (false)
305 {
306 DenoptimIO.writeGraphToJSON(new File("/tmp/graph.json"), graph);
307 StringBuilder sb = new StringBuilder();
308 String file = "/tmp/iacTree.sdf";
309 sb.append("Writing tree-like IAtomContainer to " + file+NL);
310 IAtomContainer cmol = builder.newAtomContainer();
311 try
312 {
313 cmol = mol.clone();
314 }
315 catch (Throwable t)
316 {
317 throw new DENOPTIMException(t);
318 }
319
320 sb.append("AP-per-VertexID"+NL);
321 int i=0;
322 for (long v : apsPerVertexId.keySet())
323 {
324 ArrayList<AttachmentPoint> aps = apsPerVertexId.get(v);
325 for (AttachmentPoint ap : aps)
326 {
327 i++;
328 IAtom atm = new PseudoAtom(String.valueOf(i),
329 new Point3d(ap.getDirectionVector()));
330 sb.append("Vertex: "+v+" AP-"+i+" = "+ap+NL);
331 cmol.addAtom(atm);
332 IBond bnd = new Bond(cmol.getAtom(
333 ap.getAtomPositionNumber()),
334 cmol.getAtom(mol.getAtomCount()+i-1),
335 IBond.Order.SINGLE);
336 cmol.addBond(bnd);
337 }
338 }
339 DenoptimIO.writeSDFFile(file, mol, false);
340 DenoptimIO.writeSDFFile(file, cmol, true);
341 sb.append("AP-per-Edge"+NL);
342 for (Edge e : apsPerEdge.keySet())
343 {
344 ArrayList<AttachmentPoint> aps = apsPerEdge.get(e);
345 for (AttachmentPoint ap : aps)
346 {
347 sb.append("Edge: "+e+" AP = "+ap+NL);
348 }
349 }
350 sb.append("AP-per-Atom"+NL);
351 for (IAtom a : apsPerAtom.keySet())
352 {
353 ArrayList<AttachmentPoint> aps = apsPerAtom.get(a);
354 for (AttachmentPoint ap : aps)
355 {
356 sb.append("Atom: "+mol.indexOf(a) +" AP = "+ap+NL);
357 }
358 }
359 sb.append("AP-per-Bond"+NL);
360 for (IBond b : apsPerBond.keySet())
361 {
362 ArrayList<AttachmentPoint> aps = apsPerBond.get(b);
363 for (AttachmentPoint ap : aps)
364 {
365 sb.append("Bond: "+mol.indexOf(b.getAtom(0))
366 +"-"+mol.indexOf(b.getAtom(1))+" AP = "+ap+NL);
367 }
368 }
369 logger.log(Level.FINEST, sb.toString());
370 }
371
372 // Prepare the string-representation of unused APs on this graph
373 LinkedHashMap<Integer,List<AttachmentPoint>> freeAPPerAtm =
374 new LinkedHashMap<>();
375 for (IAtom a : apsPerAtom.keySet())
376 {
377 int atmID = mol.indexOf(a);
378 if (atmID<0)
379 {
380 // source atom is not anymore there: probably it was RCA and has
381 // been removed
382 continue;
383 }
384
385 ArrayList<AttachmentPoint> aps = apsPerAtom.get(a);
386 for (AttachmentPoint ap : aps)
387 {
388 if (ap.isAvailableThroughout())
389 {
390 if (freeAPPerAtm.containsKey(atmID))
391 {
392 freeAPPerAtm.get(atmID).add(ap);
393 } else {
394 List<AttachmentPoint> lst =
395 new ArrayList<AttachmentPoint>();
396 lst.add(ap);
397 freeAPPerAtm.put(atmID,lst);
398 }
399 }
400 }
401 }
402 mol.setProperty(DENOPTIMConstants.APSTAG,
404
405 // Add usual graph-related string-based data to SDF properties
406 GraphUtils.writeSDFFields(mol, graph);
407
408 mol.setProperty(DENOPTIMConstants.MOLPROPAPxVID, apsPerVertexId);
409 mol.setProperty(DENOPTIMConstants.MOLPROPAPxEDGE, apsPerEdge);
410 mol.setProperty(DENOPTIMConstants.MOLPROPAPxATOM, apsPerAtom);
411 mol.setProperty(DENOPTIMConstants.MOLPROPAPxBOND, apsPerBond);
412
413 return mol;
414 }
415
416//------------------------------------------------------------------------------
417
436 private void append3DFragmentsDisconnected(Vertex rootVrtx,
437 IAtomContainer mol, DGraph graph,
438 boolean removeUsedRCAs, boolean rebuild,
439 Map<Long, ArrayList<AttachmentPoint>> apsPerVertexId,
440 Map<Edge,ArrayList<AttachmentPoint>> apsPerEdge,
441 Map<IAtom,ArrayList<AttachmentPoint>> apsPerAtom,
442 Map<IBond,ArrayList<AttachmentPoint>> apsPerBond)
443 throws DENOPTIMException
444 {
445 rootVrtx.setProperty(DENOPTIMConstants.VISITEDBY3DBUILDER, true);
446 long idRootVrtx = rootVrtx.getVertexId();
447
448 int preNumAtms = mol.getAtomCount();
449 IAtomContainer iacRootVrtx = null;
450 if (rootVrtx.containsAtoms())
451 {
452 iacRootVrtx = rootVrtx.getIAtomContainer(logger, randomizer,
453 removeUsedRCAs, rebuild);
454
455 if (iacRootVrtx == null)
456 {
457 String msg = this.getClass().getSimpleName()
458 + " found a building block declaring "
459 + "to containg atoms, "
460 + "but returning null atom container. "
461 + "Building blocks: " + rootVrtx;
462 throw new IllegalArgumentException(msg);
463 }
464
465 for (IAtom atm : iacRootVrtx.atoms())
466 {
467 Object prevPath = atm.getProperty(
469 if (prevPath!=null)
470 {
471 atm.setProperty(DENOPTIMConstants.ATMPROPVERTEXPATH,
472 idRootVrtx + ", " + prevPath.toString());
473 } else {
474 atm.setProperty(DENOPTIMConstants.ATMPROPVERTEXPATH,
475 idRootVrtx);
476 }
477 atm.setProperty(DENOPTIMConstants.ATMPROPVERTEXID, idRootVrtx);
478 }
479 mol.add(iacRootVrtx);
480 }
481
482 // Store APs per building block and per atom (if atom exists)
483 ArrayList<AttachmentPoint> apsOnThisFrag = new ArrayList<>();
484 for (AttachmentPoint ap : rootVrtx.getAttachmentPoints())
485 {
486 // For vertices other than the first, we adjust the pointer to the
487 // AP source atom according to the atom list of the entire molecule
488 ap.setAtomPositionNumberInMol(ap.getAtomPositionNumber() + preNumAtms);
489 apsOnThisFrag.add(ap);
490
491 if (rootVrtx.containsAtoms())
492 {
493 IAtom srcAtm = mol.getAtom(ap.getAtomPositionNumberInMol());
494 if (apsPerAtom.containsKey(srcAtm))
495 {
496 apsPerAtom.get(srcAtm).add(ap);
497 }
498 else
499 {
500 ArrayList<AttachmentPoint> apsOnThisAtm =
501 new ArrayList<AttachmentPoint>();
502 apsOnThisAtm.add(ap);
503 apsPerAtom.put(srcAtm,apsOnThisAtm);
504 }
505 }
506 }
507 apsPerVertexId.put(idRootVrtx,apsOnThisFrag);
508
509 // Recursion on all branches of the tree (i.e., all incident edges)
510 for (Edge edge : graph.getEdgesWithSrc(rootVrtx))
511 {
512 // Get the AP from the current vertex to the next
513 AttachmentPoint apSrc = edge.getSrcAP();
514
515 // Add APs to the map of APs per Edges
516 ArrayList<AttachmentPoint> apOnThisEdge =
517 new ArrayList<AttachmentPoint>();
518 apOnThisEdge.add(apSrc);
519 apOnThisEdge.add(edge.getTrgAP());
520 apsPerEdge.put(edge,apOnThisEdge);
521
522 if (rootVrtx.containsAtoms())
523 {
524 Point3d trgPtApSrc = new Point3d(apSrc.getDirectionVector());
525 Point3d srcPtApSrc = new Point3d(
526 MoleculeUtils.getPoint3d(iacRootVrtx.getAtom(
527 apSrc.getAtomPositionNumber())));
528
529 // Append next building block on AP-vector - start recursion
530 append3DFragmentsViaEdges(mol, graph,
532 srcPtApSrc,trgPtApSrc,edge,removeUsedRCAs, rebuild,
533 apsPerVertexId,apsPerEdge,apsPerAtom,apsPerBond);
534 } else {
535 // Append next building block - start recursion
536 Point3d pt = getRandomPoint(mol);
537 append3DFragmentsViaEdges(mol, graph, -1, pt, pt, edge,
538 removeUsedRCAs, rebuild,
539 apsPerVertexId,apsPerEdge,apsPerAtom,apsPerBond);
540 }
541 }
542 }
543
544//------------------------------------------------------------------------------
545
583 private void append3DFragmentsViaEdges(IAtomContainer mol, DGraph graph,
584 int idSrcAtmA, Point3d srcApA, Point3d trgApA, Edge edge,
585 boolean removeUsedRCAs, boolean rebuild,
586 Map<Long, ArrayList<AttachmentPoint>> apsPerVertexId,
587 Map<Edge,ArrayList<AttachmentPoint>> apsPerEdge,
588 Map<IAtom,ArrayList<AttachmentPoint>> apsPerAtom,
589 Map<IBond,ArrayList<AttachmentPoint>> apsPerBond)
590 throws DENOPTIMException
591 {
592 logger.log(Level.FINE, "Appending 3D fragment via edge: "+edge + NL
593 +"#Atoms on growing mol: "+mol.getAtomCount());
594
595 // Get the incoming fragment and its AP
596 Vertex inVtx = edge.getTrgAP().getOwner();
598 AttachmentPoint apB = edge.getTrgAP();
599
600 //Used to keep track of which atom comes from which vertex
601 long idInVrx = inVtx.getVertexId();
602
603 logger.log(Level.FINE, "Incoming vertex : "+inVtx);
604
605 int preNumAtms = mol.getAtomCount();
606 IAtomContainer inFrag = null;
607 if (inVtx.containsAtoms())
608 {
609 inFrag = inVtx.getIAtomContainer(logger, randomizer, removeUsedRCAs,
610 true);
611 if (inFrag == null)
612 {
613 String msg = "ThreeDimTreeBuilder found a building block "
614 + "daclaring "
615 + "to containg atoms, but returning a null atom "
616 + "container. "
617 + "Problematic building block is bbID="
618 + inVtx.getBuildingBlockId() + " bbType="
619 + inVtx.getBuildingBlockType();
620 throw new IllegalArgumentException(msg);
621 }
622
623 logger.log(Level.FINE, "Incoming IAC #atoms: " +
624 inFrag.getAtomCount());
625
626 if (alignIn3D)
627 {
628 // Define the roto-translation operation that aligns
629 // the incoming building block to the growing molecule.
630 // To this end,
631 // we work on the APs that define the spatial relation
632 // between the
633 // parent vertex, which is the growing molecule (i.e., ApA)
634 // and that on the incoming building block (i.e., ApB).
635 Point3d trgApB = new Point3d(apB.getDirectionVector());
636 Point3d srcApB = new Point3d(MoleculeUtils.getPoint3d(
637 inFrag.getAtom(apB.getAtomPositionNumber())));
638
639 // Translate atoms and APs of incoming building block so that
640 // trgApB is on srcApA
641 Point3d tr1 = new Point3d();
642 tr1.sub(trgApB,srcApA);
643 for (IAtom atm : inFrag.atoms())
644 {
645 atm.setPoint3d(MoleculeUtils.getPoint3d(atm));
646 atm.getPoint3d().sub(tr1);
647 }
648 for (AttachmentPoint ap : inVtx.getAttachmentPoints())
649 {
650 Point3d pt = new Point3d(ap.getDirectionVector());
651 pt.sub(tr1);
652 ap.setDirectionVector(pt);
653 }
654 trgApB = new Point3d(apB.getDirectionVector());
655 srcApB = new Point3d(MoleculeUtils.getPoint3d(
656 inFrag.getAtom(apB.getAtomPositionNumber())));
657
658 //Get Vectors ApA and ApB (NOTE: inverse versus of ApB!!!)
659 Vector3d vectApA = new Vector3d();
660 Vector3d vectApB = new Vector3d();
661 vectApA.sub(trgApA,srcApA);
662 vectApB.sub(srcApB,trgApB);
663 vectApA.normalize();
664 vectApB.normalize();
665
666 logger.log(Level.FINE,
667 "After 1st translation and before rotation" + NL
668 + "srcApA "+srcApA+NL
669 + "trgApA "+trgApA+NL
670 + "vectApA "+vectApA+NL
671 + "srcApB "+srcApB+NL
672 + "trgApB "+trgApB+NL
673 + "vectApB "+vectApB);
674
675 // Get rotation matrix that aligns ApB to ApA
676 double rotAng = vectApA.angle(vectApB);
677 double threshold = 0.00001;
678 if (rotAng >= threshold)
679 {
680 Vector3d rotAxis = new Vector3d();
681 if (rotAng <= (Math.PI-0.00001))
682 {
683 rotAxis.cross(vectApB,vectApA);
684 }
685 else
686 {
687 rotAxis = MathUtils.getNormalDirection(vectApA);
688 }
689 Matrix3d rotMat = new Matrix3d();
690 rotAxis.normalize();
691 rotMat.set(new AxisAngle4d(rotAxis,rotAng));
692
693 logger.log(Level.FINE,"rotAng "+rotAng+NL
694 +"rotAxis "+rotAxis
695 +"rotMat "+rotMat);
696
697 // Rotate atoms of incoming building block
698 for (IAtom atm : inFrag.atoms())
699 {
700 //At this point all atoms have point3d
701 atm.getPoint3d().sub(srcApA);
702 rotMat.transform(atm.getPoint3d());
703 atm.getPoint3d().add(srcApA);
704 }
705 // Rotate APs of incoming building block
706 for (AttachmentPoint ap :
707 inVtx.getAttachmentPoints())
708 {
709 Point3d pt = new Point3d(ap.getDirectionVector());
710 pt.sub(srcApA);
711 rotMat.transform(pt);
712 pt.add(srcApA);
713 ap.setDirectionVector(pt);
714 }
715
716 // Update points defining AP vector
717 trgApB = new Point3d(apB.getDirectionVector());
718 srcApB = new Point3d(inFrag.getAtom(
719 apB.getAtomPositionNumber()).getPoint3d());
720 }
721 else
722 {
723 logger.log(Level.FINE,"RotAng below threshold. No rotation.");
724 }
725
726 logger.log(Level.FINE,
727 "After rotation before 2nd translation"+NL
728 + "srcApA "+srcApA+NL
729 + "trgApA "+trgApA+NL
730 + "vectApA "+vectApA+NL
731 + "srcApB "+srcApB+NL
732 + "trgApB "+trgApB+NL
733 + "vectApB "+vectApB);
734
735 // Check whether this edge involves a Ring Closing Attractors
736 boolean edgeToRCA = false;
737 //if (rcParams.getRCStrategy().equals("BONDOVERLAP"))
738 if (true)
739 {
740 if (edge.getSrcAP().getOwner().isRCV() ||
741 edge.getTrgAP().getOwner().isRCV())
742 {
743 edgeToRCA = true;
744 }
745 }
746
747 // Get translation vector accounting for different length of
748 // the APs
749 vectApA.sub(trgApA,srcApA);
750 vectApB.sub(srcApB,trgApB);
751
752 Point3d tr2 = new Point3d();
753 if (edgeToRCA)
754 {
755 // Here we set translation vector as to move the
756 // incoming frag
757 // of the length of the longest AP. This is to place RCA at
758 // a bonding distance from the connected atom
759 if (vectApA.length() > vectApB.length())
760 {
761 tr2.add(vectApA);
762 }
763 else
764 {
765 // NOTE: in this case the RCA is on mol, thus no
766 // translation
767 // is needed because trgApB is already on top of srcApA
768 }
769 }
770 else
771 {
772 tr2.sub(vectApA,vectApB);
773 tr2.scale(0.5);
774 }
775
776 // Translate atoms and APs to their final position
777 for (IAtom atm : inFrag.atoms())
778 {
779 atm.getPoint3d().add(tr2);
780 if ((atm.getPoint3d().x != atm.getPoint3d().x) ||
781 (atm.getPoint3d().y != atm.getPoint3d().y) ||
782 (atm.getPoint3d().z != atm.getPoint3d().z))
783 {
784 String str = "ERROR! NaN coordinated from "
785 + "rototranslation of 3D fragment. "
786 + "Check source code. Atm: "+atm;
787 throw new DENOPTIMException(str);
788 }
789 }
790 for (AttachmentPoint ap : inVtx.getAttachmentPoints())
791 {
792 Point3d pt = new Point3d(ap.getDirectionVector());
793 pt.add(tr2);
794 if ((pt.x != pt.x ) || (pt.y != pt.y ) || (pt.z != pt.z ))
795 {
796 String str = "ERROR! NaN coordinated from "
797 + "rototranslation of 3D fragment's APs. "
798 + "Check source code.";
799 throw new DENOPTIMException(str);
800 }
801 ap.setDirectionVector(pt);
802 }
803 }
804
805 // Store vertex ID on atoms
806 for (IAtom atm : inFrag.atoms())
807 {
808 Object prevPath = atm.getProperty(
810 if (prevPath!=null)
811 {
812 atm.setProperty(DENOPTIMConstants.ATMPROPVERTEXPATH,
813 idInVrx + ", " + prevPath.toString());
814 } else {
815 atm.setProperty(DENOPTIMConstants.ATMPROPVERTEXPATH,
816 idInVrx);
817 }
818 atm.setProperty(DENOPTIMConstants.ATMPROPVERTEXID,idInVrx);
819 }
820
821 // Append atoms of new fragment to the growing molecule
822 mol.add(inFrag);
823 }
824
825 // Make the bond (if any) according to graph's edge.
826 // NB: we are still using CDK's Bond class, but we might need an
827 // implementation IBond that includes all denoptim's bond types, so
828 // that we can deal with cases where bndTyp.hasCDKAnalogue() is false .
829 BondType bndTyp = edge.getBondType();
830 if (bndTyp.hasCDKAnalogue() && idSrcAtmA>-1 && inVtx.containsAtoms())
831 {
832 IAtom atmToBind = inFrag.getAtom(apB.getAtomPositionNumber());
833 IBond bnd = new Bond(mol.getAtom(idSrcAtmA), atmToBind,
834 bndTyp.getCDKOrder());
835 mol.addBond(bnd);
836
837 // Store the APs related to this atom-to-RCA bond
838 ArrayList<AttachmentPoint> apsOnBond = new ArrayList<>();
839 apsOnBond.add(edge.getSrcAP());
840 apsOnBond.add(edge.getTrgAP());
841 apsPerBond.put(bnd,apsOnBond);
842
843 if (inVtx.isRCV())
844 {
845 atmToBind.setProperty(DENOPTIMConstants.RCAPROPAPCTORCA,
846 edge.getSrcAP().getAPClass());
847 //Since inVtx is RCV there can be only one ring, if any...
848 ArrayList<Ring> rings = graph.getRingsInvolvingVertex(inVtx);
849 if (rings.size()>0)
850 {
851 // We record the type of the ring-closing bond, not that
852 // of the Atom-to-RCA bond.
853 atmToBind.setProperty(DENOPTIMConstants.RCAPROPCHORDBNDTYP,
854 rings.get(0).getBondType());
855 atmToBind.setProperty(DENOPTIMConstants.RCAPROPRINGUSER,
856 rings.get(0));
857 } else {
858 //...but if none is there, the APClass is enough
859 atmToBind.setProperty(DENOPTIMConstants.RCAPROPCHORDBNDTYP,
860 edge.getSrcAP().getAPClass().getBondType());
861 }
862 }
863 }
864
865 // Store APs per building block and per atom (if atom exists)
866 ArrayList<AttachmentPoint> apsOnThisFrag = new ArrayList<>();
867 for (AttachmentPoint ap : inVtx.getAttachmentPoints())
868 {
869 // For vertices other than the first, we adjust the pointer to the
870 // AP source atom according to the atom list of the entire molecule
871 ap.setAtomPositionNumberInMol(ap.getAtomPositionNumber() + preNumAtms);
872 apsOnThisFrag.add(ap);
873
874 if (inVtx.containsAtoms())
875 {
876 IAtom srcAtm = mol.getAtom(ap.getAtomPositionNumberInMol());
877 if (apsPerAtom.containsKey(srcAtm))
878 {
879 apsPerAtom.get(srcAtm).add(ap);
880 }
881 else
882 {
883 ArrayList<AttachmentPoint> apsOnThisAtm =
884 new ArrayList<AttachmentPoint>();
885 apsOnThisAtm.add(ap);
886 apsPerAtom.put(srcAtm,apsOnThisAtm);
887 }
888 }
889 }
890 apsPerVertexId.put(idInVrx, apsOnThisFrag);
891
892 // Recursion on all the edges leaving from this fragment
893 for (Edge nextEdge : graph.getEdgesWithSrc(inVtx))
894 {
895 // Add APs to the map of APs per Edges
896 ArrayList<AttachmentPoint> apOnThisEdge =
897 new ArrayList<AttachmentPoint>();
898 apOnThisEdge.add(nextEdge.getSrcAP());
899 apOnThisEdge.add(nextEdge.getTrgAP());
900 apsPerEdge.put(nextEdge,apOnThisEdge);
901
902 if (inVtx.containsAtoms())
903 {
904 // Get two points defining the src AP vector in 3D
905 Point3d trgNextApA = new Point3d(
906 nextEdge.getSrcAP().getDirectionVector());
907 Point3d srcNextApA = new Point3d(MoleculeUtils.getPoint3d(
908 inFrag.getAtom(
909 nextEdge.getSrcAP().getAtomPositionNumber())));
910
911 // Append fragment on AP-vector and start recursion
912 append3DFragmentsViaEdges(mol, graph,
913 nextEdge.getSrcAP().getAtomPositionNumberInMol(),
914 srcNextApA, trgNextApA, nextEdge, removeUsedRCAs,
915 rebuild,
916 apsPerVertexId,apsPerEdge,apsPerAtom,apsPerBond);
917 } else {
918 // Append next building block - start recursion
919 Point3d pt = getRandomPoint(mol);
920 append3DFragmentsViaEdges(mol, graph, -1, pt, pt, nextEdge,
921 removeUsedRCAs, rebuild,
922 apsPerVertexId,apsPerEdge,apsPerAtom,apsPerBond);
923 }
924 }
925 }
926
927//------------------------------------------------------------------------------
928
929 private Point3d getRandomPoint(IAtomContainer mol)
930 {
931 double vx = ((double) randomizer.nextInt(100)) / 100.0;
932 double vy = ((double) randomizer.nextInt(100)) / 100.0;
933 double vz = ((double) randomizer.nextInt(100)) / 100.0;
934 Point3d c = new Point3d(0,0,0);
935 if (mol.getAtomCount() > 0)
936 {
938 }
939 return new Point3d(maxCoord*vx+c.x, maxCoord*vy+c.y, maxCoord*vz+c.z);
940 }
941
942//------------------------------------------------------------------------------
943
944}
General set of constants used in DENOPTIM.
static final Object MOLPROPAPxBOND
Key for IAtomContainer property containing the map of AttachmentPoints per atom.
static final String APSTAG
SDF tag defining attachment points.
static final String EOL
new line character
static final String ATMPROPVERTEXID
String tag of Atom property used to store the unique ID of the Vertex corresponding to the molecular ...
static final Object RCAPROPRINGUSER
Property of a IAtom representing a RingClosingAttractor.
static final Object RCAPROPCHORDBNDTYP
Property of a IAtom representing a RingClosingAttractor.
static final Object MOLPROPAPxVID
Key for IAtomContainer property containing the map of AttachmentPoints per vertex ID.
static final Object MOLPROPAPxEDGE
Key for IAtomContainer property containing the map of AttachmentPoints per edge.
static final String ATMPROPVERTEXPATH
Name of Atom property used to store the unique ID of the Vertex that owns the atom and the IDs of any...
static final Object VISITEDBY3DBUILDER
Property used to mark vertices visited by the 3D tree builder.
static final Object MOLPROPAPxATOM
Key for IAtomContainer property containing the map of AttachmentPoints per vertex ID.
static final Object RCAPROPAPCTORCA
Property of a IAtom representing a RingClosingAttractor.
An attachment point (AP) is a possibility to attach a Vertex onto the vertex holding the AP (i....
int getAtomPositionNumberInMol()
The index of the source atom in the atom list of the entire molecule.
int getAtomPositionNumber()
The index of the source atom in the atom list of the fragment.
Point3d getDirectionVector()
Returns the end of the direction vector.
static String getAPDefinitionsForSDF(LinkedHashMap< Integer, List< AttachmentPoint > > apsPerIndex)
Prepares the two strings that can be used to define AttachmentPoints in SDF files.
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
A vertex is a data structure that has an identity and holds a list of AttachmentPoints.
Definition: Vertex.java:61
int getBuildingBlockId()
Returns the index of the building block that should correspond to the position of the building block ...
Definition: Vertex.java:304
abstract boolean containsAtoms()
Vertex.BBType getBuildingBlockType()
Definition: Vertex.java:318
abstract List< AttachmentPoint > getAttachmentPoints()
abstract IAtomContainer getIAtomContainer()
void setProperty(Object key, Object property)
Definition: Vertex.java:1235
Utility methods for input/output.
static void writeSDFFile(String fileName, IAtomContainer mol)
Writes IAtomContainer to SDF file.
static void writeGraphToJSON(File file, DGraph graph)
Writes the graph to JSON file.
Tool to build build three-dimensional (3D) tree-like molecular structures from DGraph.
Randomizer randomizer
Program-specific randomizer.
ThreeDimTreeBuilder(Logger logger, Randomizer randomizer)
Constructor providing the program-specific logger and randomizer.
void append3DFragmentsDisconnected(Vertex rootVrtx, IAtomContainer mol, DGraph graph, boolean removeUsedRCAs, boolean rebuild, Map< Long, ArrayList< AttachmentPoint > > apsPerVertexId, Map< Edge, ArrayList< AttachmentPoint > > apsPerEdge, Map< IAtom, ArrayList< AttachmentPoint > > apsPerAtom, Map< IBond, ArrayList< AttachmentPoint > > apsPerBond)
A a veretex 3D structure (if any) that is not connected to anything previously added.
void setAlignBBsIn3D(boolean align)
Sets the flag that controls whether building blocks have to be aligned according to the AP vectors or...
boolean alignIn3D
Flag controlling whether to align building blocks according to the AP vectors or not.
void append3DFragmentsViaEdges(IAtomContainer mol, DGraph graph, int idSrcAtmA, Point3d srcApA, Point3d trgApA, Edge edge, boolean removeUsedRCAs, boolean rebuild, Map< Long, ArrayList< AttachmentPoint > > apsPerVertexId, Map< Edge, ArrayList< AttachmentPoint > > apsPerEdge, Map< IAtom, ArrayList< AttachmentPoint > > apsPerAtom, Map< IBond, ArrayList< AttachmentPoint > > apsPerBond)
Recursive method that appends branches of building blocks following the edges of the graph.
IAtomContainer convertGraphTo3DAtomContainer(DGraph graph, boolean removeUsedRCAs)
Creates a three-dimensional molecular representation from a given DGraph.
IAtomContainer convertGraphTo3DAtomContainer(DGraph graph)
Created a three-dimensional molecular representation from a given DGraph.
IChemObjectBuilder builder
Private builder of atom containers.
double maxCoord
Controls the maximum distance between a random place where unanchored building blocks are placed and ...
Utilities for graphs.
Definition: GraphUtils.java:40
static void writeSDFFields(IAtomContainer iac, DGraph g)
Some useful math operations.
Definition: MathUtils.java:39
static Vector3d getNormalDirection(Vector3d vecA)
Generate a vector that is perpendicular to the given one.
Definition: MathUtils.java:426
Utilities for molecule conversion.
static void removeUsedRCA(IAtomContainer mol, DGraph graph, Logger logger)
Replace used RCAs (i.e., those involved in Rings) while adding the ring closing bonds.
static void setZeroImplicitHydrogensToAllAtoms(IAtomContainer iac)
Sets zero implicit hydrogen count to all atoms.
static void ensureNoUnsetBondOrdersSilent(IAtomContainer iac)
Sets bond order = single to all otherwise unset bonds.
static Point3d calculateCentroid(IAtomContainer mol)
Calculated the centroid of the given molecule.
static Point3d getPoint3d(IAtom atm)
Return the 3D coordinates, if present.
Tool to generate random numbers and random decisions.
Definition: Randomizer.java:35
int nextInt(int i)
Returns a pseudo-random, uniformly distributed int value between 0 (inclusive) and the specified valu...
Possible chemical bond types an edge can represent.
Definition: Edge.java:305
boolean hasCDKAnalogue()
Checks if it is possible to convert this edge type into a CDK bond.
Definition: Edge.java:330