rdworks 0.37.1__tar.gz → 0.39.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.37.1 → rdworks-0.39.1}/PKG-INFO +1 -1
  2. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/__init__.py +1 -1
  3. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/conf.py +126 -80
  4. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/mol.py +8 -8
  5. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/std.py +23 -1
  6. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks.egg-info/PKG-INFO +1 -1
  7. {rdworks-0.37.1 → rdworks-0.39.1}/tests/test_basics.py +30 -2
  8. {rdworks-0.37.1 → rdworks-0.39.1}/LICENSE +0 -0
  9. {rdworks-0.37.1 → rdworks-0.39.1}/README.md +0 -0
  10. {rdworks-0.37.1 → rdworks-0.39.1}/pyproject.toml +0 -0
  11. {rdworks-0.37.1 → rdworks-0.39.1}/setup.cfg +0 -0
  12. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/autograph/__init__.py +0 -0
  13. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/autograph/autograph.py +0 -0
  14. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/autograph/centroid.py +0 -0
  15. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/autograph/dynamictreecut.py +0 -0
  16. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/autograph/nmrclust.py +0 -0
  17. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/autograph/rckmeans.py +0 -0
  18. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/bitqt/__init__.py +0 -0
  19. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/bitqt/bitqt.py +0 -0
  20. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/descriptor.py +0 -0
  21. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/display.py +0 -0
  22. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/ionized.py +0 -0
  23. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/matchedseries.py +0 -0
  24. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/mollibr.py +0 -0
  25. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/pka.py +0 -0
  26. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/predefined/Asinex_fragment.xml +0 -0
  27. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/predefined/Astex_RO3.xml +0 -0
  28. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/predefined/Baell2010_PAINS/Baell2010A.xml +0 -0
  29. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/predefined/Baell2010_PAINS/Baell2010B.xml +0 -0
  30. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/predefined/Baell2010_PAINS/Baell2010C.xml +0 -0
  31. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/predefined/Baell2010_PAINS/PAINS-less-than-015-hits.xml +0 -0
  32. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/predefined/Baell2010_PAINS/PAINS-less-than-150-hits.xml +0 -0
  33. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/predefined/Baell2010_PAINS/PAINS-more-than-150-hits.xml +0 -0
  34. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/predefined/Baell2010_PAINS/makexml.py +0 -0
  35. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/predefined/Brenk2008_Dundee/makexml.py +0 -0
  36. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/predefined/CNS.xml +0 -0
  37. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/predefined/ChEMBL_Walters/BMS.xml +0 -0
  38. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/predefined/ChEMBL_Walters/Dundee.xml +0 -0
  39. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/predefined/ChEMBL_Walters/Glaxo.xml +0 -0
  40. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/predefined/ChEMBL_Walters/Inpharmatica.xml +0 -0
  41. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/predefined/ChEMBL_Walters/LINT.xml +0 -0
  42. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/predefined/ChEMBL_Walters/MLSMR.xml +0 -0
  43. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/predefined/ChEMBL_Walters/PAINS.xml +0 -0
  44. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/predefined/ChEMBL_Walters/SureChEMBL.xml +0 -0
  45. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/predefined/ChEMBL_Walters/makexml.py +0 -0
  46. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/predefined/Hann1999_Glaxo/Hann1999.xml +0 -0
  47. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/predefined/Hann1999_Glaxo/Hann1999Acid.xml +0 -0
  48. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/predefined/Hann1999_Glaxo/Hann1999Base.xml +0 -0
  49. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/predefined/Hann1999_Glaxo/Hann1999ElPh.xml +0 -0
  50. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/predefined/Hann1999_Glaxo/Hann1999NuPh.xml +0 -0
  51. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/predefined/Hann1999_Glaxo/makexml.py +0 -0
  52. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/predefined/Kazius2005/Kazius2005.xml +0 -0
  53. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/predefined/Kazius2005/makexml.py +0 -0
  54. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/predefined/ZINC_druglike.xml +0 -0
  55. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/predefined/ZINC_fragment.xml +0 -0
  56. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/predefined/ZINC_leadlike.xml +0 -0
  57. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/predefined/fragment.xml +0 -0
  58. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/predefined/ionized/simple_smarts_pattern.csv +0 -0
  59. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/predefined/ionized/smarts_pattern.csv +0 -0
  60. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/predefined/misc/makexml.py +0 -0
  61. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/predefined/misc/reactive-part-2.xml +0 -0
  62. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/predefined/misc/reactive-part-3.xml +0 -0
  63. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/predefined/misc/reactive.xml +0 -0
  64. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/readin.py +0 -0
  65. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/rgroup.py +0 -0
  66. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/scaffold.py +0 -0
  67. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/stereoisomers.py +0 -0
  68. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/tautomers.py +0 -0
  69. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/torsion.py +0 -0
  70. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/units.py +0 -0
  71. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/utils.py +0 -0
  72. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/xml.py +0 -0
  73. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/xtb/__init__.py +0 -0
  74. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks/xtb/wrapper.py +0 -0
  75. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks.egg-info/SOURCES.txt +0 -0
  76. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks.egg-info/dependency_links.txt +0 -0
  77. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks.egg-info/requires.txt +0 -0
  78. {rdworks-0.37.1 → rdworks-0.39.1}/src/rdworks.egg-info/top_level.txt +0 -0
  79. {rdworks-0.37.1 → rdworks-0.39.1}/tests/test_decimals.py +0 -0
  80. {rdworks-0.37.1 → rdworks-0.39.1}/tests/test_gypsumdl.py +0 -0
  81. {rdworks-0.37.1 → rdworks-0.39.1}/tests/test_iupac_name.py +0 -0
  82. {rdworks-0.37.1 → rdworks-0.39.1}/tests/test_nn_xtb.py +0 -0
  83. {rdworks-0.37.1 → rdworks-0.39.1}/tests/test_web.py +0 -0
  84. {rdworks-0.37.1 → rdworks-0.39.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.37.1
3
+ Version: 0.39.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.37.1'
1
+ __version__ = '0.39.1'
2
2
 
3
3
  from rdworks.conf import Conf
4
4
  from rdworks.mol import Mol
@@ -39,11 +39,20 @@ class Conf:
39
39
  """
40
40
  assert isinstance(molecule, str | Chem.Mol) or molecule is None
41
41
 
42
- self.rdmol = None # must contain one and only one rdkit conformer
43
42
  self.name = name
43
+ self.rdmol = None # must contain one and only one rdkit conformer
44
44
  self.natoms = 0
45
- self.charge = 0 # molecular charge
46
- self.spin = 1 # molecular spin multiplicity for ASE
45
+ self.charge = 0 # molecular formal charge
46
+ self.spin = 1 # molecular spin multiplicity for ASE
47
+ # Molecular spin multiplicity describes the number of possible orientations of
48
+ # spin angular momentum for a given molecule, essentially indicating the total
49
+ # number of unpaired electrons.
50
+ # Spin Angular Momentum (S): up (+1/2) or down (-1/2)
51
+ # Spin Multiplicity (2S + 1)
52
+ # 0 unpaired electron has S = 0, 2S + 1 = 0, called a singlet.
53
+ # 1 unpaired electron has S = 1/2, 2S + 1 = 2, called a doublet (radical).
54
+ # 2 unpaired electrons has S = 1, 2S + 1 = 3, called a triplet.
55
+
47
56
  self.props = {}
48
57
 
49
58
  if molecule is None:
@@ -63,10 +72,12 @@ class Conf:
63
72
 
64
73
  num_atoms = self.rdmol.GetNumAtoms()
65
74
  tot_atoms = self.rdmol.GetNumAtoms(onlyExplicit=False)
75
+
66
76
  assert num_atoms == tot_atoms, "Conf() Error: missing hydrogens"
77
+
67
78
  self.natoms = num_atoms
68
79
  self.charge = rdmolops.GetFormalCharge(self.rdmol)
69
- self.props.update({'atoms': num_atoms})
80
+ self.props.update({'atoms': self.natoms, 'charge': self.charge})
70
81
 
71
82
  assert self.rdmol.GetConformer().Is3D(), "Conf() Error: not 3D"
72
83
 
@@ -148,7 +159,6 @@ class Conf:
148
159
 
149
160
  return self
150
161
 
151
-
152
162
 
153
163
  def optimize(self,
154
164
  calculator: str | Callable = 'MMFF94',
@@ -402,6 +412,95 @@ class Conf:
402
412
  return {i: d[:4] for i, d in enumerate(get_torsion_atoms(self.rdmol, strict))}
403
413
 
404
414
 
415
+ def torsion_energies_one(self,
416
+ calculator: str | Callable,
417
+ indices: tuple,
418
+ simplify: bool = True,
419
+ fmax: float = 0.05,
420
+ interval: float = 20.0,
421
+ use_converged_only: bool = True,
422
+ **kwargs) -> Self:
423
+ """Calculate potential energy profile for a torsion angle.
424
+
425
+ Args:
426
+ calculator (str | Callable): 'MMFF', 'UFF', or ASE calculator.
427
+ indices (tuple): atom indices (i,j,k,l) for a torsion angle
428
+ simplify (bool, optional): whether to use fragementation. Defaults to True.
429
+ fmax (float, optional): convergence limit for optimize. Defaults to 0.05.
430
+ interval (float, optional): angle intervals. Defaults to 20.0.
431
+ use_converged_only (bool, optional): whether to use only converged data. Defaults to True.
432
+
433
+ Returns:
434
+ Self: modified self.
435
+ """
436
+ ref_conf = self.copy()
437
+
438
+ data = {'indices': indices,
439
+ 'angle': [],
440
+ 'init': [],
441
+ 'last': [],
442
+ 'Converged': [],
443
+ }
444
+
445
+ if simplify:
446
+ frag, frag_ijkl = create_torsion_fragment(ref_conf.rdmol, indices)
447
+ frag_conf = Conf(frag)
448
+ for angle in np.arange(-180.0, 180.0, interval):
449
+ # Iterated numpy.ndarray does not contain the last 180: -180., ..., (180).
450
+ conf = frag_conf.copy()
451
+ conf.set_torsion(*frag_ijkl, angle) # atoms bonded to `l` move.
452
+ conf = conf.optimize(calculator, fmax, **kwargs)
453
+ # conf.optimize() updates coordinates and conf.props:
454
+ # `E_tot_init(kcal/mol)`, `E_tot(kcal/mol)`, `Converged`.
455
+ data['angle'].append(angle)
456
+ data['init'].append(conf.props['E_tot_init(kcal/mol)'])
457
+ data['last'].append(conf.props['E_tot(kcal/mol)'])
458
+ data['Converged'].append(conf.props['Converged'])
459
+ frag_cleaned, _ = clean_2d(frag, reset_isotope=True, remove_H=True)
460
+ # to serialize the molecule
461
+ data['frag'] = Chem.MolToMolBlock(frag_cleaned)
462
+ data['frag_indices'] = frag_ijkl
463
+ else:
464
+ for angle in np.arange(-180.0, 180.0, interval):
465
+ # Iterated numpy.ndarray does not contain the last 180: -180., ..., (180).
466
+ conf = ref_conf.copy()
467
+ conf.set_torsion(*indices, angle) # atoms bonded to `l` move.
468
+ conf = conf.optimize(calculator, fmax, **kwargs)
469
+ # conf.optimize() updates coordinates and conf.props:
470
+ # `E_tot_init(kcal/mol)`, `E_tot(kcal/mol)`, `Converged`.
471
+ data['angle'].append(conf.props['angle'])
472
+ data['init'].append(conf.props['E_tot_init(kcal/mol)'])
473
+ data['last'].append(conf.props['E_tot(kcal/mol)'])
474
+ data['Converged'].append(conf.props['Converged'])
475
+
476
+ # Post-processing
477
+ if use_converged_only:
478
+ data['angle'] = list(itertools.compress(data['angle'], data['Converged']))
479
+ data['init' ] = list(itertools.compress(data['init' ], data['Converged']))
480
+ data['last' ] = list(itertools.compress(data['last' ], data['Converged']))
481
+
482
+ relax = np.array(data['init']) - np.median(data['last'])
483
+ E_rel = relax - np.min(relax)
484
+
485
+ torsion_energy_profile = {
486
+ 'indices' : data['indices'],
487
+ 'angle' : np.round(np.array(data['angle']), 1).tolist(), # np.ndarray -> list for serialization
488
+ 'E_rel(kcal/mol)': np.round(E_rel, 2).tolist(), # np.ndarray -> list for serialization
489
+ }
490
+
491
+ if simplify:
492
+ torsion_energy_profile.update({
493
+ 'frag' : data.get('frag', None),
494
+ 'frag_indices' : data.get('frag_indices', None),
495
+ })
496
+
497
+ self.props['torsion'] = torsion_energy_profile
498
+ self.props['torsion_calculator'] = str(calculator)
499
+
500
+ return self
501
+
502
+
503
+
405
504
  def torsion_energies(self,
406
505
  calculator: str | Callable,
407
506
  torsion_key: int | None = None,
@@ -417,7 +516,7 @@ class Conf:
417
516
 
418
517
  Args:
419
518
  calculator (str | Callable): 'MMFF', 'UFF', or ASE calculator.
420
- torsion_key (int | None): torsion index to calculate. Defaults to None (all).
519
+ torsion_key (int | None): key to the torsion indices to calculate. Defaults to None (all).
421
520
  simplify (bool, optional): whether to use fragment surrogate. Defaults to True.
422
521
  fmax (float, optional): fmax of ASE optimizer. Defaults to 0.05.
423
522
  interval (float, optional): interval of torsion angles in degree. Defaults to 15.0.
@@ -427,80 +526,23 @@ class Conf:
427
526
  Self: modified self.
428
527
  """
429
528
 
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]}
529
+ torsion_atoms_dict = self.torsion_atoms() # {0: (5, 4, 3, 1)}
530
+ if (torsion_key is not None) and torsion_atoms_dict.get(torsion_key):
531
+ torsion_atoms_dict = {torsion_key: torsion_atoms_dict.get(torsion_key)}
532
+ # single torsion angle atom indices
435
533
 
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
534
+ conf = self.copy()
462
535
 
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
536
  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
-
537
+ for tk, indices in torsion_atoms_dict.items():
538
+ conf = conf.torsion_energies_one(calculator,
539
+ indices,
540
+ simplify,
541
+ fmax,
542
+ interval,
543
+ use_converged_only)
544
+ torsion_energy_profiles[tk] = conf.props['torsion']
545
+
504
546
  self.props['torsion'] = torsion_energy_profiles
505
547
  self.props['torsion_calculator'] = str(calculator)
506
548
 
@@ -548,8 +590,10 @@ class Conf:
548
590
  str: serialized string for json.loads()
549
591
  """
550
592
  serialized = json.dumps({
551
- 'name' : self.name,
552
- 'natoms': self.natoms,
593
+ 'name' : self.name,
594
+ 'natoms': self.natoms,
595
+ 'charge': self.charge,
596
+ 'spin': self.spin,
553
597
  'props' : recursive_round(self.props, decimals),
554
598
  'molblock' : self.to_molblock(),
555
599
  })
@@ -573,7 +617,9 @@ class Conf:
573
617
  data = json.loads(serialized)
574
618
 
575
619
  self.name = data['name']
576
- self.natoms = data['natoms']
620
+ self.natoms = int(data['natoms'])
621
+ self.charge = int(data['charge'])
622
+ self.spin = int(data['spin'])
577
623
  self.props = data['props']
578
624
  self.rdmol = Chem.MolFromMolBlock(data['molblock'], sanitize=False, removeHs=False)
579
625
 
@@ -33,7 +33,7 @@ from rdkit.ML.Cluster import Butina
33
33
  from PIL import Image
34
34
 
35
35
  from rdworks.conf import Conf
36
- from rdworks.std import desalt_smiles, standardize, clean_2d
36
+ from rdworks.std import generate_inchi_key, desalt_smiles, standardize, clean_2d
37
37
  from rdworks.xml import list_predefined_xml, get_predefined_xml, parse_xml
38
38
  from rdworks.scaffold import rigid_fragment_indices
39
39
  from rdworks.descriptor import rd_descriptor, rd_descriptor_f
@@ -83,17 +83,18 @@ class Mol:
83
83
  """
84
84
  assert isinstance(molecule, str | Chem.Mol | Conf) or molecule is None
85
85
 
86
+ self.name = ''
86
87
  self.rdmol = None # 2D, one and only one Conformer
87
88
  self.smiles = '' # isomeric SMILES
88
89
  self.confs = [] # container for 3D conformers
89
- self.name = ''
90
90
  self.InChIKey = '' # 27 characters (SHA-256 hash of InChI)
91
- self.InChI = ''
92
91
  self.props = {}
93
- self.fp = None
92
+
94
93
  self.max_workers = max_workers
95
94
  self.chunksize = chunksize
96
95
  self.progress = progress
96
+
97
+ self.fp = None
97
98
 
98
99
  if molecule is None:
99
100
  return
@@ -141,8 +142,7 @@ class Mol:
141
142
  self.name = 'untitled'
142
143
 
143
144
  self.rdmol.SetProp('_Name', self.name) # _Name can't be None
144
- self.InChI = Chem.MolToInchi(self.rdmol)
145
- self.InChIKey = inchi.InchiToInchiKey(self.InChI)
145
+ self.InChIKey = generate_inchi_key(self.rdmol)
146
146
  self.props.update({
147
147
  'aka' : [], # <-- to be set by MolLibr.unique()
148
148
  'atoms' : self.rdmol.GetNumAtoms(), # hydrogens not excluded?
@@ -1570,6 +1570,7 @@ class Mol:
1570
1570
  serialized = json.dumps({
1571
1571
  'name' : self.name,
1572
1572
  'smiles': self.smiles,
1573
+ 'InChIKey': self.InChIKey,
1573
1574
  'props' : recursive_round(self.props, decimals),
1574
1575
  'confs' : [conf.serialize() for conf in self.confs],
1575
1576
  })
@@ -1596,8 +1597,7 @@ class Mol:
1596
1597
  self.smiles = data['smiles'] # isomeric SMILES, no H
1597
1598
  self.rdmol = Chem.MolFromSmiles(data['smiles']) # for 2D depiction
1598
1599
  self.rdmol.SetProp('_Name', self.name)
1599
- self.InChI = Chem.MolToInchi(self.rdmol)
1600
- self.InChIKey = inchi.InchiToInchiKey(self.InChI)
1600
+ self.InChIKey = data['InChIKey']
1601
1601
  self.props = data['props']
1602
1602
  self.confs = [Conf().deserialize(_) for _ in data['confs']] # for 3D conformers (iterable)
1603
1603
 
@@ -1,10 +1,32 @@
1
1
  import operator
2
2
 
3
3
  from rdkit import Chem
4
- from rdkit.Chem import rdDepictor
4
+ from rdkit.Chem import rdDepictor, inchi
5
5
  from rdkit.Chem.MolStandardize import rdMolStandardize
6
6
 
7
7
 
8
+ def generate_inchi_key(rdmol: Chem.Mol) -> str:
9
+ """Generate InChIKey.
10
+
11
+ Note:
12
+ - An InChIKey is a 27-character string consisting of three parts:
13
+ - 14 characters: Derived from the connectivity layer of the InChI.
14
+ - Hyphen (-): Separates the first two blocks.
15
+ - 9 characters: Derived from the remaining InChI layers.
16
+ - A hyphen (-): Separates the second and third blocks.
17
+ - A final checksum character: Ensures the integrity of the key.
18
+
19
+ Args:
20
+ rdmol (Chem.Mol): input molecule
21
+
22
+ Returns:
23
+ str: 27-character InChIKey
24
+ """
25
+ InChI = Chem.MolToInchi(rdmol)
26
+ InChIKey = inchi.InchiToInchiKey(InChI)
27
+
28
+ return InChIKey
29
+
8
30
 
9
31
  def desalt_smiles(smiles: str) -> tuple[str, Chem.Mol]:
10
32
  """Remove salt(s) from SMILES.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rdworks
3
- Version: 0.37.1
3
+ Version: 0.39.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>
@@ -475,7 +475,6 @@ def test_torsion_fragment():
475
475
  frag, frag_ijkl = create_torsion_fragment(mol.confs[0].rdmol, ta[6])
476
476
  assert frag_ijkl == (5, 6, 7, 12)
477
477
 
478
-
479
478
  mol2 = Mol(molecule='CC(=O)Nc1ccc(O)cc1', name='acetaminophen.3').make_confs(n=1)
480
479
  ta2 = mol2.torsion_atoms()
481
480
  # {0: (5, 4, 3, 1)}
@@ -486,12 +485,41 @@ def test_torsion_fragment():
486
485
  assert frag_ijkl == ta2[0]
487
486
 
488
487
 
488
+ def test_torsion_fragment_from_conf():
489
+ from rdworks.torsion import create_torsion_fragment
490
+ mol = Mol(molecule="CC(C)C1=C(C(=C(N1CC[C@H](C[C@H](CC(=O)O)O)O)C2=CC=C(C=C2)F)C3=CC=CC=C3)C(=O)NC4=CC=CC=C4",
491
+ name="atorvastatin").make_confs(n=1)
492
+ ref_conf = mol.confs[0]
493
+ ta = ref_conf.torsion_atoms()
494
+ assert len(ta) == 12
495
+ # {0: (0, 1, 3, 7), 1: (3, 4, 32, 33), 2: (4, 5, 26, 27), 3: (7, 6, 19, 20),
496
+ # 4: (3, 7, 8, 9), 5: (7, 8, 9, 10), 6: (8, 9, 10, 18), 7: (18, 10, 11, 12),
497
+ # 8: (10, 11, 12, 17), 9: (17, 12, 13, 14), 10: (12, 13, 14, 15), 11: (36, 35, 34, 32)}
498
+ frag, frag_ijkl = create_torsion_fragment(ref_conf.rdmol, ta[6])
499
+ assert frag_ijkl == (5, 6, 7, 12)
500
+
501
+ ref_conf = ref_conf.torsion_energies(calculator='MMFF94', torsion_key=6, interval=15)
502
+
503
+ mol2 = Mol(molecule='CC(=O)Nc1ccc(O)cc1', name='acetaminophen.3').make_confs(n=1)
504
+ ref_conf2 = mol2.confs[0]
505
+ ta2 = ref_conf2.torsion_atoms()
506
+ # {0: (5, 4, 3, 1)}
507
+ assert len(ta2) == 1
508
+ frag, frag_ijkl = create_torsion_fragment(ref_conf2.rdmol, ta2[0])
509
+ # expects no fragmentation
510
+ assert frag == ref_conf2.rdmol
511
+ assert frag_ijkl == ta2[0]
512
+
513
+ ref_conf2 = ref_conf2.torsion_energies(calculator='MMFF94', interval=15)
514
+
515
+
489
516
  def test_torsion_energies():
490
517
  libr = MolLibr(torsion_dataset_smiles, torsion_dataset_names)
491
518
  with open(workdir / 'test_torsion_energies.html', 'w') as f:
492
519
  for mol in libr[:1]:
493
520
  mol = mol.make_confs().drop_confs(similar=True, similar_rmsd=0.3).sort_confs().rename()
494
- mol = mol.optimize_confs(calculator='MMFF94').torsion_energies(calculator='MMFF94', interval=15)
521
+ mol = mol.optimize_confs(calculator='MMFF94').torsion_energies(calculator='MMFF94',
522
+ interval=15)
495
523
  f.write(mol.to_html())
496
524
  print(mol.dumps('torsion', decimals=2))
497
525
 
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