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.

Files changed (79) hide show
  1. {stjames-0.0.75/stjames.egg-info → stjames-0.0.77}/PKG-INFO +1 -1
  2. {stjames-0.0.75 → stjames-0.0.77}/pyproject.toml +1 -1
  3. {stjames-0.0.75 → stjames-0.0.77}/stjames/atomium_stjames/pdb.py +21 -0
  4. {stjames-0.0.75 → stjames-0.0.77}/stjames/method.py +11 -1
  5. {stjames-0.0.75 → stjames-0.0.77}/stjames/molecule.py +87 -3
  6. {stjames-0.0.75 → stjames-0.0.77/stjames.egg-info}/PKG-INFO +1 -1
  7. {stjames-0.0.75 → stjames-0.0.77}/LICENSE +0 -0
  8. {stjames-0.0.75 → stjames-0.0.77}/README.md +0 -0
  9. {stjames-0.0.75 → stjames-0.0.77}/setup.cfg +0 -0
  10. {stjames-0.0.75 → stjames-0.0.77}/stjames/__init__.py +0 -0
  11. {stjames-0.0.75 → stjames-0.0.77}/stjames/_deprecated_solvent_settings.py +0 -0
  12. {stjames-0.0.75 → stjames-0.0.77}/stjames/atom.py +0 -0
  13. {stjames-0.0.75 → stjames-0.0.77}/stjames/atomium_stjames/__init__.py +0 -0
  14. {stjames-0.0.75 → stjames-0.0.77}/stjames/atomium_stjames/data.py +0 -0
  15. {stjames-0.0.75 → stjames-0.0.77}/stjames/atomium_stjames/mmcif.py +0 -0
  16. {stjames-0.0.75 → stjames-0.0.77}/stjames/atomium_stjames/utilities.py +0 -0
  17. {stjames-0.0.75 → stjames-0.0.77}/stjames/base.py +0 -0
  18. {stjames-0.0.75 → stjames-0.0.77}/stjames/basis_set.py +0 -0
  19. {stjames-0.0.75 → stjames-0.0.77}/stjames/calculation.py +0 -0
  20. {stjames-0.0.75 → stjames-0.0.77}/stjames/compute_settings.py +0 -0
  21. {stjames-0.0.75 → stjames-0.0.77}/stjames/constraint.py +0 -0
  22. {stjames-0.0.75 → stjames-0.0.77}/stjames/correction.py +0 -0
  23. {stjames-0.0.75 → stjames-0.0.77}/stjames/data/__init__.py +0 -0
  24. {stjames-0.0.75 → stjames-0.0.77}/stjames/data/bragg_radii.json +0 -0
  25. {stjames-0.0.75 → stjames-0.0.77}/stjames/data/elements.py +0 -0
  26. {stjames-0.0.75 → stjames-0.0.77}/stjames/data/isotopes.json +0 -0
  27. {stjames-0.0.75 → stjames-0.0.77}/stjames/data/nist_isotopes.json +0 -0
  28. {stjames-0.0.75 → stjames-0.0.77}/stjames/data/read_nist_isotopes.py +0 -0
  29. {stjames-0.0.75 → stjames-0.0.77}/stjames/data/symbol_element.json +0 -0
  30. {stjames-0.0.75 → stjames-0.0.77}/stjames/diis_settings.py +0 -0
  31. {stjames-0.0.75 → stjames-0.0.77}/stjames/grid_settings.py +0 -0
  32. {stjames-0.0.75 → stjames-0.0.77}/stjames/int_settings.py +0 -0
  33. {stjames-0.0.75 → stjames-0.0.77}/stjames/message.py +0 -0
  34. {stjames-0.0.75 → stjames-0.0.77}/stjames/mode.py +0 -0
  35. {stjames-0.0.75 → stjames-0.0.77}/stjames/opt_settings.py +0 -0
  36. {stjames-0.0.75 → stjames-0.0.77}/stjames/pdb.py +0 -0
  37. {stjames-0.0.75 → stjames-0.0.77}/stjames/periodic_cell.py +0 -0
  38. {stjames-0.0.75 → stjames-0.0.77}/stjames/py.typed +0 -0
  39. {stjames-0.0.75 → stjames-0.0.77}/stjames/scf_settings.py +0 -0
  40. {stjames-0.0.75 → stjames-0.0.77}/stjames/settings.py +0 -0
  41. {stjames-0.0.75 → stjames-0.0.77}/stjames/solvent.py +0 -0
  42. {stjames-0.0.75 → stjames-0.0.77}/stjames/status.py +0 -0
  43. {stjames-0.0.75 → stjames-0.0.77}/stjames/task.py +0 -0
  44. {stjames-0.0.75 → stjames-0.0.77}/stjames/thermochem_settings.py +0 -0
  45. {stjames-0.0.75 → stjames-0.0.77}/stjames/types.py +0 -0
  46. {stjames-0.0.75 → stjames-0.0.77}/stjames/workflows/__init__.py +0 -0
  47. {stjames-0.0.75 → stjames-0.0.77}/stjames/workflows/admet.py +0 -0
  48. {stjames-0.0.75 → stjames-0.0.77}/stjames/workflows/basic_calculation.py +0 -0
  49. {stjames-0.0.75 → stjames-0.0.77}/stjames/workflows/bde.py +0 -0
  50. {stjames-0.0.75 → stjames-0.0.77}/stjames/workflows/conformer.py +0 -0
  51. {stjames-0.0.75 → stjames-0.0.77}/stjames/workflows/conformer_search.py +0 -0
  52. {stjames-0.0.75 → stjames-0.0.77}/stjames/workflows/descriptors.py +0 -0
  53. {stjames-0.0.75 → stjames-0.0.77}/stjames/workflows/docking.py +0 -0
  54. {stjames-0.0.75 → stjames-0.0.77}/stjames/workflows/electronic_properties.py +0 -0
  55. {stjames-0.0.75 → stjames-0.0.77}/stjames/workflows/fukui.py +0 -0
  56. {stjames-0.0.75 → stjames-0.0.77}/stjames/workflows/hydrogen_bond_basicity.py +0 -0
  57. {stjames-0.0.75 → stjames-0.0.77}/stjames/workflows/ion_mobility.py +0 -0
  58. {stjames-0.0.75 → stjames-0.0.77}/stjames/workflows/irc.py +0 -0
  59. {stjames-0.0.75 → stjames-0.0.77}/stjames/workflows/macropka.py +0 -0
  60. {stjames-0.0.75 → stjames-0.0.77}/stjames/workflows/molecular_dynamics.py +0 -0
  61. {stjames-0.0.75 → stjames-0.0.77}/stjames/workflows/multistage_opt.py +0 -0
  62. {stjames-0.0.75 → stjames-0.0.77}/stjames/workflows/pka.py +0 -0
  63. {stjames-0.0.75 → stjames-0.0.77}/stjames/workflows/protein_cofolding.py +0 -0
  64. {stjames-0.0.75 → stjames-0.0.77}/stjames/workflows/redox_potential.py +0 -0
  65. {stjames-0.0.75 → stjames-0.0.77}/stjames/workflows/scan.py +0 -0
  66. {stjames-0.0.75 → stjames-0.0.77}/stjames/workflows/solubility.py +0 -0
  67. {stjames-0.0.75 → stjames-0.0.77}/stjames/workflows/spin_states.py +0 -0
  68. {stjames-0.0.75 → stjames-0.0.77}/stjames/workflows/tautomer.py +0 -0
  69. {stjames-0.0.75 → stjames-0.0.77}/stjames/workflows/workflow.py +0 -0
  70. {stjames-0.0.75 → stjames-0.0.77}/stjames.egg-info/SOURCES.txt +0 -0
  71. {stjames-0.0.75 → stjames-0.0.77}/stjames.egg-info/dependency_links.txt +0 -0
  72. {stjames-0.0.75 → stjames-0.0.77}/stjames.egg-info/requires.txt +0 -0
  73. {stjames-0.0.75 → stjames-0.0.77}/stjames.egg-info/top_level.txt +0 -0
  74. {stjames-0.0.75 → stjames-0.0.77}/tests/test_constraints.py +0 -0
  75. {stjames-0.0.75 → stjames-0.0.77}/tests/test_from_extxyz.py +0 -0
  76. {stjames-0.0.75 → stjames-0.0.77}/tests/test_molecule.py +0 -0
  77. {stjames-0.0.75 → stjames-0.0.77}/tests/test_pdb.py +0 -0
  78. {stjames-0.0.75 → stjames-0.0.77}/tests/test_rounding.py +0 -0
  79. {stjames-0.0.75 → stjames-0.0.77}/tests/test_settings.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: stjames
