$darkmode
DENOPTIM
MoleculeUtils.java
Go to the documentation of this file.
1/*
2 * DENOPTIM
3 * Copyright (C) 2019 Vishwesh Venkatraman <vishwesh.venkatraman@ntnu.no> and
4 * Marco Foscato <marco.foscato@uib.no>
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as published
8 * by the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Affero General Public License for more details.
15 *
16 * You should have received a copy of the GNU Affero General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20package denoptim.utils;
21
22import java.io.File;
23import java.util.ArrayList;
24import java.util.Collections;
25import java.util.HashMap;
26import java.util.HashSet;
27import java.util.List;
28import java.util.Map;
29import java.util.Set;
30import java.util.logging.Level;
31import java.util.logging.Logger;
32
33import javax.vecmath.Point2d;
34import javax.vecmath.Point3d;
35
36import org.openscience.cdk.Atom;
37import org.openscience.cdk.AtomContainer;
38import org.openscience.cdk.AtomRef;
39import org.openscience.cdk.CDKConstants;
40import org.openscience.cdk.PseudoAtom;
41import org.openscience.cdk.aromaticity.Kekulization;
42import org.openscience.cdk.config.IsotopeFactory;
43import org.openscience.cdk.config.Isotopes;
44import org.openscience.cdk.depict.Depiction;
45import org.openscience.cdk.depict.DepictionGenerator;
46import org.openscience.cdk.exception.CDKException;
47import org.openscience.cdk.geometry.GeometryUtil;
48import org.openscience.cdk.graph.ConnectivityChecker;
49import org.openscience.cdk.inchi.InChIGenerator;
50import org.openscience.cdk.inchi.InChIGeneratorFactory;
51import org.openscience.cdk.interfaces.IAtom;
52import org.openscience.cdk.interfaces.IAtomContainer;
53import org.openscience.cdk.interfaces.IAtomContainerSet;
54import org.openscience.cdk.interfaces.IAtomType.Hybridization;
55import org.openscience.cdk.interfaces.IBond;
56import org.openscience.cdk.interfaces.IChemObjectBuilder;
57import org.openscience.cdk.interfaces.IElement;
58import org.openscience.cdk.layout.StructureDiagramGenerator;
59import org.openscience.cdk.qsar.DescriptorValue;
60import org.openscience.cdk.qsar.IMolecularDescriptor;
61import org.openscience.cdk.qsar.descriptors.molecular.RotatableBondsCountDescriptor;
62import org.openscience.cdk.qsar.descriptors.molecular.WeightDescriptor;
63import org.openscience.cdk.qsar.result.DoubleResult;
64import org.openscience.cdk.qsar.result.IntegerResult;
65import org.openscience.cdk.silent.SilentChemObjectBuilder;
66import org.openscience.cdk.smiles.SmiFlavor;
67import org.openscience.cdk.smiles.SmilesGenerator;
68import org.openscience.cdk.tools.manipulator.AtomContainerManipulator;
69
70import denoptim.constants.DENOPTIMConstants;
71import denoptim.exception.DENOPTIMException;
72import denoptim.graph.APClass;
73import denoptim.graph.AttachmentPoint;
74import denoptim.graph.DGraph;
75import denoptim.graph.Edge.BondType;
76import denoptim.graph.Fragment;
77import denoptim.graph.Ring;
78import denoptim.graph.Vertex;
79import denoptim.graph.Vertex.BBType;
80import denoptim.graph.rings.RingClosingAttractor;
81import denoptim.io.DenoptimIO;
82import denoptim.logging.StaticLogger;
83import io.github.dan2097.jnainchi.InchiFlag;
84import io.github.dan2097.jnainchi.InchiOptions;
85import io.github.dan2097.jnainchi.InchiStatus;
86
87
93public class MoleculeUtils
94{
95 private static final StructureDiagramGenerator SDG =
96 new StructureDiagramGenerator();
97 private static final SmilesGenerator SMGEN = new SmilesGenerator(
98 SmiFlavor.Generic);
99 private static final IChemObjectBuilder builder =
100 SilentChemObjectBuilder.getInstance();
101
102//------------------------------------------------------------------------------
103
104 public static double getMolecularWeight(IAtomContainer mol)
105 throws DENOPTIMException
106 {
107 double ret_wd = 0;
108 try
109 {
110 WeightDescriptor wd = new WeightDescriptor();
111 Object[] pars = {"*"};
112 wd.setParameters(pars);
113 ret_wd = ((DoubleResult) wd.calculate(mol).getValue())
114 .doubleValue();
115 }
116 catch (Exception ex)
117 {
118 throw new DENOPTIMException(ex);
119 }
120 return ret_wd;
121 }
122
123//------------------------------------------------------------------------------
124
134 public static boolean isDummy(IAtom atm)
135 {
136 String el = getSymbolOrLabel(atm);
137 return DENOPTIMConstants.DUMMYATMSYMBOL.equals(el);
138 }
139
140//------------------------------------------------------------------------------
141
149 public static boolean isElement(IAtom atom)
150 {
151 String symbol = atom.getSymbol();
152 return isElement(symbol);
153 }
154
155//------------------------------------------------------------------------------
156
164 public static boolean isElement(String symbol)
165 {
166 boolean res = false;
167 IsotopeFactory ifact = null;
168 try {
169 ifact = Isotopes.getInstance();
170 if (ifact.isElement(symbol))
171 {
172 @SuppressWarnings("unused")
173 IElement el = ifact.getElement(symbol);
174 res = true;
175 }
176 } catch (Throwable t) {
177 throw new Error("ERROR! Unable to create Isotope.");
178 }
179 return res;
180 }
181
182//------------------------------------------------------------------------------
183
190 public static void removeRCA(IAtomContainer mol) {
191
192 // convert PseudoAtoms to H
193 for (IAtom a : mol.atoms())
194 {
195 boolean isRca = false;
196 Set<String> rcaElSymbols = RingClosingAttractor.RCATYPEMAP.keySet();
197 for (String rcaEl : rcaElSymbols)
198 {
199 if (MoleculeUtils.getSymbolOrLabel(a).equals(rcaEl))
200 {
201 isRca = true;
202 break;
203 }
204 }
205 if (isRca)
206 {
207 IAtom newAtm = new Atom("H",new Point3d(a.getPoint3d()));
208 newAtm.setProperties(a.getProperties());
209 AtomContainerManipulator.replaceAtomByAtom(mol,a,newAtm);
210 }
211 }
212 }
213
214//------------------------------------------------------------------------------
215
224 public static void removeUsedRCA(IAtomContainer mol, DGraph graph,
225 Logger logger) throws DENOPTIMException {
226
227 // add ring-closing bonds
228 ArrayList<Vertex> usedRcvs = graph.getUsedRCVertices();
229 Map<Vertex,ArrayList<Integer>> vIdToAtmId =
231 if (vIdToAtmId.size() == 0)
232 {
233 // No used RCV to remove.
234 return;
235 }
236 ArrayList<IAtom> atmsToRemove = new ArrayList<>();
237 ArrayList<Boolean> doneVertices =
238 new ArrayList<>(Collections.nCopies(usedRcvs.size(),false));
239
240 for (Vertex v : usedRcvs)
241 {
242 if (doneVertices.get(usedRcvs.indexOf(v)))
243 {
244 continue;
245 }
246 ArrayList<Ring> rings = graph.getRingsInvolvingVertex(v);
247 if (rings.size() != 1)
248 {
249 String s = "Unexpected inconsistency between used RCV list "
250 + v + " in {" + usedRcvs + "}"
251 + "and list of DENOPTIMRings "
252 + "{" + rings + "}. Check Code!";
253 throw new DENOPTIMException(s);
254 }
255 Vertex vH = rings.get(0).getHeadVertex();
256 Vertex vT = rings.get(0).getTailVertex();
257 IAtom aH = mol.getAtom(vIdToAtmId.get(vH).get(0));
258 IAtom aT = mol.getAtom(vIdToAtmId.get(vT).get(0));
259 if (mol.getConnectedAtomsList(aH).size() == 0
260 || mol.getConnectedAtomsList(aT).size() == 0)
261 {
262 // This can happen when building a graph with empty vertexes
263 continue;
264 }
265 int iSrcH = mol.indexOf(mol.getConnectedAtomsList(aH).get(0));
266 int iSrcT = mol.indexOf(mol.getConnectedAtomsList(aT).get(0));
267 atmsToRemove.add(aH);
268 atmsToRemove.add(aT);
269
270 BondType bndTyp = rings.get(0).getBondType();
271 if (bndTyp.hasCDKAnalogue())
272 {
273 mol.addBond(iSrcH, iSrcT, bndTyp.getCDKOrder());
274 } else {
275 logger.log(Level.WARNING, "WARNING! "
276 + "Attempt to add ring closing bond "
277 + "did not add any actual chemical bond because the "
278 + "bond type of the chord is '" + bndTyp +"'.");
279 }
280
281 doneVertices.set(usedRcvs.indexOf(vH),true);
282 doneVertices.set(usedRcvs.indexOf(vT),true);
283 }
284
285 // Adapt atom indexes in APs to the upcoming change of atom list
286 ArrayList<Integer> removedIds = new ArrayList<Integer>();
287 for (IAtom a : atmsToRemove)
288 {
289 removedIds.add(mol.indexOf(a));
290 }
291 Collections.sort(removedIds);
292 for (Vertex v : graph.getVertexList())
293 {
294 for (AttachmentPoint ap : v.getAttachmentPoints())
295 {
296 int apSrcId = ap.getAtomPositionNumberInMol();
297 int countOfAtmsBEforeSrc = 0;
298 for (Integer removingId : removedIds)
299 {
300 if (removingId < apSrcId)
301 {
302 countOfAtmsBEforeSrc++;
303 } else if (removingId > apSrcId)
304 {
305 break;
306 }
307 }
308 ap.setAtomPositionNumberInMol(apSrcId-countOfAtmsBEforeSrc);
309 }
310 }
311
312 // remove used RCAs
313 for (IAtom a : atmsToRemove)
314 {
315 mol.removeAtom(a);
316 }
317 }
318
319//------------------------------------------------------------------------------
320
330 public static String getSMILESForMolecule(IAtomContainer mol, Logger logger)
331 throws DENOPTIMException {
332 IAtomContainer fmol = builder.newAtomContainer();
333 try
334 {
335 fmol = mol.clone();
336 }
337 catch (CloneNotSupportedException e)
338 {
339 throw new DENOPTIMException(e);
340 }
341
342 // remove Dummy atoms
345 fmol = dan.removeDummyInHapto(fmol);
346 fmol = dan.removeDummy(fmol);
347
348 // convert PseudoAtoms to H
349 removeRCA(fmol);
350
351 // WARNING: assumptions on implicit H count and bond orders!
354
355 String smiles = "";
356 try
357 {
358 smiles = SMGEN.create(fmol);
359 }
360 catch (Throwable t)
361 {
362 t.printStackTrace();
363 String fileName = "failed_generation_of_SMILES.sdf";
364 logger.log(Level.WARNING, "WARNING: Skipping calculation of SMILES. "
365 + "See file '" + fileName + "'");
366 DenoptimIO.writeSDFFile(fileName,fmol,false);
367 smiles = "calculation_or_SMILES_crashed";
368 }
369 return smiles;
370 }
371
372//------------------------------------------------------------------------------
373
384 public static IAtomContainer generate2DCoordinates(IAtomContainer ac,
385 Logger logger) throws DENOPTIMException
386 {
387 IAtomContainer fmol = builder.newAtomContainer();
388 try
389 {
390 fmol = ac.clone();
391 }
392 catch (CloneNotSupportedException e)
393 {
394 throw new DENOPTIMException(e);
395 }
396
397 // remove Dummy atoms
400 fmol = dan.removeDummyInHapto(fmol);
401
402 // remove ring-closing attractors
403 removeRCA(fmol);
404
405 // Generate 2D structure diagram (for each connected component).
406 IAtomContainer ac2d = builder.newAtomContainer();
407 IAtomContainerSet som = ConnectivityChecker.partitionIntoMolecules(
408 fmol);
409
410 for (int n = 0; n < som.getAtomContainerCount(); n++)
411 {
412 synchronized (SDG)
413 {
414 IAtomContainer mol = som.getAtomContainer(n);
415 SDG.setMolecule(mol, true);
416 try
417 {
418 // Generate 2D coordinates for this molecule.
419 SDG.generateCoordinates();
420 mol = SDG.getMolecule();
421 }
422 catch (Exception e)
423 {
424 throw new DENOPTIMException(e);
425 }
426
427 ac2d.add(mol); // add 2D molecule.
428 }
429 }
430
431 return GeometryUtil.has2DCoordinates(ac2d) ? ac2d : null;
432 }
433
434//------------------------------------------------------------------------------
435
447 public static String getInChIKeyForMolecule(IAtomContainer mol,
448 Logger logger) throws DENOPTIMException {
449 InchiOptions options = new InchiOptions.InchiOptionsBuilder()
450 .withFlag(InchiFlag.AuxNone)
451 .withFlag(InchiFlag.RecMet)
452 .withFlag(InchiFlag.SUU)
453 .build();
454 return getInChIKeyForMolecule(mol, options, logger);
455 }
456
457//------------------------------------------------------------------------------
458
467 public static String getInChIKeyForMolecule(IAtomContainer mol,
468 InchiOptions options, Logger logger) throws DENOPTIMException {
469 IAtomContainer fmol = builder.newAtomContainer();
470 try
471 {
472 fmol = mol.clone();
473 }
474 catch (Throwable t)
475 {
476 throw new DENOPTIMException(t);
477 }
478
479 // remove Dummy atoms before generating the inchi
482 fmol = dan.removeDummyInHapto(fmol);
483
484 // remove PseudoAtoms
485 removeRCA(fmol);
486
487 String inchikey;
488 try
489 {
490 InChIGeneratorFactory factory = InChIGeneratorFactory.getInstance();
491 InChIGenerator gen = factory.getInChIGenerator(fmol, options);
492 InchiStatus ret = gen.getStatus();
493 if (ret == InchiStatus.WARNING)
494 {
495 //String error = gen.getMessage();
496 //InChI generated, but with warning message
497 //return new ObjectPair(null, error);
498 }
499 else if (ret != InchiStatus.SUCCESS)
500 {
501 return null;
502 }
503 inchikey = gen.getInchiKey();
504 }
505 catch (CDKException cdke)
506 {
507 throw new DENOPTIMException(cdke);
508 }
509 if (inchikey.length() > 0)
510 return inchikey;
511 else
512 return null;
513 }
514
515//------------------------------------------------------------------------------
516
524 public static int getNumberOfRotatableBonds(IAtomContainer mol)
525 throws DENOPTIMException {
526 int value;
527 try
528 {
529 IMolecularDescriptor descriptor =
530 new RotatableBondsCountDescriptor();
531 descriptor.setParameters(new Object[]{Boolean.FALSE,Boolean.FALSE});
532 DescriptorValue result = descriptor.calculate(mol);
533 value = ((IntegerResult)result.getValue()).intValue();
534 }
535 catch (CDKException cdke)
536 {
537 throw new DENOPTIMException(cdke);
538 }
539 return value;
540 }
541
542//------------------------------------------------------------------------------
543
550 public static int getHeavyAtomCount(IAtomContainer mol)
551 {
552 int n = 0;
553 for (IAtom atm : mol.atoms())
554 {
555 if (MoleculeUtils.isElement(atm)
556 && !MoleculeUtils.getSymbolOrLabel(atm).equals("H"))
557 n++;
558 }
559 return n;
560 }
561
562//------------------------------------------------------------------------------
563
571 public static int countAtomsOfElement(IAtomContainer mol, String symbol)
572 {
573 int n = 0;
574 for (IAtom atm : mol.atoms())
575 {
576 if (atm.getSymbol().equals(symbol))
577 n++;
578 }
579 return n;
580 }
581
582//------------------------------------------------------------------------------
583
595 public static Map<Vertex,ArrayList<Integer>> getVertexToAtomIdMap(
596 ArrayList<Vertex> vertLst,
597 IAtomContainer mol
598 ) {
599
600 ArrayList<Long> vertIDs = new ArrayList<>();
601 for (Vertex v : vertLst) {
602 vertIDs.add(v.getVertexId());
603 }
604
605 Map<Vertex,ArrayList<Integer>> map = new HashMap<>();
606 for (IAtom atm : mol.atoms())
607 {
608 long vID = Long.parseLong(atm.getProperty(
610 if (vertIDs.contains(vID))
611 {
612 Vertex v = vertLst.get(vertIDs.indexOf(vID));
613 int atmID = mol.indexOf(atm);
614 if (map.containsKey(v))
615 {
616 map.get(v).add(atmID);
617 }
618 else
619 {
620 ArrayList<Integer> atmLst = new ArrayList<>();
621 atmLst.add(atmID);
622 map.put(v, atmLst);
623 }
624 }
625 }
626 return map;
627 }
628
629//------------------------------------------------------------------------------
630
639 public static void moleculeToPNG(IAtomContainer mol, String filename,
640 Logger logger) throws DENOPTIMException
641 {
642 IAtomContainer iac = mol;
643
644 if (!GeometryUtil.has2DCoordinates(mol))
645 {
646 iac = generate2DCoordinates(mol, logger);
647 }
648
649 if (iac == null)
650 {
651 throw new DENOPTIMException("Failed to generate 2D coordinates.");
652 }
653
654 try
655 {
656 Depiction depiction = new DepictionGenerator().depict(iac);
657 depiction.writeTo(filename);
658 } catch (Exception e)
659 {
660 throw new DENOPTIMException("Failed to write image to "+filename);
661 }
662 }
663
664//------------------------------------------------------------------------------
665
673 public static Point3d getPoint3d(IAtom atm)
674 {
675 Point3d p = atm.getPoint3d();
676
677 if (p == null)
678 {
679 Point2d p2d = atm.getPoint2d();
680 if (p2d == null)
681 {
682 p = new Point3d(0.0, 0.0, 0.0);
683 } else {
684 p = new Point3d(p2d.x, p2d.y, 0.0);
685 }
686 }
687 return p;
688 }
689
690//------------------------------------------------------------------------------
691
692 //TODO: should we set only values that would otherwise be null?
693
698 public static void setZeroImplicitHydrogensToAllAtoms(IAtomContainer iac)
699 {
700 for (IAtom atm : iac.atoms()) {
701 atm.setImplicitHydrogenCount(0);
702 }
703 }
704
705//------------------------------------------------------------------------------
706
710 public static void explicitHydrogens(IAtomContainer mol)
711 {
712 AtomContainerManipulator.convertImplicitToExplicitHydrogens(mol);
713
714 }
715
716//------------------------------------------------------------------------------
717
725 public static void ensureNoUnsetBondOrdersSilent(IAtomContainer iac)
726 {
727 try {
729 } catch (CDKException e) {
730 StaticLogger.appLogger.log(Level.WARNING, "Kekulization failed. "
731 + "Bond orders will be unreliable as all unset bonds are"
732 + "now converted to single-order bonds.");
733 }
734 for (IBond bnd : iac.bonds())
735 {
736 if (bnd.getOrder().equals(IBond.Order.UNSET))
737 {
738 bnd.setOrder(IBond.Order.SINGLE);
739 }
740 }
741 }
742
743//------------------------------------------------------------------------------
744
753 public static void ensureNoUnsetBondOrders(IAtomContainer iac) throws CDKException
754 {
755 Kekulization.kekulize(iac);
756
757 for (IBond bnd : iac.bonds())
758 {
759 if (bnd.getOrder().equals(IBond.Order.UNSET))
760 {
761 bnd.setOrder(IBond.Order.SINGLE);
762 }
763 }
764 }
765
766//------------------------------------------------------------------------------
767
775 public static String missmatchingAromaticity(IAtomContainer mol)
776 {
777 String cause = "";
778 for (IAtom atm : mol.atoms())
779 {
780 //Check carbons with or without aromatic flags
781 if (atm.getSymbol().equals("C") && atm.getFormalCharge() == 0
782 && mol.getConnectedBondsCount(atm) == 3)
783 {
784 if (atm.getFlag(CDKConstants.ISAROMATIC))
785 {
786 int n = numOfBondsWithBO(atm, mol, IBond.Order.DOUBLE);
787 if (n == 0)
788 {
789 cause = "Aromatic atom " + getAtomRef(atm,mol)
790 + " has 3 connected atoms but no double bonds";
791 return cause;
792 }
793 } else {
794 for (IAtom nbr : mol.getConnectedAtomsList(atm))
795 {
796 if (nbr.getSymbol().equals("C"))
797 {
798 if (nbr.getFormalCharge() == 0)
799 {
800 if (mol.getConnectedBondsCount(nbr) == 3)
801 {
802 int nNbr = numOfBondsWithBO(nbr, mol,
803 IBond.Order.SINGLE);
804 int nAtm = numOfBondsWithBO(atm, mol,
805 IBond.Order.SINGLE);
806 if ((nNbr == 3) && (nAtm == 3))
807 {
808 cause = "Connected atoms "
809 + getAtomRef(atm, mol) + " "
810 + getAtomRef(nbr, mol)
811 + " have 3 connected atoms "
812 + "but no double bond. They are "
813 + "likely to be aromatic but no "
814 + "aromaticity has been reported.";
815 return cause;
816 }
817 }
818 }
819 }
820 }
821 }
822 }
823 }
824 return cause;
825 }
826
827//------------------------------------------------------------------------------
828
837 public static int numOfBondsWithBO(IAtom atm, IAtomContainer mol,
838 IBond.Order order)
839 {
840 int n = 0;
841 for (IBond bnd : mol.getConnectedBondsList(atm))
842 {
843 if (bnd.getOrder() == order)
844 n++;
845 }
846 return n;
847 }
848
849//------------------------------------------------------------------------------
850
860 public static IAtomContainer makeSameAs(IAtomContainer mol) throws DENOPTIMException
861 {
862 IChemObjectBuilder builder = SilentChemObjectBuilder.getInstance();
863 IAtomContainer iac = builder.newAtomContainer();
864
865 for (IAtom oAtm : mol.atoms())
866 {
867 IAtom nAtm = MoleculeUtils.makeSameAtomAs(oAtm,true,true);
868 iac.addAtom(nAtm);
869 }
870
871 for (IBond oBnd : mol.bonds())
872 {
873 if (oBnd.getAtomCount() != 2)
874 {
875 throw new DENOPTIMException("Unable to deal with bonds "
876 + "involving more than two atoms.");
877 }
878 int ia = mol.indexOf(oBnd.getAtom(0));
879 int ib = mol.indexOf(oBnd.getAtom(1));
880 iac.addBond(ia,ib,oBnd.getOrder());
881 }
882 return iac;
883 }
884
885//------------------------------------------------------------------------------
886
899 public static IAtom makeSameAtomAs(IAtom oAtm)
900 {
901 return makeSameAtomAs(oAtm, false, false);
902 }
903
904//------------------------------------------------------------------------------
905
919 public static IAtom makeSameAtomAs(IAtom oAtm, boolean ignoreValence,
920 boolean ignoreImplicitH)
921 {
922 IAtom nAtm = null;
923 String s = getSymbolOrLabel(oAtm);
924 if (MoleculeUtils.isElement(oAtm))
925 {
926 nAtm = new Atom(s);
927 } else {
928 nAtm = new PseudoAtom(s);
929 }
930 if (oAtm.getPoint3d() != null)
931 {
932 Point3d p3d = oAtm.getPoint3d();
933 nAtm.setPoint3d(new Point3d(p3d.x, p3d.y, p3d.z));
934 } else if (oAtm.getPoint2d() != null)
935 {
936 Point2d p2d = oAtm.getPoint2d();
937 nAtm.setPoint3d(new Point3d(p2d.x, p2d.y, 0.00001));
938 }
939 if (oAtm.getFormalCharge() != null)
940 nAtm.setFormalCharge(oAtm.getFormalCharge());
941 if (oAtm.getBondOrderSum() != null)
942 nAtm.setBondOrderSum(oAtm.getBondOrderSum());
943 if (oAtm.getCharge() != null)
944 nAtm.setCharge(oAtm.getCharge());
945 if (oAtm.getValency() != null && !ignoreValence)
946 nAtm.setValency(oAtm.getValency());
947 if (oAtm.getExactMass() != null)
948 nAtm.setExactMass(oAtm.getExactMass());
949 if (oAtm.getMassNumber() != null)
950 nAtm.setMassNumber(oAtm.getMassNumber());
951 if (oAtm.getFormalNeighbourCount() != null)
952 nAtm.setFormalNeighbourCount(oAtm.getFormalNeighbourCount());
953 if (oAtm.getFractionalPoint3d() != null)
954 nAtm.setFractionalPoint3d(new Point3d(oAtm.getFractionalPoint3d()));
955 if (oAtm.getHybridization() != null)
956 nAtm.setHybridization(Hybridization.valueOf(
957 oAtm.getHybridization().toString()));
958 if (oAtm.getImplicitHydrogenCount() != null && !ignoreImplicitH)
959 nAtm.setImplicitHydrogenCount(oAtm.getImplicitHydrogenCount());
960 if (oAtm.getMaxBondOrder() != null)
961 nAtm.setMaxBondOrder(oAtm.getMaxBondOrder());
962 if (oAtm.getNaturalAbundance() != null)
963 nAtm.setNaturalAbundance(oAtm.getNaturalAbundance());
964
965 return nAtm;
966 }
967
968//------------------------------------------------------------------------------
969
979 public static String getSymbolOrLabel(IAtom atm)
980 {
981 String s = "none";
982 if (MoleculeUtils.isElement(atm))
983 {
984 s = atm.getSymbol();
985 } else {
986 //TODO: one day will account for the possibility of having any
987 // other implementation of IAtom
988 IAtom a = null;
989 if (atm instanceof AtomRef)
990 {
991 a = ((AtomRef) atm).deref();
992 if (a instanceof PseudoAtom)
993 {
994 s = ((PseudoAtom) a).getLabel();
995 } else {
996 // WARNING: we fall back to standard behavior, but there
997 // could still be cases where this is not good...
998 s = a.getSymbol();
999 }
1000 } else if (atm instanceof PseudoAtom)
1001 {
1002 s = ((PseudoAtom) atm).getLabel();
1003 }
1004 }
1005 return s;
1006 }
1007
1008//------------------------------------------------------------------------------
1009
1014 public static String getAtomRef(IAtom atm, IAtomContainer mol)
1015 {
1016 return atm.getSymbol() + (mol.indexOf(atm) +1);
1017 }
1018
1019//------------------------------------------------------------------------------
1020
1026 public static Point3d calculateCentroid(IAtomContainer mol)
1027 {
1028 Point3d c = new Point3d(0,0,0);
1029 for (IAtom atm : mol.atoms())
1030 {
1031 c.add(getPoint3d(atm));
1032 }
1033 c.scale(1.0 / ((double) mol.getAtomCount()));
1034 return c;
1035 }
1036
1037//------------------------------------------------------------------------------
1038
1060 public static IAtomContainer extractIACForSubgraph(IAtomContainer wholeIAC,
1061 DGraph subGraph, DGraph wholeGraph, Logger logger,
1062 Randomizer randomizer) throws DENOPTIMException
1063 {
1064 IAtomContainer iac = makeSameAs(wholeIAC);
1065
1066 Set<Long> wantedVIDs = new HashSet<Long>();
1067 Map<Long,Vertex> wantedVertexesMap = new HashMap<>();
1068 for (Vertex v : subGraph.getVertexList())
1069 {
1070 Object o = v.getProperty(DENOPTIMConstants.STOREDVID);
1071 if (o == null)
1072 {
1073 throw new DENOPTIMException("Property '"
1074 + DENOPTIMConstants.STOREDVID + "' not defined in "
1075 + "vertex " + v + ", but is needed to extract "
1076 + "substructure.");
1077 }
1078 wantedVIDs.add(((Long) o).longValue());
1079 wantedVertexesMap.put(((Long) o).longValue(), v);
1080 }
1081
1082 // Identify the destiny of each atom: keep, remove, or make AP from it.
1083 IAtomContainer toRemove = new AtomContainer();
1084 Map<IAtom,IAtom> toAP = new HashMap<IAtom,IAtom>();
1085 Map<IAtom,AttachmentPoint> mapAtmToAPInG =
1086 new HashMap<IAtom,AttachmentPoint>();
1087 Map<IAtom,APClass> apcMap = new HashMap<IAtom,APClass>();
1088 for (int i=0; i<wholeIAC.getAtomCount(); i++)
1089 {
1090 IAtom cpAtm = iac.getAtom(i);
1091 IAtom oriAtm = wholeIAC.getAtom(i);
1092 Object o = oriAtm.getProperty(DENOPTIMConstants.ATMPROPVERTEXID);
1093 if (o == null)
1094 {
1095 throw new DENOPTIMException("Property '"
1097 + "' not defined in atom "
1098 + oriAtm.getSymbol() + wholeIAC.indexOf(oriAtm)
1099 + ", but is needed to extract substructure.");
1100 }
1101 long vid = ((Long) o).longValue();
1102 if (wantedVIDs.contains(vid))
1103 {
1104 continue; //keep this atom cpAtm
1105 }
1106 // Now, decide if the current atom should become an AP
1107 boolean willBecomeAP = false;
1108 for (IAtom nbr : wholeIAC.getConnectedAtomsList(oriAtm))
1109 {
1110 Object oNbr = nbr.getProperty(DENOPTIMConstants.ATMPROPVERTEXID);
1111 if (oNbr == null)
1112 {
1113 throw new DENOPTIMException("Property '"
1115 + "' not defined in atom "
1116 + nbr.getSymbol() + wholeIAC.indexOf(nbr)
1117 + ", but is needed to extract substructure.");
1118 }
1119 long nbrVid = ((Long) oNbr).longValue();
1120 if (wantedVIDs.contains(nbrVid))
1121 {
1122 // cpAtm is connected to an atom to keep and will therefore
1123 // become an AP. Note that the connection may go through
1124 // a chord in the graph, i.e., a pairs of RCVs!
1125 toAP.put(cpAtm,iac.getAtom(wholeIAC.indexOf(nbr)));
1126 AttachmentPoint apInWholeGraph =
1127 wholeGraph.getAPOnLeftVertexID(nbrVid,vid);
1128 if (apInWholeGraph == null)
1129 {
1130 String debugFile = "failedAPIdentificationIACSubGraph"
1131 + wholeGraph.getGraphId() + ".sdf";
1132 DenoptimIO.writeGraphToSDF(new File(debugFile),
1133 wholeGraph, willBecomeAP, logger, randomizer);
1134 throw new DENOPTIMException("Unmexpected null AP from "
1135 + nbrVid + " " + vid +" on " + wholeGraph
1136 + " See " + debugFile);
1137 }
1138 AttachmentPoint apInSubGraph =
1139 wantedVertexesMap.get(nbrVid).getAP(apInWholeGraph.getIndexInOwner());
1140 mapAtmToAPInG.put(cpAtm, apInSubGraph);
1141 APClass apc = apInWholeGraph.getAPClass();
1142 apcMap.put(cpAtm, apc);
1143 willBecomeAP = true;
1144 break;
1145 }
1146 }
1147 if (!willBecomeAP)
1148 toRemove.addAtom(cpAtm);
1149 }
1150
1151 iac.remove(toRemove);
1152
1153 // NB: the molecular representation in frag is NOT iac! It's a clone of it
1154 Fragment frag = new Fragment(iac,BBType.FRAGMENT);
1155
1156 List<IAtom> atmosToRemove = new ArrayList<>();
1157 List<IBond> bondsToRemove = new ArrayList<>();
1158 for (IAtom trgAtmInIAC : toAP.keySet())
1159 {
1160 IAtom trgAtm = frag.getAtom(iac.indexOf(trgAtmInIAC));
1161 IAtom srcAtmInISC = toAP.get(trgAtmInIAC);
1162 IAtom srcAtm = frag.getAtom(iac.indexOf(srcAtmInISC));
1163
1164 AttachmentPoint apInG = mapAtmToAPInG.get(trgAtmInIAC);
1165
1166 // Make Attachment point
1167 Point3d srcP3d = MoleculeUtils.getPoint3d(srcAtm);
1168 Point3d trgP3d = MoleculeUtils.getPoint3d(trgAtm);
1169 double currentLength = srcP3d.distance(trgP3d);
1170 //TODO-V3+? change hard-coded value with property of AP, when such
1171 // property will be available, i. e., one refactoring of AP and
1172 // atom coordinates is done.
1173 double idealLength = 1.53;
1174 /*
1175 double idealLength = apInG.getProperty(
1176 DENOPTIMConstants.APORIGINALLENGTH);
1177 */
1178 Point3d vector = new Point3d();
1179 vector.x = srcP3d.x + (trgP3d.x - srcP3d.x)*(idealLength/currentLength);
1180 vector.y = srcP3d.y + (trgP3d.y - srcP3d.y)*(idealLength/currentLength);
1181 vector.z = srcP3d.z + (trgP3d.z - srcP3d.z)*(idealLength/currentLength);
1182
1183 AttachmentPoint createdAP = frag.addAPOnAtom(srcAtm,
1184 apcMap.get(trgAtmInIAC), vector);
1185 createdAP.setProperty(DENOPTIMConstants.LINKAPS, apInG);
1186
1187 for (IBond bnd : frag.bonds())
1188 {
1189 if (bnd.contains(trgAtm))
1190 {
1191 bondsToRemove.add(bnd);
1192 }
1193 }
1194 atmosToRemove.add(trgAtm);
1195 }
1196
1197 // Remove atom that has become an AP and associated bonds
1198 for (IBond bnd : bondsToRemove)
1199 {
1200 frag.removeBond(bnd);
1201 }
1202 for (IAtom a : atmosToRemove)
1203 {
1204 frag.removeAtom(a);
1205 }
1206
1207 frag.updateAPs();
1208
1209 return frag.getIAtomContainer();
1210 }
1211
1212//------------------------------------------------------------------------------
1213
1219 public static int getDimensions(IAtomContainer mol)
1220 {
1221 final int is2D = 2;
1222 final int is3D = 3;
1223 final int not2or3D = -1;
1224
1225 int numOf2D = 0;
1226 int numOf3D = 0;
1227
1228 for (IAtom atm : mol.atoms())
1229 {
1230 Point2d p2d = new Point2d();
1231 Point3d p3d = new Point3d();
1232 p2d = atm.getPoint2d();
1233 boolean have2D = true;
1234 if (p2d == null)
1235 {
1236 have2D = false;
1237 p3d = atm.getPoint3d();
1238 if (p3d == null)
1239 {
1240 return not2or3D;
1241 }
1242 }
1243 ArrayList<Double> pointer = new ArrayList<Double>();
1244 try {
1245 if (have2D)
1246 {
1247 pointer.add(p2d.x);
1248 pointer.add(p2d.y);
1249 numOf2D++;
1250 } else {
1251 pointer.add(p3d.x);
1252 pointer.add(p3d.y);
1253 pointer.add(p3d.z);
1254 numOf3D++;
1255 }
1256 } catch (Throwable t) {
1257 return not2or3D;
1258 }
1259 }
1260
1261 if (numOf2D == mol.getAtomCount())
1262 return is2D;
1263 else if (numOf3D == mol.getAtomCount())
1264 return is3D;
1265 else
1266 return not2or3D;
1267 }
1268
1269//------------------------------------------------------------------------------
1270
1271}
General set of constants used in DENOPTIM.
static final String ATMPROPVERTEXID
String tag of Atom property used to store the unique ID of the Vertex corresponding to the molecular ...
static final String DUMMYATMSYMBOL
Symbol of dummy atom.
static final Object STOREDVID
Key of the property remembering vertex IDs.
static final Object LINKAPS
Key of property used to records references of APs.
An attachment point (AP) is a possibility to attach a Vertex onto the vertex holding the AP (i....
APClass getAPClass()
Returns the Attachment Point class.
void setProperty(Object key, Object property)
Container for the list of vertices and the edges that connect them.
Definition: DGraph.java:102
Class representing a continuously connected portion of chemical object holding attachment points.
Definition: Fragment.java:61
IBond removeBond(int position)
Definition: Fragment.java:878
AttachmentPoint addAPOnAtom(IAtom srcAtm, APClass apc, Point3d vector)
Add an attachment point to the specifies atom.
Definition: Fragment.java:424
IAtom getAtom(int number)
Definition: Fragment.java:843
void removeAtom(IAtom atom)
Definition: Fragment.java:899
IAtomContainer getIAtomContainer()
Definition: Fragment.java:788
void updateAPs()
Changes the properties of each APs as to reflect the current atom list.
Definition: Fragment.java:511
Iterable< IBond > bonds()
Definition: Fragment.java:829
A vertex is a data structure that has an identity and holds a list of AttachmentPoints.
Definition: Vertex.java:61
The RingClosingAttractor represent the available valence/connection that allows to close a ring.
static final Map< String, String > RCATYPEMAP
Recognized types of RingClosingAttractor and compatible types.
Utility methods for input/output.
static void writeSDFFile(String fileName, IAtomContainer mol)
Writes IAtomContainer to SDF file.
static void writeGraphToSDF(File file, DGraph graph, boolean append, boolean make3D, Logger logger, Randomizer randomizer)
Writes the graph to SDF file.
Logger class for DENOPTIM.
static final Logger appLogger
Toll to add/remove dummy atoms from linearities or multi-hapto sites.
IAtomContainer removeDummy(IAtomContainer mol)
Removes all dummy atoms and the bonds connecting them to other atoms.
IAtomContainer removeDummyInHapto(IAtomContainer mol)
Utilities for molecule conversion.
static int numOfBondsWithBO(IAtom atm, IAtomContainer mol, IBond.Order order)
Returns the number of bonds, with a certain bond order, surrounding the given atom.
static final StructureDiagramGenerator SDG
static String getInChIKeyForMolecule(IAtomContainer mol, Logger logger)
Generates the InChI key for the given atom container.
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 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 IAtom makeSameAtomAs(IAtom oAtm, boolean ignoreValence, boolean ignoreImplicitH)
Method that constructs an atom that reflect the same atom given as parameter in terms of element symb...
static int getNumberOfRotatableBonds(IAtomContainer mol)
Count number of rotatable bonds.
static int countAtomsOfElement(IAtomContainer mol, String symbol)
Count atoms with the given elemental symbol.
static int getDimensions(IAtomContainer mol)
Determines the dimensionality of the given chemical object.
static String getSMILESForMolecule(IAtomContainer mol, Logger logger)
Returns the SMILES representation of the molecule.
static IAtomContainer generate2DCoordinates(IAtomContainer ac, Logger logger)
Generates 2D coordinates for the molecule.
static void removeRCA(IAtomContainer mol)
Replace any PseudoAtoms representing ring closing attractors with H.
static double getMolecularWeight(IAtomContainer mol)
static String missmatchingAromaticity(IAtomContainer mol)
Looks for carbon atoms that are flagged as aromatic, but do not have any double bond and are,...
static IAtom makeSameAtomAs(IAtom oAtm)
Method that constructs an atom that reflect the same atom given as parameter in terms of element symb...
static final IChemObjectBuilder builder
static String getSymbolOrLabel(IAtom atm)
Gets either the elemental symbol (for standard atoms) of the label (for pseudo-atoms).
static void ensureNoUnsetBondOrdersSilent(IAtomContainer iac)
Sets bond order = single to all otherwise unset bonds.
static boolean isDummy(IAtom atm)
Checks if the given atom is a dummy atom based on the elemental symbol and the string used for dummy ...
static String getAtomRef(IAtom atm, IAtomContainer mol)
static final SmilesGenerator SMGEN
static void ensureNoUnsetBondOrders(IAtomContainer iac)
Sets bond order = single to all otherwise unset bonds.
static Map< Vertex, ArrayList< Integer > > getVertexToAtomIdMap(ArrayList< Vertex > vertLst, IAtomContainer mol)
Method to generate the map making in relation DENOPTIMVertex ID and atom index in the IAtomContainer ...
static void explicitHydrogens(IAtomContainer mol)
Converts all the implicit hydrogens to explicit.
static void moleculeToPNG(IAtomContainer mol, String filename, Logger logger)
Generate a PNG image from molecule mol
static Point3d calculateCentroid(IAtomContainer mol)
Calculated the centroid of the given molecule.
static Point3d getPoint3d(IAtom atm)
Return the 3D coordinates, if present.
static boolean isElement(String symbol)
Check element symbol corresponds to real element of Periodic Table.
static IAtomContainer extractIACForSubgraph(IAtomContainer wholeIAC, DGraph subGraph, DGraph wholeGraph, Logger logger, Randomizer randomizer)
Selects only the atoms that originate from a subgraph of a whole graph that originated the whole mole...
static boolean isElement(IAtom atom)
Check element symbol corresponds to real element of Periodic Table.
static int getHeavyAtomCount(IAtomContainer mol)
The heavy atom count.
static String getInChIKeyForMolecule(IAtomContainer mol, InchiOptions options, Logger logger)
Generates the INCHI key for the molecule.
Tool to generate random numbers and random decisions.
Definition: Randomizer.java:35
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
The type of building block.
Definition: Vertex.java:86