$darkmode
DENOPTIM
RotationalSpaceUtils.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.utils;
20
21import java.io.BufferedReader;
22import java.io.File;
23import java.io.FileReader;
24import java.io.IOException;
25import java.util.ArrayList;
26import java.util.HashMap;
27import java.util.Map;
28import java.util.logging.Level;
29import java.util.logging.Logger;
30import java.util.TreeMap;
31
32import org.openscience.cdk.graph.SpanningTree;
33import org.openscience.cdk.interfaces.IAtom;
34import org.openscience.cdk.interfaces.IAtomContainer;
35import org.openscience.cdk.interfaces.IBond;
36import org.openscience.cdk.interfaces.IChemObjectBuilder;
37import org.openscience.cdk.interfaces.IRingSet;
38import org.openscience.cdk.isomorphism.Mappings;
39import org.openscience.cdk.silent.RingSet;
40import org.openscience.cdk.silent.SilentChemObjectBuilder;
41
42import denoptim.constants.DENOPTIMConstants;
43import denoptim.exception.DENOPTIMException;
44import denoptim.io.DenoptimIO;
45
46
55{
56 private static final IChemObjectBuilder builder =
57 SilentChemObjectBuilder.getInstance();
58
59 public static final String PROPERTY_RSU_ATMID = "RSU-ATMID";
60
61 // Properties to store the details of the constrained rotatable bonds
62 public static final String PROPERTY_ROTDBDCSTR_DEF = "ROTDBDCSTR_DEF";
63 public static final String PROPERTY_ROTDBDCSTR_VALUE = "ROTDBDCSTR_VALUE";
64
65//------------------------------------------------------------------------------
66
86 public static ArrayList<ObjectPair> defineRotatableBonds(IAtomContainer mol,
87 String defRotBndsFile, boolean addIterfragBonds, boolean excludeRings,
88 Logger logger) throws DENOPTIMException
89 {
90 ArrayList<ObjectPair> rotatableBonds = new ArrayList<ObjectPair>();
91
92 // Set all bond flags
93 for (IBond b : mol.bonds())
94 {
95 b.setProperty(DENOPTIMConstants.BONDPROPROTATABLE,"false");
96 }
97
98 // Deal with inter-fragment bonds
99 if (addIterfragBonds)
100 {
101 rotatableBonds.addAll(getInterVertexBonds(mol));
102 }
103
104 // We'll use SMARTS so get rid of pseudoatoms that can create problems
105 // We'll use this modified IAtomContainer only when dealing with SMARTS
106 IAtomContainer locMol = builder.newAtomContainer();
107 try {
108 locMol = mol.clone();
109 for (int iAtm=0; iAtm<locMol.getAtomCount(); iAtm++)
110 {
111 locMol.getAtom(iAtm).setProperty(PROPERTY_RSU_ATMID, iAtm);
112 }
113 } catch (Throwable t) {
114 throw new DENOPTIMException(t);
115 }
116 MoleculeUtils.removeRCA(locMol);
117
118 if (!defRotBndsFile.equals(""))
119 {
120 // Get definition of rotational space as list of SMARTS queries
121 Map<String,String> listQueries = getRotationalSpaceDefinition(
122 defRotBndsFile);
123
124 // Get bonds matching one of the definitions of rotatable bonds
125 ManySMARTSQuery msq = new ManySMARTSQuery(locMol,listQueries);
126 if (msq.hasProblems())
127 {
128 String msg = "WARNING! Attempt to match rotatable bonds returned "
129 + "an error! Selecting only fragment-fragment "
130 + "bonds. Details: " + msq.getMessage();
131 logger.log(Level.WARNING, msg);
132 } else {
133 //Transform list of indeces
134 for (String name : listQueries.keySet())
135 {
136 //Skip if no match
137 if (msq.getNumMatchesOfQuery(name) == 0)
138 {
139 continue;
140 }
141
142 //Put all matches in one list
143 Mappings matches = msq.getMatchesOfSMARTS(name);
144 for (int[] singleMatch : matches)
145 {
146 //Check assumption on number of atoms involved in each bond
147 if (singleMatch.length != 2)
148 {
149 throw new Error("DENOPTIM can only deal with bonds "
150 + "involving 2 atoms. Check bond "
151 + singleMatch);
152 }
153
154 //NOTE the index refers to the IAtomContainer locMol that is a
155 //clone of mol but has no RCAs, so we take the atm index in the
156 // oridinal mol fro mthe atm property
157
158 int idAtmA = (int) locMol.getAtom(singleMatch[0]).getProperty(PROPERTY_RSU_ATMID);
159 int idAtmB = (int) locMol.getAtom(singleMatch[1]).getProperty(PROPERTY_RSU_ATMID);
160
161 // Compare with bonds already in the list
162 boolean alreadyThere = false;
163 for (ObjectPair op : rotatableBonds)
164 {
165 int a1 = ((Integer)op.getFirst()).intValue();
166 int a2 = ((Integer)op.getSecond()).intValue();
167 if (((a1 == idAtmA) && (a2 == idAtmB)) ||
168 ((a2 == idAtmA) && (a1 == idAtmB)))
169 {
170 alreadyThere = true;
171 break;
172 }
173 }
174 if (!alreadyThere)
175 {
176 ObjectPair newRotBnd = new ObjectPair(
177 Integer.valueOf(idAtmA),
178 Integer.valueOf(idAtmB));
179 rotatableBonds.add(newRotBnd);
180 }
181 }
182 }
183 }
184 }
185
186 // Find all rings to identify cyclic bonds
187 SpanningTree st = new SpanningTree(mol);
188 IRingSet allRings = new RingSet();
189 if (excludeRings)
190 {
191 try {
192 allRings = st.getAllRings();
193 } catch (Exception ex) {
194 throw new DENOPTIMException(ex);
195 }
196 }
197
198 // Purge list and store information as bond property
199 ArrayList<ObjectPair> toRemove = new ArrayList<ObjectPair>();
200 for (ObjectPair op : rotatableBonds)
201 {
202 int a1 = ((Integer)op.getFirst()).intValue();
203 int a2 = ((Integer)op.getSecond()).intValue();
204
205 IBond bnd = mol.getBond(mol.getAtom(a1),mol.getAtom(a2));
206
207 if (excludeRings)
208 {
209 IRingSet rs = allRings.getRings(bnd);
210 if (!rs.isEmpty())
211 {
212 logger.log(Level.FINE, "Ignoring cyclic bond: "+bnd);
213 toRemove.add(op);
214 continue;
215 }
216 }
217 bnd.setProperty(DENOPTIMConstants.BONDPROPROTATABLE, "true");
218 }
219
220 if (excludeRings)
221 {
222 for (ObjectPair op : toRemove)
223 {
224 rotatableBonds.remove(op);
225 }
226 }
227
228 return rotatableBonds;
229 }
230
231//------------------------------------------------------------------------------
232
241 public static ArrayList<ObjectPair> getInterVertexBonds(IAtomContainer mol)
242 throws DENOPTIMException
243 {
244 ArrayList<ObjectPair> interfragBonds = new ArrayList<ObjectPair>();
245
247 for (IBond bnd : mol.bonds())
248 {
249 if (bnd.getAtomCount() == 2)
250 {
251 IAtom atmA = bnd.getAtom(0);
252 IAtom atmB = bnd.getAtom(1);
253 String fragIdA = atmA.getProperty(p).toString();
254 String fragIdB = atmB.getProperty(p).toString();
255
256 if (!fragIdA.equals(fragIdB) &&
257 (mol.getConnectedBondsCount(atmA) != 1) &&
258 (mol.getConnectedBondsCount(atmB) != 1))
259 {
260 ObjectPair newRotBnd = new ObjectPair();
261 int idAtmA = mol.indexOf(atmA);
262 int idAtmB = mol.indexOf(atmB);
263 if (idAtmA < idAtmB)
264 {
265 newRotBnd = new ObjectPair(Integer.valueOf(idAtmA),
266 Integer.valueOf(idAtmB));
267 } else {
268 newRotBnd = new ObjectPair(Integer.valueOf(idAtmB),
269 Integer.valueOf(idAtmA));
270 }
271 interfragBonds.add(newRotBnd);
272 }
273 }
274 else
275 {
276 String str = "ERROR! Unable to handle bonds involving other "
277 + "than two atoms.";
278 throw new DENOPTIMException(str);
279 }
280 }
281 return interfragBonds;
282 }
283
284//------------------------------------------------------------------------------
293 public static Map<String,String> getRotationalSpaceDefinition(
294 String filename) throws DENOPTIMException
295 {
296 Map<String,String> mapOfSMARTS = new HashMap<String,String>();
297
298 //Read the file
299 if (filename.equals(null))
300 {
301 DENOPTIMException ex = new DENOPTIMException("Pointer to file is "
302 + " null! Cannot read definition of rotational space.");
303 throw ex;
304 }
305 File f = new File(filename);
306 if (!f.exists())
307 {
308 DENOPTIMException ex = new DENOPTIMException("File '" + filename
309 + "' does not exist! Cannot find definition of rotational "
310 + "space.");
311 throw ex;
312 }
313 BufferedReader br = null;
314 String line;
315 try
316 {
317 br = new BufferedReader(new FileReader(filename));
318 while ((line = br.readLine()) != null)
319 {
320 if (line.trim().length() == 0)
321 continue;
322
323 if (line.trim().startsWith("#"))
324 continue;
325
326 String[] parts = line.split("\\s+");
327 if (parts.length != 2)
328 {
329 throw new DENOPTIMException("Unable to understand "
330 + "rotational Space definition. "
331 + "Check line '"+ line +"' in file "
332 + filename);
333 } else {
334 String key = parts[0];
335 String smarts = parts[1];
336 if (mapOfSMARTS.keySet().contains(key))
337 {
338 throw new DENOPTIMException("Duplicate definition of "
339 + "rotatabe bond named '" + key + "'. "
340 + "Check line '"+ line +"' in file "
341 + filename);
342 }
343
344 //Everything is OK, thus store this definition
345 mapOfSMARTS.put(key,smarts);
346 }
347 }
348 }
349 catch (IOException nfe)
350 {
351 throw new DENOPTIMException(nfe);
352 }
353 finally
354 {
355 try
356 {
357 if (br != null)
358 {
359 br.close();
360 }
361 }
362 catch (IOException ioe)
363 {
364 throw new DENOPTIMException(ioe);
365 }
366 }
367
368 return mapOfSMARTS;
369 }
370
371//------------------------------------------------------------------------------
372
388 IAtomContainer mol, ArrayList<ObjectPair> rotatableBonds,
389 String defRotBndContrFile, Logger logger) throws DENOPTIMException
390 {
391 // We'll use SMARTS so get rid of pseudoatoms that can create problems
392 // We'll use this modified IAtomContainer only when dealing with SMARTS
393 IAtomContainer locMol = builder.newAtomContainer();
394 try {
395 locMol = mol.clone();
396 for (int iAtm=0; iAtm<locMol.getAtomCount(); iAtm++)
397 {
398 locMol.getAtom(iAtm).setProperty(PROPERTY_RSU_ATMID, iAtm);
399 }
400 } catch (Throwable t) {
401 throw new DENOPTIMException(t);
402 }
403 MoleculeUtils.removeRCA(locMol);
404
405 if (defRotBndContrFile.equals(""))
406 {
407 // Nothing to be done
408 return;
409 }
410
411 // Get definition of constrained rotatable bonds as list of SMARTS queries
412 TreeMap<String,RotBndConstraint> mapOfConstraints = getRotationalConstraintsDefinition(
413 defRotBndContrFile);
414
415 TreeMap<String,String> listQueries = new TreeMap<String,String>();
416 for (Map.Entry<String,RotBndConstraint> e : mapOfConstraints.entrySet())
417 {
418 listQueries.put(e.getKey(),e.getValue().getSmarts());
419 }
420
421 // Get bonds matching one of the definitions of rotatable bonds
422 ManySMARTSQuery msq = new ManySMARTSQuery(locMol,listQueries);
423 if (msq.hasProblems())
424 {
425 String msg = "ERROR! Attempt to match constrained rotatable bonds returned "
426 + "an error!";
427 throw new Error(msg);
428 } else {
429 //Transform list of indeces
430 for (Map.Entry<String,String> e : listQueries.entrySet())
431 {
432 String name = e.getKey();
433
434 //Skip if no match
435 if (msq.getNumMatchesOfQuery(name) == 0)
436 {
437 continue;
438 }
439
440 //Put all matches in one list
441 Mappings matches = msq.getMatchesOfSMARTS(name);
442 for (int[] singleMatch : matches)
443 {
444 //Check assumption on number of atoms involved in each bond
445 if (singleMatch.length != 4)
446 {
447 throw new Error("Expecting a constrain to be defined by 4 atoms. "
448 + "Check this match of constraint '" + name + "': "
449 + singleMatch);
450 }
451
452 //NOTE the index refers to the IAtomContainer locMol that is a
453 //clone of mol but has no RCAs, so we take the atm index in the
454 // original mol from the atm property
455
456 int idAtmA = (int) locMol.getAtom(singleMatch[0]).getProperty(PROPERTY_RSU_ATMID);
457 int idAtmB = (int) locMol.getAtom(singleMatch[1]).getProperty(PROPERTY_RSU_ATMID);
458 int idAtmC = (int) locMol.getAtom(singleMatch[2]).getProperty(PROPERTY_RSU_ATMID);
459 int idAtmD = (int) locMol.getAtom(singleMatch[3]).getProperty(PROPERTY_RSU_ATMID);
460
461
462 // Compare with bonds already in the list
463 ObjectPair opToRemove = null;
464 for (ObjectPair op : rotatableBonds)
465 {
466 // this check also ensures that one and only one constraint is applied to any matched bond
467 int a1 = ((Integer)op.getFirst()).intValue();
468 int a2 = ((Integer)op.getSecond()).intValue();
469 if (((a1 == idAtmB) && (a2 == idAtmC)) ||
470 ((a2 == idAtmB) && (a1 == idAtmC)))
471 {
472 opToRemove = op;
473 break;
474 }
475 }
476 if (opToRemove!=null)
477 {
478 rotatableBonds.remove(opToRemove);
479 IBond bnd = mol.getBond(mol.getAtom(idAtmB), mol.getAtom(idAtmC));
480 bnd.setProperty(PROPERTY_ROTDBDCSTR_DEF, new IAtom[]{
481 mol.getAtom(idAtmA), mol.getAtom(idAtmB),
482 mol.getAtom(idAtmC), mol.getAtom(idAtmD)});
483 bnd.setProperty(PROPERTY_ROTDBDCSTR_VALUE,
484 mapOfConstraints.get(name).getValue());
485 bnd.setProperty(DENOPTIMConstants.BONDPROPROTATABLE, "false");
486 logger.log(Level.INFO, "Constraining dihedral along bond "
487 + MoleculeUtils.getAtomRef(mol.getAtom(idAtmB), mol)
488 + "-"
489 + MoleculeUtils.getAtomRef(mol.getAtom(idAtmC), mol)
490 + " (rotatable bond constrain: '" + name + "')");
491 }
492 }
493 }
494 }
495 }
496
497//------------------------------------------------------------------------------
498
505 public static TreeMap<String,RotBndConstraint> getRotationalConstraintsDefinition(
506 String filename) throws DENOPTIMException
507 {
508 TreeMap<String,RotBndConstraint> mapOfConstraints = new TreeMap<String,RotBndConstraint>();
509
510 //Read the file
511 if (filename.equals(null))
512 {
513 DENOPTIMException ex = new DENOPTIMException("Pointer to file is "
514 + " null! Cannot read definition of rotational constraints.");
515 throw ex;
516 }
517 File f = new File(filename);
518 if (!f.exists())
519 {
520 DENOPTIMException ex = new DENOPTIMException("File '" + filename
521 + "' does not exist! Cannot find definition of rotational "
522 + "constraints.");
523 throw ex;
524 }
525 BufferedReader br = null;
526 String line;
527 try
528 {
529 br = new BufferedReader(new FileReader(filename));
530 while ((line = br.readLine()) != null)
531 {
532 if (line.trim().length() == 0)
533 continue;
534
535 if (line.trim().startsWith("#"))
536 continue;
537
538 String[] parts = line.split("\\s+");
539 // Expectedf ormat: name smarts value
540 if (parts.length != 3)
541 {
542 throw new DENOPTIMException("Unable to understand "
543 + "rotational constraints definition. "
544 + "Check line '"+ line +"' in file "
545 + filename);
546 } else {
547 String key = parts[0];
548 String smarts = parts[1];
549 double value = Double.parseDouble(parts[2]);
550 if (mapOfConstraints.keySet().contains(key))
551 {
552 throw new DENOPTIMException("Duplicate definition of "
553 + "rotational contraint named '" + key + "'. "
554 + "Check line '"+ line +"' in file "
555 + filename);
556 }
557
558 //Everything is OK, thus store this definition
559 mapOfConstraints.put(key, new RotBndConstraint(key,smarts,value));
560 }
561 }
562 }
563 catch (IOException nfe)
564 {
565 throw new DENOPTIMException(nfe);
566 }
567 finally
568 {
569 try
570 {
571 if (br != null)
572 {
573 br.close();
574 }
575 }
576 catch (IOException ioe)
577 {
578 throw new DENOPTIMException(ioe);
579 }
580 }
581 return mapOfConstraints;
582 }
583
584//------------------------------------------------------------------------------
585}
General set of constants used in DENOPTIM.
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 String BONDPROPROTATABLE
String tag of Bond's property used to store the property of being rotatable.
Container of lists of atoms matching a list of SMARTS.
Mappings getMatchesOfSMARTS(String ref)
int getNumMatchesOfQuery(String query)
Utilities for molecule conversion.
static void removeRCA(IAtomContainer mol)
Replace any PseudoAtoms representing ring closing attractors with H.
static String getAtomRef(IAtom atm, IAtomContainer mol)
This class is the equivalent of the Pair data structure used in C++ Although AbstractMap....
Definition: ObjectPair.java:30
The definition of a constraint applying to rotatable bonds.
Tool box for definition and management of the rotational space, which is given by the list of rotatab...
static final IChemObjectBuilder builder
static TreeMap< String, RotBndConstraint > getRotationalConstraintsDefinition(String filename)
Reads the definition of constraints on the rotatable bonds.
static ArrayList< ObjectPair > getInterVertexBonds(IAtomContainer mol)
Search for all bonds connecting atoms corresponding to two different vertices in the DENOPTIMGraph.
static void processConstrainedRotatableBonds(IAtomContainer mol, ArrayList< ObjectPair > rotatableBonds, String defRotBndContrFile, Logger logger)
Process the constrained rotatable bonds.
static Map< String, String > getRotationalSpaceDefinition(String filename)
Read a formatted file and return a map with all the SMARTS queries identifying rotatable bonds.
static ArrayList< ObjectPair > defineRotatableBonds(IAtomContainer mol, String defRotBndsFile, boolean addIterfragBonds, boolean excludeRings, Logger logger)
Define the rotational space (a.k.a.