stjames 0.0.101__tar.gz → 0.0.103__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 (82) hide show
  1. {stjames-0.0.101/stjames.egg-info → stjames-0.0.103}/PKG-INFO +1 -1
  2. {stjames-0.0.101 → stjames-0.0.103}/pyproject.toml +1 -1
  3. {stjames-0.0.101 → stjames-0.0.103}/stjames/engine.py +1 -2
  4. {stjames-0.0.101 → stjames-0.0.103}/stjames/method.py +4 -9
  5. {stjames-0.0.101 → stjames-0.0.103}/stjames/types.py +20 -10
  6. {stjames-0.0.101 → stjames-0.0.103}/stjames/workflows/conformer_search.py +9 -4
  7. {stjames-0.0.101 → stjames-0.0.103}/stjames/workflows/docking.py +3 -3
  8. {stjames-0.0.101 → stjames-0.0.103}/stjames/workflows/macropka.py +4 -4
  9. {stjames-0.0.101 → stjames-0.0.103}/stjames/workflows/multistage_opt.py +7 -2
  10. {stjames-0.0.101 → stjames-0.0.103}/stjames/workflows/nmr.py +3 -3
  11. {stjames-0.0.101 → stjames-0.0.103}/stjames/workflows/protein_cofolding.py +2 -2
  12. {stjames-0.0.101 → stjames-0.0.103}/stjames/workflows/solubility.py +28 -4
  13. {stjames-0.0.101 → stjames-0.0.103/stjames.egg-info}/PKG-INFO +1 -1
  14. {stjames-0.0.101 → stjames-0.0.103}/LICENSE +0 -0
  15. {stjames-0.0.101 → stjames-0.0.103}/README.md +0 -0
  16. {stjames-0.0.101 → stjames-0.0.103}/setup.cfg +0 -0
  17. {stjames-0.0.101 → stjames-0.0.103}/stjames/__init__.py +0 -0
  18. {stjames-0.0.101 → stjames-0.0.103}/stjames/_deprecated_solvent_settings.py +0 -0
  19. {stjames-0.0.101 → stjames-0.0.103}/stjames/atom.py +0 -0
  20. {stjames-0.0.101 → stjames-0.0.103}/stjames/atomium_stjames/__init__.py +0 -0
  21. {stjames-0.0.101 → stjames-0.0.103}/stjames/atomium_stjames/data.py +0 -0
  22. {stjames-0.0.101 → stjames-0.0.103}/stjames/atomium_stjames/mmcif.py +0 -0
  23. {stjames-0.0.101 → stjames-0.0.103}/stjames/atomium_stjames/pdb.py +0 -0
  24. {stjames-0.0.101 → stjames-0.0.103}/stjames/atomium_stjames/utilities.py +0 -0
  25. {stjames-0.0.101 → stjames-0.0.103}/stjames/base.py +0 -0
  26. {stjames-0.0.101 → stjames-0.0.103}/stjames/basis_set.py +0 -0
  27. {stjames-0.0.101 → stjames-0.0.103}/stjames/calculation.py +0 -0
  28. {stjames-0.0.101 → stjames-0.0.103}/stjames/compute_settings.py +0 -0
  29. {stjames-0.0.101 → stjames-0.0.103}/stjames/constraint.py +0 -0
  30. {stjames-0.0.101 → stjames-0.0.103}/stjames/correction.py +0 -0
  31. {stjames-0.0.101 → stjames-0.0.103}/stjames/data/__init__.py +0 -0
  32. {stjames-0.0.101 → stjames-0.0.103}/stjames/data/bragg_radii.json +0 -0
  33. {stjames-0.0.101 → stjames-0.0.103}/stjames/data/elements.py +0 -0
  34. {stjames-0.0.101 → stjames-0.0.103}/stjames/data/isotopes.json +0 -0
  35. {stjames-0.0.101 → stjames-0.0.103}/stjames/data/nist_isotopes.json +0 -0
  36. {stjames-0.0.101 → stjames-0.0.103}/stjames/data/read_nist_isotopes.py +0 -0
  37. {stjames-0.0.101 → stjames-0.0.103}/stjames/data/symbol_element.json +0 -0
  38. {stjames-0.0.101 → stjames-0.0.103}/stjames/message.py +0 -0
  39. {stjames-0.0.101 → stjames-0.0.103}/stjames/mode.py +0 -0
  40. {stjames-0.0.101 → stjames-0.0.103}/stjames/molecule.py +0 -0
  41. {stjames-0.0.101 → stjames-0.0.103}/stjames/opt_settings.py +0 -0
  42. {stjames-0.0.101 → stjames-0.0.103}/stjames/optimization/__init__.py +0 -0
  43. {stjames-0.0.101 → stjames-0.0.103}/stjames/optimization/freezing_string_method.py +0 -0
  44. {stjames-0.0.101 → stjames-0.0.103}/stjames/pdb.py +0 -0
  45. {stjames-0.0.101 → stjames-0.0.103}/stjames/periodic_cell.py +0 -0
  46. {stjames-0.0.101 → stjames-0.0.103}/stjames/py.typed +0 -0
  47. {stjames-0.0.101 → stjames-0.0.103}/stjames/scf_settings.py +0 -0
  48. {stjames-0.0.101 → stjames-0.0.103}/stjames/settings.py +0 -0
  49. {stjames-0.0.101 → stjames-0.0.103}/stjames/solvent.py +0 -0
  50. {stjames-0.0.101 → stjames-0.0.103}/stjames/status.py +0 -0
  51. {stjames-0.0.101 → stjames-0.0.103}/stjames/task.py +0 -0
  52. {stjames-0.0.101 → stjames-0.0.103}/stjames/thermochem_settings.py +0 -0
  53. {stjames-0.0.101 → stjames-0.0.103}/stjames/workflows/__init__.py +0 -0
  54. {stjames-0.0.101 → stjames-0.0.103}/stjames/workflows/admet.py +0 -0
  55. {stjames-0.0.101 → stjames-0.0.103}/stjames/workflows/basic_calculation.py +0 -0
  56. {stjames-0.0.101 → stjames-0.0.103}/stjames/workflows/bde.py +0 -0
  57. {stjames-0.0.101 → stjames-0.0.103}/stjames/workflows/conformer.py +0 -0
  58. {stjames-0.0.101 → stjames-0.0.103}/stjames/workflows/descriptors.py +0 -0
  59. {stjames-0.0.101 → stjames-0.0.103}/stjames/workflows/double_ended_ts_search.py +0 -0
  60. {stjames-0.0.101 → stjames-0.0.103}/stjames/workflows/electronic_properties.py +0 -0
  61. {stjames-0.0.101 → stjames-0.0.103}/stjames/workflows/fukui.py +0 -0
  62. {stjames-0.0.101 → stjames-0.0.103}/stjames/workflows/hydrogen_bond_basicity.py +0 -0
  63. {stjames-0.0.101 → stjames-0.0.103}/stjames/workflows/ion_mobility.py +0 -0
  64. {stjames-0.0.101 → stjames-0.0.103}/stjames/workflows/irc.py +0 -0
  65. {stjames-0.0.101 → stjames-0.0.103}/stjames/workflows/molecular_dynamics.py +0 -0
  66. {stjames-0.0.101 → stjames-0.0.103}/stjames/workflows/pka.py +0 -0
  67. {stjames-0.0.101 → stjames-0.0.103}/stjames/workflows/pose_analysis_md.py +0 -0
  68. {stjames-0.0.101 → stjames-0.0.103}/stjames/workflows/redox_potential.py +0 -0
  69. {stjames-0.0.101 → stjames-0.0.103}/stjames/workflows/scan.py +0 -0
  70. {stjames-0.0.101 → stjames-0.0.103}/stjames/workflows/spin_states.py +0 -0
  71. {stjames-0.0.101 → stjames-0.0.103}/stjames/workflows/tautomer.py +0 -0
  72. {stjames-0.0.101 → stjames-0.0.103}/stjames/workflows/workflow.py +0 -0
  73. {stjames-0.0.101 → stjames-0.0.103}/stjames.egg-info/SOURCES.txt +0 -0
  74. {stjames-0.0.101 → stjames-0.0.103}/stjames.egg-info/dependency_links.txt +0 -0
  75. {stjames-0.0.101 → stjames-0.0.103}/stjames.egg-info/requires.txt +0 -0
  76. {stjames-0.0.101 → stjames-0.0.103}/stjames.egg-info/top_level.txt +0 -0
  77. {stjames-0.0.101 → stjames-0.0.103}/tests/test_constraints.py +0 -0
  78. {stjames-0.0.101 → stjames-0.0.103}/tests/test_from_extxyz.py +0 -0
  79. {stjames-0.0.101 → stjames-0.0.103}/tests/test_molecule.py +0 -0
  80. {stjames-0.0.101 → stjames-0.0.103}/tests/test_pdb.py +0 -0
  81. {stjames-0.0.101 → stjames-0.0.103}/tests/test_rounding.py +0 -0
  82. {stjames-0.0.101 → stjames-0.0.103}/tests/test_settings.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: stjames
3
- Version: 0.0.101
3
+ Version: 0.0.103
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.101"
3
+ version = "0.0.103"
4
4
  description = "standardized JSON atom/molecule encoding scheme"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -4,12 +4,11 @@ from .base import LowercaseStrEnum
4
4
  class Engine(LowercaseStrEnum):
5
5
  AIMNET2 = "aimnet2"
6
6
  MACE = "mace"
7
- OCP24 = "ocp24"
8
7
  OMOL25 = "omol25"
9
8
  ORB = "orb"
10
9
  TBLITE = "tblite"
11
10
  XTB = "xtb"
12
- TERRACHEM = "terrachem"
11
+ TERACHEM = "terachem"
13
12
  PYSCF = "pyscf"
14
13
  PSI4 = "psi4"
15
14
  OPENFF = "openff"
@@ -34,12 +34,11 @@ class Method(LowercaseStrEnum):
34
34
  AIMNET2_WB97MD3 = "aimnet2_wb97md3"
35
35
  MACE_MP_0 = "mace_mp_0"
36
36
  MACE_MP_0B2_L = "mace_mp_0b2_l"
37
- OCP24_S = "ocp24_s"
38
- OCP24_L = "ocp24_l"
39
37
  OMOL25_CONSERVING_S = "omol25_conserving_s"
40
38
  UMA_M_OMOL = "uma_m_omol"
41
39
  UMA_S_OMOL = "uma_s_omol"
42
40
  ORB_V3_CONSERVATIVE_INF_OMAT = "orb_v3_conservative_inf_omat"
41
+ ORB_V3_CONSERVATIVE_OMOL = "orb_v3_conservative_omol"
43
42
 
44
43
  GFN_FF = "gfn_ff"
45
44
  GFN0_XTB = "gfn0_xtb"
@@ -77,11 +76,9 @@ class Method(LowercaseStrEnum):
77
76
  return Engine.AIMNET2
78
77
  case Method.MACE_MP_0B2_L:
79
78
  return Engine.MACE
80
- case Method.OCP24_S | Method.OCP24_L:
81
- return Engine.OCP24
82
79
  case Method.OMOL25_CONSERVING_S | Method.UMA_M_OMOL | Method.UMA_S_OMOL:
83
80
  return Engine.OMOL25
84
- case Method.ORB_V3_CONSERVATIVE_INF_OMAT:
81
+ case Method.ORB_V3_CONSERVATIVE_INF_OMAT | Method.ORB_V3_CONSERVATIVE_OMOL:
85
82
  return Engine.ORB
86
83
  case method if method in XTB_METHODS:
87
84
  return Engine.TBLITE if is_periodic else Engine.XTB
@@ -96,12 +93,11 @@ class Method(LowercaseStrEnum):
96
93
 
