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