rowan-python 3.1.4__py3-none-any.whl → 3.1.6__py3-none-any.whl

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.
rowan/__init__.py CHANGED
@@ -2,15 +2,21 @@
2
2
  from . import constants
3
3
  from stjames import (
4
4
  Atom,
5
+ BandStructure,
5
6
  BindingPoseContact,
7
+ HydrationBridgeResidue,
8
+ HydrationSite,
6
9
  ConformerGenSettings,
7
10
  Constraint,
8
11
  ConstraintType,
9
12
  ConformerClusteringSettings,
10
13
  ConformerGenSettingsUnion,
11
14
  Correction,
15
+ CovalentInhibitorScanSettings,
16
+ DockingSettings,
12
17
  Engine,
13
18
  ETKDGSettings,
19
+ GninaSettings,
14
20
  GreedyClusteringSettings,
15
21
  iMTDSettings,
16
22
  KMeansClusteringSettings,
@@ -27,8 +33,10 @@ from stjames import (
27
33
  Settings,
28
34
  Solvent,
29
35
  SolventSettings,
36
+ SinglePointEnergySettings,
30
37
  Task,
31
38
  VibrationalMode,
39
+ VinaSettings,
32
40
  )
33
41
  from stjames.workflows.relative_binding_free_energy_perturbation import RBFEGraph, RBFEGraphEdge
34
42
  from stjames.excited_state_settings import TDDFTSettings
@@ -40,11 +48,9 @@ from stjames.engine_compatibility import (
40
48
  METHOD_ENGINES,
41
49
  get_supported_corrections,
42
50
  )
43
- from stjames.optimization.freezing_string_method import (
44
- FSMInterpolation,
45
- FSMOptimizationCoordinates,
46
- FSMSettings,
47
- )
51
+ from stjames.optimization.band_method import NEBSettings
52
+ from stjames.optimization.interpolation import Interpolation
53
+ from stjames.optimization.string_method import FSMSettings, StringMethodSettings
48
54
 
49
55
  api_key: str | None = None
50
56
  project_uuid: str | None = None
rowan/molecule.py CHANGED
@@ -237,6 +237,50 @@ class Molecule(BaseModel):
237
237
  """Nuclear gradient (Hartree/Bohr)."""
238
238
  return self._stjames.gradient
239
239
 
240
+ @property
241
+ def symmetry(self) -> str | int | None:
242
+ """Space group number (1–230) or point group symbol."""
243
+ return self._stjames.symmetry
244
+
245
+ @property
246
+ def xrd_peaks(self) -> list[tuple[int, int, int, float]] | None:
247
+ """XRD reflections as (h, k, l, intensity) tuples."""
248
+ return self._stjames.x_ray_diffraction_peaks
249
+
250
+ @property
251
+ def band_structure(self) -> "stjames.BandStructure | None":
252
+ """Electronic band structure and DOS (periodic systems only)."""
253
+ return self._stjames.band_structure
254
+
255
+ @property
256
+ def band_gap(self) -> float | None:
257
+ """Band gap (Hartree)."""
258
+ bs = self._stjames.band_structure
259
+ return bs.band_gap if bs is not None else None
260
+
261
+ @property
262
+ def density_of_states(self) -> list[tuple[float, float]] | None:
263
+ """Total DOS as (energy in Hartree, k-weighted count) pairs (Fermi level = 0)."""
264
+ bs = self._stjames.band_structure
265
+ return bs.total_density_of_states if bs is not None else None
266
+
267
+ @property
268
+ def elastic_tensor(
269
+ self,
270
+ ) -> (
271
+ tuple[
272
+ tuple[float, float, float, float, float, float],
273
+ tuple[float, float, float, float, float, float],
274
+ tuple[float, float, float, float, float, float],
275
+ tuple[float, float, float, float, float, float],
276
+ tuple[float, float, float, float, float, float],
277
+ tuple[float, float, float, float, float, float],
278
+ ]
279
+ | None
280
+ ):
281
+ """Elastic stiffness matrix in GPa (Voigt order: xx yy zz yz xz xy)."""
282
+ return self._stjames.elastic_tensor
283
+
240
284
  # -- Geometric utilities --
241
285
 
242
286
  def distance(self, i: int, j: int) -> float:
@@ -47,7 +47,17 @@ from .bde import (
47
47
  find_cx_bonds,
48
48
  submit_bde_workflow,
49
49
  )
50
+ from .binding_affinity import (
51
+ BindingAffinityResult,
52
+ BindingAffinityScore,
53
+ submit_binding_affinity_workflow,
54
+ )
50
55
  from .conformer_search import ConformerSearchResult, submit_conformer_search_workflow
56
+ from .covalent_inhibitor_scan import (
57
+ CovalentInhibitorScanPoint,
58
+ CovalentInhibitorScanResult,
59
+ submit_covalent_inhibitor_scan_workflow,
60
+ )
51
61
  from .descriptors import DescriptorsResult, submit_descriptors_workflow
52
62
  from .docking import DockingResult, DockingScore, submit_docking_workflow
53
63
  from .double_ended_ts_search import (
@@ -59,6 +59,7 @@ class AnalogueDockingResult(WorkflowResult):
59
59
  posebusters_valid=s.posebusters_valid,
60
60
  strain=s.strain,
61
61
  rmsd=s.rmsd,
62
+ mmgbsa_score=s.mmgbsa_score,
62
63
  )
63
64
  for s in scores
64
65
  ]
@@ -136,6 +136,54 @@ class BasicCalculationResult(WorkflowResult):
136
136
  mol = self.molecule
137
137
  return mol.frequencies if mol else None
138
138
 
