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