$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 IAtomContainer mol = builder.newAtomContainer();
243
244 // WARNING: assumption that the graph is an healthy spanning tree, as
245 // it should always be in DENOPTIM.
246 Vertex rootVrtx = graph.getSourceVertex();
247 long idRootVrtx = rootVrtx.getVertexId();
248
249 IAtomContainer iacRootVrtx = null;
250 if (rootVrtx.containsAtoms())
251 {
252 iacRootVrtx = rootVrtx.getIAtomContainer(logger, randomizer,
253 removeUsedRCAs, rebuild);
254
255 if (iacRootVrtx == null)
256 {
257 String msg = "ThreeDimTreeBuilder found a building block daclaring "
258 + "to containg atoms, but returning null atom container. "
259 + "Building blocks: " + rootVrtx;
260 throw new IllegalArgumentException(msg);
261 }
262
263 for (IAtom atm : iacRootVrtx.atoms())
264 {
265 Object prevPath = atm.getProperty(
267 if (prevPath!=null)
268 {
269 atm.setProperty(DENOPTIMConstants.ATMPROPVERTEXPATH,
270 idRootVrtx + ", " + prevPath.toString());
271 } else {
272 atm.setProperty(DENOPTIMConstants.ATMPROPVERTEXPATH,
273 idRootVrtx);
274 }
275 atm.setProperty(DENOPTIMConstants.ATMPROPVERTEXID,idRootVrtx);
276 }
277 mol.add(iacRootVrtx);
278 }
279
280 // Store APs in maps
281 Map<Long,ArrayList<AttachmentPoint>> apsPerVertexId = new HashMap<>();
282 Map<Edge,ArrayList<AttachmentPoint>> apsPerEdge = new HashMap<>();
283 Map<IAtom,ArrayList<AttachmentPoint>> apsPerAtom = new HashMap<>();
284 Map<IBond,ArrayList<AttachmentPoint>> apsPerBond = new HashMap<>();
285 ArrayList<AttachmentPoint> apsOnThisFrag = new ArrayList<>();
286 for (AttachmentPoint ap : rootVrtx.getAttachmentPoints())
287 {
288 // For first vertex the atomPositionNumber remains the same
289 ap.setAtomPositionNumberInMol(ap.getAtomPositionNumber());
290 apsOnThisFrag.add(ap);
291 if (rootVrtx.containsAtoms())
292 {
293 IAtom srcAtm = iacRootVrtx.getAtom(ap.getAtomPositionNumber());
294 if (apsPerAtom.containsKey(srcAtm))
295 {
296 apsPerAtom.get(srcAtm).add(ap);
297 }
298 else
299 {
300 ArrayList<AttachmentPoint> apsOnThisAtm =
301 new ArrayList<>();
302 apsOnThisAtm.add(ap);
303 apsPerAtom.put(srcAtm,apsOnThisAtm);
304 }
305 }
306 }
307 apsPerVertexId.put(idRootVrtx,apsOnThisFrag);
308
309 // Recursion on all branches of the tree (i.e., all incident edges)
310 for (Edge edge : graph.getEdgesWithSrc(rootVrtx))
311 {
312 // Get the AP from the current vertex to the next
313 AttachmentPoint apSrc = edge.getSrcAP();
314
315 // Add APs to the map of APs per Edges
316 ArrayList<AttachmentPoint> apOnThisEdge =
317 new ArrayList<AttachmentPoint>();
318 apOnThisEdge.add(apSrc);
319 apOnThisEdge.add(edge.getTrgAP());
320 apsPerEdge.put(edge,apOnThisEdge);
321
322 if (rootVrtx.containsAtoms())
323 {
324 Point3d trgPtApSrc = new Point3d(apSrc.getDirectionVector());
325 Point3d srcPtApSrc = new Point3d(
326 MoleculeUtils.getPoint3d(iacRootVrtx.getAtom(
327 apSrc.getAtomPositionNumber())));
328
329 // Append next building block on AP-vector - start recursion
330 append3DFragmentsViaEdges(mol, graph,
331 apSrc.getAtomPositionNumber(),
332 srcPtApSrc,trgPtApSrc,edge,removeUsedRCAs, rebuild,
333 apsPerVertexId,apsPerEdge,apsPerAtom,apsPerBond);
334 } else {
335 // Append next building block - start recursion
336 Point3d pt = getRandomPoint(mol);
337 append3DFragmentsViaEdges(mol, graph, -1, pt, pt, edge,
338 removeUsedRCAs, rebuild,
339 apsPerVertexId,apsPerEdge,apsPerAtom,apsPerBond);
340 }
341 }
342
343 if (removeUsedRCAs)
344 {
345 // This is where we make the rings-closing bonds.
346 // Unused RCAs should have been already replaced by capping groups
347 // (or removed, if no capping needed), by changing the graph with
348 // GraphConversionTool.removeUnusedRCVs
349 // So, this will deal only with used RCAs.
350
351 // WARNING: since we are changing the atom list, we need to make
352 // sure we retail consistency between the changing atom list of
353 // the atom indexes stored in the APs, and in particular, the
354 // index returned by getAtomPositionNumberInMol.
356 }
357
358 if (setCDKRequirements)
359 {
362 }
363
364 // Code that may turn out useful for deep level debugging
365 if (false)
366 {
367 DenoptimIO.writeGraphToJSON(new File("/tmp/graph.json"), graph);
368 StringBuilder sb = new StringBuilder();
369 String file = "/tmp/iacTree.sdf";
370 sb.append("Writing tree-like IAtomContainer to " + file+NL);
371 IAtomContainer cmol = builder.newAtomContainer();
372 try
373 {
374 cmol = mol.clone();
375 }
376 catch (Throwable t)
377 {
378 throw new DENOPTIMException(t);
379 }
380
381 sb.append("AP-per-VertexID"+NL);
382 int i=0;
383 for (long v : apsPerVertexId.keySet())
384 {
385 ArrayList<AttachmentPoint> aps = apsPerVertexId.get(v);
386 for (AttachmentPoint ap : aps)
387 {
388 i++;
389 IAtom atm = new PseudoAtom(String.valueOf(i),
390 new Point3d(ap.getDirectionVector()));
391 sb.append("Vertex: "+v+" AP-"+i+" = "+ap+NL);
392 cmol.addAtom(atm);
393 IBond bnd = new Bond(cmol.getAtom(
394 ap.getAtomPositionNumber()),
395 cmol.getAtom(mol.getAtomCount()+i-1),
396 IBond.Order.SINGLE);
397 cmol.addBond(bnd);
398 }
399 }
400 DenoptimIO.writeSDFFile(file, mol, false);
401 DenoptimIO.writeSDFFile(file, cmol, true);
402 sb.append("AP-per-Edge"+NL);
403 for (Edge e : apsPerEdge.keySet())
404 {
405 ArrayList<AttachmentPoint> aps = apsPerEdge.get(e);
406 for (AttachmentPoint ap : aps)
407 {
408 sb.append("Edge: "+e+" AP = "+ap+NL);
409 }
410 }
411 sb.append("AP-per-Atom"+NL);
412 for (IAtom a : apsPerAtom.keySet())
413 {
414 ArrayList<AttachmentPoint> aps = apsPerAtom.get(a);
415 for (AttachmentPoint ap : aps)
416 {
417 sb.append("Atom: "+mol.indexOf(a) +" AP = "+ap+NL);
418 }
419 }
420 sb.append("AP-per-Bond"+NL);
421 for (IBond b : apsPerBond.keySet())
422 {
423 ArrayList<AttachmentPoint> aps = apsPerBond.get(b);
424 for (AttachmentPoint ap : aps)
425 {
426 sb.append("Bond: "+mol.indexOf(b.getAtom(0))
427 +"-"+mol.indexOf(b.getAtom(1))+" AP = "+ap+NL);
428 }
429 }
430 System.out.println(sb.toString());
431 }
432
433 // Prepare the string-representation of unused APs on this graph
434 LinkedHashMap<Integer,List<AttachmentPoint>> freeAPPerAtm =
435 new LinkedHashMap<>();
436 for (IAtom a : apsPerAtom.keySet())
437 {
438 int atmID = mol.indexOf(a);
439 if (atmID<0)
440 {
441 // source atom is not anymore there: probably it was RCA and has
442 // been removed
443 continue;
444 }
445
446 ArrayList<AttachmentPoint> aps = apsPerAtom.get(a);
447 for (AttachmentPoint ap : aps)
448 {
449 if (ap.isAvailableThroughout())
450 {
451 if (freeAPPerAtm.containsKey(atmID))
452 {
453 freeAPPerAtm.get(atmID).add(ap);
454 } else {
455 List<AttachmentPoint> lst =
456 new ArrayList<AttachmentPoint>();
457 lst.add(ap);
458 freeAPPerAtm.put(atmID,lst);
459 }
460 }
461 }
462 }
463 mol.setProperty(DENOPTIMConstants.APSTAG,
465
466 // Add usual graph-related string-based data to SDF properties
467 GraphUtils.writeSDFFields(mol, graph);
468
469 mol.setProperty(DENOPTIMConstants.MOLPROPAPxVID, apsPerVertexId);
470 mol.setProperty(DENOPTIMConstants.MOLPROPAPxEDGE, apsPerEdge);
471 mol.setProperty(DENOPTIMConstants.MOLPROPAPxATOM, apsPerAtom);
472 mol.setProperty(DENOPTIMConstants.MOLPROPAPxBOND, apsPerBond);
473
474 return mol;
475 }
476
477//------------------------------------------------------------------------------
478
512 private void append3DFragmentsViaEdges(IAtomContainer mol, DGraph graph,
513 int idSrcAtmA, Point3d srcApA, Point3d trgApA, Edge edge,
514 boolean removeUsedRCAs, boolean rebuild,
515 Map<Long, ArrayList<AttachmentPoint>> apsPerVertexId,
516 Map<Edge,ArrayList<AttachmentPoint>> apsPerEdge,
517 Map<IAtom,ArrayList<AttachmentPoint>> apsPerAtom,
518 Map<IBond,ArrayList<AttachmentPoint>> apsPerBond)
519 throws DENOPTIMException
520 {
521 logger.log(Level.FINE, "Appending 3D fragment via edge: "+edge + NL
522 +"#Atoms on growing mol: "+mol.getAtomCount());
523
524 // Get the incoming fragment and its AP
525 Vertex inVtx = edge.getTrgAP().getOwner();
526 AttachmentPoint apB = edge.getTrgAP();
527
528 //Used to keep track of which atom comes from which vertex
529 long idInVrx = inVtx.getVertexId();
530
531 logger.log(Level.FINE, "Incoming vertex : "+inVtx);
532
533 int preNumAtms = mol.getAtomCount();
534 IAtomContainer inFrag = null;
535 if (inVtx.containsAtoms())
536 {
537 inFrag = inVtx.getIAtomContainer(logger, randomizer, removeUsedRCAs,
538 true);
539 if (inFrag == null)
540 {
541 String msg = "ThreeDimTreeBuilder found a building block "
542 + "daclaring "
543 + "to containg atoms, but returning a null atom "
544 + "container. "
545 + "Problematic building block is bbID="
546 + inVtx.getBuildingBlockId() + " bbType="
547 + inVtx.getBuildingBlockType();
548 throw new IllegalArgumentException(msg);
549 }
550
551 logger.log(Level.FINE, "Incoming IAC #atoms: " +
552 inFrag.getAtomCount());
553
554 if (alignIn3D)
555 {
556 // Define the roto-translation operation that aligns
557 // the incoming building block to the growing molecule.
558 // To this end,
559 // we work on the APs that define the spatial relation
560 // between the
561 // parent vertex, which is the growing molecule (i.e., ApA)
562 // and that on the incoming building block (i.e., ApB).
563 Point3d trgApB = new Point3d(apB.getDirectionVector());
564 Point3d srcApB = new Point3d(MoleculeUtils.getPoint3d(
565 inFrag.getAtom(apB.getAtomPositionNumber())));
566
567 // Translate atoms and APs of incoming building block so that
568 // trgApB is on srcApA
569 Point3d tr1 = new Point3d();
570 tr1.sub(trgApB,srcApA);
571 for (IAtom atm : inFrag.atoms())
572 {
573 atm.setPoint3d(MoleculeUtils.getPoint3d(atm));
574 atm.getPoint3d().sub(tr1);
575 }
576 for (AttachmentPoint ap : inVtx.getAttachmentPoints())
577 {
578 Point3d pt = new Point3d(ap.getDirectionVector());
579 pt.sub(tr1);
580 ap.setDirectionVector(pt);
581 }
582 trgApB = new Point3d(apB.getDirectionVector());
583 srcApB = new Point3d(MoleculeUtils.getPoint3d(
584 inFrag.getAtom(apB.getAtomPositionNumber())));
585
586 //Get Vectors ApA and ApB (NOTE: inverse versus of ApB!!!)
587 Vector3d vectApA = new Vector3d();
588 Vector3d vectApB = new Vector3d();
589 vectApA.sub(trgApA,srcApA);
590 vectApB.sub(srcApB,trgApB);
591 vectApA.normalize();
592 vectApB.normalize();
593
594 logger.log(Level.FINE,
595 "After 1st translation and before rotation" + NL
596 + "srcApA "+srcApA+NL
597 + "trgApA "+trgApA+NL
598 + "vectApA "+vectApA+NL
599 + "srcApB "+srcApB+NL
600 + "trgApB "+trgApB+NL
601 + "vectApB "+vectApB);
602
603 // Get rotation matrix that aligns ApB to ApA
604 double rotAng = vectApA.angle(vectApB);
605 double threshold = 0.00001;
606 if (rotAng >= threshold)
607 {
608 Vector3d rotAxis = new Vector3d();
609 if (rotAng <= (Math.PI-0.00001))
610 {
611 rotAxis.cross(vectApB,vectApA);
612 }
613 else
614 {
615 rotAxis = MathUtils.getNormalDirection(vectApA);
616 }
617 Matrix3d rotMat = new Matrix3d();
618 rotAxis.normalize();
619 rotMat.set(new AxisAngle4d(rotAxis,rotAng));
620
621 logger.log(Level.FINE,"rotAng "+rotAng+NL
622 +"rotAxis "+rotAxis
623 +"rotMat "+rotMat);
624
625 // Rotate atoms of incoming building block
626 for (IAtom atm : inFrag.atoms())
627 {
628 //At this point all atoms have point3d
629 atm.getPoint3d().sub(srcApA);
630 rotMat.transform(atm.getPoint3d());
631 atm.getPoint3d().add(srcApA);
632 }
633 // Rotate APs of incoming building block
634 for (AttachmentPoint ap :
635 inVtx.getAttachmentPoints())
636 {
637 Point3d pt = new Point3d(ap.getDirectionVector());
638 pt.sub(srcApA);
639 rotMat.transform(pt);
640 pt.add(srcApA);
641 ap.setDirectionVector(pt);
642 }
643
644 // Update points defining AP vector
645 trgApB = new Point3d(apB.getDirectionVector());
646 srcApB = new Point3d(inFrag.getAtom(
647 apB.getAtomPositionNumber()).getPoint3d());
648 }
649 else
650 {
651 logger.log(Level.FINE,"RotAng below threshold. No rotation.");
652 }
653
654 logger.log(Level.FINE,
655 "After rotation before 2nd translation"+NL
656 + "srcApA "+srcApA+NL
657 + "trgApA "+trgApA+NL
658 + "vectApA "+vectApA+NL
659 + "srcApB "+srcApB+NL
660 + "trgApB "+trgApB+NL
661 + "vectApB "+vectApB);
662
663 // Check whether this edge involves a Ring Closing Attractors
664 boolean edgeToRCA = false;
665 //if (rcParams.getRCStrategy().equals("BONDOVERLAP"))
666 if (true)
667 {
668 if (edge.getSrcAP().getOwner().isRCV() ||
669 edge.getTrgAP().getOwner().isRCV())
670 {
671 edgeToRCA = true;
672 }
673 }
674
675 // Get translation vector accounting for different length of
676 // the APs
677 vectApA.sub(trgApA,srcApA);
678 vectApB.sub(srcApB,trgApB);
679
680 Point3d tr2 = new Point3d();
681 if (edgeToRCA)
682 {
683 // Here we set translation vector as to move the
684 // incoming frag
685 // of the length of the longest AP. This is to place RCA at
686 // a bonding distance from the connected atom
687 if (vectApA.length() > vectApB.length())
688 {
689 tr2.add(vectApA);
690 }
691 else
692 {
693 // NOTE: in this case the RCA is on mol, thus no
694 // translation
695 // is needed because trgApB is already on top of srcApA
696 }
697 }
698 else
699 {
700 tr2.sub(vectApA,vectApB);
701 tr2.scale(0.5);
702 }
703
704 // Translate atoms and APs to their final position
705 for (IAtom atm : inFrag.atoms())
706 {
707 atm.getPoint3d().add(tr2);
708 if ((atm.getPoint3d().x != atm.getPoint3d().x) ||
709 (atm.getPoint3d().y != atm.getPoint3d().y) ||
710 (atm.getPoint3d().z != atm.getPoint3d().z))
711 {
712 String str = "ERROR! NaN coordinated from "
713 + "rototranslation of 3D fragment. "
714 + "Check source code. Atm: "+atm;
715 throw new DENOPTIMException(str);
716 }
717 }
718 for (AttachmentPoint ap : inVtx.getAttachmentPoints())
719 {
720 Point3d pt = new Point3d(ap.getDirectionVector());
721 pt.add(tr2);
722 if ((pt.x != pt.x ) || (pt.y != pt.y ) || (pt.z != pt.z ))
723 {
724 String str = "ERROR! NaN coordinated from "
725 + "rototranslation of 3D fragment's APs. "
726 + "Check source code.";
727 throw new DENOPTIMException(str);
728 }
729 ap.setDirectionVector(pt);
730 }
731 }
732
733 // Store vertex ID on atoms
734 for (IAtom atm : inFrag.atoms())
735 {
736 Object prevPath = atm.getProperty(
738 if (prevPath!=null)
739 {
740 atm.setProperty(DENOPTIMConstants.ATMPROPVERTEXPATH,
741 idInVrx + ", " + prevPath.toString());
742 } else {
743 atm.setProperty(DENOPTIMConstants.ATMPROPVERTEXPATH,
744 idInVrx);
745 }
746 atm.setProperty(DENOPTIMConstants.ATMPROPVERTEXID,idInVrx);
747 }
748
749 // Append atoms of new fragment to the growing molecule
750 mol.add(inFrag);
751 }
752
753 // Make the bond (if any) according to graph's edge.
754 // NB: we are still using CDK's Bond class, but we might need an
755 // implementation IBond that includes all denoptim's bond types, so
756 // that we can deal with cases where bndTyp.hasCDKAnalogue() is false .
757 BondType bndTyp = edge.getBondType();
758 if (bndTyp.hasCDKAnalogue() && idSrcAtmA>-1 && inVtx.containsAtoms())
759 {
760 IAtom atmToBind = inFrag.getAtom(apB.getAtomPositionNumber());
761 IBond bnd = new Bond(mol.getAtom(idSrcAtmA), atmToBind,
762 bndTyp.getCDKOrder());
763 mol.addBond(bnd);
764
765 // Store the APs related to this atom-to-RCA bond
766 ArrayList<AttachmentPoint> apsOnBond = new ArrayList<>();
767 apsOnBond.add(edge.getSrcAP());
768 apsOnBond.add(edge.getTrgAP());
769 apsPerBond.put(bnd,apsOnBond);
770
771 if (inVtx.isRCV())
772 {
773 atmToBind.setProperty(DENOPTIMConstants.RCAPROPAPCTORCA,
774 edge.getSrcAP().getAPClass());
775 //Since inVtx is RCV there can be only one ring, if any...
776 ArrayList<Ring> rings = graph.getRingsInvolvingVertex(inVtx);
777 if (rings.size()>0)
778 {
779 // We record the type of the ring-closing bond, not that
780 // of the Atom-to-RCA bond.
781 atmToBind.setProperty(DENOPTIMConstants.RCAPROPCHORDBNDTYP,
782 rings.get(0).getBondType());
783 atmToBind.setProperty(DENOPTIMConstants.RCAPROPRINGUSER,
784 rings.get(0));
785 } else {
786 //...but if none is there, the APClass is enough
787 atmToBind.setProperty(DENOPTIMConstants.RCAPROPCHORDBNDTYP,
788 edge.getSrcAP().getAPClass().getBondType());
789 }
790 }
791 }
792
793 // Store APs per building block and per atom (if atom exists)
794 ArrayList<AttachmentPoint> apsOnThisFrag = new ArrayList<>();
795 for (AttachmentPoint ap : inVtx.getAttachmentPoints())
796 {
797 // For vertices other than the first, we adjust the pointer to the
798 // AP source atom according to the atom list of the entire molecule
799 ap.setAtomPositionNumberInMol(ap.getAtomPositionNumber()
800 + preNumAtms);
801 apsOnThisFrag.add(ap);
802
803 if (inVtx.containsAtoms())
804 {
805 IAtom srcAtm = mol.getAtom(ap.getAtomPositionNumberInMol());
806 if (apsPerAtom.containsKey(srcAtm))
807 {
808 apsPerAtom.get(srcAtm).add(ap);
809 }
810 else
811 {
812 ArrayList<AttachmentPoint> apsOnThisAtm =
813 new ArrayList<AttachmentPoint>();
814 apsOnThisAtm.add(ap);
815 apsPerAtom.put(srcAtm,apsOnThisAtm);
816 }
817 }
818 }
819 apsPerVertexId.put(idInVrx, apsOnThisFrag);
820
821 // Recursion on all the edges leaving from this fragment
822 for (Edge nextEdge : graph.getEdgesWithSrc(inVtx))
823 {
824 // Add APs to the map of APs per Edges
825 ArrayList<AttachmentPoint> apOnThisEdge =
826 new ArrayList<AttachmentPoint>();
827 apOnThisEdge.add(nextEdge.getSrcAP());
828 apOnThisEdge.add(nextEdge.getTrgAP());
829 apsPerEdge.put(nextEdge,apOnThisEdge);
830
831 if (inVtx.containsAtoms())
832 {
833 // Get two points defining the src AP vector in 3D
834 Point3d trgNextApA = new Point3d(
835 nextEdge.getSrcAP().getDirectionVector());
836 Point3d srcNextApA = new Point3d(MoleculeUtils.getPoint3d(
837 inFrag.getAtom(
838 nextEdge.getSrcAP().getAtomPositionNumber())));
839
840 // Append fragment on AP-vector and start recursion
841 append3DFragmentsViaEdges(mol, graph,
842 nextEdge.getSrcAP().getAtomPositionNumberInMol(),
843 srcNextApA, trgNextApA, nextEdge, removeUsedRCAs,
844 rebuild,
845 apsPerVertexId,apsPerEdge,apsPerAtom,apsPerBond);
846 } else {
847 // Append next building block - start recursion
848 Point3d pt = getRandomPoint(mol);
849 append3DFragmentsViaEdges(mol, graph, -1, pt, pt, nextEdge,
850 removeUsedRCAs, rebuild,
851 apsPerVertexId,apsPerEdge,apsPerAtom,apsPerBond);
852 }
853 }
854 }
855
856//------------------------------------------------------------------------------
857
858 private Point3d getRandomPoint(IAtomContainer mol)
859 {
860 double vx = ((double) randomizer.nextInt(100)) / 100.0;
861 double vy = ((double) randomizer.nextInt(100)) / 100.0;
862 double vz = ((double) randomizer.nextInt(100)) / 100.0;
863 Point3d c = new Point3d(0,0,0);
864 if (mol.getAtomCount() > 0)
865 {
867 }
868 return new Point3d(maxCoord*vx+c.x, maxCoord*vy+c.y, maxCoord*vz+c.z);
869 }
870
871//------------------------------------------------------------------------------
872
873}
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 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 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:102
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:284
abstract boolean containsAtoms()
Vertex.BBType getBuildingBlockType()
Definition: Vertex.java:298
abstract List< AttachmentPoint > getAttachmentPoints()
abstract IAtomContainer getIAtomContainer()
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 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:303
boolean hasCDKAnalogue()
Checks if it is possible to convert this edge type into a CDK bond.
Definition: Edge.java:328