$darkmode
DENOPTIM
PathClosabilityTools.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.graph.rings;
20
21import java.util.ArrayList;
22import java.util.Arrays;
23import java.util.Collections;
24import java.util.HashMap;
25import java.util.Iterator;
26import java.util.List;
27import java.util.Map;
28import java.util.Set;
29import java.util.logging.Level;
30import java.util.logging.Logger;
31
32import javax.vecmath.Point3d;
33
34import org.openscience.cdk.Bond;
35import org.openscience.cdk.graph.ShortestPaths;
36import org.openscience.cdk.graph.matrix.TopologicalMatrix;
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.fragspace.FragmentSpace;
46import denoptim.graph.APClass;
47import denoptim.graph.AttachmentPoint;
48import denoptim.graph.DGraph;
49import denoptim.graph.Edge;
50import denoptim.graph.Edge.BondType;
51import denoptim.graph.EmptyVertex;
52import denoptim.graph.Fragment;
53import denoptim.graph.Ring;
54import denoptim.graph.Vertex;
55import denoptim.graph.Vertex.BBType;
56import denoptim.utils.ManySMARTSQuery;
57import denoptim.utils.MoleculeUtils;
58import denoptim.utils.ObjectPair;
59import denoptim.utils.RingClosingUtils;
60
61
71{
72
73//-----------------------------------------------------------------------------
74
86 public static boolean isCloseable(PathSubGraph subGraph,
87 IAtomContainer mol, RingClosureParameters settings)
88 {
89 boolean closable = false;
90
91
92 // Cases that we may want to exclude:
93 //
94 // Exclude adding fused cycle of vertexes on already fused system.
95 // This can be achieved by controlling APClass compatibilities, if the
96 // APClasses are also chosen to describe the role of the vertex bearing
97 // the AP in the system, e.g., its role in an aromatic system.
98 // Otherwise, we can see if the vertexes in the path already belong to
99 // cycles: This is fast, so it might be better to do it even if acting
100 // at the level of the molecular representation will deliver the same
101 // information.
102
103
104 switch (settings.getClosabilityEvalMode())
105 {
106 case -1:
107 closable = true; // ring size has been evaluated before
108 break;
109 case 0:
110 closable = evaluateConstitutionalClosability(subGraph, mol,
111 settings);
112 break;
113 case 1:
114 closable = evaluate3DPathClosability(subGraph, mol, settings);
115 break;
116 case 2:
117 closable = evaluateConstitutionalClosability(subGraph, mol,
118 settings) && evaluate3DPathClosability(subGraph, mol,
119 settings);
120 break;
121 default:
122 String s = "Unrecognized closability evaluation mode";
123 throw new IllegalArgumentException(s);
124 }
125 return closable;
126 }
127
128//-----------------------------------------------------------------------------
129
142 private static boolean evaluateConstitutionalClosability(
143 PathSubGraph subGraph, IAtomContainer iac,
144 RingClosureParameters settings)
145 {
146 settings.getLogger().log(Level.FINE, "Evaluating constitutional "
147 + "closability of path: " + subGraph.getVertecesPath());
148
149 // Get a working copy of the molecular container
150 IChemObjectBuilder builder = SilentChemObjectBuilder.getInstance();
151 IAtomContainer mol = builder.newAtomContainer();
152 try
153 {
154 mol = iac.clone();
155 }
156 catch (CloneNotSupportedException e)
157 {
158 throw new IllegalArgumentException(e);
159 }
161
162 // Identify atoms of molecular representation that correspond to
163 // this path of vertices
164 Map<Vertex,ArrayList<Integer>> vIdToAtmId =
166 (ArrayList<Vertex>) subGraph.getVertecesPath(),
167 mol);
168 List<Integer> atmIdsInVrtxPath = new ArrayList<Integer>();
169 for (Vertex v : subGraph.getVertecesPath())
170 {
171 atmIdsInVrtxPath.addAll(vIdToAtmId.get(v));
172 }
173
174 // Find atoms to remove: keep only the atoms that belong to the
175 // vertex path and their neighbours
176 ArrayList<IAtom> toRemove = new ArrayList<IAtom>();
177 for (int i=0; i<mol.getAtomCount(); i++)
178 {
179 if (!atmIdsInVrtxPath.contains(i))
180 {
181 IAtom candAtm = mol.getAtom(i);
182 boolean isNeighbour = false;
183 List<IAtom> nbrs = mol.getConnectedAtomsList(candAtm);
184 for (IAtom nbrAtm : nbrs)
185 {
186 if (atmIdsInVrtxPath.contains(mol.indexOf(nbrAtm)))
187 {
188 isNeighbour = true;
189 break;
190 }
191 }
192 if (!isNeighbour)
193 {
194 toRemove.add(candAtm);
195 }
196 }
197 }
198 // Deal with RCAs: head and tail vertices
199 IAtom atmH = mol.getAtom(vIdToAtmId.get(subGraph.getHeadVertex()).get(0));
200 IAtom atmT = mol.getAtom(vIdToAtmId.get(subGraph.getTailVertex()).get(0));
201 IAtom srcH = mol.getConnectedAtomsList(atmH).get(0);
202 IAtom srcT = mol.getConnectedAtomsList(atmT).get(0);
203 int iSrcH = mol.indexOf(srcH);
204 int iSrcT = mol.indexOf(srcT);
205 toRemove.add(atmH);
206 toRemove.add(atmT);
207
208 BondType bndTyp = subGraph.getEdgesPath().get(0).getBondType();
209 if (bndTyp.hasCDKAnalogue())
210 {
211 mol.addBond(iSrcH, iSrcT, bndTyp.getCDKOrder());
212 } else {
213 settings.getLogger().log(Level.WARNING,
214 "WARNING! Attempt to add ring closing bond "
215 + "did not add any actual chemical bond because the "
216 + "bond type of the chord is '" + bndTyp +"'.");
217 }
218
219 // Remove atoms
220 for (IAtom a : toRemove)
221 {
222 mol.removeAtom(a);
223 }
224
225 StringBuilder sb = new StringBuilder();
226 sb.append("Molecular representation of path includes:");
227 for (IAtom a : mol.atoms())
228 {
229 sb.append(" " + a.getSymbol() + mol.indexOf(a) + " "
230 + a.getProperties());
231 }
232 settings.getLogger().log(Level.FINEST, sb.toString());
233
234 boolean closable = false;
235
236 // Evaluate requirement based on elements contained in the ring
237 boolean spanRequiredEls = false;
238 Set<String> reqRingEl = settings.getRequiredRingElements();
239 if (reqRingEl.size() != 0)
240 {
241 // Prepare shortest atom path
242 List<IAtom> atomsPath = new ArrayList<IAtom>();
243 ShortestPaths sp = new ShortestPaths(mol, srcH);
244 atomsPath = new ArrayList<IAtom>(Arrays.asList(sp.atomsTo(srcT)));
245
246 // Look for the required elements
247 for (String el : reqRingEl)
248 {
249 for (IAtom a : atomsPath)
250 {
251 if (MoleculeUtils.getSymbolOrLabel(a).equals(el))
252 {
253 spanRequiredEls = true;
254 break;
255 }
256 }
257 if (spanRequiredEls)
258 {
259 break;
260 }
261 }
262 if (!spanRequiredEls)
263 {
264 settings.getLogger().log(Level.FINER,
265 "Candidate ring doesn't involve any among the required "
266 + "elements.");
267 return false;
268 }
269 closable = true;
270 }
271
272 // Try to find a match for any of the SMARTS queries
273 Map<String,String> smarts = settings.getConstitutionalClosabilityConds();
274 if (smarts.size() != 0)
275 {
276 closable = false;
277 ManySMARTSQuery msq = new ManySMARTSQuery(mol,smarts);
278 if (msq.hasProblems())
279 {
280 String msg = "Attempt to match SMARTS for "
281 + "constitution-based ring-closability conditions "
282 + "returned an error! Ignoring " + msq.getMessage();
283 settings.getLogger().log(Level.WARNING,msg);
284 }
285 for (String name : smarts.keySet())
286 {
287 if (msq.getNumMatchesOfQuery(name) > 0)
288 {
289 settings.getLogger().log(Level.FINER,
290 "Candidate closable path matches constitutional "
291 + "closability criterion: " + smarts.get(name));
292 closable = true;
293 break;
294 }
295 }
296 }
297
298 settings.getLogger().log(Level.FINE,
299 "Contitutional closability: " + closable);
300 return closable;
301 }
302
303//-----------------------------------------------------------------------------
304
320 private static boolean evaluate3DPathClosability(PathSubGraph subGraph,
321 IAtomContainer mol, RingClosureParameters settings)
322 {
323 String chainId = subGraph.getChainID();
324 settings.getLogger().log(Level.FINE,
325 "Evaluating 3D closability of path: "
326 + subGraph.getVertecesPath()+" ChainID: "+chainId);
327
329
331 boolean closable = false;
332
333 String foundID = rca.containsChain(subGraph);
334 if (foundID != "")
335 {
336 // Get all info from archive
337 closable = rca.getClosabilityOfChain(foundID);
338 rcc = rca.getRCCsOfChain(foundID);
339 if (settings.checkInterdependentChains()
340 && settings.doExhaustiveConfSrch())
341 {
342 try
343 {
344 subGraph.makeMolecularRepresentation(mol, false,
345 settings.getLogger(), settings.getRandomizer());
346 } catch (DENOPTIMException e)
347 {
348 settings.getLogger().warning("Could not create 3D model "
349 + "for potentially closeable path that will be "
350 + "considered not ring-closable.");
351 return false;
352 }
353 subGraph.setRCC(rcc);
354 }
355 }
356 else
357 {
358 // Need to generate 3D molecular representation
359 try {
360 subGraph.makeMolecularRepresentation(mol, true,
361 settings.getLogger(), settings.getRandomizer());
362 } catch (DENOPTIMException e)
363 {
364 settings.getLogger().warning("Could not create 3D model "
365 + "for potentially closeable path that will be "
366 + "considered not ring-closable.");
367 return false;
368 }
369
370 List<IAtom> atomsPath = subGraph.getAtomPath();
371 List<IBond> bondsPath = subGraph.getBondPath();
372
373 // Define rotatability
374 ArrayList<Boolean> rotatability = new ArrayList<Boolean>();
375 for (int i=0; i < bondsPath.size(); i++)
376 {
377 IBond bnd = bondsPath.get(i);
378 Object rotFlag = bnd.getProperty(
380 if (rotFlag == null || !Boolean.valueOf(rotFlag.toString()))
381 {
382 rotatability.add(false);
383 }
384 else
385 {
386 rotatability.add(true);
387 }
388 }
389 settings.getLogger().log(Level.FINE, "Rotatability: "+rotatability);
390
391 // Define initial values of dihedrals
392 // NOTE: these angles are not calculated on the atoms of the chain,
393 // but on the reference points that are unique for each bond
394 // This should allow to compare conformations of different
395 // chains that share one or more bonds
396 ArrayList<ArrayList<Point3d>> dihRefs =
397 subGraph.getDihedralRefPoints();
398 if (dihRefs.size() != rotatability.size()-2)
399 {
400 throw new IllegalStateException("Number of bonds and number of "
401 + "dihidrals angles are inconsistent in PathSubGraph."
402 + " Contact the author.");
403 }
404
405 // find ring closing conformations
406 ArrayList<ArrayList<Double>> closableConfs =
407 new ArrayList<ArrayList<Double>>();
408 closable = RingClosureFinder.evaluateClosability(atomsPath,
409 rotatability,
410 dihRefs,
411 closableConfs,
412 settings);
413
414 // store in object graph
415 rcc = new RingClosingConformations(chainId, closableConfs);
416 subGraph.setRCC(rcc);
417
418 // put ring-closure information in archive for further use
419 rca.storeEntry(chainId,closable,rcc);
420 }
421
422 settings.getLogger().log(Level.FINE, "Path closablility: "+closable);
423
424 return closable;
425 }
426
427//-----------------------------------------------------------------------------
428
429}
General set of constants used in DENOPTIM.
static final String BONDPROPROTATABLE
String tag of Bond's property used to store the property of being rotatable.
A vertex is a data structure that has an identity and holds a list of AttachmentPoints.
Definition: Vertex.java:62
Tool box for determining whether a chain of atoms, i.e., a path, can be folded as to form a ring-clos...
static boolean evaluate3DPathClosability(PathSubGraph subGraph, IAtomContainer mol, RingClosureParameters settings)
Method to evaluate the closability of a single path in a graph representing a molecule.
static boolean evaluateConstitutionalClosability(PathSubGraph subGraph, IAtomContainer iac, RingClosureParameters settings)
Method to evaluate the closability of a single path considering only its constitution.
static boolean isCloseable(PathSubGraph subGraph, IAtomContainer mol, RingClosureParameters settings)
Method to evaluate the closability of a single path in a graph.
This object represents a path in a DGraph.
List< IBond > getBondPath()
Returns the list of bonds in the path between the head and the tail.
void makeMolecularRepresentation(IAtomContainer mol, boolean make3D, Logger logger, Randomizer randomizer)
Creates the molecular representation, list of atoms and bonds involved in the path between the head a...
List< Vertex > getVertecesPath()
Returns the list of verteces involved.
String getChainID()
Returns the string representation of the path.
List< IAtom > getAtomPath()
Returns the list of atoms in the path between the head and the tail.
Vertex getHeadVertex()
Returns the vertex representing the head of the chain.
ArrayList< ArrayList< Point3d > > getDihedralRefPoints()
Returns the list of point to be used to define the torsion of a bond uniquely (independently on the s...
Vertex getTailVertex()
Returns the vertex representing the tail of the chain.
List< Edge > getEdgesPath()
Returns the list of edges involved.
void setRCC(RingClosingConformations rcc)
Set the ring closing conformations to this object.
Serializable object to store/get a list of conformations that allow to close a ring from an open chai...
Tool to explore the conformational space of chains of atoms and identify ring closing conformations.
static boolean evaluateClosability(List< IAtom > path, ArrayList< Boolean > rotatability, ArrayList< ArrayList< Point3d > > dihRefs, ArrayList< ArrayList< Double > > closableConfs, RingClosureParameters settings)
Giving a list of points in 3D space (the path) this method evaluates whether it exists at least one c...
Parameters and setting related to handling ring closures.
Data structure to store and handle information about sub-structures (i.e., chains of fragments) and r...
RingClosingConformations getRCCsOfChain(String chainId)
void storeEntry(String chainId, boolean closable, RingClosingConformations rcc)
Append a new closable chain entry to the archive.
Logger getLogger()
Get the name of the program specific logger.
Randomizer getRandomizer()
Returns the current program-specific randomizer.
Container of lists of atoms matching a list of SMARTS.
int getNumMatchesOfQuery(String query)
Utilities for molecule conversion.
static void removeRCA(IAtomContainer mol)
Replace any PseudoAtoms representing ring closing attractors with H.
static String getSymbolOrLabel(IAtom atm)
Gets either the elemental symbol (for standard atoms) of the label (for pseudo-atoms).
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 ...
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