rowan-python 2.1.7__py3-none-any.whl → 2.1.8__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.
@@ -844,7 +844,7 @@ async def _single_conformers(
844
844
  raise NoConformersError("This molecule has no conformers")
845
845
 
846
846
  sorted_data = sorted(
847
- zip(data["energies"], data["conformer_uuids"]),
847
+ zip(data["energies"], data["conformer_uuids"], strict=True),
848
848
  key=lambda x: x[0],
849
849
  )
850
850
 
rowan/workflow.py CHANGED
@@ -1,5 +1,7 @@
1
+ import logging
1
2
  import time
2
3
  from datetime import datetime
4
+ from pathlib import Path
3
5
  from typing import Any, Literal, Self, TypeAlias
4
6
 
5
7
  import stjames
@@ -10,6 +12,9 @@ from stjames.optimization.freezing_string_method import FSMSettings
10
12
  from .protein import Protein
11
13
  from .utils import api_client
12
14
 
15
+ logger = logging.getLogger(__name__)
16
+ logger.setLevel(logging.INFO)
17
+
13
18
  RdkitMol: TypeAlias = Chem.rdchem.Mol | Chem.rdchem.RWMol
14
19
  StJamesMolecule: TypeAlias = stjames.Molecule
15
20
 
@@ -216,6 +221,25 @@ class Workflow(BaseModel):
216
221
  response = client.delete(f"/workflow/{self.uuid}/delete_workflow_data")
217
222
  response.raise_for_status()
218
223
 
224
+ def download_msa_files(self, msa_format: stjames.MSAFormat, path: Path | None = None) -> None:
225
+ if self.workflow_type != "msa":
226
+ raise ValueError("This workflow is not an MSA workflow.")
227
+
228
+ if path is None:
229
+ path = Path.cwd()
230
+
231
+ if not path.exists():
232
+ path.mkdir(parents=True, exist_ok=True)
233
+
234
+ with api_client() as client:
235
+ response = client.get(
236
+ f"/workflow/{self.uuid}/get_msa_files", params={"msa_format": msa_format.value}
237
+ )
238
+ response.raise_for_status()
239
+
240
+ with open(path / f"{self.name}-msa.tar.gz", "wb") as f:
241
+ f.write(response.content)
242
+
219
243
 
220
244
  def submit_workflow(
221
245
  workflow_type: stjames.WORKFLOW_NAME,
@@ -272,6 +296,75 @@ def submit_workflow(
272
296
  return Workflow(**response.json())
273
297
 
274
298
 
299
+ def batch_submit_workflow(
300
+ workflow_type: stjames.WORKFLOW_NAME,
301
+ workflow_data: dict[str, Any] | None = None,
302
+ initial_molecules: list[dict[str, Any] | StJamesMolecule | RdkitMol] | None = None,
303
+ initial_smileses: list[str] | None = None,
304
+ names: list[str] | None = None,
305
+ folder_uuid: str | None = None,
306
+ max_credits: int | None = None,
307
+ ) -> list[Workflow]:
308
+ """
309
+ Submits a batch of workflows to the API. Each workflow will be submitted with the
310
+ same workflow type, workflow data, and folder UUID, but with different initial molecules and/or
311
+ SMILES strings.
312
+
313
+ :param workflow_type: The type of workflow to submit.
314
+ :param workflow_data: A dictionary containing the data required to run the workflow.
315
+ :param initial_molecules: A list of molecule objects to use as the initial molecules in the
316
+ workflows.
317
+ At least one of a molecule or SMILES must be provided.
318
+ :param initial_smileses: A list of SMILES strings to use as the initial molecules in the
319
+ workflows.
320
+ At least one of a molecule or SMILES must be provided.
321
+ :param names: A list of names to give to the workflows.
322
+ :param folder_uuid: The UUID of the folder to store the workflow in.
323
+ :param max_credits: The maximum number of credits to use for the workflow. This applies per
324
+ workflow, not for the batch.
325
+ :return: A list of Workflow objects representing the submitted workflows.
326
+ :raises ValueError: If neither `initial_smiles` nor a valid `initial_molecule` is provided.
327
+ :raises HTTPError: If the API request fails.
328
+ """
329
+ if workflow_type not in stjames.WORKFLOW_MAPPING:
330
+ raise ValueError(
331
+ "Invalid workflow type. Must be one of:\n " + "\n ".join(stjames.WORKFLOW_MAPPING)
332
+ )
333
+
334
+ if initial_molecules and initial_smileses:
335
+ raise ValueError(
336
+ "You must provide either `initial_molecules` or `initial_smileses`, but not both."
337
+ )
338
+
339
+ if names:
340
+ if initial_molecules and len(names) != len(initial_molecules):
341
+ logger.warning(
342
+ "The number of names is not the same as the number of initial "
343
+ "molecules. Generic names of the form 'Batch Workflow {i}' "
344
+ "will be used."
345
+ )
346
+ if initial_smileses and len(names) != len(initial_smileses):
347
+ logger.warning(
348
+ "The number of names is not the same as the number of initial SMILES."
349
+ "Generic names of the form 'Batch Workflow {i}' will be used."
350
+ )
351
+
352
+ data = {
353
+ "names": names,
354
+ "folder_uuid": folder_uuid,
355
+ "workflow_type": workflow_type,
356
+ "workflow_data": workflow_data,
357
+ "max_credits": max_credits,
358
+ "initial_molecules": initial_molecules,
359
+ "initial_smileses": initial_smileses,
360
+ }
361
+
362
+ with api_client() as client:
363
+ response = client.post("/workflow/batch_submit", json=data)
364
+ response.raise_for_status()
365
+ return [Workflow(**workflow_data) for workflow_data in response.json()]
366
+
367
+
275
368
  def retrieve_workflow(uuid: str) -> Workflow:
276
369
  """
277
370
  Retrieves a workflow from the API.
@@ -402,7 +495,7 @@ def submit_basic_calculation_workflow(
402
495
  tasks=tasks,
403
496
  mode=mode,
404
497
  ),
405
- engine=engine,
498
+ engine=engine or method.default_engine(),
406
499
  )
407
500
 
408
501
  data = {
@@ -551,8 +644,10 @@ def submit_solubility_workflow(
551
644
 
552
645
 
553
646
  def submit_pka_workflow(
554
- initial_molecule: dict[str, Any] | StJamesMolecule | RdkitMol,
647
+ initial_molecule: dict[str, Any] | StJamesMolecule | RdkitMol | str,
555
648
  pka_range: tuple[int, int] = (2, 12),
649
+ method: Literal["aimnet2_wagen2024", "chemprop_nevolianis2025"] = "aimnet2_wagen2024",
650
+ solvent: str = "water",
556
651
  deprotonate_elements: list[int] | None = None,
557
652
  protonate_elements: list[int] | None = None,
558
653
  mode: str = "careful",
@@ -564,7 +659,10 @@ def submit_pka_workflow(
564
659
  Submits a pKa workflow to the API.
565
660
 
566
661
  :param initial_molecule: The molecule to calculate the pKa of.
662
+ Valid input types include `stjames.Molecule`, RDKit molecule, and SMILES.
567
663
  :param pka_range: The range of pKa values to calculate.
664
+ :param method: The algorithm used to compute pKa values.
665
+ :param solvent: The solvent in which pKa values will be computed.
568
666
  :param deprotonate_elements: The elements to deprotonate. Given by atomic number.
569
667
  :param protonate_elements: The elements to protonate. Given by atomic number.
570
668
  :param mode: The mode to run the calculation in. See
@@ -576,20 +674,30 @@ def submit_pka_workflow(
576
674
  :return: A Workflow object representing the submitted workflow.
577
675
  :raises requests.HTTPError: if the request to the API fails.
578
676
  """
579
- if isinstance(initial_molecule, StJamesMolecule):
580
- initial_molecule = initial_molecule.model_dump()
677
+ initial_smiles: str = ""
678
+ initial_stjames_mol: StJamesMolecule | None = None
679
+
680
+ if isinstance(initial_molecule, dict):
681
+ initial_stjames_mol = StJamesMolecule.model_validate(initial_molecule)
682
+ elif isinstance(initial_molecule, StJamesMolecule):
683
+ initial_stjames_mol = initial_molecule
581
684
  elif isinstance(initial_molecule, RdkitMol):
582
- initial_molecule = StJamesMolecule.from_rdkit(initial_molecule, cid=0)
685
+ initial_stjames_mol = StJamesMolecule.from_rdkit(initial_molecule, cid=0)
686
+ elif isinstance(initial_molecule, str):
687
+ initial_smiles = initial_molecule
583
688
 
584
689
  protonate_elements = protonate_elements or [7]
585
690
  deprotonate_elements = deprotonate_elements or [7, 8, 16]
586
691
 
587
692
  workflow = stjames.pKaWorkflow(
588
- initial_molecule=initial_molecule,
693
+ initial_molecule=initial_stjames_mol,
694
+ initial_smiles=initial_smiles,
589
695
  pka_range=pka_range,
590
696
  deprotonate_elements=deprotonate_elements,
591
697
  protonate_elements=protonate_elements,
592
698
  mode=mode,
699
+ solvent=solvent,
700
+ microscopic_pka_method=method,
593
701
  )
594
702
 
595
703
  data = {
@@ -597,7 +705,8 @@ def submit_pka_workflow(
597
705
  "folder_uuid": folder_uuid,
598
706
  "workflow_type": "pka",
599
707
  "workflow_data": workflow.model_dump(),
600
- "initial_molecule": initial_molecule,
708
+ "initial_molecule": initial_stjames_mol.model_dump() if initial_stjames_mol else None,
709
+ "initial_smiles": initial_smiles,
601
710
  "max_credits": max_credits,
602
711
  }
603
712
 
@@ -1040,6 +1149,9 @@ def submit_docking_workflow(
1040
1149
  protein: str | Protein,
1041
1150
  pocket: list[list[float]],
1042
1151
  initial_molecule: dict[str, Any] | StJamesMolecule | RdkitMol | None = None,
1152
+ executable: str = "vina",
1153
+ scoring_function: str = "vinardo",
1154
+ exhaustiveness: float = 8,
1043
1155
  do_csearch: bool = False,
1044
1156
  do_optimization: bool = False,
1045
1157
  do_pose_refinement: bool = False,
@@ -1050,15 +1162,18 @@ def submit_docking_workflow(
1050
1162
  """
1051
1163
  Submits a Docking workflow to the API.
1052
1164
 
1053
- :param protein: The protein to dock. Can be fed as a uuid or a Protein object.
1165
+ :param protein: The protein to dock. Can be input as a uuid or a Protein object.
1054
1166
  :param initial_molecule: The initial molecule to be docked
1167
+ :param executable: Which docking implementation to use.
1168
+ :param scoring_function: Which docking scoring function to use.
1169
+ :param exhaustiveness: Which exhaustiveness to employ.
1055
1170
  :param do_csearch: Whether to perform a conformational search on the ligand.
1056
1171
  :param do_optimization: Whether to perform an optimization on the ligand.
1057
1172
  :param do_pose_refinement: Whether or not to optimize output poses.
1058
1173
  :param name: The name of the workflow.
1059
1174
  :param folder_uuid: The UUID of the folder to place the workflow in.
1060
1175
  :param max_credits: The maximum number of credits to use for the workflow.
1061
- :return: A Workflow object representing the submitted IRC workflow.
1176
+ :return: A Workflow object representing the submitted docking workflow.
1062
1177
  :raises requests.HTTPError: if the request to the API fails.
1063
1178
  """
1064
1179
 
@@ -1070,6 +1185,12 @@ def submit_docking_workflow(
1070
1185
  if isinstance(protein, Protein):
1071
1186
  protein = protein.uuid
1072
1187
 
1188
+ docking_settings = {
1189
+ "executable": executable,
1190
+ "exhaustiveness": exhaustiveness,
1191
+ "scoring_function": scoring_function,
1192
+ }
1193
+
1073
1194
  workflow = stjames.DockingWorkflow(
1074
1195
  initial_molecule=initial_molecule,
1075
1196
  target_uuid=protein,
@@ -1077,6 +1198,7 @@ def submit_docking_workflow(
1077
1198
  do_csearch=do_csearch,
1078
1199
  do_optimization=do_optimization,
1079
1200
  do_pose_refinement=do_pose_refinement,
1201
+ docking_settings=docking_settings,
1080
1202
  )
1081
1203
 
1082
1204
  data = {
@@ -1283,3 +1405,137 @@ def submit_double_ended_ts_search_workflow(
1283
1405
  response = client.post("/workflow", json=data)
1284
1406
  response.raise_for_status()
1285
1407
  return Workflow(**response.json())
1408
+
1409
+
1410
+ def submit_pose_analysis_md_workflow(
1411
+ protein: str | Protein,
1412
+ initial_smiles: str,
1413
+ num_trajectories: int = 1,
1414
+ simulation_time_ns: int = 10,
1415
+ ligand_residue_name: str = "LIG",
1416
+ name: str = "Pose-Analysis MD Workflow",
1417
+ folder_uuid: str | None = None,
1418
+ max_credits: int | None = None,
1419
+ ) -> Workflow:
1420
+ """
1421
+ Submits a pose-analysis MD workflow to the API.
1422
+
1423
+ :param protein: The *holo* protein on which MD will be run.
1424
+ Can be input as a uuid or a Protein object.
1425
+ :param initial_smiles: The SMILES for the ligand.
1426
+ :param num_trajectories: The number of trajectories to run.
1427
+ :param simulation_time_ns: How long to run the simulation for, in nanoseconds.
1428
+ :param ligand_residue_name: The name of the residue corresponding to the ligand.
1429
+ :param name: The name of the workflow.
1430
+ :param folder_uuid: The UUID of the folder to place the workflow in.
1431
+ :param max_credits: The maximum number of credits to use for the workflow.
1432
+ :return: A Workflow object representing the submitted workflow.
1433
+ :raises requests.HTTPError: if the request to the API fails.
1434
+ """
1435
+
1436
+ if isinstance(protein, Protein):
1437
+ protein = protein.uuid
1438
+
1439
+ workflow = stjames.PoseAnalysisMolecularDynamicsWorkflow(
1440
+ protein_uuid=protein,
1441
+ initial_smiles=initial_smiles,
1442
+ num_trajectories=num_trajectories,
1443
+ simulation_time_ns=simulation_time_ns,
1444
+ ligand_residue_name=ligand_residue_name,
1445
+ )
1446
+
1447
+ data = {
1448
+ "name": name,
1449
+ "folder_uuid": folder_uuid,
1450
+ "workflow_type": "pose_analysis_md",
1451
+ "workflow_data": workflow.model_dump(serialize_as_any=True),
1452
+ "initial_smiles": initial_smiles,
1453
+ "max_credits": max_credits,
1454
+ }
1455
+
1456
+ with api_client() as client:
1457
+ response = client.post("/workflow", json=data)
1458
+ response.raise_for_status()
1459
+ return Workflow(**response.json())
1460
+
1461
+
1462
+ def submit_batch_docking_workflow(
1463
+ smiles_list: list[str],
1464
+ protein: str | Protein,
1465
+ pocket: list[list[float]],
1466
+ executable: str = "qvina2",
1467
+ scoring_function: str = "vina",
1468
+ exhaustiveness: float = 8,
1469
+ name: str = "Batch Docking Workflow",
1470
+ folder_uuid: str | None = None,
1471
+ max_credits: int | None = None,
1472
+ ) -> Workflow:
1473
+ """
1474
+ Submits a batch docking workflow to the API.
1475
+
1476
+ :param smiles_list: The SMILES strings to dock.
1477
+ :param protein: The protein to dock. Can be input as a uuid or a Protein object.
1478
+ :param executable: Which docking implementation to use.
1479
+ :param scoring_function: Which docking scoring function to use.
1480
+ :param exhaustiveness: Which exhaustiveness to employ.
1481
+ :param name: The name of the workflow.
1482
+ :param folder_uuid: The UUID of the folder to place the workflow in.
1483
+ :param max_credits: The maximum number of credits to use for the workflow.
1484
+ :return: A Workflow object representing the submitted batch docking workflow.
1485
+ :raises requests.HTTPError: if the request to the API fails.
1486
+ """
1487
+
1488
+ docking_settings = {
1489
+ "executable": executable,
1490
+ "exhaustiveness": exhaustiveness,
1491
+ "scoring_function": scoring_function,
1492
+ }
1493
+
1494
+ workflow = stjames.BatchDockingWorkflow(
1495
+ initial_smiles_list=smiles_list,
1496
+ target=protein,
1497
+ pocket=pocket,
1498
+ docking_settings=docking_settings,
1499
+ )
1500
+
1501
+ data = {
1502
+ "name": name,
1503
+ "folder_uuid": folder_uuid,
1504
+ "workflow_type": "batch_docking",
1505
+ "workflow_data": workflow.model_dump(serialize_as_any=True),
1506
+ "max_credits": max_credits,
1507
+ }
1508
+
1509
+ with api_client() as client:
1510
+ response = client.post("/workflow", json=data)
1511
+ response.raise_for_status()
1512
+ return Workflow(**response.json())
1513
+
1514
+
1515
+ def submit_msa_workflow(
1516
+ initial_protein_sequences: list[stjames.ProteinSequence | str],
1517
+ output_formats: list[stjames.MSAFormat],
1518
+ name: str = "MSA Workflow",
1519
+ folder_uuid: str | None = None,
1520
+ max_credits: int | None = None,
1521
+ ) -> Workflow:
1522
+ """
1523
+ Submits an MSA workflow to the API.
1524
+ """
1525
+ workflow = stjames.MSAWorkflow(
1526
+ initial_protein_sequences=initial_protein_sequences,
1527
+ output_formats=output_formats,
1528
+ )
1529
+
1530
+ data = {
1531
+ "name": name,
1532
+ "folder_uuid": folder_uuid,
1533
+ "workflow_type": "msa",
1534
+ "workflow_data": workflow.model_dump(serialize_as_any=True),
1535
+ "max_credits": max_credits,
1536
+ }
1537
+
1538
+ with api_client() as client:
1539
+ response = client.post("/workflow", json=data)
1540
+ response.raise_for_status()
1541
+ return Workflow(**response.json())
@@ -1,17 +1,17 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rowan-python
3
- Version: 2.1.7
3
+ Version: 2.1.8
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
7
7
  Author-email: Corin Wagen <corin@rowansci.com>
8
8
  License-File: LICENSE
9
- Requires-Python: >=3.9
9
+ Requires-Python: >=3.11
10
10
  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.109
14
+ Requires-Dist: stjames>=0.0.125
15
15
  Description-Content-Type: text/markdown
16
16
 
17
17
  # Rowan Python Library
@@ -6,10 +6,10 @@ rowan/protein.py,sha256=bMemvLZua_pnTrYOxHFZ4jFlRH9KgpYvtjj5M2__28k,8026
6
6
  rowan/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
7
  rowan/user.py,sha256=Dl--NPUPATKCs2VmILsW8HnLiunG0Lxr0n6mKuHm21U,3891
8
8
  rowan/utils.py,sha256=64II-cPOe_SFJK302Bm8hP62d_3_CgnTVYCbn3zKT7U,3334
9
- rowan/workflow.py,sha256=ec4ygHqsyldHn6-gpyPwzHfsGcQXRZOauIjdHtdzJuE,48826
9
+ rowan/workflow.py,sha256=tPvFXYC9O8Qr_9HVGDzYTgW-3MrFcDLGqbGDzlOq5tI,58562
10
10
  rowan/rowan_rdkit/__init__.py,sha256=EATX2VRzywzKxqkpCUMTf7RNQLkWsfi5VcCNDW6EIiw,503
11
- rowan/rowan_rdkit/chem_utils.py,sha256=i7-EmAcmvHYtc9NiZblLY_k2DoQKofAZo5KT2qtkUCI,34775
12
- rowan_python-2.1.7.dist-info/METADATA,sha256=XWltMwFRX9VyDOTy7rUE86IDXL8xBakq-SCcJpDBOPo,1599
13
- rowan_python-2.1.7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
14
- rowan_python-2.1.7.dist-info/licenses/LICENSE,sha256=i05z7xEhyrg6f8j0lR3XYjShnF-MJGFQ-DnpsZ8yiVI,1084
15
- rowan_python-2.1.7.dist-info/RECORD,,
11
+ rowan/rowan_rdkit/chem_utils.py,sha256=sKCzul2e0ldVYTBImhTwso7ddNgPKmvS-OmvCEjVJH0,34788
12
+ rowan_python-2.1.8.dist-info/METADATA,sha256=H4imf_mPDAVYyT8l7AYJsC947qE2yfu0labnvuhDdX4,1600
13
+ rowan_python-2.1.8.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
14
+ rowan_python-2.1.8.dist-info/licenses/LICENSE,sha256=i05z7xEhyrg6f8j0lR3XYjShnF-MJGFQ-DnpsZ8yiVI,1084
15
+ rowan_python-2.1.8.dist-info/RECORD,,