rdworks 0.36.3__tar.gz → 0.37.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. {rdworks-0.36.3 → rdworks-0.37.1}/PKG-INFO +1 -1
  2. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/__init__.py +1 -1
  3. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/conf.py +133 -7
  4. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/mol.py +36 -94
  5. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/std.py +2 -0
  6. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/torsion.py +6 -1
  7. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks.egg-info/PKG-INFO +1 -1
  8. {rdworks-0.36.3 → rdworks-0.37.1}/tests/test_basics.py +14 -3
  9. {rdworks-0.36.3 → rdworks-0.37.1}/LICENSE +0 -0
  10. {rdworks-0.36.3 → rdworks-0.37.1}/README.md +0 -0
  11. {rdworks-0.36.3 → rdworks-0.37.1}/pyproject.toml +0 -0
  12. {rdworks-0.36.3 → rdworks-0.37.1}/setup.cfg +0 -0
  13. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/autograph/__init__.py +0 -0
  14. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/autograph/autograph.py +0 -0
  15. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/autograph/centroid.py +0 -0
  16. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/autograph/dynamictreecut.py +0 -0
  17. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/autograph/nmrclust.py +0 -0
  18. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/autograph/rckmeans.py +0 -0
  19. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/bitqt/__init__.py +0 -0
  20. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/bitqt/bitqt.py +0 -0
  21. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/descriptor.py +0 -0
  22. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/display.py +0 -0
  23. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/ionized.py +0 -0
  24. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/matchedseries.py +0 -0
  25. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/mollibr.py +0 -0
  26. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/pka.py +0 -0
  27. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/Asinex_fragment.xml +0 -0
  28. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/Astex_RO3.xml +0 -0
  29. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/Baell2010_PAINS/Baell2010A.xml +0 -0
  30. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/Baell2010_PAINS/Baell2010B.xml +0 -0
  31. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/Baell2010_PAINS/Baell2010C.xml +0 -0
  32. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/Baell2010_PAINS/PAINS-less-than-015-hits.xml +0 -0
  33. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/Baell2010_PAINS/PAINS-less-than-150-hits.xml +0 -0
  34. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/Baell2010_PAINS/PAINS-more-than-150-hits.xml +0 -0
  35. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/Baell2010_PAINS/makexml.py +0 -0
  36. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/Brenk2008_Dundee/makexml.py +0 -0
  37. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/CNS.xml +0 -0
  38. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/ChEMBL_Walters/BMS.xml +0 -0
  39. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/ChEMBL_Walters/Dundee.xml +0 -0
  40. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/ChEMBL_Walters/Glaxo.xml +0 -0
  41. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/ChEMBL_Walters/Inpharmatica.xml +0 -0
  42. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/ChEMBL_Walters/LINT.xml +0 -0
  43. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/ChEMBL_Walters/MLSMR.xml +0 -0
  44. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/ChEMBL_Walters/PAINS.xml +0 -0
  45. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/ChEMBL_Walters/SureChEMBL.xml +0 -0
  46. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/ChEMBL_Walters/makexml.py +0 -0
  47. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/Hann1999_Glaxo/Hann1999.xml +0 -0
  48. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/Hann1999_Glaxo/Hann1999Acid.xml +0 -0
  49. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/Hann1999_Glaxo/Hann1999Base.xml +0 -0
  50. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/Hann1999_Glaxo/Hann1999ElPh.xml +0 -0
  51. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/Hann1999_Glaxo/Hann1999NuPh.xml +0 -0
  52. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/Hann1999_Glaxo/makexml.py +0 -0
  53. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/Kazius2005/Kazius2005.xml +0 -0
  54. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/Kazius2005/makexml.py +0 -0
  55. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/ZINC_druglike.xml +0 -0
  56. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/ZINC_fragment.xml +0 -0
  57. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/ZINC_leadlike.xml +0 -0
  58. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/fragment.xml +0 -0
  59. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/ionized/simple_smarts_pattern.csv +0 -0
  60. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/ionized/smarts_pattern.csv +0 -0
  61. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/misc/makexml.py +0 -0
  62. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/misc/reactive-part-2.xml +0 -0
  63. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/misc/reactive-part-3.xml +0 -0
  64. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/misc/reactive.xml +0 -0
  65. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/readin.py +0 -0
  66. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/rgroup.py +0 -0
  67. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/scaffold.py +0 -0
  68. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/stereoisomers.py +0 -0
  69. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/tautomers.py +0 -0
  70. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/units.py +0 -0
  71. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/utils.py +0 -0
  72. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/xml.py +0 -0
  73. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/xtb/__init__.py +0 -0
  74. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/xtb/wrapper.py +0 -0
  75. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks.egg-info/SOURCES.txt +0 -0
  76. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks.egg-info/dependency_links.txt +0 -0
  77. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks.egg-info/requires.txt +0 -0
  78. {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks.egg-info/top_level.txt +0 -0
  79. {rdworks-0.36.3 → rdworks-0.37.1}/tests/test_decimals.py +0 -0
  80. {rdworks-0.36.3 → rdworks-0.37.1}/tests/test_gypsumdl.py +0 -0
  81. {rdworks-0.36.3 → rdworks-0.37.1}/tests/test_iupac_name.py +0 -0
  82. {rdworks-0.36.3 → rdworks-0.37.1}/tests/test_nn_xtb.py +0 -0
  83. {rdworks-0.36.3 → rdworks-0.37.1}/tests/test_web.py +0 -0
  84. {rdworks-0.36.3 → rdworks-0.37.1}/tests/test_xtb_wrapper.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rdworks
3
- Version: 0.36.3
3
+ Version: 0.37.1
4
4
  Summary: Frequently used tasks built on RDKit and other tools
5
5
  Author-email: Sung-Hun Bae <sunghun.bae@gmail.com>
6
6
  Maintainer-email: Sung-Hun Bae <sunghun.bae@gmail.com>
@@ -1,4 +1,4 @@
1
- __version__ = '0.36.3'
1
+ __version__ = '0.37.1'
2
2
 
3
3
  from rdworks.conf import Conf
4
4
  from rdworks.mol import Mol
@@ -3,6 +3,7 @@ import copy
3
3
  import json
4
4
  import numpy as np
5
5
  import ase
6
+ import itertools
6
7
 
7
8
  from collections.abc import Callable
8
9
  from typing import Self
@@ -15,6 +16,8 @@ from rdkit.Chem import rdMolTransforms, AllChem, rdMolAlign, rdmolops
15
16
  from rdkit.Chem.Draw import rdMolDraw2D
16
17
  from PIL import Image
17
18
 
19
+ from rdworks.std import clean_2d
20
+ from rdworks.torsion import get_torsion_atoms, create_torsion_fragment
18
21
  from rdworks.units import ev2kcalpermol, pm2angstrom
19
22
  from rdworks.utils import recursive_round
20
23
  from rdworks.xtb.wrapper import GFN2xTB
@@ -387,7 +390,124 @@ class Conf:
387
390
  raise RuntimeError("ASE calculator error")
388
391
 
389
392
 
390
- def torsion_angle(self, i:int, j:int, k:int, l:int) -> float:
393
+ def torsion_atoms(self, strict: bool = True) -> dict[int, tuple]:
394
+ """Determine torsion/dihedral angle atoms (i-j-k-l) and rotating group for each rotatable bond (j-k).
395
+
396
+ Args:
397
+ strict (bool): whether to exclude amide/imide/ester/acid bonds.
398
+
399
+ Returns:
400
+ {torsion_key: (i, j, k, l), ...,}
401
+ """
402
+ return {i: d[:4] for i, d in enumerate(get_torsion_atoms(self.rdmol, strict))}
403
+
404
+
405
+ def torsion_energies(self,
406
+ calculator: str | Callable,
407
+ torsion_key: int | None = None,
408
+ simplify: bool = True,
409
+ fmax: float = 0.05,
410
+ interval: float = 20.0,
411
+ use_converged_only: bool = True,
412
+ **kwargs,
413
+ ) -> Self:
414
+ """Calculates potential energy profiles for each torsion angle using ASE optimizer.
415
+
416
+ It uses the first conformer as a reference.
417
+
418
+ Args:
419
+ calculator (str | Callable): 'MMFF', 'UFF', or ASE calculator.
420
+ torsion_key (int | None): torsion index to calculate. Defaults to None (all).
421
+ simplify (bool, optional): whether to use fragment surrogate. Defaults to True.
422
+ fmax (float, optional): fmax of ASE optimizer. Defaults to 0.05.
423
+ interval (float, optional): interval of torsion angles in degree. Defaults to 15.0.
424
+ use_converged_only (bool, optional): whether to use only converged data. Defaults to True.
425
+
426
+ Returns:
427
+ Self: modified self.
428
+ """
429
+
430
+ if torsion_key is None:
431
+ torsion_atoms_indices = self.torsion_atoms()
432
+ # {0: (5, 4, 3, 1)}
433
+ else:
434
+ torsion_atoms_indices = {torsion_key: self.torsion_atoms()[torsion_key]}
435
+
436
+ ref_conf = self.copy()
437
+
438
+ data = {}
439
+
440
+ if simplify:
441
+ for tk, indices in torsion_atoms_indices.items():
442
+ frag, frag_ijkl = create_torsion_fragment(ref_conf.rdmol, indices)
443
+ frag_conf = Conf(frag)
444
+ data[tk] = {'indices': indices, 'angle':[], 'init':[], 'last':[], 'Converged':[]}
445
+ for angle in np.arange(-180.0, 180.0, interval):
446
+ # Iterated numpy.ndarray does not contain the last 180: -180., ..., (180).
447
+ conf = frag_conf.copy()
448
+ conf.props.update({'torsion_key': tk, 'angle': float(angle)})
449
+ conf.set_torsion(*frag_ijkl, angle) # atoms bonded to `l` move.
450
+ conf = conf.optimize(calculator, fmax, **kwargs)
451
+ # conf.optimize() updates coordinates and conf.props:
452
+ # `angle`, `E_tot_init(kcal/mol)`, `E_tot(kcal/mol)`, `Converged`.
453
+ tk = conf.props['torsion_key']
454
+ data[tk]['angle'].append(conf.props['angle'])
455
+ data[tk]['init'].append(conf.props['E_tot_init(kcal/mol)'])
456
+ data[tk]['last'].append(conf.props['E_tot(kcal/mol)'])
457
+ data[tk]['Converged'].append(conf.props['Converged'])
458
+ frag_cleaned, _ = clean_2d(frag, reset_isotope=True, remove_H=True)
459
+ # to serialize the molecule
460
+ data[tk]['frag'] = Chem.MolToMolBlock(frag_cleaned)
461
+ data[tk]['frag_indices'] = frag_ijkl
462
+
463
+ else:
464
+ # mol.confs will be populated with torsion conformers.
465
+ # It is designed for a batch optimization in the future.
466
+ torsion_angle_confs = []
467
+ for tk, indices in torsion_atoms_indices.items():
468
+ data[tk] = {'indices': indices, 'angle':[], 'init':[], 'last':[], 'Converged':[]}
469
+ for angle in np.arange(-180.0, 180.0, interval):
470
+ # Iterated numpy.ndarray does not contain the last 180: -180., ..., (180).
471
+ x = ref_conf.copy()
472
+ x.props.update({'torsion_key': tk, 'angle': float(angle)})
473
+ x.set_torsion(*indices, angle) # atoms bonded to `l` move.
474
+ torsion_angle_confs.append(x)
475
+
476
+ # Calculate relaxation energies
477
+ for conf in torsion_angle_confs:
478
+ conf = conf.optimize(calculator, fmax, **kwargs)
479
+ # conf.optimize() updates coordinates and conf.props:
480
+ # `angle`, `E_tot_init(kcal/mol)`, `E_tot(kcal/mol)`, `Converged`.
481
+ tk = conf.props['torsion_key']
482
+ data[tk]['angle'].append(conf.props['angle'])
483
+ data[tk]['init'].append(conf.props['E_tot_init(kcal/mol)'])
484
+ data[tk]['last'].append(conf.props['E_tot(kcal/mol)'])
485
+ data[tk]['Converged'].append(conf.props['Converged'])
486
+
487
+ # Post-processing
488
+ torsion_energy_profiles = {}
489
+ for tk, dictdata in data.items():
490
+ if use_converged_only:
491
+ dictdata['angle'] = list(itertools.compress(dictdata['angle'], dictdata['Converged']))
492
+ dictdata['init'] = list(itertools.compress(dictdata['init'], dictdata['Converged']))
493
+ dictdata['last'] = list(itertools.compress(dictdata['last'], dictdata['Converged']))
494
+ relax = np.array(dictdata['init']) - np.median(dictdata['last'])
495
+ E_rel = relax - np.min(relax)
496
+ torsion_energy_profiles[tk] = {
497
+ 'indices' : dictdata['indices'],
498
+ 'angle' : np.round(np.array(dictdata['angle']), 1).tolist(), # np.ndarray -> list for serialization
499
+ 'E_rel(kcal/mol)': np.round(E_rel, 2).tolist(), # np.ndarray -> list for serialization
500
+ 'frag' : dictdata.get('frag', None),
501
+ 'frag_indices' : dictdata.get('frag_indices', None),
502
+ }
503
+
504
+ self.props['torsion'] = torsion_energy_profiles
505
+ self.props['torsion_calculator'] = str(calculator)
506
+
507
+ return self
508
+
509
+
510
+ def torsion_angle(self, i: int, j: int, k: int, l: int) -> float:
391
511
  """Get dihedral angle (i-j-k-l) in degrees.
392
512
 
393
513
  Args:
@@ -419,8 +539,10 @@ class Conf:
419
539
  return json.dumps(self.props)
420
540
 
421
541
 
422
- def serialize(self, decimals:int=3) -> str:
423
- """Serialize information necessary to rebuild.
542
+ def serialize(self, decimals: int = 3) -> str:
543
+ """Serialize information necessary to rebuild a Conf object.
544
+ Args:
545
+ decimals (int, optional): number of decimal places for float data type. Defaults to 3.
424
546
 
425
547
  Returns:
426
548
  str: serialized string for json.loads()
@@ -435,14 +557,18 @@ class Conf:
435
557
  return serialized
436
558
 
437
559
 
438
- def deserialize(self, serialized:str) -> Self:
439
- """De-serialize information and rebuild.
560
+ def deserialize(self, serialized: str) -> Self:
561
+ """De-serialize information and rebuild a Conf object.
562
+
563
+ Example:
564
+ serialized = conf1.serialize()
565
+ conf2 = Conf().deserialize(serialized)
440
566
 
441
567
  Args:
442
- serialized (str): _description_
568
+ serialized (str): serialized string.
443
569
 
444
570
  Returns:
445
- Self: _description_
571
+ Self: modified self.
446
572
  """
447
573
  data = json.loads(serialized)
448
574
 
@@ -942,18 +942,6 @@ class Mol:
942
942
  return [atom.GetAtomicNum() for atom in self.rdmol.GetAtoms()]
943
943
 
944
944
 
945
- def torsion_atoms(self, strict: bool = True) -> dict[int, tuple]:
946
- """Determine torsion/dihedral angle atoms (i-j-k-l) and rotating group for each rotatable bond (j-k).
947
-
948
- Args:
949
- strict (bool): whether to exclude amide/imide/ester/acid bonds.
950
-
951
- Returns:
952
- {torsion_key: (i, j, k, l), ...,}
953
- """
954
- return {i: d[:4] for i, d in enumerate(get_torsion_atoms(self.rdmol, strict))}
955
-
956
-
957
945
  def compute(self, **kwargs) -> Self:
958
946
  """Change settings for parallel computing.
959
947
 
@@ -972,6 +960,18 @@ class Mol:
972
960
  return self
973
961
 
974
962
 
963
+ def torsion_atoms(self, strict: bool = True) -> dict[int, tuple]:
964
+ """Determine torsion/dihedral angle atoms (i-j-k-l) and rotating group for each rotatable bond (j-k).
965
+
966
+ Args:
967
+ strict (bool): whether to exclude amide/imide/ester/acid bonds.
968
+
969
+ Returns:
970
+ {torsion_key: (i, j, k, l), ...,}
971
+ """
972
+ return {i: d[:4] for i, d in enumerate(get_torsion_atoms(self.rdmol, strict))}
973
+
974
+
975
975
  def torsion_energies(self,
976
976
  calculator: str | Callable,
977
977
  torsion_key: int | None = None,
@@ -997,86 +997,16 @@ class Mol:
997
997
  Self: modified self.
998
998
  """
999
999
  assert self.count() > 0, "torsion_energies() requires at least one conformer"
1000
-
1001
- self = self.compute(**kwargs)
1002
-
1003
- if torsion_key is None:
1004
- torsion_atoms_indices = self.torsion_atoms()
1005
- else:
1006
- torsion_atoms_indices = {torsion_key: self.torsion_atoms()[torsion_key]}
1007
-
1008
1000
  ref_conf = self.confs[0].copy()
1009
-
1010
- data = {}
1011
-
1012
- if simplify:
1013
- for tk, indices in torsion_atoms_indices.items():
1014
- frag, frag_ijkl = create_torsion_fragment(ref_conf.rdmol, indices)
1015
- frag_conf = Conf(frag)
1016
- data[tk] = {'indices': indices, 'angle':[], 'init':[], 'last':[], 'Converged':[]}
1017
- for angle in np.arange(-180.0, 180.0, interval):
1018
- # Iterated numpy.ndarray does not contain the last 180: -180., ..., (180).
1019
- conf = frag_conf.copy()
1020
- conf.props.update({'torsion_key': tk, 'angle': float(angle)})
1021
- conf.set_torsion(*frag_ijkl, angle) # atoms bonded to `l` move.
1022
- conf = conf.optimize(calculator, fmax, **kwargs)
1023
- # conf.optimize() updates coordinates and conf.props:
1024
- # `angle`, `E_tot_init(kcal/mol)`, `E_tot(kcal/mol)`, `Converged`.
1025
- tk = conf.props['torsion_key']
1026
- data[tk]['angle'].append(conf.props['angle'])
1027
- data[tk]['init'].append(conf.props['E_tot_init(kcal/mol)'])
1028
- data[tk]['last'].append(conf.props['E_tot(kcal/mol)'])
1029
- data[tk]['Converged'].append(conf.props['Converged'])
1030
- frag_cleaned, _ = clean_2d(frag, reset_isotope=True, remove_H=True)
1031
- rdDepictor.Compute2DCoords(frag_cleaned)
1032
- # to serialize the molecule
1033
- data[tk]['frag'] = Chem.MolToMolBlock(frag_cleaned)
1034
- data[tk]['frag_indices'] = frag_ijkl
1035
-
1036
- else:
1037
- # mol.confs will be populated with torsion conformers.
1038
- # It is designed for a batch optimization in the future.
1039
- mol = self.copy()
1040
- mol.confs = []
1041
- for tk, indices in torsion_atoms_indices.items():
1042
- data[tk] = {'indices': indices, 'angle':[], 'init':[], 'last':[], 'Converged':[]}
1043
- for angle in np.arange(-180.0, 180.0, interval):
1044
- # Iterated numpy.ndarray does not contain the last 180: -180., ..., (180).
1045
- x = ref_conf.copy()
1046
- x.props.update({'torsion_key': tk, 'angle': float(angle)})
1047
- x.set_torsion(*indices, angle) # atoms bonded to `l` move.
1048
- mol.confs.append(x)
1049
-
1050
- # Calculate relaxation energies
1051
- for conf in mol.confs:
1052
- conf = conf.optimize(calculator, fmax, **kwargs)
1053
- # conf.optimize() updates coordinates and conf.props:
1054
- # `angle`, `E_tot_init(kcal/mol)`, `E_tot(kcal/mol)`, `Converged`.
1055
- tk = conf.props['torsion_key']
1056
- data[tk]['angle'].append(conf.props['angle'])
1057
- data[tk]['init'].append(conf.props['E_tot_init(kcal/mol)'])
1058
- data[tk]['last'].append(conf.props['E_tot(kcal/mol)'])
1059
- data[tk]['Converged'].append(conf.props['Converged'])
1060
-
1061
- # Post-processing
1062
- torsion_energy_profiles = {}
1063
- for tk, dictdata in data.items():
1064
- if use_converged_only:
1065
- dictdata['angle'] = list(itertools.compress(dictdata['angle'], dictdata['Converged']))
1066
- dictdata['init'] = list(itertools.compress(dictdata['init'], dictdata['Converged']))
1067
- dictdata['last'] = list(itertools.compress(dictdata['last'], dictdata['Converged']))
1068
- relax = np.array(dictdata['init']) - np.median(dictdata['last'])
1069
- E_rel = relax - np.min(relax)
1070
- torsion_energy_profiles[tk] = {
1071
- 'indices' : dictdata['indices'],
1072
- 'angle' : np.round(np.array(dictdata['angle']), 1).tolist(), # np.ndarray -> list for serialization
1073
- 'E_rel(kcal/mol)': np.round(E_rel, 2).tolist(), # np.ndarray -> list for serialization
1074
- 'frag' : dictdata.get('frag', None),
1075
- 'frag_indices' : dictdata.get('frag_indices', None),
1076
- }
1077
-
1078
- self.props['torsion'] = torsion_energy_profiles
1079
- self.props['torsion_calculator'] = str(calculator)
1001
+ ref_conf = ref_conf.torsion_energies(calculator,
1002
+ torsion_key,
1003
+ simplify,
1004
+ fmax,
1005
+ interval,
1006
+ use_converged_only,
1007
+ **kwargs)
1008
+ for k in ['torsion', 'torsion_calculator']:
1009
+ self.props[k] = ref_conf.props[k]
1080
1010
 
1081
1011
  return self
1082
1012
 
@@ -1628,7 +1558,15 @@ class Mol:
1628
1558
  return json.dumps(props)
1629
1559
 
1630
1560
 
1631
- def serialize(self, decimals: int = 2) -> str:
1561
+ def serialize(self, decimals: int = 2) -> str:
1562
+ """Serialize information necessary to rebuild a Mol object.
1563
+
1564
+ Args:
1565
+ decimals (int, optional): number of decimal places for float data type. Defaults to 2.
1566
+
1567
+ Returns:
1568
+ str: serialized string for json.loads()
1569
+ """
1632
1570
  serialized = json.dumps({
1633
1571
  'name' : self.name,
1634
1572
  'smiles': self.smiles,
@@ -1640,10 +1578,14 @@ class Mol:
1640
1578
 
1641
1579
 
1642
1580
  def deserialize(self, serialized: str) -> Self:
1643
- """Updates self with the serialized string input.
1581
+ """De-serialize the information and build a new Mol object.
1582
+
1583
+ Example:
1584
+ serialized = mol1.serialize()
1585
+ mol2 = Mol().deserialize(serialized)
1644
1586
 
1645
1587
  Args:
1646
- serialized (str): input
1588
+ serialized (str): serialized string.
1647
1589
 
1648
1590
  Returns:
1649
1591
  Self: modified self.
@@ -180,4 +180,6 @@ def clean_2d(rdmol: Chem.Mol,
180
180
  if remove_H:
181
181
  mol = Chem.RemoveHs(mol)
182
182
 
183
+ rdDepictor.Compute2DCoords(mol)
184
+
183
185
  return (mol, conformers)
@@ -453,10 +453,14 @@ def create_torsion_fragment(rdmol: Chem.Mol,
453
453
 
454
454
  candidates = find_bonds_to_prune(rdmol, torsion_indices)
455
455
 
456
+ if not candidates:
457
+ # no fragmentation
458
+ return rdmol, torsion_indices
459
+
456
460
  if GFN2xTB().version() is not None:
461
+ # filter candidate(s) by Wiberg bond order (WBO) if xTB is available
457
462
  jk = tuple(sorted([j, k]))
458
463
  wbo_passed_candidates = {}
459
- # filter candidate(s) by Wiberg bond order (WBO)
460
464
  parent = GFN2xTB(rdmol).singlepoint()
461
465
  assert hasattr(parent, 'wbo'), "create_torsion_fragment() Error: no wbo for parent"
462
466
  for bond_idx, (p, q) in candidates.items():
@@ -470,6 +474,7 @@ def create_torsion_fragment(rdmol: Chem.Mol,
470
474
  wbo_passed_candidates[bond_idx] = (p, q)
471
475
  frag_multi_breaks = create_fragment_on_bonds(rdmol, wbo_passed_candidates)
472
476
  else:
477
+ # skip WBO filtering
473
478
  frag_multi_breaks = create_fragment_on_bonds(rdmol, candidates)
474
479
 
475
480
  frag_ijkl = get_fragment_idx(rdmol, (i, j, k, l), frag_multi_breaks)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rdworks
3
- Version: 0.36.3
3
+ Version: 0.37.1
4
4
  Summary: Frequently used tasks built on RDKit and other tools
5
5
  Author-email: Sung-Hun Bae <sunghun.bae@gmail.com>
6
6
  Maintainer-email: Sung-Hun Bae <sunghun.bae@gmail.com>
@@ -469,12 +469,23 @@ def test_torsion_fragment():
469
469
  name="atorvastatin").make_confs(n=1)
470
470
  ta = mol.torsion_atoms()
471
471
  assert len(ta) == 12
472
- # ta[6] = (8, 9, 10, 18, _rot, _fix)
473
- # _rot and _fix are not used
474
- frag, frag_ijkl = create_torsion_fragment(mol.confs[0].rdmol, (8, 9, 10, 18, None, None))
472
+ # {0: (0, 1, 3, 7), 1: (3, 4, 32, 33), 2: (4, 5, 26, 27), 3: (7, 6, 19, 20),
473
+ # 4: (3, 7, 8, 9), 5: (7, 8, 9, 10), 6: (8, 9, 10, 18), 7: (18, 10, 11, 12),
474
+ # 8: (10, 11, 12, 17), 9: (17, 12, 13, 14), 10: (12, 13, 14, 15), 11: (36, 35, 34, 32)}
475
+ frag, frag_ijkl = create_torsion_fragment(mol.confs[0].rdmol, ta[6])
475
476
  assert frag_ijkl == (5, 6, 7, 12)
476
477
 
477
478
 
479
+ mol2 = Mol(molecule='CC(=O)Nc1ccc(O)cc1', name='acetaminophen.3').make_confs(n=1)
480
+ ta2 = mol2.torsion_atoms()
481
+ # {0: (5, 4, 3, 1)}
482
+ assert len(ta2) == 1
483
+ frag, frag_ijkl = create_torsion_fragment(mol2.confs[0].rdmol, ta2[0])
484
+ # expects no fragmentation
485
+ assert frag == mol2.confs[0].rdmol
486
+ assert frag_ijkl == ta2[0]
487
+
488
+
478
489
  def test_torsion_energies():
479
490
  libr = MolLibr(torsion_dataset_smiles, torsion_dataset_names)
480
491
  with open(workdir / 'test_torsion_energies.html', 'w') as f:
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes