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.
- {rdworks-0.36.3 → rdworks-0.37.1}/PKG-INFO +1 -1
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/__init__.py +1 -1
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/conf.py +133 -7
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/mol.py +36 -94
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/std.py +2 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/torsion.py +6 -1
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks.egg-info/PKG-INFO +1 -1
- {rdworks-0.36.3 → rdworks-0.37.1}/tests/test_basics.py +14 -3
- {rdworks-0.36.3 → rdworks-0.37.1}/LICENSE +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/README.md +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/pyproject.toml +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/setup.cfg +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/autograph/__init__.py +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/autograph/autograph.py +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/autograph/centroid.py +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/autograph/dynamictreecut.py +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/autograph/nmrclust.py +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/autograph/rckmeans.py +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/bitqt/__init__.py +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/bitqt/bitqt.py +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/descriptor.py +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/display.py +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/ionized.py +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/matchedseries.py +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/mollibr.py +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/pka.py +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/Asinex_fragment.xml +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/Astex_RO3.xml +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/Baell2010_PAINS/Baell2010A.xml +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/Baell2010_PAINS/Baell2010B.xml +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/Baell2010_PAINS/Baell2010C.xml +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/Baell2010_PAINS/PAINS-less-than-015-hits.xml +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/Baell2010_PAINS/PAINS-less-than-150-hits.xml +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/Baell2010_PAINS/PAINS-more-than-150-hits.xml +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/Baell2010_PAINS/makexml.py +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/Brenk2008_Dundee/makexml.py +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/CNS.xml +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/ChEMBL_Walters/BMS.xml +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/ChEMBL_Walters/Dundee.xml +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/ChEMBL_Walters/Glaxo.xml +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/ChEMBL_Walters/Inpharmatica.xml +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/ChEMBL_Walters/LINT.xml +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/ChEMBL_Walters/MLSMR.xml +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/ChEMBL_Walters/PAINS.xml +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/ChEMBL_Walters/SureChEMBL.xml +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/ChEMBL_Walters/makexml.py +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/Hann1999_Glaxo/Hann1999.xml +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/Hann1999_Glaxo/Hann1999Acid.xml +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/Hann1999_Glaxo/Hann1999Base.xml +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/Hann1999_Glaxo/Hann1999ElPh.xml +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/Hann1999_Glaxo/Hann1999NuPh.xml +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/Hann1999_Glaxo/makexml.py +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/Kazius2005/Kazius2005.xml +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/Kazius2005/makexml.py +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/ZINC_druglike.xml +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/ZINC_fragment.xml +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/ZINC_leadlike.xml +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/fragment.xml +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/ionized/simple_smarts_pattern.csv +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/ionized/smarts_pattern.csv +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/misc/makexml.py +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/misc/reactive-part-2.xml +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/misc/reactive-part-3.xml +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/predefined/misc/reactive.xml +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/readin.py +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/rgroup.py +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/scaffold.py +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/stereoisomers.py +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/tautomers.py +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/units.py +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/utils.py +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/xml.py +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/xtb/__init__.py +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks/xtb/wrapper.py +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks.egg-info/SOURCES.txt +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks.egg-info/dependency_links.txt +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks.egg-info/requires.txt +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/src/rdworks.egg-info/top_level.txt +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/tests/test_decimals.py +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/tests/test_gypsumdl.py +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/tests/test_iupac_name.py +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/tests/test_nn_xtb.py +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/tests/test_web.py +0 -0
- {rdworks-0.36.3 → rdworks-0.37.1}/tests/test_xtb_wrapper.py +0 -0
@@ -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
|
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):
|
568
|
+
serialized (str): serialized string.
|
443
569
|
|
444
570
|
Returns:
|
445
|
-
Self:
|
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
|
-
|
1011
|
-
|
1012
|
-
|
1013
|
-
|
1014
|
-
|
1015
|
-
|
1016
|
-
|
1017
|
-
|
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
|
-
"""
|
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):
|
1588
|
+
serialized (str): serialized string.
|
1647
1589
|
|
1648
1590
|
Returns:
|
1649
1591
|
Self: modified self.
|
@@ -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)
|
@@ -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
|
-
#
|
473
|
-
#
|
474
|
-
|
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
|
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
|
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
|
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
|
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
|
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
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|