$darkmode
DENOPTIM
CuttingRule.java
Go to the documentation of this file.
1package denoptim.programs.fragmenter;
2
3import java.util.HashMap;
4import java.util.List;
5import java.util.Map;
6import java.util.logging.Level;
7import java.util.logging.Logger;
8
9import org.openscience.cdk.interfaces.IAtom;
10import org.openscience.cdk.interfaces.IAtomContainer;
11import org.openscience.cdk.isomorphism.Mappings;
12
13import denoptim.constants.DENOPTIMConstants;
14import denoptim.exception.DENOPTIMException;
15import denoptim.graph.APClass;
16import denoptim.utils.ManySMARTSQuery;
17import denoptim.utils.MoleculeUtils;
18
31public class CuttingRule
32{
37 private String ruleName;
38
42 private APClass apc0;
43
47 private APClass apc1;
48
52 private String smartsAtm0;
53
57 private String smartsAtm1;
58
62 private String smartsBnd;
63
68 private int priority;
69
73 private List<String> opts;
74
75
76//------------------------------------------------------------------------------
77
88 public CuttingRule(String ruleName, String smartsAtm0, String smartsAtm1,
89 String smartsBnd, int priority, List<String> opts)
90 {
91 this.ruleName = ruleName;
92 if (ruleName.isEmpty() || ruleName.isBlank())
93 throw new IllegalArgumentException("ruleName cannot be '"
94 + ruleName + "'");
95 if (smartsAtm0.isEmpty() || smartsAtm0.isBlank())
96 throw new IllegalArgumentException("SMARTS for atom cannot be '"
97 + smartsAtm0 + "'");
98 if (smartsAtm1.isEmpty() || smartsAtm1.isBlank())
99 throw new IllegalArgumentException("SMARTS for atom cannot be '"
100 + smartsAtm1 + "'");
101 if (smartsBnd.isEmpty() || smartsBnd.isBlank())
102 throw new IllegalArgumentException("SMARTS for bond cannot be '"
103 + smartsBnd + "'");
104 this.smartsAtm0 = smartsAtm0;
105 this.smartsAtm1 = smartsAtm1;
106 this.smartsBnd = smartsBnd;
107 this.priority = priority;
108 this.opts = opts;
109 try
110 {
112 + "0");
114 + "1");
115 } catch (DENOPTIMException e)
116 {
117 //cannot happen
118 }
119 }
120
121//------------------------------------------------------------------------------
122
126 public String getName()
127 {
128 return ruleName;
129 }
130
131
132//------------------------------------------------------------------------------
133
137 public int getPriority()
138 {
139 return priority;
140 }
141
142//------------------------------------------------------------------------------
143
148 {
149 return apc0;
150 }
151
152//------------------------------------------------------------------------------
153
158 {
159 if (this.isSymmetric())
160 return apc0;
161 else
162 return apc1;
163 }
164
165//------------------------------------------------------------------------------
166
171 {
172 if (this.isSymmetric() && apc.equals(apc0))
173 {
174 return apc0;
175 } else if (!this.isSymmetric() && apc.equals(apc0)) {
176 return apc1;
177 } else if (!this.isSymmetric() && apc.equals(apc1)) {
178 return apc0;
179 } else {
180 return null;
181 }
182 }
183
184//------------------------------------------------------------------------------
185
189 public String getWholeSMARTSRule()
190 {
192 }
193
194//------------------------------------------------------------------------------
195
199 public String getSMARTSAtom0()
200 {
201 return smartsAtm0;
202 }
203
204//------------------------------------------------------------------------------
205
209 public String getSMARTSAtom1()
210 {
211 return smartsAtm1;
212 }
213
214//------------------------------------------------------------------------------
215
219 public String getSMARTSBnd()
220 {
221 return smartsBnd;
222 }
223
224//------------------------------------------------------------------------------
225
229 public boolean hasOptions()
230 {
231 if (opts.size() > 0)
232 return true;
233 else
234 return false;
235 }
236
237//------------------------------------------------------------------------------
238
242 public boolean isHAPTO()
243 {
244 if (opts.contains("HAPTO"))
245 return true;
246 else
247 return false;
248 }
249
250//------------------------------------------------------------------------------
251
255 public boolean isSymmetric()
256 {
257 if (smartsAtm0.equals(smartsAtm1))
258 return true;
259 else
260 return false;
261 }
262
263//------------------------------------------------------------------------------
264
268 public List<String> getOptions()
269 {
270 return opts;
271 }
272
273//------------------------------------------------------------------------------
274
279 public int getBondOrder()
280 {
281 int res = -1;
282
283 //Easy case
284 String s = smartsBnd;
285 if (s.contains("!@"))
286 {
287 s = s.substring(0,s.indexOf("!@"));
288 }
289 if (s.equals("-"))
290 res = 1;
291 else if (s.equals("="))
292 res = 2;
293 else if (s.equals("#"))
294 res = 3;
295 return res;
296
297 // well, in general it's not so easy...
298 //TODO make it more general.
299 }
300
301//------------------------------------------------------------------------------
302
306 public boolean involvesMetal()
307 {
308 for (String el : DENOPTIMConstants.ALL_METALS)
309 {
310 if (smartsAtm0.contains(el))
311 {
312 return true;
313 }
314 }
315
316 //analyze atom 1
317 for (String el : DENOPTIMConstants.ALL_METALS)
318 {
319 if (smartsAtm1.contains(el))
320 {
321 return true;
322 }
323 }
324 return false;
325 }
326
327//------------------------------------------------------------------------------
328
332 public String toString()
333 {
334 String str = ruleName+"_"+smartsAtm0+smartsBnd+smartsAtm1+
335 "_priority:"+priority+
336 "_opts:"+opts;
337 return str;
338 }
339
340//------------------------------------------------------------------------------
341
342 @Override
343 public boolean equals(Object o)
344 {
345 if (!(o instanceof CuttingRule))
346 return false;
347
348 CuttingRule other = (CuttingRule) o;
349 if (!this.getName().equals(other.getName()))
350 return false;
351 if (!this.getSMARTSAtom0().equals(other.getSMARTSAtom0()))
352 return false;
353 if (!this.getSMARTSAtom1().equals(other.getSMARTSAtom1()))
354 return false;
355 if (!this.getSMARTSBnd().equals(other.getSMARTSBnd()))
356 return false;
357 if (this.getPriority() != other.getPriority())
358 return false;
359 if (this.getOptions().size() != other.getOptions().size())
360 return false;
361 for (int i=0; i<this.getOptions().size(); i++)
362 {
363 if (!this.getOptions().get(i).equals(other.getOptions().get(i)))
364 return false;
365 }
366 return true;
367 }
368
369//------------------------------------------------------------------------------
370
378 public Boolean satisfiesOptions(MatchedBond matchedBond, Logger logger)
379 {
380 IAtom atmS = matchedBond.getAtmSubClass0();
381 IAtom atmT = matchedBond.getAtmSubClass1();
382 IAtomContainer mol = atmS.getContainer();
383 int idxSInMol = mol.indexOf(atmS);
384 int idxTInMol = mol.indexOf(atmT);
385
386 boolean hasHapto = false;
387 boolean checkRings = false;
388 int minSzRing = -1;
389 boolean checkOMRings = false;
390 int minSzOMRing = -1;
391 for (String opt : opts)
392 {
393 if (opt.startsWith("HAPTO"))
394 hasHapto = true;
395
396 if (opt.startsWith("RING>"))
397 {
398 checkRings = true;
399 minSzRing = Integer.parseInt(opt.replace("RING>","").trim());
400
401 } else if (opt.startsWith("OMRING"))
402 {
403 checkOMRings = true;
404 minSzOMRing = Integer.parseInt(opt.replace("OMRING>","").trim());
405 }
406 }
407
408 // from here it is all about OM/Ring, and these are both incompatible
409 // with HAPTO. So if we have HAPTO we should not test OM/Ring-options
410 if (hasHapto)
411 return true;
412
413 // Build SMARTS queries for rings as large as needed to test criteria
414 Map<String,String> allSmarts = new HashMap<String,String>();
415 StringBuilder smartsBuilder = new StringBuilder();
416 // "ring size" i = 1
417 smartsBuilder.append(this.getSMARTSAtom0());
418 smartsBuilder.append("1");
419 smartsBuilder.append(this.getSMARTSBnd());
420 // "ring size" i=2
421 smartsBuilder.append(this.getSMARTSAtom1());
422 smartsBuilder.append("~");
423 // "ring size" from 3 and on
424 for (int i=3; i<Math.max(minSzRing, minSzOMRing)+1; i++)
425 {
426 smartsBuilder.append("[*]~");
427 allSmarts.put("ring"+i, smartsBuilder.toString()+"1");
428 }
429
430 // Find all rings matching the queries
431 ManySMARTSQuery msq = new ManySMARTSQuery(mol, allSmarts);
432 if (msq.hasProblems())
433 {
434 if (logger!=null)
435 {
436 logger.log(Level.WARNING, "Problem matching SMARTS for OM/RING "
437 + "options. Ignoring bond for which we cannot check if "
438 + "we satisfy OM/RING options. " + msq.getMessage());
439 }
440 return false;
441 }
442
443 for (int ringSize=3; ringSize<Math.max(minSzRing, minSzOMRing)+1; ringSize++)
444 {
445 String smartsName = "ring"+ringSize;
446 if (msq.getNumMatchesOfQuery(smartsName) == 0)
447 {
448 continue;
449 }
450
451 // Get atoms matching cutting rule queries
452 Mappings atomsInAllRings = msq.getMatchesOfSMARTS(smartsName);
453 for (int[] atmsInOneRing : atomsInAllRings)
454 {
455 if (atmsInOneRing[0]==idxSInMol && atmsInOneRing[1]==idxTInMol)
456 {
457 boolean isOMRing = false;
458 for (int j=0; j<atmsInOneRing.length; j++)
459 {
460 IAtom atmInRing = mol.getAtom(atmsInOneRing[j]);
461
462 if (MoleculeUtils.isElement(atmInRing)
463 && DENOPTIMConstants.ALL_METALS.contains(
464 atmInRing.getSymbol()))
465 {
466 isOMRing = true;
467 break;
468 }
469 }
470 if (!isOMRing && checkRings && ringSize<=minSzRing)
471 {
472
473 logger.log(Level.FINEST,"Bond between " + idxSInMol
474 + " and " + idxTInMol + " matches SMARTS of "
475 + "cutting rule '" + this.ruleName
476 + "', but does not satisfy "
477 + "RING>" + minSzRing + " as it is part of "
478 + "a " + ringSize + "-member organic-only ring.");
479 return false;
480 }
481 if (isOMRing && checkOMRings && ringSize<=minSzOMRing)
482 {
483 logger.log(Level.FINEST,"Bond between " + idxSInMol
484 + " and " + idxTInMol + " matches SMARTS of "
485 + "cutting rule '" + this.ruleName
486 + "', but does not satisfy "
487 + "OMRING>" + minSzOMRing + " as it is part of "
488 + "a " + ringSize + "-member ring including a "
489 + "metal.");
490 return false;
491 }
492 }
493 }
494 }
495 return true;
496 }
497
498//------------------------------------------------------------------------------
499
500}
General set of constants used in DENOPTIM.
static ArrayList< String > ALL_METALS
Elemental symbols of all metal elements, including alkaly, transition metals, actinides,...
static final String SEPARATORAPPROPSCL
Separator between APClass and APSubClass and coordinates.
boolean equals(Object o)
Definition: APClass.java:488
static APClass make(String ruleAndSubclass)
Creates an APClass if it does not exist already, or returns the reference to the existing instance.
Definition: APClass.java:136
A cutting rule with three SMARTS queries (atom 1, bond, atom2) and options.
APClass getAPClass0()
Get the AP class with sub class 0.
APClass apc1
Second APClass derived from this rule.
String getName()
Returns the name of the cutting rule.
String smartsAtm1
SMARTS query matching the second atom.
String smartsBnd
SMARTS query matching the bond between first and second atom.
Boolean satisfiesOptions(MatchedBond matchedBond, Logger logger)
Checks if a given bond satisfies the additional options of this rule beyond the matching of the SMART...
int getBondOrder()
Tries to identify the bond order of the matched bond by searching for the corresponding,...
APClass getComplementaryAPClass(APClass apc)
Get complementary class.
String getSMARTSAtom1()
Get the SMARTS query of the second atom (SubClass 1)
List< String > getOptions()
Returns the list of options.
String smartsAtm0
SMARTS query matching the first atom.
CuttingRule(String ruleName, String smartsAtm0, String smartsAtm1, String smartsBnd, int priority, List< String > opts)
Constructor for a cutting rule.
APClass getAPClass1()
Get the AP class with sub class 1.
String getSMARTSAtom0()
Get the SMARTS query of the first atom (SubClass 0)
String toString()
Returns the string representing this rule.
String getWholeSMARTSRule()
Returns the SMARTS query of the whole rule.
int priority
Priority index of this rule.
String getSMARTSBnd()
Get the SMARTS query of the bond.
List< String > opts
Additional Options.
APClass apc0
First APClass derived from this rule.
IAtom getAtmSubClass1()
Returns the atom matching subclass '1'.
IAtom getAtmSubClass0()
Returns the atom matching subclass '0'.
Container of lists of atoms matching a list of SMARTS.
Mappings getMatchesOfSMARTS(String ref)
int getNumMatchesOfQuery(String query)
Utilities for molecule conversion.
static boolean isElement(IAtom atom)
Check element symbol corresponds to real element of Periodic Table.