3
- Version: 0.0.75
3
+ Version: 0.0.77
4
4
  Summary: standardized JSON atom/molecule encoding scheme
5
5
  Author-email: Corin Wagen <corin@rowansci.com>
6
6
  Project-URL: Homepage, https://github.com/rowansci/stjames
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "stjames"
3
- version = "0.0.75"
3
+ version = "0.0.77"
4
4
  description = "standardized JSON atom/molecule encoding scheme"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -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, Method.OCP24_S, Method.OCP24_L, Method.RM1, Method.ORB_V3_CONSERVATIVE_INF_OMAT, Method.EGRET_1, Method.EGRET_1E, Method.EGRET_1T
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, atom1: PositiveInt, atom2: PositiveInt) -> float:
79
+ def distance(self, i: PositiveInt, j: PositiveInt) -> float:
79
80
  r"""
80
- Get the distance between atoms.
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[atom1 - 1].position, self.atoms[atom2 - 1].position)) ** 0.5 # type: ignore [no-any-return,unused-ignore]
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)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: stjames
3
- Version: 0.0.75
3
+ Version: 0.0.77
4
4
  Summary: standardized JSON atom/molecule encoding scheme
5
5
  Author-email: Corin Wagen <corin@rowansci.com>
6
6
  Project-URL: Homepage, https://github.com/rowansci/stjames
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