$darkmode
DENOPTIM
DummyAtomHandler.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
22
23import java.util.ArrayList;
24import java.util.Collections;
25import java.util.HashSet;
26import java.util.List;
27import java.util.Set;
28import java.util.logging.Level;
29import java.util.logging.Logger;
30
31import javax.vecmath.Point2d;
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;
40
41import denoptim.constants.DENOPTIMConstants;
42import denoptim.exception.DENOPTIMException;
43import denoptim.graph.AttachmentPoint;
44import denoptim.graph.Fragment;
45import denoptim.io.DenoptimIO;
46
53public class DummyAtomHandler
54{
58 private String elm = "";
59
60 //Recursion flag for reporting info
61 int recNum = 1;
62
66 private Logger logger;
67
68
69//------------------------------------------------------------------------------
70
78 public DummyAtomHandler(String elm, Logger logger)
79 {
80 this.elm = elm;
81 this.logger = logger;
82 }
83
84//------------------------------------------------------------------------------
85
90 public IAtomContainer removeDummy(IAtomContainer mol)
91 {
92 List<IAtom> dummiesList = new ArrayList<>();
93
94 //Identify the target atoms to be treated
95 for (IAtom atm : mol.atoms())
96 {
97 String symbol = MoleculeUtils.getSymbolOrLabel(atm);
98 if (elm.equals(""))
99 {
100 if (DENOPTIMConstants.DUMMYATMSYMBOL.equals(symbol))
101 {
102 dummiesList.add(atm);
103 }
104 }
105 else
106 {
107 if (symbol.equals(elm))
108 {
109 dummiesList.add(atm);
110 }
111 }
112 }
113
114 if (logger.isLoggable(Level.FINEST))
115 {
116 StringBuilder sb = new StringBuilder();
117 sb.append("Found "+dummiesList.size()+" dummy atoms:");
118 for (IAtom du : dummiesList)
119 {
120 sb.append(" " + getSDFAtomNumber(mol,du)+" size: "
121 + dummiesList.size() + DenoptimIO.NL);
122 }
123 logger.log(Level.FINEST,sb.toString());
124 }
125
126 //Delete dummy atoms and change connectivity
127 for (IAtom du : dummiesList)
128 {
129 //Remove Du-[*] Bonds
130 List<IAtom> nbrOfDu = mol.getConnectedAtomsList(du);
131 for (IAtom nbr : nbrOfDu)
132 mol.removeBond(du,nbr);
133
134 //Remove Du atom
135 mol.removeAtom(du);
136 logger.log(Level.FINEST, "NOTE! Atom Numbers change: "
137 + "dummy atom deleted");
138 }
139 return mol;
140 }
141
142//------------------------------------------------------------------------------
143
144 public IAtomContainer removeDummyInHapto(IAtomContainer mol)
145 throws DENOPTIMException
146 {
147 List<IAtom> dummiesList = new ArrayList<>();
148
149 //Identify the target atoms to be treated
150 for (IAtom atm : mol.atoms())
151 {
152 String symbol = MoleculeUtils.getSymbolOrLabel(atm);
153 if (elm.equals(""))
154 {
155 if (DENOPTIMConstants.DUMMYATMSYMBOL.equals(symbol))
156 {
157 dummiesList.add(atm);
158 }
159 } else {
160 if (symbol.equals(elm))
161 {
162 dummiesList.add(atm);
163 }
164 }
165 }
166
167 if (logger.isLoggable(Level.FINEST))
168 {
169 StringBuilder sb = new StringBuilder();
170 sb.append("Found "+dummiesList.size()+" dummy atoms:");
171 for (IAtom du : dummiesList)
172 {
173 sb.append(" " + getSDFAtomNumber(mol,du)+" size: "
174 + dummiesList.size() + DenoptimIO.NL);
175 }
176 logger.log(Level.FINEST,sb.toString());
177 }
178
179 //Delete dummy atoms and change connectivity
180 for (IAtom du : dummiesList)
181 {
182 //Remove Du-[*] Bonds
183 List<IAtom> nbrOfDu = mol.getConnectedAtomsList(du);
184 int numOfTerms = nbrOfDu.size();
185 for (IAtom nbr : nbrOfDu)
186 mol.removeBond(du,nbr);
187
188 List<Boolean> found = getFlagsVector(numOfTerms);
189 List<Set<IAtom>> goupsOfTerms = new ArrayList<>();
190 List<Integer> hapticity = new ArrayList<>();
191 for (int i=0; i<numOfTerms; i++)
192 {
193 //Was it found already?
194 if (found.get(i))
195 continue;
196
197 //Look for ligand starting from this term
198 IAtom nbrI = nbrOfDu.get(i);
199 Set<IAtom> ligOfNbrI =
200 exploreConnectedToAtom(nbrI,nbrOfDu,mol,found);
201 if (!ligOfNbrI.isEmpty())
202 {
203 goupsOfTerms.add(ligOfNbrI);
204 hapticity.add(ligOfNbrI.size());
205 }
206
207 } //end of loop over neighbours of Dummy
208
209 if (logger.isLoggable(Level.FINEST))
210 {
211 StringBuilder sb = new StringBuilder();
212 sb.append("There are "+goupsOfTerms.size()+" groups of terms");
213 for (int i=0; i<goupsOfTerms.size(); i++)
214 {
215 Set<IAtom> s = goupsOfTerms.get(i);
216 sb.append(" Group "+i+" - Hapticity: "+hapticity.get(i)
217 +" => "+DenoptimIO.NL);
218 for (IAtom sa : s)
219 {
220 sb.append((mol.indexOf(sa)+1)+
222 }
223 sb.append(DenoptimIO.NL);
224 }
225 logger.log(Level.FINEST, sb.toString());
226 }
227
228 // If Du is in between groups, connectivity has to be fixed
229 if (goupsOfTerms.size() > 1)
230 {
231 //Idenfify the ligand corresponding to Du
232 int ligandID = -1;
233 boolean ligandFound = false;
234 List<Point3d> allCandidates = new ArrayList<Point3d>();
235 for (int i=0; i<goupsOfTerms.size(); i++)
236 {
237 Set<IAtom> grp = goupsOfTerms.get(i);
238
239 //Identify center of the group of terms
240 Point3d candidateDuP3d = new Point3d();
241 for (IAtom atm : grp)
242 {
243 try
244 {
245 Point3d ligP3d = atm.getPoint3d();
246 candidateDuP3d.x = candidateDuP3d.x + ligP3d.x;
247 candidateDuP3d.y = candidateDuP3d.y + ligP3d.y;
248 candidateDuP3d.z = candidateDuP3d.z + ligP3d.z;
249 }
250 catch (Throwable t)
251 {
252 Point2d ligP2d = atm.getPoint2d();
253 candidateDuP3d.x = candidateDuP3d.x + ligP2d.x;
254 candidateDuP3d.y = candidateDuP3d.y + ligP2d.y;
255 candidateDuP3d.z = 0.0000;
256 }
257 }
258 allCandidates.add(candidateDuP3d);
259 candidateDuP3d.x = candidateDuP3d.x / (double) hapticity.get(i);
260 candidateDuP3d.y = candidateDuP3d.y / (double) hapticity.get(i);
261 candidateDuP3d.z = candidateDuP3d.z / (double) hapticity.get(i);
262
263 //Get coords of du
264 Point3d dummyP3d = new Point3d();
265 try
266 {
267 Point3d du3d = du.getPoint3d();
268 dummyP3d.x = du3d.x;
269 dummyP3d.y = du3d.y;
270 dummyP3d.z = du3d.z;
271 }
272 catch (Throwable t)
273 {
274 Point2d du2d = du.getPoint2d();
275 dummyP3d.x = du2d.x;
276 dummyP3d.y = du2d.y;
277 dummyP3d.z = 0.0000;
278 }
279
280 //Check if Du correspond to the centroid of this group
281 // WARNING! Hard-coded threshold.
282 double dist = candidateDuP3d.distance(dummyP3d);
283 if (dist < 0.002)
284 {
285 if (ligandFound)
286 {
287 String msg = "More then one group of atoms may "
288 + "correspond to the ligand. Not able "
289 + "to identify the ligand!";
290 throw new DENOPTIMException(msg);
291 }
292 ligandID = i;
293 ligandFound = true;
294 }
295 }
296
297 //In case of no matching return the error
298 if (!ligandFound)
299 {
300 String msg = "Dummy atom does not seem to be placed at the "
301 + "centroid of a multihapto ligand. "
302 + "Du: " + du
303 + "Candidates: " + allCandidates
304 + "See current molecule in 'error.sdf'";
305 DenoptimIO.writeSDFFile("error.sdf",mol,false);
306 throw new DENOPTIMException(msg);
307 }
308
309 //Connect every atom from the multihapto ligand whith
310 // the cetral atom/atoms
311 Set<IAtom> ligand = goupsOfTerms.get(ligandID);
312 for (int i=0; i<goupsOfTerms.size(); i++)
313 {
314 if (i == ligandID)
315 continue;
316
317 Set<IAtom> grp = goupsOfTerms.get(i);
318
319 for (IAtom centralAtm : grp)
320 {
321 for (IAtom ligandAtm : ligand)
322 {
323 logger.log(Level.FINEST, "Making a bond between: "
324 + mol.indexOf(ligandAtm)
326 ligandAtm) + " - "
327 + mol.indexOf(centralAtm)
329 centralAtm));
330 IBond bnd = new Bond(ligandAtm,centralAtm);
331 mol.addBond(bnd);
332 }
333 }
334 }
335 }
336
337 //Remove Du atom
338 mol.removeAtom(du);
339 logger.log(Level.FINEST, "NOTE! Atom Numbers change: "
340 + "dummy atom deleted");
341 } //end loop over Du
342
343 return mol;
344 }
345
346//------------------------------------------------------------------------------
347
358 private Set<IAtom> exploreConnectedToAtom(IAtom seed, List<IAtom> inList,
359 IAtomContainer mol, List<Boolean> doneFlag)
360 {
361 Set<IAtom> outSet = new HashSet<>();
362
363 //Deal with the seed
364 int idx = inList.indexOf(seed);
365 doneFlag.set(idx,true);
366 outSet.add(seed);
367
368 //Look for other atoms reachable from here
369 List<IAtom> connToSeed = mol.getConnectedAtomsList(seed);
370 connToSeed.retainAll(inList);
371 for (IAtom nbr : connToSeed)
372 {
373 int idx2 = inList.indexOf(nbr);
374 if (!doneFlag.get(idx2))
375 {
376 recNum++;
377 Set<IAtom> recursiveOut =
378 exploreConnectedToAtom(nbr,inList,mol, doneFlag);
379 recNum--;
380 outSet.addAll(recursiveOut);
381 }
382 }
383 return outSet;
384 }
385
386//------------------------------------------------------------------------------
387
395 private List<Boolean> getFlagsVector(int size)
396 {
397 //create a vector with false entries
398 List<Boolean> flg = new ArrayList<>();
399 for (int i = 0; i<size; i++) {
400 flg.add(false);
401 }
402 return flg;
403 }
404
405//------------------------------------------------------------------------------
406
407 private static int getSDFAtomNumber(IAtomContainer mol, IAtom atm)
408 {
409 return mol.indexOf(atm) + 1;
410 }
411
412//------------------------------------------------------------------------------
413
427 public static void addDummiesOnLinearities(Fragment frag, double angLim)
428 {
429 loopOverAtoms:
430 for (IAtom atm : frag.atoms())
431 {
432 Point3d pC = MoleculeUtils.getPoint3d(atm);
433 List<IAtom> nbrs = frag.getConnectedAtomsList(atm);
434 List<AttachmentPoint> nbrAP = frag.getAPsFromAtom(atm);
435
436 // Is there a linearity-breaking dummy atom already?
437 for (IAtom nbr : nbrs)
438 {
439 if (MoleculeUtils.isDummy(nbr)
440 && frag.getConnectedAtomsCount(nbr)==1
441 && frag.getAPCountOnAtom(nbr)==0)
442 {
443 // WARNING: we do not check the values of the angles!
444 // We assume that nbr is a linearity-breaking Du.
445 continue loopOverAtoms;
446 }
447 }
448
449 // Decide if this atom is at the center of a linearity,
450 // and possibly add linearity-breaking dummy atom
451 loopOverNeigborsOfOneAtom:
452 for(int i=0; i<(nbrs.size()+nbrAP.size()); i++)
453 {
454 Point3d pL = null;
455 if (i<nbrs.size())
456 pL = MoleculeUtils.getPoint3d(nbrs.get(i));
457 else {
458 pL = nbrAP.get(i-nbrs.size()).getDirectionVector();
459 if (pL == null)
460 continue;
461 }
462 for (int j=i+1; j<(nbrs.size()+nbrAP.size()); j++)
463 {
464 Point3d pR = null;
465 if (j<nbrs.size())
466 pR = MoleculeUtils.getPoint3d(nbrs.get(j));
467 else {
468 pR = nbrAP.get(j-nbrs.size()).getDirectionVector();
469 if (pR == null)
470 continue;
471 }
472 double angle = MathUtils.angle(pL, pC, pR);
473 if (angle > angLim)
474 {
475 // APs' heads are locations to avoid
476 List<Point3d> placesToAvoid = new ArrayList<Point3d>();
477 for (AttachmentPoint ap : nbrAP)
478 placesToAvoid.add(ap.getDirectionVector());
479
480 // Create dummy atom
481 IAtom dummyAtm = getDummyInSafeDirection(atm, pR,
482 frag.getIAtomContainer(), placesToAvoid);
483 frag.addAtom(dummyAtm);
484
485 // Connect dummy atom
486 IBond dummyBnd = new Bond(dummyAtm,atm);
487 frag.addBond(dummyBnd);
488 break loopOverNeigborsOfOneAtom;
489 }
490 }
491 }
492 }
493 }
494
495//------------------------------------------------------------------------------
496
514 public static IAtom getDummyInSafeDirection(IAtom atmA, Point3d pB,
515 IAtomContainer mol, List<Point3d> placesToAvoid)
516 {
517 Point3d pA = MoleculeUtils.getPoint3d(atmA);
518
519 // Get the forbidden areas: those in proximity of
520 // atoms connected to A, or opposite (180 deg) to them
521 List<Vector3d> allBusyDirections = new ArrayList<Vector3d>();
522 List<IAtom> nbrs = mol.getConnectedAtomsList(atmA);
523 for (IAtom nbr : nbrs)
524 {
525 Point3d pLoc = MoleculeUtils.getPoint3d(nbr);
526 Vector3d vLoc = CartesianSpaceUtils.getVectorFromTo(pA, pLoc);
527 vLoc.normalize();
528 allBusyDirections.add(vLoc);
529
530 Vector3d vLocOpposite = new Vector3d(vLoc.x * -1.0, vLoc.y * -1.0,
531 vLoc.z * -1.0);
532 vLocOpposite.normalize();
533 allBusyDirections.add(vLocOpposite);
534 }
535 for (Point3d pOther : placesToAvoid)
536 {
537 Vector3d vLoc = CartesianSpaceUtils.getVectorFromTo(pA, pOther);
538 vLoc.normalize();
539 allBusyDirections.add(vLoc);
540
541 Vector3d vLocOpposite = new Vector3d(vLoc.x * -1.0, vLoc.y * -1.0,
542 vLoc.z * -1.0);
543 vLocOpposite.normalize();
544 allBusyDirections.add(vLocOpposite);
545 }
546
547 //Get vector perpendicular (normal) to AB
548 Vector3d vAB = CartesianSpaceUtils.getVectorFromTo(pA, pB);
549 vAB.normalize();
550 Vector3d vNorm = CartesianSpaceUtils.getNormalDirection(vAB);
551 vNorm.normalize();
552
553 // Rotate the normal vector to check different angles
554 ArrayList<Vector3d> allAttempts = new ArrayList<Vector3d>();
555 ArrayList<Double> allAttemptsMinVal = new ArrayList<Double>();
556 double angStep = 22.0;
557 double maxStepD = 360.0 / angStep;
558 int maxStep = (int) maxStepD;
559 double forbiddenRadius = 0.2;
560 for(int j=1; j<3; j++)
561 {
562 for(int step = 0; step<maxStep; step++)
563 {
564 Vector3d vADuTry = new Vector3d();
565 if (j==1)
566 {
567 vADuTry = new Vector3d(vNorm.x,vNorm.y,vNorm.z);
568 } else if (j==2) {
570 new Vector3d(vNorm.x*(Math.sqrt(2.0)),
571 vNorm.y*(Math.sqrt(2.0)),
572 vNorm.z*(Math.sqrt(2.0))));
573 }
574 vADuTry.normalize();
575
576 // Get the new candidate position by rotation
577 double ang = angStep * step;
579
580 // Check if the candidate position is too close to a forbidden
581 // place. Use distance since they are all originating from A and
582 // normalized.
583 boolean skip = false;
584 double min = 10.0;
585 for (int i=0; i<allBusyDirections.size(); i++)
586 {
587 Vector3d busyDir = allBusyDirections.get(i);
588 Vector3d diffVec = CartesianSpaceUtils.getDiffOfVector(
589 vADuTry, busyDir);
590 double l = diffVec.length();
591 if (l < forbiddenRadius)
592 {
593 skip = true;
594 break;
595 }
596 if (l<min)
597 min = l;
598 }
599
600 if (skip)
601 continue;
602
603 // Store the surviving ones:
604 // those that are not too close to forbidden regions;
605 allAttempts.add(vADuTry);
606 allAttemptsMinVal.add(min);
607 } //end of loop over angles around AB (torsion of AB)
608 } //end of loop over angle with AB
609
610 // Find the best candidate: the most distant from forbidden areas
611 double max = Collections.max(allAttemptsMinVal);
612 int best = allAttemptsMinVal.indexOf(max);
613 Vector3d vADu = new Vector3d();
614 vADu = allAttempts.get(best);
615
616 // Create the dummy in the best position found
618 Point3d duP3dB = new Point3d(vADu.x, vADu.y, vADu.z);
619 IAtom dummyAtm = new PseudoAtom(DENOPTIMConstants.DUMMYATMSYMBOL, duP3dB);
620
621 return dummyAtm;
622 }
623
624//------------------------------------------------------------------------------
625}
General set of constants used in DENOPTIM.
static final String DUMMYATMSYMBOL
Symbol of dummy atom.
An attachment point (AP) is a possibility to attach a Vertex onto the vertex holding the AP (i....
Class representing a continuously connected portion of chemical object holding attachment points.
Definition: Fragment.java:61
List< IAtom > getConnectedAtomsList(IAtom atom)
Definition: Fragment.java:943
int getAPCountOnAtom(int srcAtmId)
Returns the number of APs currently defined on a specific atom source.
Definition: Fragment.java:475
void addBond(IBond bond)
Definition: Fragment.java:871
Iterable< IAtom > atoms()
Definition: Fragment.java:822
ArrayList< AttachmentPoint > getAPsFromAtom(IAtom srcAtm)
Definition: Fragment.java:455
void addAtom(IAtom atom)
Definition: Fragment.java:836
IAtomContainer getIAtomContainer()
Definition: Fragment.java:788
int getConnectedAtomsCount(IAtom atom)
Definition: Fragment.java:950
Utility methods for input/output.
static void writeSDFFile(String fileName, IAtomContainer mol)
Writes IAtomContainer to SDF file.
static final String NL
Newline character from system.
Utilities for working in the Cartesian space.
static Vector3d getSumOfVector(Vector3d A, Vector3d B)
Get sum of vector A and B.
static Vector3d getDiffOfVector(Vector3d A, Vector3d B)
Calculates the vector difference of vectors A and B.
static void translateOrigin(Vector3d v, Point3d newOrigin)
Changes the origin of a vector.
static Vector3d getNormalDirection(Vector3d dir)
Generate a vector that is perpendicular to the given one.
static Vector3d getVectorFromTo(Point3d a, Point3d b)
Creates an object Vector3d that originates from point a and goes to point b.
static void rotatedVectorWAxisAngle(Vector3d v, Vector3d axis, double ang)
Rotate a vector according to a given rotation axis and angle.
Toll to add/remove dummy atoms from linearities or multi-hapto sites.
static void addDummiesOnLinearities(Fragment frag, double angLim)
Append dummy atoms on otherwise linear arrangements of atoms.
List< Boolean > getFlagsVector(int size)
Generates a vector of boolean flags.
static int getSDFAtomNumber(IAtomContainer mol, IAtom atm)
Set< IAtom > exploreConnectedToAtom(IAtom seed, List< IAtom > inList, IAtomContainer mol, List< Boolean > doneFlag)
Explore connected systems in a list of atoms and returns all the atoms that can be reached starting f...
static IAtom getDummyInSafeDirection(IAtom atmA, Point3d pB, IAtomContainer mol, List< Point3d > placesToAvoid)
Generates a dummy atom 0.1 nm from atom A in a place that is safe for a dummy atom.
DummyAtomHandler(String elm, Logger logger)
Constructor defining a specific elemental symbol to consider the symbol of dummy atoms.
String elm
The elemental symbol that we consider to be the symbol of a dummy atom.
Logger logger
Program-specific logger.
IAtomContainer removeDummy(IAtomContainer mol)
Removes all dummy atoms and the bonds connecting them to other atoms.
IAtomContainer removeDummyInHapto(IAtomContainer mol)
Some useful math operations.
Definition: MathUtils.java:39
static double angle(Point3d a, Point3d b, Point3d c)
Calculate the angle between the 3 points.
Definition: MathUtils.java:284
Utilities for molecule conversion.
static String getSymbolOrLabel(IAtom atm)
Gets either the elemental symbol (for standard atoms) of the label (for pseudo-atoms).
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 Point3d getPoint3d(IAtom atm)
Return the 3D coordinates, if present.