97
94
  PrepackagedNNPMethod = Literal[
98
95
  Method.AIMNET2_WB97MD3,
99
- Method.OCP24_S,
100
- Method.OCP24_L,
101
96
  Method.OMOL25_CONSERVING_S,
102
97
  Method.UMA_M_OMOL,
103
98
  Method.UMA_S_OMOL,
104
99
  Method.ORB_V3_CONSERVATIVE_INF_OMAT,
100
+ Method.ORB_V3_CONSERVATIVE_OMOL,
105
101
  Method.EGRET_1,
106
102
  Method.EGRET_1E,
107
103
  Method.EGRET_1T,
@@ -109,12 +105,11 @@ PrepackagedNNPMethod = Literal[
109
105
 
110
106
  PREPACKAGED_NNP_METHODS = [
111
107
  Method.AIMNET2_WB97MD3,
112
- Method.OCP24_S,
113
- Method.OCP24_L,
114
108
  Method.OMOL25_CONSERVING_S,
115
109
  Method.UMA_M_OMOL,
116
110
  Method.UMA_S_OMOL,
117
111
  Method.ORB_V3_CONSERVATIVE_INF_OMAT,
112
+ Method.ORB_V3_CONSERVATIVE_OMOL,
118
113
  Method.EGRET_1,
119
114
  Method.EGRET_1E,
120
115
  Method.EGRET_1T,
@@ -1,4 +1,4 @@
1
- from typing import Callable, Iterable, TypeAlias
1
+ from typing import Callable, Iterable, Protocol, TypeAlias, overload
2
2
 
3
3
  UUID: TypeAlias = str
4
4
 
@@ -10,22 +10,32 @@ FloatPerAtom: TypeAlias = list[float]
10
10
  Matrix3x3: TypeAlias = tuple[Vector3D, Vector3D, Vector3D]
11
11
 
12
12
 
13
- def round_list(round_to: int = 6) -> Callable[[Iterable[float]], list[float]]:
14
- """Create a validator that rounds each float in a list to a given number of decimal places."""
13
+ class _Iterable_Rounder(Protocol):
14
+ """Callable with return types conditioned on the input type(s)."""
15
15
 
16
- def rounder(values: Iterable[float]) -> list[float]:
17
- return [round(value, round_to) for value in values]
16
+ @overload
17
+ def __call__(self, values: None, /) -> None: ...
18
+ @overload
19
+ def __call__(self, values: Iterable[float], /) -> list[float]: ...
20
+ @overload
21
+ def __call__(self, values: Iterable[float | None], /) -> list[float | None]: ...
18
22
 
19
- return rounder
20
23
 
24
+ def round_list(round_to: int = 6) -> _Iterable_Rounder:
25
+ """Round values (even if Nones), preserving types."""
21
26
 
22
- def round_optional_list(round_to: int = 6) -> Callable[[Iterable[float] | None], list[float] | None]:
23
- """Create a validator that rounds each float in a list to a given number of decimal places."""
27
+ @overload
28
+ def rounder(values: None, /) -> None: ...
29
+ @overload
30
+ def rounder(values: Iterable[float], /) -> list[float]: ...
31
+ @overload
32
+ def rounder(values: Iterable[float | None], /) -> list[float | None]: ...
24
33
 
25
- def rounder(values: Iterable[float] | None) -> list[float] | None:
34
+ def rounder(values: Iterable[float] | Iterable[float | None] | None, /) -> list[float] | list[float | None] | None:
26
35
  if values is None:
27
36
  return None
28
- return [round(value, round_to) for value in values]
37
+
38
+ return [round(v, round_to) if v is not None else None for v in values]
29
39
 
30
40
  return rounder
31
41
 
@@ -1,7 +1,7 @@
1
1
  """Conformer search workflow."""
2
2
 
3
3
  from abc import ABC
4
- from typing import Annotated, Self, Sequence, TypeVar
4
+ from typing import Annotated, Literal, Self, Sequence, TypeVar
5
5
 
6
6
  from pydantic import AfterValidator, BaseModel, Field, field_validator, model_validator
7
7
 
@@ -94,6 +94,7 @@ class ETKDGSettings(ConformerGenSettings):
94
94
  num_confs_considered: int = 100
95
95
  max_mmff_iterations: int = 500
96
96
  max_mmff_energy: float | None = 30
97
+ settings_type: Literal["etkdg"] = "etkdg"
97
98
 
98
99
  @field_validator("constraints")
99
100
  def check_constraints(cls, constraints: Sequence[Constraint]) -> Sequence[Constraint]:
@@ -209,6 +210,7 @@ class iMTDSettings(ConformerGenSettings, ABC):
209
210
  speed: iMTDSpeeds = iMTDSpeeds.QUICK
210
211
  reopt: bool = _sentinel # type: ignore [assignment]
211
212
  free_energy_weights: bool = False
213
+ settings_type: Literal["imtd"] = "imtd"
212
214
 
213
215
  @model_validator(mode="after")
214
216
  def validate_and_build_imtdgc_settings(self) -> Self:
@@ -253,6 +255,9 @@ class iMTDsMTDSettings(iMTDSettings):
253
255
  run_type: str = "imtd-smtd"
254
256
 
255
257
 
258
+ ConformerGenSettingsUnion = Annotated[ETKDGSettings | iMTDSettings, Field(discriminator="settings_type")]
259
+
260
+
256
261
  class ConformerGenMixin(BaseModel):
257
262
  """
258
263
  Mixin for workflows that need conformer generation.
@@ -265,7 +270,7 @@ class ConformerGenMixin(BaseModel):
265
270
  """
266
271
 
267
272
  conf_gen_mode: Mode = Mode.RAPID
268
- conf_gen_settings: ConformerGenSettings = _sentinel # type: ignore [assignment]
273
+ conf_gen_settings: ConformerGenSettingsUnion = _sentinel # type: ignore [assignment]
269
274
  constraints: Sequence[Constraint] = tuple()
270
275
  nci: bool = False
271
276
  max_confs: int | None = None
@@ -273,8 +278,8 @@ class ConformerGenMixin(BaseModel):
273
278
  @model_validator(mode="after")
274
279
  def validate_and_build_conf_gen_settings(self) -> Self:
275
280
  """Validate and build the ConformerGenSettings."""
276
- if self.conf_gen_settings is not _sentinel and self.conf_gen_mode != Mode.MANUAL:
277
- raise ValueError("Cannot specify conf_gen_settings with non-MANUAL mode")
281
+ if self.conf_gen_settings is not _sentinel:
282
+ return self
278
283
 
279
284
  match self.conf_gen_mode:
280
285
  case Mode.MANUAL:
@@ -7,7 +7,7 @@ from pydantic import AfterValidator, ConfigDict, field_validator, model_validato
7
7
  from ..base import Base, round_float
8
8
  from ..pdb import PDB
9
9
  from ..types import UUID, Vector3D
10
- from .conformer_search import ConformerGenSettings, ETKDGSettings
10
+ from .conformer_search import ConformerGenSettingsUnion, ETKDGSettings
11
11
  from .workflow import MoleculeWorkflow
12
12
 
13
13
 
@@ -79,12 +79,12 @@ class DockingWorkflow(MoleculeWorkflow):
79
79
  pocket: tuple[Vector3D, Vector3D]
80
80
 
81
81
  do_csearch: bool = True
82
- conformer_gen_settings: ConformerGenSettings = ETKDGSettings(mode="reckless")
82
+ conformer_gen_settings: ConformerGenSettingsUnion = ETKDGSettings(mode="reckless")
83
83
 
84
84
  do_optimization: bool = True
85
85
  # optimization_settings - here in future once we have a cleaner mode sol'n, ccw 7.9.25
86
86
 
87
- docking_settings: DockingSettings = VinaSettings()
87
+ docking_settings: VinaSettings = VinaSettings()
88
88
 
89
89
  do_pose_refinement: bool = True
90
90
 
@@ -4,7 +4,7 @@ from typing import Annotated, Optional, Self
4
4
 
5
5
  from pydantic import AfterValidator, model_validator
6
6
 
7
- from ..base import Base, round_float
7
+ from ..base import Base, round_float, round_optional_float
8
8
  from ..types import round_list
9
9
  from .workflow import SMILESWorkflow
10
10
 
@@ -72,9 +72,9 @@ class MacropKaWorkflow(SMILESWorkflow):
72
72
 
73
73
  microstates: list[MacropKaMicrostate] = []
74
74
  pKa_values: list[MacropKaValue] = []
75
- isoelectric_point: Annotated[Optional[float], AfterValidator(round_float(3))] = None
76
- solvation_energy: Annotated[Optional[float], AfterValidator(round_float(3))] = None
77
- kpuu_probability: Annotated[Optional[float], AfterValidator(round_float(3))] = None
75
+ isoelectric_point: Annotated[Optional[float], AfterValidator(round_optional_float(3))] = None
76
+ solvation_energy: Annotated[Optional[float], AfterValidator(round_optional_float(3))] = None
77
+ kpuu_probability: Annotated[Optional[float], AfterValidator(round_optional_float(3))] = None
78
78
 
79
79
  microstate_weights_by_pH: list[
80
80
  tuple[
@@ -1,5 +1,6 @@
1
1
  """Multi-stage optimization workflow."""
2
2
 
3
+ import logging
3
4
  import re
4
5
  from typing import Self, Sequence
5
6
 
@@ -18,6 +19,8 @@ from ..task import Task
18
19
  from ..types import UUID
19
20
  from .workflow import MoleculeWorkflow
20
21
 
22
+ logger = logging.getLogger(__name__)
23
+
21
24
 
22
25
  class MultiStageOptSettings(BaseModel):
23
26
  """
@@ -109,7 +112,8 @@ class MultiStageOptSettings(BaseModel):
109
112
  pass
110
113
 
111
114
  case (mode, True):
112
- raise ValueError(f"Cannot specify optimization_settings or singlepoint_settings with {mode=}")
115
+ logger.debug(f"Mode {mode=} specified with optimization_settings or singlepoint_settings, ignoring")
116
+ pass
113
117
 
114
118
  case (mode, False):
115
119
  self._assign_settings_by_mode(mode)
@@ -253,7 +257,8 @@ class MultiStageOptMixin(BaseModel):
253
257
  pass
254
258
 
255
259
  case (mso_mode, msos) if msos is not _sentinel_msos:
256
- raise ValueError(f"Cannot specify multistage_opt_settings with {mso_mode=}, {msos=}")
260
+ logger.debug(f"Mode {mso_mode=} specified with multistage_opt_settings, ignoring")
261
+ pass
257
262
 
258
263
  case (mso_mode, _):
259
264
  self.multistage_opt_settings = MultiStageOptSettings(
@@ -8,7 +8,7 @@ from ..base import Base, LowercaseStrEnum, round_float
8
8
  from ..mode import Mode
9
9
  from ..settings import Settings
10
10
  from ..solvent import Solvent
11
- from ..types import UUID, round_list, round_optional_list
11
+ from ..types import UUID, round_list
12
12
  from .conformer_search import ConformerGenSettings, iMTDSettings
13
13
  from .multistage_opt import MultiStageOptSettings
14
14
  from .workflow import MoleculeWorkflow
@@ -66,8 +66,8 @@ class NMRSpectroscopyWorkflow(MoleculeWorkflow):
66
66
 
67
67
  conformers: list[UUID] = []
68
68
  boltzmann_weights: Annotated[list[float], AfterValidator(round_list(3))] = []
69
- per_conformer_chemical_shifts: list[Annotated[list[float | None], AfterValidator(round_optional_list(3))]] = []
70
- chemical_shifts: Annotated[list[float | None], AfterValidator(round_optional_list(3))] = []
69
+ per_conformer_chemical_shifts: list[Annotated[list[float | None], AfterValidator(round_list(3))]] = []
70
+ chemical_shifts: Annotated[list[float | None], AfterValidator(round_list(3))] = []
71
71
  symmetry_equivalent_nuclei: list[list[int]] = []
72
72
 
73
73
  predicted_peaks: dict[int, list[NMRPeak]] = {}
@@ -5,7 +5,7 @@ from typing import Annotated, Literal
5
5
  from pydantic import AfterValidator, BaseModel, ConfigDict
6
6
 
7
7
  from ..base import LowercaseStrEnum, round_float
8
- from ..types import UUID, round_optional_list
8
+ from ..types import UUID, round_list
9
9
  from .workflow import FASTAWorkflow
10
10
 
11
11
 
@@ -86,4 +86,4 @@ class ProteinCofoldingWorkflow(FASTAWorkflow):
86
86
  scores: CofoldingScores | None = None
87
87
  model: CofoldingModel = CofoldingModel.BOLTZ_2
88
88
  affinity_score: AffinityScore | None = None
89
- lddt: Annotated[list[float] | None, AfterValidator(round_optional_list(3))] = None
89
+ lddt: Annotated[list[float] | None, AfterValidator(round_list(3))] = None
@@ -4,10 +4,17 @@ from typing import Annotated, Self
4
4
 
5
5
  from pydantic import AfterValidator, BaseModel, model_validator
6
6
 
7
+ from ..base import LowercaseStrEnum
7
8
  from ..types import round_list
8
9
  from .workflow import SMILESWorkflow
9
10
 
10
11
 
12
+ class SolubilityMethod(LowercaseStrEnum):
13
+ FASTSOLV = "fastsolv"
14
+ KINGFISHER = "kingfisher"
15
+ ESOL = "esol"
16
+
17
+
11
18
  class SolubilityResult(BaseModel):
12
19
  """
13
20
  Solubility result.
@@ -17,7 +24,7 @@ class SolubilityResult(BaseModel):
17
24
  """
18
25
 
19
26
  solubilities: Annotated[list[float], AfterValidator(round_list(6))]
20
- uncertainties: Annotated[list[float], AfterValidator(round_list(6))]
27
+ uncertainties: Annotated[list[float | None], AfterValidator(round_list(6))]
21
28
 
22
29
  @model_validator(mode="after")
23
30
  def check_size(self) -> Self:
@@ -41,9 +48,10 @@ class SolubilityWorkflow(SMILESWorkflow):
41
48
  :param solubilities: {solvent: SolubilityResult}
42
49
  """
43
50
 
51
+ solubility_method: SolubilityMethod = SolubilityMethod.FASTSOLV
44
52
  initial_smiles: str
45
- solvents: list[str]
46
- temperatures: list[float] = [273.15, 298.15, 323.15, 348.15, 373.15, 398.15, 423.15] # 0–150 °C
53
+ solvents: list[str] = ["O"]
54
+ temperatures: list[float] = [298.15]
47
55
 
48
56
  solubilities: dict[str, SolubilityResult] = {}
49
57
 
@@ -55,6 +63,22 @@ class SolubilityWorkflow(SMILESWorkflow):
55
63
  raise ValueError(f"Solubilities for {solvent} must have the same length as temperatures.")
56
64
 
57
65
  if solvent not in self.solvents:
58
- raise ValueError(f"Solvent {solvent} not in initial list of solvents")
66
+ raise ValueError(f"Solvent {solvent} not in initial list of solvents.")
67
+
68
+ return self
69
+
70
+ @model_validator(mode="after")
71
+ def check_solvent_temperature(self) -> Self:
72
+ """Check that models with limited domain of applicability are predicting within correct domain."""
73
+ match self.solubility_method:
74
+ case SolubilityMethod.KINGFISHER | SolubilityMethod.ESOL:
75
+ if (len(self.solvents) > 1) or (self.solvents[0] != "O"):
76
+ raise ValueError(f"Method `{self.solubility_method}` can only predict aqueous solubility, so `solvents` must be [`O`] only.")
77
+ if (len(self.temperatures) > 1) or (abs(self.temperatures[0] - 298.15) > 0.1):
78
+ raise ValueError(
79
+ f"Method `{self.solubility_method}` can only predict solubility at room temperature, so `temperatures` must be [298.15] only."
80
+ )
81
+ case _:
82
+ pass
59
83
 
60
84
  return self
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: stjames
3
- Version: 0.0.101
3
+ Version: 0.0.103
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