stjames 0.0.75__tar.gz → 0.0.77__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.
Potentially problematic release.
This version of stjames might be problematic. Click here for more details.
- {stjames-0.0.75/stjames.egg-info → stjames-0.0.77}/PKG-INFO +1 -1
- {stjames-0.0.75 → stjames-0.0.77}/pyproject.toml +1 -1
- {stjames-0.0.75 → stjames-0.0.77}/stjames/atomium_stjames/pdb.py +21 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/method.py +11 -1
- {stjames-0.0.75 → stjames-0.0.77}/stjames/molecule.py +87 -3
- {stjames-0.0.75 → stjames-0.0.77/stjames.egg-info}/PKG-INFO +1 -1
- {stjames-0.0.75 → stjames-0.0.77}/LICENSE +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/README.md +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/setup.cfg +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/__init__.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/_deprecated_solvent_settings.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/atom.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/atomium_stjames/__init__.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/atomium_stjames/data.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/atomium_stjames/mmcif.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/atomium_stjames/utilities.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/base.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/basis_set.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/calculation.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/compute_settings.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/constraint.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/correction.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/data/__init__.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/data/bragg_radii.json +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/data/elements.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/data/isotopes.json +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/data/nist_isotopes.json +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/data/read_nist_isotopes.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/data/symbol_element.json +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/diis_settings.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/grid_settings.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/int_settings.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/message.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/mode.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/opt_settings.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/pdb.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/periodic_cell.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/py.typed +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/scf_settings.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/settings.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/solvent.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/status.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/task.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/thermochem_settings.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/types.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/workflows/__init__.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/workflows/admet.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/workflows/basic_calculation.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/workflows/bde.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/workflows/conformer.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/workflows/conformer_search.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/workflows/descriptors.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/workflows/docking.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/workflows/electronic_properties.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/workflows/fukui.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/workflows/hydrogen_bond_basicity.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/workflows/ion_mobility.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/workflows/irc.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/workflows/macropka.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/workflows/molecular_dynamics.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/workflows/multistage_opt.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/workflows/pka.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/workflows/protein_cofolding.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/workflows/redox_potential.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/workflows/scan.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/workflows/solubility.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/workflows/spin_states.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/workflows/tautomer.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames/workflows/workflow.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames.egg-info/SOURCES.txt +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames.egg-info/dependency_links.txt +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames.egg-info/requires.txt +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/stjames.egg-info/top_level.txt +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/tests/test_constraints.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/tests/test_from_extxyz.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/tests/test_molecule.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/tests/test_pdb.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/tests/test_rounding.py +0 -0
- {stjames-0.0.75 → stjames-0.0.77}/tests/test_settings.py +0 -0
|
@@ -526,6 +526,25 @@ def add_atom_to_non_polymer(line: str, model: dict[Any, Any], res_id: str, aniso
|
|
|
526
526
|
}
|
|
527
527
|
|
|
528
528
|
|
|
529
|
+
def guess_element_from_name(atom_name: str) -> str | None:
|
|
530
|
+
atom_name = atom_name.strip()
|
|
531
|
+
if not atom_name:
|
|
532
|
+
return None
|
|
533
|
+
|
|
534
|
+
# Case 1: Atom name starts with a digit (e.g. '1HG1') → element is second character
|
|
535
|
+
if atom_name[0].isdigit() and len(atom_name) > 1:
|
|
536
|
+
return atom_name[1].upper()
|
|
537
|
+
|
|
538
|
+
# # Case 2: Atom name starts with a letter
|
|
539
|
+
# if len(atom_name) >= 2 and atom_name[:2].isalpha():
|
|
540
|
+
# possible = atom_name[:2].strip().capitalize()
|
|
541
|
+
# # Check for common two-letter elements
|
|
542
|
+
# if possible in {"Cl", "Br", "Fe", "Mg", "Zn", "Ca", "Na", "Cu", "Mn", "Co", "Ni"}:
|
|
543
|
+
# return possible
|
|
544
|
+
# Fallback to first letter
|
|
545
|
+
return atom_name[0].upper()
|
|
546
|
+
|
|
547
|
+
|
|
529
548
|
def atom_line_to_dict(line: str, aniso_dict: dict[Any, Any]) -> dict[str, Any]:
|
|
530
549
|
"""Converts an ATOM or HETATM record to an atom dictionary.
|
|
531
550
|
|
|
@@ -545,6 +564,8 @@ def atom_line_to_dict(line: str, aniso_dict: dict[Any, Any]) -> dict[str, Any]:
|
|
|
545
564
|
if line[60:66].strip():
|
|
546
565
|
a["bvalue"] = float(line[60:66].strip())
|
|
547
566
|
a["element"] = line[76:78].strip() or None
|
|
567
|
+
if not a["element"]:
|
|
568
|
+
a["element"] = guess_element_from_name(a["name"])
|
|
548
569
|
if line[78:80].strip():
|
|
549
570
|
try:
|
|
550
571
|
a["charge"] = int(line[78:80].strip())
|
|
@@ -35,6 +35,7 @@ class Method(LowercaseStrEnum):
|
|
|
35
35
|
MACE_MP_0B2_L = "mace_mp_0b2_l"
|
|
36
36
|
OCP24_S = "ocp24_s"
|
|
37
37
|
OCP24_L = "ocp24_l"
|
|
38
|
+
OMOL25_CONSERVING_S = "omol25_conserving_s"
|
|
38
39
|
ORB_V2 = "orb_v2"
|
|
39
40
|
ORB_V3_CONSERVATIVE_INF_OMAT = "orb_v3_conservative_inf_omat"
|
|
40
41
|
|
|
@@ -56,13 +57,22 @@ class Method(LowercaseStrEnum):
|
|
|
56
57
|
|
|
57
58
|
|
|
58
59
|
PrepackagedNNPMethod = Literal[
|
|
59
|
-
Method.AIMNET2_WB97MD3,
|
|
60
|
+
Method.AIMNET2_WB97MD3,
|
|
61
|
+
Method.OCP24_S,
|
|
62
|
+
Method.OCP24_L,
|
|
63
|
+
Method.OMOL25_CONSERVING_S,
|
|
64
|
+
Method.RM1,
|
|
65
|
+
Method.ORB_V3_CONSERVATIVE_INF_OMAT,
|
|
66
|
+
Method.EGRET_1,
|
|
67
|
+
Method.EGRET_1E,
|
|
68
|
+
Method.EGRET_1T,
|
|
60
69
|
]
|
|
61
70
|
|
|
62
71
|
PREPACKAGED_NNP_METHODS = [
|
|
63
72
|
Method.AIMNET2_WB97MD3,
|
|
64
73
|
Method.OCP24_S,
|
|
65
74
|
Method.OCP24_L,
|
|
75
|
+
Method.OMOL25_CONSERVING_S,
|
|
66
76
|
Method.RM1,
|
|
67
77
|
Method.ORB_V3_CONSERVATIVE_INF_OMAT,
|
|
68
78
|
Method.EGRET_1,
|
|
@@ -2,6 +2,7 @@ import re
|
|
|
2
2
|
from pathlib import Path
|
|
3
3
|
from typing import Annotated, Any, Iterable, Optional, Self, Sequence, TypeAlias, TypedDict, TypeVar
|
|
4
4
|
|
|
5
|
+
import numpy as np
|
|
5
6
|
import pydantic
|
|
6
7
|
from pydantic import AfterValidator, NonNegativeInt, PositiveInt, ValidationError
|
|
7
8
|
from rdkit import Chem
|
|
@@ -75,15 +76,34 @@ class Molecule(Base):
|
|
|
75
76
|
def __len__(self) -> int:
|
|
76
77
|
return len(self.atoms)
|
|
77
78
|
|
|
78
|
-
def distance(self,
|
|
79
|
+
def distance(self, i: PositiveInt, j: PositiveInt) -> float:
|
|
79
80
|
r"""
|
|
80
|
-
|
|
81
|
+
Calculate the distance between atoms.
|
|
81
82
|
|
|
82
83
|
>>> mol = Molecule.from_xyz("H 0 1 0\nH 0 0 1")
|
|
83
84
|
>>> mol.distance(1, 2)
|
|
84
85
|
1.4142135623730951
|
|
85
86
|
"""
|
|
86
|
-
return sum((q2 - q1) ** 2 for q1, q2 in zip(self.atoms[
|
|
87
|
+
return sum((q2 - q1) ** 2 for q1, q2 in zip(self.atoms[i - 1].position, self.atoms[j - 1].position)) ** 0.5 # type: ignore [no-any-return,unused-ignore]
|
|
88
|
+
|
|
89
|
+
def angle(self, i: PositiveInt, j: PositiveInt, k: PositiveInt, degrees: bool = True) -> float:
|
|
90
|
+
r"""
|
|
91
|
+
Calculate the angle between three atoms.
|
|
92
|
+
|
|
93
|
+
>>> Molecule.from_xyz("H 0 0 0\nO 0 0 1\nH 0 1 1").angle(0, 1, 2)
|
|
94
|
+
90.0
|
|
95
|
+
"""
|
|
96
|
+
|
|
97
|
+
return angle(self.coordinates[i], self.coordinates[j], self.coordinates[k], degrees=degrees)
|
|
98
|
+
|
|
99
|
+
def dihedral(self, i: int, j: int, k: int, l: int, degrees: bool = True, positive_domain: bool = True) -> float:
|
|
100
|
+
r"""
|
|
101
|
+
Calculate the dihedral angle between four atoms.
|
|
102
|
+
|
|
103
|
+
>>> Molecule.from_xyz("H 0 0 0\nO 0 0 1\nO 0 1 1\nH 1 1 1").dihedral(0, 1, 2, 3)
|
|
104
|
+
270.0
|
|
105
|
+
"""
|
|
106
|
+
return dihedral(self.coordinates[i], self.coordinates[j], self.coordinates[k], self.coordinates[l], degrees=degrees, positive_domain=positive_domain)
|
|
87
107
|
|
|
88
108
|
@property
|
|
89
109
|
def coordinates(self) -> Vector3DPerAtom:
|
|
@@ -447,3 +467,67 @@ def parse_extxyz_comment_line(line: str) -> EXTXYZMetadata:
|
|
|
447
467
|
prop_dict[key] = value # type: ignore [literal-required]
|
|
448
468
|
|
|
449
469
|
return prop_dict
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
def angle(p0: Sequence[float], p1: Sequence[float], p2: Sequence[float], degrees: bool = True) -> float:
|
|
473
|
+
"""
|
|
474
|
+
Angle between three points.
|
|
475
|
+
|
|
476
|
+
:param i, j, k: positions of points
|
|
477
|
+
:param degrees: whether to return in degrees
|
|
478
|
+
:return: angle in radians or degrees
|
|
479
|
+
"""
|
|
480
|
+
a0, a1, a2 = map(np.asarray, (p0, p1, p2))
|
|
481
|
+
u = a1 - a0
|
|
482
|
+
v = a1 - a2
|
|
483
|
+
|
|
484
|
+
nu = np.linalg.norm(u)
|
|
485
|
+
nv = np.linalg.norm(v)
|
|
486
|
+
cos_theta = np.dot(u, v) / (nu * nv)
|
|
487
|
+
cos_theta = np.clip(cos_theta, -1.0, 1.0)
|
|
488
|
+
|
|
489
|
+
ang = np.arccos(cos_theta)
|
|
490
|
+
if degrees:
|
|
491
|
+
return float(np.degrees(ang))
|
|
492
|
+
return float(ang)
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
def dihedral(p0: Sequence[float], p1: Sequence[float], p2: Sequence[float], p3: Sequence[float], degrees: bool = True, positive_domain: bool = True) -> float:
|
|
496
|
+
"""
|
|
497
|
+
Dihedral angle between four points.
|
|
498
|
+
|
|
499
|
+
:param p0, p1, p2, p3: points
|
|
500
|
+
:param degrees: whether to return in degrees
|
|
501
|
+
:param positive_domain: (0, 360] if True else (-180, 180]
|
|
502
|
+
:return: angle in degrees or radians (or nan if collinearities detected)
|
|
503
|
+
|
|
504
|
+
>>> a = [0, 0, 0]
|
|
505
|
+
>>> b = [0, 0, 1]
|
|
506
|
+
>>> c = [0, 1, 1]
|
|
507
|
+
>>> d1 = [0, 1, 2]
|
|
508
|
+
>>> d2 = [0.5, 1, 1.5]
|
|
509
|
+
>>> dihedral(a, b, c, d1)
|
|
510
|
+
180.0
|
|
511
|
+
>>> dihedral(a, b, c, d2, positive_domain=False)
|
|
512
|
+
-135.0
|
|
513
|
+
"""
|
|
514
|
+
a0, a1, a2, a3 = map(np.asarray, (p0, p1, p2, p3))
|
|
515
|
+
b0 = a1 - a0
|
|
516
|
+
b1 = a2 - a1
|
|
517
|
+
b2 = a3 - a2
|
|
518
|
+
|
|
519
|
+
b1 = b1 / np.linalg.norm(b1)
|
|
520
|
+
|
|
521
|
+
v = b1 * np.dot(b0, b1) - b0
|
|
522
|
+
w = b2 - b1 * np.dot(b2, b1)
|
|
523
|
+
|
|
524
|
+
x = np.dot(v, w)
|
|
525
|
+
y = np.dot(np.cross(b1, v), w)
|
|
526
|
+
ang = np.arctan2(y, x)
|
|
527
|
+
|
|
528
|
+
if positive_domain and ang < 0:
|
|
529
|
+
ang += 2 * np.pi
|
|
530
|
+
|
|
531
|
+
if degrees:
|
|
532
|
+
return float(np.degrees(ang))
|
|
533
|
+
return float(ang)
|
|
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
|