139
+ @property
140
+ def symmetry(self) -> str | int | None:
141
+ """Space group number (1–230) or point group symbol."""
142
+ mol = self.molecule
143
+ return mol.symmetry if mol else None
144
+
145
+ @property
146
+ def xrd_peaks(self) -> list[tuple[int, int, int, float]] | None:
147
+ """XRD reflections as (h, k, l, intensity) tuples."""
148
+ mol = self.molecule
149
+ return mol.xrd_peaks if mol else None
150
+
151
+ @property
152
+ def band_structure(self) -> stjames.BandStructure | None:
153
+ """Electronic band structure and DOS (periodic systems only)."""
154
+ mol = self.molecule
155
+ return mol.band_structure if mol else None
156
+
157
+ @property
158
+ def band_gap(self) -> float | None:
159
+ """Band gap (Hartree)."""
160
+ mol = self.molecule
161
+ return mol.band_gap if mol else None
162
+
163
+ @property
164
+ def density_of_states(self) -> list[tuple[float, float]] | None:
165
+ """Total DOS as (energy in Hartree, k-weighted count) pairs (Fermi level = 0)."""
166
+ mol = self.molecule
167
+ return mol.density_of_states if mol else None
168
+
169
+ @property
170
+ def elastic_tensor(
171
+ self,
172
+ ) -> (
173
+ tuple[
174
+ tuple[float, float, float, float, float, float],
175
+ tuple[float, float, float, float, float, float],
176
+ tuple[float, float, float, float, float, float],
177
+ tuple[float, float, float, float, float, float],
178
+ tuple[float, float, float, float, float, float],
179
+ tuple[float, float, float, float, float, float],
180
+ ]
181
+ | None
182
+ ):
183
+ """Elastic stiffness matrix in GPa (Voigt order: xx yy zz yz xz xy)."""
184
+ mol = self.molecule
185
+ return mol.elastic_tensor if mol else None
186
+
139
187
 
140
188
  def settings_from_preset(preset: PresetName, **overrides: Any) -> stjames.Settings:
141
189
  """
@@ -0,0 +1,140 @@
1
+ """Binding affinity workflow — SQM-based scoring of protein–ligand complexes."""
2
+
3
+ from dataclasses import dataclass
4
+
5
+ import stjames
6
+ from stjames import SinglePointEnergySettings
7
+
8
+ from ..folder import Folder
9
+ from ..protein import Protein
10
+ from ..types import StructureInput
11
+ from ..utils import api_client
12
+ from .base import (
13
+ Message,
14
+ Workflow,
15
+ WorkflowResult,
16
+ molecule_to_dict,
17
+ parse_messages,
18
+ register_result,
19
+ require_coordinates,
20
+ )
21
+
22
+
23
+ @dataclass(frozen=True, slots=True)
24
+ class BindingAffinityScore:
25
+ """
26
+ Binding affinity score for a single pose.
27
+
28
+ :param binding_affinity: binding affinity in kcal/mol
29
+ :param strain: strain energy in kcal/mol, or None if not computed
30
+ """
31
+
32
+ binding_affinity: float
33
+ strain: float | None
34
+
35
+
36
+ @register_result("binding_affinity")
37
+ class BindingAffinityResult(WorkflowResult):
38
+ """Result from a binding affinity workflow."""
39
+
40
+ _stjames_class = stjames.BindingAffinityWorkflow
41
+
42
+ def __repr__(self) -> str:
43
+ n = len(self.scores)
44
+ return f"<BindingAffinityResult scores={n}>"
45
+
46
+ @property
47
+ def scores(self) -> list[BindingAffinityScore]:
48
+ """Binding affinity scores for each scored pose."""
49
+ return [
50
+ BindingAffinityScore(
51
+ binding_affinity=r.binding_affinity,
52
+ strain=r.strain,
53
+ )
54
+ for r in (self._workflow.binding_affinity_results or [])
55
+ ]
56
+
57
+ @property
58
+ def messages(self) -> list[Message]:
59
+ """Any messages or warnings from the workflow."""
60
+ return parse_messages(self._workflow.messages)
61
+
62
+
63
+ def submit_binding_affinity_workflow(
64
+ protein: str | Protein,
65
+ ligand_residue_name: str | None = None,
66
+ ligand_structures: list[StructureInput] | None = None,
67
+ binding_affinity_settings: SinglePointEnergySettings | None = None,
68
+ name: str = "Binding Affinity Workflow",
69
+ folder_uuid: str | None = None,
70
+ folder: Folder | None = None,
71
+ max_credits: int | None = None,
72
+ webhook_url: str | None = None,
73
+ is_draft: bool = False,
74
+ ) -> Workflow:
75
+ """
76
+ Submits a binding affinity workflow to the API.
77
+
78
+ Scores ligand poses using SQM-based energies. Two submission modes:
79
+
80
+ **Mode 1 — holo protein:** protein already contains the bound ligand. Pass
81
+ ``ligand_residue_name`` to identify which residue is the ligand vs. the receptor.
82
+ Do not pass ``ligand_structures``.
83
+
84
+ **Mode 2 — apo protein + external poses:** protein has no bound ligand. Pass
85
+ ``ligand_structures`` with poses that are already in the protein's coordinate frame.
86
+ Do not pass ``ligand_residue_name``. When scoring multiple ligands, prefer this mode
87
+ over separate per-ligand workflows — all poses share the same pocket geometry.
88
+
89
+ :param protein: protein structure. Can be input as a UUID or a Protein object.
90
+ :param ligand_residue_name: residue name identifying the ligand in a holo protein PDB
91
+ (mode 1 only).
92
+ :param ligand_structures: external ligand poses to score, already in the protein's
93
+ coordinate frame. Must have 3D coordinates (mode 2 only).
94
+ :param binding_affinity_settings: SQM settings controlling geometry optimization and
95
+ energy evaluation. Defaults to PM6-D3H4X/COSMO optimization followed by
96
+ PM6-D3H4X/COSMO2 single-point in water.
97
+ :param name: name of the workflow.
98
+ :param folder_uuid: UUID of the folder to place the workflow in.
99
+ :param folder: Folder object to store the workflow in.
100
+ :param max_credits: maximum number of credits to use for the workflow.
101
+ :param webhook_url: URL that Rowan will POST to when the workflow completes.
102
+ :param is_draft: if True, submit the workflow as a draft without starting execution.
103
+ :returns: Workflow object representing the submitted workflow.
104
+ :raises ValueError: if folder arguments conflict.
105
+ :raises requests.HTTPError: if the request to the API fails.
106
+ """
107
+ if folder and folder_uuid:
108
+ raise ValueError("Provide either `folder` or `folder_uuid`, not both.")
109
+ if folder:
110
+ folder_uuid = folder.uuid
111
+ if isinstance(protein, Protein):
112
+ protein = protein.uuid
113
+
114
+ mol_dicts: list[dict] = []
115
+ if ligand_structures:
116
+ for mol in ligand_structures:
117
+ require_coordinates(mol)
118
+ mol_dicts.append(molecule_to_dict(mol))
119
+
120
+ workflow = stjames.BindingAffinityWorkflow(
121
+ protein=protein,
122
+ ligand_residue_name=ligand_residue_name,
123
+ ligand_structures=mol_dicts or [],
124
+ binding_affinity_settings=binding_affinity_settings or SinglePointEnergySettings(),
125
+ )
126
+
127
+ data = {
128
+ "workflow_type": "binding_affinity",
129
+ "workflow_data": workflow.model_dump(serialize_as_any=True, mode="json"),
130
+ "name": name,
131
+ "folder_uuid": folder_uuid,
132
+ "max_credits": max_credits,
133
+ "webhook_url": webhook_url,
134
+ "is_draft": is_draft,
135
+ }
136
+
137
+ with api_client() as client:
138
+ response = client.post("/workflow", json=data)
139
+ response.raise_for_status()
140
+ return Workflow(**response.json())
@@ -0,0 +1,129 @@
1
+ """Covalent inhibitor scan workflow - bond scan for a covalent inhibitor reaction."""
2
+
3
+ from dataclasses import dataclass
4
+
5
+ import stjames
6
+
7
+ from ..folder import Folder
8
+ from ..molecule import Molecule
9
+ from ..protein import Protein
10
+ from ..utils import api_client
11
+ from .base import Workflow, WorkflowResult, register_result
12
+
13
+
14
+ @dataclass(frozen=True, slots=True)
15
+ class CovalentInhibitorScanPoint:
16
+ """
17
+ A point along the covalent inhibitor bond scan.
18
+
19
+ :param index: index of the point along the scan
20
+ :param distance: scanned bond distance, in Å
21
+ :param molecule: Molecule at the point
22
+ :param energy: implicit-solvent single-point energy at this geometry, in Hartree
23
+ :param uuid: UUID of the scan-point calculation
24
+ """
25
+
26
+ index: int
27
+ distance: float
28
+ molecule: Molecule
29
+ energy: float | None
30
+ uuid: str | None
31
+
32
+
33
+ @register_result("covalent_inhibitor_scan")
34
+ class CovalentInhibitorScanResult(WorkflowResult):
35
+ """Result from a covalent inhibitor scan workflow."""
36
+
37
+ _stjames_class = stjames.CovalentInhibitorScanWorkflow
38
+
39
+ def __repr__(self) -> str:
40
+ return f"<CovalentInhibitorScanResult points={len(self.scan_points)}>"
41
+
42
+ @property
43
+ def scan_points(self) -> list[CovalentInhibitorScanPoint]:
44
+ """Points along the scan, ordered by scan index (scan_start to scan_stop)."""
45
+ return [
46
+ CovalentInhibitorScanPoint(
47
+ index=p.index,
48
+ distance=p.distance,
49
+ molecule=Molecule.from_stjames(p.molecule),
50
+ energy=p.energy,
51
+ uuid=p.uuid,
52
+ )
53
+ for p in self._workflow.scan_points
54
+ ]
55
+
56
+ def get_energies(self) -> list[tuple[float, float | None]]:
57
+ """
58
+ Get scan distances paired with single-point energies.
59
+
60
+ :returns: List of (distance, energy) tuples, in Å and Hartree respectively.
61
+ """
62
+ return [(p.distance, p.energy) for p in self.scan_points]
63
+
64
+
65
+ def submit_covalent_inhibitor_scan_workflow(
66
+ protein: str | Protein,
67
+ protein_reactive_atom_index: int,
68
+ ligand_reactive_atom_index: int,
69
+ ligand_smiles: str,
70
+ settings: stjames.CovalentInhibitorScanSettings | None = None,
71
+ name: str = "Covalent Inhibitor Scan Workflow",
72
+ folder_uuid: str | None = None,
73
+ folder: Folder | None = None,
74
+ max_credits: int | None = None,
75
+ webhook_url: str | None = None,
76
+ is_draft: bool = False,
77
+ ) -> Workflow:
78
+ """
79
+ Submits a covalent inhibitor scan workflow to the API.
80
+
81
+ :param protein: covalently docked protein-ligand complex (protein plus the ligand as a
82
+ non-polymer residue). Can be a UUID or a Protein object.
83
+ :param protein_reactive_atom_index: 0-based index of the reacting protein atom, in PDB
84
+ record order.
85
+ :param ligand_reactive_atom_index: 0-based index of the reacting ligand atom, in PDB
86
+ record order.
87
+ :param ligand_smiles: SMILES of the ligand, whose net charge sets the ligand charge of the QM
88
+ cluster.
89
+ :param settings: settings controlling the scan. Defaults to
90
+ `stjames.CovalentInhibitorScanSettings()`.
91
+ :param name: name of the workflow.
92
+ :param folder_uuid: UUID of the folder to place the workflow in.
93
+ :param folder: Folder object to store the workflow in.
94
+ :param max_credits: maximum number of credits to use for the workflow.
95
+ :param webhook_url: URL that Rowan will POST to when the workflow completes.
96
+ :param is_draft: if True, submit the workflow as a draft without starting execution.
97
+ :returns: Workflow object representing the submitted workflow.
98
+ :raises requests.HTTPError: if the request to the API fails.
99
+ """
100
+ if folder and folder_uuid:
101
+ raise ValueError("Provide either `folder` or `folder_uuid`, not both.")
102
+ if folder:
103
+ folder_uuid = folder.uuid
104
+ if isinstance(protein, Protein):
105
+ protein = protein.uuid
106
+
107
+ workflow = stjames.CovalentInhibitorScanWorkflow(
108
+ protein=protein,
109
+ protein_reactive_atom_index=protein_reactive_atom_index,
110
+ ligand_reactive_atom_index=ligand_reactive_atom_index,
111
+ initial_smiles=ligand_smiles,
112
+ settings=settings or stjames.CovalentInhibitorScanSettings(),
113
+ )
114
+
115
+ data = {
116
+ "workflow_type": "covalent_inhibitor_scan",
117
+ "workflow_data": workflow.model_dump(mode="json"),
118
+ "initial_smiles": ligand_smiles,
119
+ "name": name,
120
+ "folder_uuid": folder_uuid,
121
+ "max_credits": max_credits,
122
+ "webhook_url": webhook_url,
123
+ "is_draft": is_draft,
124
+ }
125
+
126
+ with api_client() as client:
127
+ response = client.post("/workflow", json=data)
128
+ response.raise_for_status()
129
+ return Workflow(**response.json())
@@ -1,5 +1,6 @@
1
1
  """Docking workflow - molecular docking to protein targets."""
2
2
 
3
+ import warnings
3
4
  from dataclasses import dataclass
4
5
 
5
6
  import stjames
@@ -21,7 +22,11 @@ from .base import (
21
22
 
22
23
  @dataclass(frozen=True, slots=True)
23
24
  class DockingScore:
24
- """A docking pose with its score."""
25
+ """A docking pose with its scores.
26
+
27
+ :param score: Docking score in kcal/mol.
28
+ :param mmgbsa_score: MM/GBSA binding free energy estimate in kcal/mol.
29
+ """
25
30
 
26
31
  score: float
27
32
  pose: str | None = None
@@ -29,6 +34,7 @@ class DockingScore:
29
34
  posebusters_valid: bool = False
30
35
  strain: float | None = None
31
36
  rmsd: float | None = None
37
+ mmgbsa_score: float | None = None
32
38
 
33
39
 
34
40
  @register_result("docking")
@@ -59,6 +65,7 @@ class DockingResult(WorkflowResult):
59
65
  posebusters_valid=s.posebusters_valid,
60
66
  strain=s.strain,
61
67
  rmsd=s.rmsd,
68
+ mmgbsa_score=s.mmgbsa_score,
62
69
  )
63
70
  for s in self._workflow.scores
64
71
  ]
@@ -146,10 +153,11 @@ def submit_docking_workflow(
146
153
  protein: str | Protein,
147
154
  pocket: list[list[float]],
148
155
  initial_molecule: StructureInput,
149
- executable: str = "vina",
150
- scoring_function: str = "vinardo",
151
- exhaustiveness: float = 8,
152
- max_poses: int = 4,
156
+ docking_settings: stjames.VinaSettings | stjames.GninaSettings | None = None,
157
+ executable: str | None = None,
158
+ scoring_function: str | None = None,
159
+ exhaustiveness: float | None = None,
160
+ max_poses: int | None = None,
153
161
  do_csearch: bool = False,
154
162
  do_optimization: bool = False,
155
163
  do_pose_refinement: bool = True,
@@ -166,10 +174,17 @@ def submit_docking_workflow(
166
174
  :param protein: Protein to dock. Can be input as a uuid or a Protein object.
167
175
  :param pocket: Binding pocket as ``[[cx, cy, cz], [sx, sy, sz]]`` — center (Å) and box size (Å).
168
176
  :param initial_molecule: Initial molecule to be docked.
169
- :param executable: Which docking implementation to use.
170
- :param scoring_function: Which docking scoring function to use.
171
- :param exhaustiveness: Which exhaustiveness to employ.
172
- :param max_poses: Maximum number of poses generated per input conformer.
177
+ :param docking_settings: Settings controlling the docking engine, e.g. ``VinaSettings`` or
178
+ ``GninaSettings`` (for noncovalent or covalent gnina docking). If provided, the deprecated
179
+ ``executable``, ``scoring_function``, ``exhaustiveness``, and ``max_poses`` are ignored.
180
+ :param executable: Deprecated, use `docking_settings=VinaSettings(executable=...)` instead.
181
+ Which Vina docking implementation to use.
182
+ :param scoring_function: Deprecated, use `docking_settings=VinaSettings(scoring_function=...)`
183
+ instead. Which Vina docking scoring function to use.
184
+ :param exhaustiveness: Deprecated, use `docking_settings=VinaSettings(exhaustiveness=...)`
185
+ instead. Which exhaustiveness to employ.
186
+ :param max_poses: Deprecated, use `docking_settings=VinaSettings(max_poses=...)` instead.
187
+ Maximum number of poses generated per input conformer.
173
188
  :param do_csearch: Whether to perform a conformational search on the ligand.
174
189
  :param do_optimization: Whether to perform an optimization on the ligand.
175
190
  :param do_pose_refinement: Whether or not to optimize output poses.
@@ -192,12 +207,26 @@ def submit_docking_workflow(
192
207
  if isinstance(protein, Protein):
193
208
  protein = protein.uuid
194
209
 
195
- docking_settings = stjames.VinaSettings(
196
- executable=executable,
197
- exhaustiveness=exhaustiveness,
198
- max_poses=max_poses,
199
- scoring_function=scoring_function,
200
- )
210
+ legacy_vina_kwargs = {
211
+ k: v
212
+ for k, v in {
213
+ "executable": executable,
214
+ "exhaustiveness": exhaustiveness,
215
+ "max_poses": max_poses,
216
+ "scoring_function": scoring_function,
217
+ }.items()
218
+ if v is not None
219
+ }
220
+ if legacy_vina_kwargs:
221
+ warnings.warn(
222
+ "executable, scoring_function, exhaustiveness, and max_poses are deprecated; "
223
+ "pass docking_settings=VinaSettings(...) instead.",
224
+ DeprecationWarning,
225
+ stacklevel=2,
226
+ )
227
+
228
+ if docking_settings is None:
229
+ docking_settings = stjames.VinaSettings(**legacy_vina_kwargs)
201
230
 
202
231
  workflow = stjames.DockingWorkflow(
203
232
  initial_molecule=mol_dict,
@@ -210,7 +239,6 @@ def submit_docking_workflow(
210
239
  )
211
240
 
212
241
  workflow_data = workflow.model_dump(serialize_as_any=True, mode="json")
213
- workflow_data["docking_settings"].setdefault("settings_type", "vina")
214
242
 
215
243
  data = {
216
244
  "workflow_type": "docking",
@@ -4,7 +4,8 @@ from dataclasses import dataclass
4
4
  from typing import Any
5
5
 
6
6
  import stjames
7
- from stjames.optimization.freezing_string_method import FSMSettings
7
+ from stjames.optimization.band_method import NEBSettings
8
+ from stjames.optimization.string_method import StringMethodSettings
8
9
 
9
10
  from ..calculation import Calculation, retrieve_calculation
10
11
  from ..folder import Folder
@@ -151,7 +152,7 @@ def submit_double_ended_ts_search_workflow(
151
152
  reactant: StructureInput,
152
153
  product: StructureInput,
153
154
  calculation_settings: stjames.Settings | dict[str, Any] | None = None,
154
- search_settings: FSMSettings | dict[str, Any] | None = None,
155
+ search_settings: StringMethodSettings | NEBSettings | dict[str, Any] | None = None,
155
156
  optimize_inputs: bool = True,
156
157
  optimize_ts: bool = True,
157
158
  name: str = "Double-Ended TS Search Workflow",
@@ -167,7 +168,7 @@ def submit_double_ended_ts_search_workflow(
167
168
  :param reactant: reactant Molecule.
168
169
  :param product: product Molecule.
169
170
  :param calculation_settings: Settings to use for calculations. Defaults to omol25_conserving_s.
170
- :param search_settings: Settings for the transition state search. Defaults to FSMSettings().
171
+ :param search_settings: Settings for the transition state search; resolves to FSM
171
172
  :param optimize_inputs: Whether to optimize the reactant and product before the search.
172
173
  :param optimize_ts: Whether to optimize the found transition state.
173
174
  :param name: name of the workflow.
@@ -195,7 +196,7 @@ def submit_double_ended_ts_search_workflow(
195
196
  reactant=reactant_dict,
196
197
  product=product_dict,
197
198
  calculation_settings=calculation_settings,
198
- search_settings=search_settings or FSMSettings(),
199
+ search_settings=search_settings or StringMethodSettings(),
199
200
  optimize_inputs=optimize_inputs,
200
201
  optimize_ts=optimize_ts,
201
202
  )
rowan/workflows/irc.py CHANGED
@@ -165,6 +165,44 @@ class IRCResult(WorkflowResult):
165
165
  energies: list[float] = [m.energy for m in self.backward_molecules if m.energy is not None]
166
166
  return to_relative_kcal(energies) if relative else energies
167
167
 
168
+ @property
169
+ def forward_step_sizes(self) -> list[float]:
170
+ """Actual step sizes taken along the forward IRC path (Å√amu)."""
171
+ return list(self._workflow.step_sizes_forward)
172
+
173
+ @property
174
+ def backward_step_sizes(self) -> list[float]:
175
+ """Actual step sizes taken along the backward IRC path (Å√amu)."""
176
+ return list(self._workflow.step_sizes_backward)
177
+
178
+ @property
179
+ def forward_endpoint_calculation(self) -> Calculation | None:
180
+ """Optimization of the forward endpoint (if ``optimize_endpoints=True``, lazily fetched)."""
181
+ if "forward_endpoint_calc" not in self._cache:
182
+ uuid = self._workflow.endpoint_opt_forward
183
+ self._cache["forward_endpoint_calc"] = retrieve_calculation(uuid) if uuid else None
184
+ return self._cache["forward_endpoint_calc"]
185
+
186
+ @property
187
+ def backward_endpoint_calculation(self) -> Calculation | None:
188
+ """Optimization of backward endpoint (if ``optimize_endpoints=True``, lazily fetched)."""
189
+ if "backward_endpoint_calc" not in self._cache:
190
+ uuid = self._workflow.endpoint_opt_backward
191
+ self._cache["backward_endpoint_calc"] = retrieve_calculation(uuid) if uuid else None
192
+ return self._cache["backward_endpoint_calc"]
193
+
194
+ @property
195
+ def forward_endpoint_molecule(self) -> Molecule | None:
196
+ """Optimized forward endpoint molecule (if ``optimize_endpoints=True``)."""
197
+ calc = self.forward_endpoint_calculation
198
+ return calc.molecule if calc else None
199
+
200
+ @property
201
+ def backward_endpoint_molecule(self) -> Molecule | None:
202
+ """Optimized backward endpoint molecule (if ``optimize_endpoints=True``)."""
203
+ calc = self.backward_endpoint_calculation
204
+ return calc.molecule if calc else None
205
+
168
206
 
169
207
  def submit_irc_workflow(
170
208
  initial_molecule: StructureInput,
@@ -177,6 +215,7 @@ def submit_irc_workflow(
177
215
  preopt: bool = True,
178
216
  step_size: float = 0.05,
179
217
  max_irc_steps: int = 30,
218
+ optimize_endpoints: bool = False,
180
219
  name: str = "IRC Workflow",
181
220
  folder_uuid: str | None = None,
182
221
  folder: Folder | None = None,
@@ -195,8 +234,9 @@ def submit_irc_workflow(
195
234
  :param engine: Engine for the calculation (and optional preopt)
196
235
  :param pbc_dft_settings: PBC DFT settings for the IRC (and optional preopt)
197
236
  :param preopt: Whether to perform a pre-optimization of the TS guess
198
- :param step_size: Step size for the IRC calculation
237
+ :param step_size: Step size for the IRC calculation (0.001-0.5 Å√amu)
199
238
  :param max_irc_steps: Maximum number of IRC steps to perform
239
+ :param optimize_endpoints: Whether to optimize the endpoint geometries once the IRC completes
200
240
  :param name: Name for the workflow
201
241
  :param folder_uuid: UUID of the folder to place the workflow in
202
242
  :param folder: Folder object to store the workflow in
@@ -244,6 +284,7 @@ def submit_irc_workflow(
244
284
  preopt=preopt,
245
285
  step_size=step_size,
246
286
  max_irc_steps=max_irc_steps,
287
+ optimize_endpoints=optimize_endpoints,
247
288
  )
248
289
 
249
290
  data = {
@@ -73,6 +73,11 @@ class PoseAnalysisMDResult(WorkflowResult):
73
73
  for t in raw
74
74
  ]
75
75
 
76
+ @property
77
+ def hydration_sites(self) -> list[stjames.HydrationSite]:
78
+ """Hydration sites identified across all trajectories."""
79
+ return self._workflow.hydration_sites or []
80
+
76
81
  @property
77
82
  def average_rmsds(self) -> list[float | None]:
78
83
  """Average ligand RMSD per trajectory (Angstrom)."""
@@ -80,6 +80,7 @@ class CofoldingResult:
80
80
  :param scores: Confidence scores for the prediction.
81
81
  :param affinity_score: Predicted binding affinity (if computed).
82
82
  :param strain: Ligand strain energy (if computed).
83
+ :param mmgbsa_score: MM/GBSA binding free energy estimate in kcal/mol (if computed).
83
84
  :param posebusters_valid: Whether the pose passes PoseBusters validation.
84
85
  :param lddt: Per-residue LDDT confidence scores.
85
86
  :param pose_uuid: UUID of the pose.
@@ -90,6 +91,7 @@ class CofoldingResult:
90
91
  scores: CofoldingScores | None = None
91
92
  affinity_score: AffinityScore | None = None
92
93
  strain: float | None = None
94
+ mmgbsa_score: float | None = None
93
95
  posebusters_valid: bool | None = None
94
96
  lddt: list[float] | None = None
95
97
  pose_uuid: str | None = None
@@ -197,6 +199,7 @@ class ProteinCofoldingResult(WorkflowResult):
197
199
  scores=scores,
198
200
  affinity_score=affinity_score,
199
201
  strain=getattr(r, "strain", None),
202
+ mmgbsa_score=getattr(r, "mmgbsa_score", None),
200
203
  posebusters_valid=getattr(r, "posebusters_valid", None),
201
204
  lddt=list(lddt) if lddt else None,
202
205
  pose_uuid=getattr(r, "pose", None),
@@ -97,6 +97,9 @@ def submit_relative_binding_free_energy_graph_workflow(
97
97
  greedy_k_min_cut: int = 3,
98
98
  refine_cutoff: float | None = None,
99
99
  seed_graph: RBFEGraph | None = None,
100
+ generate_intermediate_ligands: bool = False,
101
+ intermediate_max_dummy_atoms: int = 25,
102
+ intermediate_min_dummy_atom_improvement: int = 5,
100
103
  name: str = "RBFE Graph",
101
104
  folder_uuid: str | None = None,
102
105
  folder: Folder | None = None,
@@ -120,6 +123,12 @@ def submit_relative_binding_free_energy_graph_workflow(
120
123
  :param seed_graph: RBFE graph from a prior run to extend, as returned by a
121
124
  completed result's ``graph``. Its existing edges (and any computed results)
122
125
  are preserved, and only edges for newly added ligands are built.
126
+ :param generate_intermediate_ligands: Generate virtual intermediate ligands to make
127
+ difficult edges easier.
128
+ :param intermediate_max_dummy_atoms: An edge with more dummy atoms than this is split
129
+ when a generated intermediate brings both resulting legs back to at most this value.
130
+ :param intermediate_min_dummy_atom_improvement: Minimum reduction in the worst leg's
131
+ dummy-atom count required for a generated intermediate to be accepted.
123
132
  :param name: Name of the workflow.
124
133
  :param folder_uuid: UUID of the folder to place the workflow in.
125
134
  :param folder: Folder object to store the workflow in.
@@ -169,6 +178,9 @@ def submit_relative_binding_free_energy_graph_workflow(
169
178
  greedy_k_min_cut=greedy_k_min_cut,
170
179
  refine_cutoff=refine_cutoff,
171
180
  seed_graph=seed_graph,
181
+ generate_intermediate_ligands=generate_intermediate_ligands,
182
+ intermediate_max_dummy_atoms=intermediate_max_dummy_atoms,
183
+ intermediate_min_dummy_atom_improvement=intermediate_min_dummy_atom_improvement,
172
184
  )
173
185
 
174
186
  data = {
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rowan-python
3
- Version: 3.1.4
3
+ Version: 3.1.6
4
4
  Summary: Rowan Python Library
5
5
  Project-URL: Homepage, https://github.com/rowansci/rowan-client
6
6
  Project-URL: Bug Tracker, https://github.com/rowansci/rowan-client/issues
@@ -11,7 +11,7 @@ Requires-Dist: httpx
11
11
  Requires-Dist: nest-asyncio
12
12
  Requires-Dist: rdkit
13
13
  Requires-Dist: setuptools
14
- Requires-Dist: stjames>=0.0.208
14
+ Requires-Dist: stjames>=0.0.228
15
15
  Description-Content-Type: text/markdown
16
16
 
17
17
  # Rowan Python Library
@@ -1,34 +1,36 @@
1
- rowan/__init__.py,sha256=AfjV9IsjgykiU39Eh9vjUP_3at-OSqqhSgNhPxutma0,1456
1
+ rowan/__init__.py,sha256=95VjN9XTgEICFLCcB4oUbT_nsU_EvulDqMDJjBuSTI8,1714
2
2
  rowan/api_keys.py,sha256=TvG5l5MmQ3Qt8Z3Y7jCA_lcrwEHuI7xHy-KLbIdQ8_A,4793
3
3
  rowan/calculation.py,sha256=nuUtLTtn9D-FCM9TJl3h5euBgIYqa5fr5r_PSNBS7Xo,3809
4
4
  rowan/config.py,sha256=TejQKSxnzNKKTNL9-2bCLq6RvAh54oVA5Ivl1p_ZT8Q,20899
5
5
  rowan/constants.py,sha256=emCH4m9OL2Hm5E-6mJGM_FgzrK_JrZT-FiKJ6pMNQ4Y,84
6
6
  rowan/folder.py,sha256=RRg68dObluJ2RCTKX3rb7RxziOXOQUYxkfQSU2R5VMk,13708
7
- rowan/molecule.py,sha256=W6eBuIQAjZs5JiQFmyRnbcbj2Qy2XbZnm_J0WuMslG4,13881
7
+ rowan/molecule.py,sha256=1l-3YCN_A75Uz_Q-dMWuhkeF8rhD1z2JQM7AENyjKYM,15512
8
8
  rowan/project.py,sha256=RtxYE9jv3Yz6fH5I56iDGRG5EbwlOHiSK-HF1uxc0d4,4582
9
9
  rowan/protein.py,sha256=T2JAqfncP80yV5gEWYA-TlpNY3WeWaNHPE0Xg5voU24,17968
10
10
  rowan/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
11
  rowan/types.py,sha256=MvcYvEYr6N9RXLal44MqsnMxoWfcBa-VLD_rGsW4HOc,591
12
12
  rowan/user.py,sha256=rOCYVhSoRY71fCG_OCSD7cLAWWnAMANWGitF3ogfq30,6212
13
13
  rowan/utils.py,sha256=DBphY1he8Vl3RNf6CbLp5BSX9RG5EfHzhMs-9TmcE_g,4299
14
- rowan/workflows/__init__.py,sha256=ohl2q-Nqgs4E9cl5KIGigQgE2ifiTmY5dib5R_qNxO4,4510
14
+ rowan/workflows/__init__.py,sha256=pkmAl8aPDznu15bSuRW0j8id-B7Xmzy_tq3LgZfQ7NQ,4786
15
15
  rowan/workflows/admet.py,sha256=jDx1Kkgmp0XaeojPzaCFY_yUwvqtSM7Cezps-93cqPw,2865
16
- rowan/workflows/analogue_docking.py,sha256=IxVaYD8ITsLFfvZ6N7v4KmvIS25DcUQkv-_tMhEfG_4,9707
16
+ rowan/workflows/analogue_docking.py,sha256=TshOw71RmK5XaaTIHz_Q1opiDU9x1L38tXwxoYUZaxY,9756
17
17
  rowan/workflows/base.py,sha256=k858otXLuorw4MZ1hvqZI_m0n_38htC7Hy3OMCX1TOA,32961
18
- rowan/workflows/basic_calculation.py,sha256=CnAnbRYrnjbxgcPAZxPUARUjaqPCnAAjf-RFku8R1r0,11240
18
+ rowan/workflows/basic_calculation.py,sha256=EovymT6fV6mxmt4G4kRtiskSPGB7w7KAHqBaGtgQI2Q,12941
19
19
  rowan/workflows/batch_docking.py,sha256=ePnb1hcsmnUfffl1-k9HNkOC09RjY9bZS9XcqAGNeFs,3621
20
20
  rowan/workflows/bde.py,sha256=1ieYUeu-5Lg637eSjhbRr_6qSIiIcjhsLOzaA4zbg3o,6681
21
+ rowan/workflows/binding_affinity.py,sha256=Pengz5gmhYvRd3P6yTe1p7nLZnDENp_gwDJo2q5kiEg,5089
21
22
  rowan/workflows/conformer_search.py,sha256=9_DpRfmeNWIVm68Y95zcHCK5Q0yt9le6W70rMeIQndY,15467
22
23
  rowan/workflows/constants.py,sha256=el8jWE9gnGTLNWn5_n_V0H362vIRneOqgy7BOQ8CScg,575
24
+ rowan/workflows/covalent_inhibitor_scan.py,sha256=BNqGLE2CjSlkOlXj_7KcsX9GEstMD3Ts-v2qIlRWK48,4717
23
25
  rowan/workflows/descriptors.py,sha256=QjjpzPDpAO_EhPOK--gle6sGpVHMpdAs_yrvC0oiseY,3092
24
- rowan/workflows/docking.py,sha256=VTauRB6knysUsAzGhEhI95F6BQLAuZbNEA468wByohc,8031
25
- rowan/workflows/double_ended_ts_search.py,sha256=eZNEXhfh5W5j5JcEzKnKMMf9HPbIM6HDLnzoh_vVFXk,8115
26
+ rowan/workflows/docking.py,sha256=SfFzZER-fYGPn9OZyEE9BSAestjCbbUaP4z9OapA1ko,9391
27
+ rowan/workflows/double_ended_ts_search.py,sha256=nse7oK-8R5h_FtpZZAzes0TQCs2XOdEbdyZFOECK8B0,8193
26
28
  rowan/workflows/electronic_properties.py,sha256=MXbsI4OHyXWt3f4Qc5g7-SNjyyEoYzocWVdcP1H06sY,8436
27
29
  rowan/workflows/fukui.py,sha256=Lyx3FgSdidzPMj-PpSzOb9uXCZj2S_52p8zBYxBbprg,4412
28
30
  rowan/workflows/hydrogen_bond_donor_acceptor_strength.py,sha256=xgFFemIZpIOkgkiYx20TmCexWNxNgRZl4H4t-zfQqUE,5254
29
31
  rowan/workflows/interaction_energy_decomposition.py,sha256=wK9Mc9uGmFBG8H03LRRRz3nZzM6p7Z2ol1yiZbUB5ec,6176
30
32
  rowan/workflows/ion_mobility.py,sha256=GqVVPRffaBJvygB3mwbMJq2aW7y19J3AnkDx_ZMIRss,4021
31
- rowan/workflows/irc.py,sha256=Zf1zh-UjTrQnZ4fwQ4jDJKh80nsyMymAPK6qUoOShKI,10075
33
+ rowan/workflows/irc.py,sha256=qA8rLvhSkyOBBDtUw8Yq1iaRHiCUil0pI_HsMhVcuEQ,12069
32
34
  rowan/workflows/macropka.py,sha256=IxGwr9LziIwj_x6pXEhJN6SywS_KULziuYoETNqM370,5684
33
35
  rowan/workflows/membrane_permeability.py,sha256=wHZ1IPlXwBB343uVAm7EuhedcMPd-Uy4jMqHfXRHwqY,4680
34
36
  rowan/workflows/msa.py,sha256=k_FlxxtdgvmxDN3LyLDEQeFfI8_lyBJz1KFtz3S4oNo,4816
@@ -36,11 +38,11 @@ rowan/workflows/multistage_optimization.py,sha256=04kZPC3SmVOxqEQOwyBrS3DlelM-b8
36
38
  rowan/workflows/nmr.py,sha256=68be_Xgiy1AykGdIL4fR1pqKllD1nLm3IidsStzKtz4,5521
37
39
  rowan/workflows/pka.py,sha256=NZ-3i-cH_b7YOHj2iq0roXEcZmC8SYapmzMOX6ObRqw,8546
38
40
  rowan/workflows/pocket_detection.py,sha256=aGHY0puxekp4c4nsNYHcvKCe1fsetygL04BcSvNFvE8,3864
39
- rowan/workflows/pose_analysis_md.py,sha256=XJIfvn-H7GA6lVtw9uKjlgznVSSsr9bJiDt_PjKlPbA,11572
41
+ rowan/workflows/pose_analysis_md.py,sha256=5TRsCBFD0QObE5tpmCOxi137xsKMfQrRO6ef8TLrnkk,11767
40
42
  rowan/workflows/protein_binder_design.py,sha256=pgywTQDuXHplZYka-61_S6CC4WTPDCwJrbupp825eC4,9281
41
- rowan/workflows/protein_cofolding.py,sha256=CV13VUHC0NLDDaS1-GGTQ3RySkzEE30xzu1_4oPu6lo,16239
43
+ rowan/workflows/protein_cofolding.py,sha256=haT0liJorxyHgO02WX8WslqAu7Dc7QOSs6VbE9ZJwuw,16433
42
44
  rowan/workflows/protein_md.py,sha256=zqKEtL514uchgbxsyO4vhokzTU-X4lTFA1BM5O8HvTU,10117
43
- rowan/workflows/rbfe_graph.py,sha256=7UJA3ZBbvtaB9l1KQi9279Pc89moVnBHO-XJpxEr5uQ,7238
45
+ rowan/workflows/rbfe_graph.py,sha256=0eMfvwtQhyajUC8T-bhdcYNKi6Pkja6x6dHjCnEwxPw,8081
44
46
  rowan/workflows/redox_potential.py,sha256=bBeT1K9XGVpjCFZeeuv7Vtr3EYHN2okJ4LqYc3gMV04,5427
45
47
  rowan/workflows/relative_binding_free_energy_perturbation.py,sha256=vi5Qj5aDAERtjcHDsgzI6vRV_BZJnJ8boOj5aG6GXzo,13817
46
48
  rowan/workflows/scan.py,sha256=R5Y6Md3ohPoFVJ4NYvx3_k3JNwMnVw1CDsNPD9S8sr4,7518
@@ -49,7 +51,7 @@ rowan/workflows/solvent_dependent_conformers.py,sha256=Z2xESmcM8WcTsR2TSTUa--Rrh
49
51
  rowan/workflows/spin_states.py,sha256=rjkgie2-XVNIN7O6P93yn6EPv9Ogjy8iTj3ufZIsUgY,9331
50
52
  rowan/workflows/strain.py,sha256=kCW_BlX__sdQG1JVbFZuB-57rkpnxa_JCw3h4uKdGDk,6425
51
53
  rowan/workflows/tautomer_search.py,sha256=mbRl0ZJ7wibueRF8c8_idhXJ1rtwg7LBt--5QwQ_Cck,5767
52
- rowan_python-3.1.4.dist-info/METADATA,sha256=qogpVoI_aoX_24Z2diW8JdOR2sphEYp-HuLtTFvvN-w,2052
53
- rowan_python-3.1.4.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
54
- rowan_python-3.1.4.dist-info/licenses/LICENSE,sha256=i05z7xEhyrg6f8j0lR3XYjShnF-MJGFQ-DnpsZ8yiVI,1084
55
- rowan_python-3.1.4.dist-info/RECORD,,
54
+ rowan_python-3.1.6.dist-info/METADATA,sha256=DNhG5-OE8U_DwL4OgWgBf3tKivdvSYRMW9kmh_kq48g,2052
55
+ rowan_python-3.1.6.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
56
+ rowan_python-3.1.6.dist-info/licenses/LICENSE,sha256=i05z7xEhyrg6f8j0lR3XYjShnF-MJGFQ-DnpsZ8yiVI,1084
57
+ rowan_python-3.1.6.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.30.1
2
+ Generator: hatchling 1.31.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any