rowan-python 3.1.3__tar.gz → 3.1.4__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.
Files changed (179) hide show
  1. {rowan_python-3.1.3 → rowan_python-3.1.4}/PKG-INFO +1 -1
  2. {rowan_python-3.1.3 → rowan_python-3.1.4}/docs/index.md +32 -0
  3. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/batch_docking.py +1 -1
  4. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/boltz_paired_msa.py +2 -2
  5. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/boltz_single_msa.py +2 -2
  6. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/chai_paired_msa.py +2 -2
  7. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/chai_single_msa.py +2 -2
  8. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/colabfold_paired_msa.py +2 -2
  9. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/colabfold_single_msa.py +2 -2
  10. rowan_python-3.1.4/examples/docking.py +39 -0
  11. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/docking_screen.py +5 -2
  12. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/pocket_detection.py +1 -1
  13. rowan_python-3.1.4/examples/resubmit_with_perturbations.py +39 -0
  14. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/scan.py +7 -7
  15. {rowan_python-3.1.3 → rowan_python-3.1.4}/pyproject.toml +1 -1
  16. {rowan_python-3.1.3 → rowan_python-3.1.4}/rowan/__init__.py +6 -0
  17. {rowan_python-3.1.3 → rowan_python-3.1.4}/rowan/calculation.py +16 -8
  18. {rowan_python-3.1.3 → rowan_python-3.1.4}/rowan/folder.py +120 -1
  19. {rowan_python-3.1.3 → rowan_python-3.1.4}/rowan/molecule.py +73 -0
  20. {rowan_python-3.1.3 → rowan_python-3.1.4}/rowan/protein.py +92 -13
  21. {rowan_python-3.1.3 → rowan_python-3.1.4}/rowan/utils.py +21 -1
  22. {rowan_python-3.1.3 → rowan_python-3.1.4}/rowan/workflows/base.py +22 -3
  23. {rowan_python-3.1.3 → rowan_python-3.1.4}/rowan/workflows/basic_calculation.py +1 -1
  24. {rowan_python-3.1.3 → rowan_python-3.1.4}/rowan/workflows/docking.py +10 -7
  25. {rowan_python-3.1.3 → rowan_python-3.1.4}/rowan/workflows/fukui.py +2 -19
  26. {rowan_python-3.1.3 → rowan_python-3.1.4}/rowan/workflows/msa.py +15 -22
  27. {rowan_python-3.1.3 → rowan_python-3.1.4}/rowan/workflows/pka.py +2 -6
  28. {rowan_python-3.1.3 → rowan_python-3.1.4}/rowan/workflows/protein_md.py +22 -0
  29. {rowan_python-3.1.3 → rowan_python-3.1.4}/skills/computational-chemistry-and-biology/SKILL.md +4 -2
  30. {rowan_python-3.1.3 → rowan_python-3.1.4}/skills/computational-chemistry-and-biology/reference/conformer_search.md +1 -1
  31. {rowan_python-3.1.3 → rowan_python-3.1.4}/skills/computational-chemistry-and-biology/reference/docking.md +14 -9
  32. rowan_python-3.1.4/skills/computational-chemistry-and-biology/reference/folders_and_projects.md +59 -0
  33. {rowan_python-3.1.3 → rowan_python-3.1.4}/skills/computational-chemistry-and-biology/reference/msa.md +3 -3
  34. {rowan_python-3.1.3 → rowan_python-3.1.4}/skills/computational-chemistry-and-biology/reference/pose_analysis_md.md +6 -1
  35. {rowan_python-3.1.3 → rowan_python-3.1.4}/skills/computational-chemistry-and-biology/reference/protein_md.md +1 -0
  36. {rowan_python-3.1.3 → rowan_python-3.1.4}/skills/computational-chemistry-and-biology/reference/protein_prep.md +2 -2
  37. {rowan_python-3.1.3 → rowan_python-3.1.4}/skills/computational-chemistry-and-biology/reference/scan.md +11 -11
  38. {rowan_python-3.1.3 → rowan_python-3.1.4}/.envrc +0 -0
  39. {rowan_python-3.1.3 → rowan_python-3.1.4}/.github/workflows/build-and-deploy-docs.yml +0 -0
  40. {rowan_python-3.1.3 → rowan_python-3.1.4}/.github/workflows/publish-skill.yml +0 -0
  41. {rowan_python-3.1.3 → rowan_python-3.1.4}/.github/workflows/python-publish.yml +0 -0
  42. {rowan_python-3.1.3 → rowan_python-3.1.4}/.github/workflows/test.yml +0 -0
  43. {rowan_python-3.1.3 → rowan_python-3.1.4}/.gitignore +0 -0
  44. {rowan_python-3.1.3 → rowan_python-3.1.4}/.pre-commit-config.yaml +0 -0
  45. {rowan_python-3.1.3 → rowan_python-3.1.4}/AGENTS.md +0 -0
  46. {rowan_python-3.1.3 → rowan_python-3.1.4}/CLAUDE.md +0 -0
  47. {rowan_python-3.1.3 → rowan_python-3.1.4}/GEMINI.md +0 -0
  48. {rowan_python-3.1.3 → rowan_python-3.1.4}/LICENSE +0 -0
  49. {rowan_python-3.1.3 → rowan_python-3.1.4}/README.md +0 -0
  50. {rowan_python-3.1.3 → rowan_python-3.1.4}/docs/images/deciduous-tree-favicon.png +0 -0
  51. {rowan_python-3.1.3 → rowan_python-3.1.4}/docs/stylesheets/colors.css +0 -0
  52. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/PROTAC_solubility.py +0 -0
  53. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/admet.py +0 -0
  54. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/analogue_docking.py +0 -0
  55. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/aqueous_solubility.py +0 -0
  56. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/basic_calculation.py +0 -0
  57. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/basic_calculation_from_json.py +0 -0
  58. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/basic_calculation_with_constraint.py +0 -0
  59. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/basic_calculation_with_solvent.py +0 -0
  60. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/bde.py +0 -0
  61. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/cofolding_screen.py +0 -0
  62. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/conformer_dependent_redox.py +0 -0
  63. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/conformers.py +0 -0
  64. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/conformers_screen.py +0 -0
  65. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/data/1iep_receptorH.pdb +0 -0
  66. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/data/Al_FCC.xyz +0 -0
  67. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/data/citalopram_1iep.xyz +0 -0
  68. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/data/ibuprofen_conformers.sdf +0 -0
  69. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/data/tyk2_ligands.sdf +0 -0
  70. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/data/tyk2_structure.pdb +0 -0
  71. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/data/workflow_example.json +0 -0
  72. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/dcd_download.py +0 -0
  73. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/descriptors.py +0 -0
  74. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/double_ended_ts_search.py +0 -0
  75. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/electronic_properties.py +0 -0
  76. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/estimate_workflow.py +0 -0
  77. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/fukui_index.py +0 -0
  78. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/hydrogen_bond_basicity.py +0 -0
  79. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/interaction_energy_decomposition.py +0 -0
  80. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/ion_mobility.py +0 -0
  81. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/irc.py +0 -0
  82. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/macropka.py +0 -0
  83. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/membrane_permeability.py +0 -0
  84. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/multistage_optimization.py +0 -0
  85. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/nmr.py +0 -0
  86. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/optimization.py +0 -0
  87. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/pdb_download.py +0 -0
  88. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/periodic_dft.py +0 -0
  89. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/phenol_pka.py +0 -0
  90. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/pka.py +0 -0
  91. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/pose_analysis_md.py +0 -0
  92. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/project_scoped_api_key.py +0 -0
  93. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/protein_binder_design.py +0 -0
  94. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/protein_cofolding.py +0 -0
  95. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/protein_cofolding_with_constraints.py +0 -0
  96. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/protein_cofolding_with_templates.py +0 -0
  97. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/protein_md.py +0 -0
  98. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/rbfe_graph.py +0 -0
  99. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/rbfe_resubmit.py +0 -0
  100. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/redox_potential.py +0 -0
  101. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/relative_binding_free_energy_perturbation.py +0 -0
  102. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/retrieve_workflow.py +0 -0
  103. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/solvent_dependent_conformers.py +0 -0
  104. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/spin_states.py +0 -0
  105. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/strain.py +0 -0
  106. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/tautomer.py +0 -0
  107. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/template.py +0 -0
  108. {rowan_python-3.1.3 → rowan_python-3.1.4}/examples/webhook.py +0 -0
  109. {rowan_python-3.1.3 → rowan_python-3.1.4}/mkdocs.yml +0 -0
  110. {rowan_python-3.1.3 → rowan_python-3.1.4}/pixi.lock +0 -0
  111. {rowan_python-3.1.3 → rowan_python-3.1.4}/rowan/api_keys.py +0 -0
  112. {rowan_python-3.1.3 → rowan_python-3.1.4}/rowan/config.py +0 -0
  113. {rowan_python-3.1.3 → rowan_python-3.1.4}/rowan/constants.py +0 -0
  114. {rowan_python-3.1.3 → rowan_python-3.1.4}/rowan/project.py +0 -0
  115. {rowan_python-3.1.3 → rowan_python-3.1.4}/rowan/py.typed +0 -0
  116. {rowan_python-3.1.3 → rowan_python-3.1.4}/rowan/types.py +0 -0
  117. {rowan_python-3.1.3 → rowan_python-3.1.4}/rowan/user.py +0 -0
  118. {rowan_python-3.1.3 → rowan_python-3.1.4}/rowan/workflows/__init__.py +0 -0
  119. {rowan_python-3.1.3 → rowan_python-3.1.4}/rowan/workflows/admet.py +0 -0
  120. {rowan_python-3.1.3 → rowan_python-3.1.4}/rowan/workflows/analogue_docking.py +0 -0
  121. {rowan_python-3.1.3 → rowan_python-3.1.4}/rowan/workflows/batch_docking.py +0 -0
  122. {rowan_python-3.1.3 → rowan_python-3.1.4}/rowan/workflows/bde.py +0 -0
  123. {rowan_python-3.1.3 → rowan_python-3.1.4}/rowan/workflows/conformer_search.py +0 -0
  124. {rowan_python-3.1.3 → rowan_python-3.1.4}/rowan/workflows/constants.py +0 -0
  125. {rowan_python-3.1.3 → rowan_python-3.1.4}/rowan/workflows/descriptors.py +0 -0
  126. {rowan_python-3.1.3 → rowan_python-3.1.4}/rowan/workflows/double_ended_ts_search.py +0 -0
  127. {rowan_python-3.1.3 → rowan_python-3.1.4}/rowan/workflows/electronic_properties.py +0 -0
  128. {rowan_python-3.1.3 → rowan_python-3.1.4}/rowan/workflows/hydrogen_bond_donor_acceptor_strength.py +0 -0
  129. {rowan_python-3.1.3 → rowan_python-3.1.4}/rowan/workflows/interaction_energy_decomposition.py +0 -0
  130. {rowan_python-3.1.3 → rowan_python-3.1.4}/rowan/workflows/ion_mobility.py +0 -0
  131. {rowan_python-3.1.3 → rowan_python-3.1.4}/rowan/workflows/irc.py +0 -0
  132. {rowan_python-3.1.3 → rowan_python-3.1.4}/rowan/workflows/macropka.py +0 -0
  133. {rowan_python-3.1.3 → rowan_python-3.1.4}/rowan/workflows/membrane_permeability.py +0 -0
  134. {rowan_python-3.1.3 → rowan_python-3.1.4}/rowan/workflows/multistage_optimization.py +0 -0
  135. {rowan_python-3.1.3 → rowan_python-3.1.4}/rowan/workflows/nmr.py +0 -0
  136. {rowan_python-3.1.3 → rowan_python-3.1.4}/rowan/workflows/pocket_detection.py +0 -0
  137. {rowan_python-3.1.3 → rowan_python-3.1.4}/rowan/workflows/pose_analysis_md.py +0 -0
  138. {rowan_python-3.1.3 → rowan_python-3.1.4}/rowan/workflows/protein_binder_design.py +0 -0
  139. {rowan_python-3.1.3 → rowan_python-3.1.4}/rowan/workflows/protein_cofolding.py +0 -0
  140. {rowan_python-3.1.3 → rowan_python-3.1.4}/rowan/workflows/rbfe_graph.py +0 -0
  141. {rowan_python-3.1.3 → rowan_python-3.1.4}/rowan/workflows/redox_potential.py +0 -0
  142. {rowan_python-3.1.3 → rowan_python-3.1.4}/rowan/workflows/relative_binding_free_energy_perturbation.py +0 -0
  143. {rowan_python-3.1.3 → rowan_python-3.1.4}/rowan/workflows/scan.py +0 -0
  144. {rowan_python-3.1.3 → rowan_python-3.1.4}/rowan/workflows/solubility.py +0 -0
  145. {rowan_python-3.1.3 → rowan_python-3.1.4}/rowan/workflows/solvent_dependent_conformers.py +0 -0
  146. {rowan_python-3.1.3 → rowan_python-3.1.4}/rowan/workflows/spin_states.py +0 -0
  147. {rowan_python-3.1.3 → rowan_python-3.1.4}/rowan/workflows/strain.py +0 -0
  148. {rowan_python-3.1.3 → rowan_python-3.1.4}/rowan/workflows/tautomer_search.py +0 -0
  149. {rowan_python-3.1.3 → rowan_python-3.1.4}/skills/computational-chemistry-and-biology/reference/admet.md +0 -0
  150. {rowan_python-3.1.3 → rowan_python-3.1.4}/skills/computational-chemistry-and-biology/reference/analogue_docking.md +0 -0
  151. {rowan_python-3.1.3 → rowan_python-3.1.4}/skills/computational-chemistry-and-biology/reference/basic_calculation.md +0 -0
  152. {rowan_python-3.1.3 → rowan_python-3.1.4}/skills/computational-chemistry-and-biology/reference/batch_docking.md +0 -0
  153. {rowan_python-3.1.3 → rowan_python-3.1.4}/skills/computational-chemistry-and-biology/reference/bde.md +0 -0
  154. {rowan_python-3.1.3 → rowan_python-3.1.4}/skills/computational-chemistry-and-biology/reference/descriptors.md +0 -0
  155. {rowan_python-3.1.3 → rowan_python-3.1.4}/skills/computational-chemistry-and-biology/reference/double_ended_ts_search.md +0 -0
  156. {rowan_python-3.1.3 → rowan_python-3.1.4}/skills/computational-chemistry-and-biology/reference/electronic_properties.md +0 -0
  157. {rowan_python-3.1.3 → rowan_python-3.1.4}/skills/computational-chemistry-and-biology/reference/fukui.md +0 -0
  158. {rowan_python-3.1.3 → rowan_python-3.1.4}/skills/computational-chemistry-and-biology/reference/hydrogen_bond_donor_acceptor_strength.md +0 -0
  159. {rowan_python-3.1.3 → rowan_python-3.1.4}/skills/computational-chemistry-and-biology/reference/interaction_energy_decomposition.md +0 -0
  160. {rowan_python-3.1.3 → rowan_python-3.1.4}/skills/computational-chemistry-and-biology/reference/ion_mobility.md +0 -0
  161. {rowan_python-3.1.3 → rowan_python-3.1.4}/skills/computational-chemistry-and-biology/reference/irc.md +0 -0
  162. {rowan_python-3.1.3 → rowan_python-3.1.4}/skills/computational-chemistry-and-biology/reference/macropka.md +0 -0
  163. {rowan_python-3.1.3 → rowan_python-3.1.4}/skills/computational-chemistry-and-biology/reference/membrane_permeability.md +0 -0
  164. {rowan_python-3.1.3 → rowan_python-3.1.4}/skills/computational-chemistry-and-biology/reference/multistage_optimization.md +0 -0
  165. {rowan_python-3.1.3 → rowan_python-3.1.4}/skills/computational-chemistry-and-biology/reference/nmr.md +0 -0
  166. {rowan_python-3.1.3 → rowan_python-3.1.4}/skills/computational-chemistry-and-biology/reference/pka.md +0 -0
  167. {rowan_python-3.1.3 → rowan_python-3.1.4}/skills/computational-chemistry-and-biology/reference/pocket_detection.md +0 -0
  168. {rowan_python-3.1.3 → rowan_python-3.1.4}/skills/computational-chemistry-and-biology/reference/protein_binder_design.md +0 -0
  169. {rowan_python-3.1.3 → rowan_python-3.1.4}/skills/computational-chemistry-and-biology/reference/protein_cofolding.md +0 -0
  170. {rowan_python-3.1.3 → rowan_python-3.1.4}/skills/computational-chemistry-and-biology/reference/rbfe_graph.md +0 -0
  171. {rowan_python-3.1.3 → rowan_python-3.1.4}/skills/computational-chemistry-and-biology/reference/redox_potential.md +0 -0
  172. {rowan_python-3.1.3 → rowan_python-3.1.4}/skills/computational-chemistry-and-biology/reference/relative_binding_free_energy_perturbation.md +0 -0
  173. {rowan_python-3.1.3 → rowan_python-3.1.4}/skills/computational-chemistry-and-biology/reference/solubility.md +0 -0
  174. {rowan_python-3.1.3 → rowan_python-3.1.4}/skills/computational-chemistry-and-biology/reference/solvent_dependent_conformers.md +0 -0
  175. {rowan_python-3.1.3 → rowan_python-3.1.4}/skills/computational-chemistry-and-biology/reference/spin_states.md +0 -0
  176. {rowan_python-3.1.3 → rowan_python-3.1.4}/skills/computational-chemistry-and-biology/reference/strain.md +0 -0
  177. {rowan_python-3.1.3 → rowan_python-3.1.4}/skills/computational-chemistry-and-biology/reference/tautomer_search.md +0 -0
  178. {rowan_python-3.1.3 → rowan_python-3.1.4}/skills/computational-chemistry-and-biology/reference/webhooks.md +0 -0
  179. {rowan_python-3.1.3 → rowan_python-3.1.4}/skills/computational-chemistry-and-biology/scripts/check_env.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rowan-python
3
- Version: 3.1.3
3
+ Version: 3.1.4
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
@@ -23,6 +23,16 @@
23
23
  - batch_submit_workflow
24
24
  - batch_poll_status
25
25
 
26
+ ::: rowan.calculation
27
+ handler: python
28
+ options:
29
+ show_source: false
30
+ show_root_heading: false
31
+ show_root_toc_entry: false
32
+ members_order: source
33
+ group_by_category: true
34
+ filters: ["!^_"]
35
+
26
36
  ## ADMET
27
37
  ::: rowan.workflows.admet
28
38
  handler: python
@@ -254,6 +264,17 @@
254
264
  group_by_category: true
255
265
  filters: ["!^__"]
256
266
 
267
+ ## Pocket Detection
268
+ ::: rowan.workflows.pocket_detection
269
+ handler: python
270
+ options:
271
+ show_source: false
272
+ show_root_heading: false
273
+ show_root_toc_entry: false
274
+ members_order: source
275
+ group_by_category: true
276
+ filters: ["!^__"]
277
+
257
278
  ## Pose Analysis MD
258
279
  ::: rowan.workflows.pose_analysis_md
259
280
  handler: python
@@ -397,6 +418,17 @@
397
418
  group_by_category: true
398
419
  filters: ["!^__"]
399
420
 
421
+ ## API Keys
422
+ ::: rowan.api_keys
423
+ handler: python
424
+ options:
425
+ show_source: false
426
+ show_root_heading: false
427
+ show_root_toc_entry: false
428
+ members_order: source
429
+ group_by_category: true
430
+ filters: ["!^__"]
431
+
400
432
  ## Folder Class and Functions
401
433
  ::: rowan.folder
402
434
  handler: python
@@ -13,7 +13,7 @@ ligands = [
13
13
  ]
14
14
 
15
15
  protein = rowan.create_protein_from_pdb_id(
16
- "CDK2", "1HCK", project_uuid=rowan.default_project().uuid
16
+ "1HCK", name="CDK2", project_uuid=rowan.default_project().uuid
17
17
  )
18
18
 
19
19
  protein.prepare()
@@ -14,7 +14,7 @@ msa_workflow = rowan.submit_msa_workflow(
14
14
  "VLSPADKTNVKAAWGKVGAHAGEYGAEALERMFLSFPTTKTYFPHFDLSHGSAQVKGHGKKVADALTNAVAHVDDMPNALSALSDLHAHKLRVDPVNFKLLSHCLLVTLAAHLPAEFTPAVHASLDKFLASVSTVLTSKYR",
15
15
  "VHLTPEEKSAVTALWGKVNVDEVGGEALGRLLVVYPWTQRFFESFGDLSTPDAVMGNPKVKAHGKKVLGAFSDGLAHLDNLKGTFATLSELHCDKLHVDPENFRLLGNVLVCVLAHHFGKEFTPPVQAAYQKVVAGVANALAHKYH",
16
16
  ],
17
- output_formats=["boltz"],
17
+ output_formats=[rowan.MSAFormat.BOLTZ],
18
18
  name="Boltz Paired MSA Example",
19
19
  folder=folder,
20
20
  )
@@ -23,7 +23,7 @@ print(f"View workflow privately at: https://labs.rowansci.com/msa/{msa_workflow.
23
23
 
24
24
  msa_result = msa_workflow.result()
25
25
 
26
- msa_result.download_files("boltz", path=msa_directory)
26
+ msa_result.download_files(rowan.MSAFormat.BOLTZ, path=msa_directory)
27
27
 
28
28
  tar_path = next(msa_directory.glob("*.tar.gz"))
29
29
  with tarfile.open(tar_path, "r") as tar_ref:
@@ -13,7 +13,7 @@ msa_workflow = rowan.submit_msa_workflow(
13
13
  initial_protein_sequences=[
14
14
  "HPETLVKVKDAEDQLGARVGYIELDLNSGKILESFRPEERFPMMSTFKVLLCGAVLSRIDAGQEQLGRRIHYSQNDLVEYSPVTEKHLTDGMTVRELCSAAITMSDNTAANLLLTTIGGPKELTAFLHNMGDHVTRLDRWEPELNEAIPNDERDTTMPVAMATTLRKLLTGELLTLASRQQLIDWMEADKVAGPLLRSALPAGWFIADKSGAGERGSRGIIAALGPDGKPSRIVVIYTTGSQATMDERNRQIAEIGASLIKHW"
15
15
  ],
16
- output_formats=["boltz"],
16
+ output_formats=[rowan.MSAFormat.BOLTZ],
17
17
  name="Boltz MSA Example",
18
18
  folder=folder,
19
19
  )
@@ -22,7 +22,7 @@ print(f"View workflow privately at: https://labs.rowansci.com/msa/{msa_workflow.
22
22
 
23
23
  msa_result = msa_workflow.result()
24
24
 
25
- msa_result.download_files("boltz", path=msa_directory)
25
+ msa_result.download_files(rowan.MSAFormat.BOLTZ, path=msa_directory)
26
26
 
27
27
  tar_path = next(msa_directory.glob("*.tar.gz"))
28
28
  with tarfile.open(tar_path, "r") as tar_ref:
@@ -25,7 +25,7 @@ msa_workflow = rowan.submit_msa_workflow(
25
25
  "VLSPADKTNVKAAWGKVGAHAGEYGAEALERMFLSFPTTKTYFPHFDLSHGSAQVKGHGKKVADALTNAVAHVDDMPNALSALSDLHAHKLRVDPVNFKLLSHCLLVTLAAHLPAEFTPAVHASLDKFLASVSTVLTSKYR",
26
26
  "VHLTPEEKSAVTALWGKVNVDEVGGEALGRLLVVYPWTQRFFESFGDLSTPDAVMGNPKVKAHGKKVLGAFSDGLAHLDNLKGTFATLSELHCDKLHVDPENFRLLGNVLVCVLAHHFGKEFTPPVQAAYQKVVAGVANALAHKYH",
27
27
  ],
28
- output_formats=["chai"],
28
+ output_formats=[rowan.MSAFormat.CHAI],
29
29
  name="CHAI Paired MSA Example",
30
30
  folder=folder,
31
31
  )
@@ -34,7 +34,7 @@ print(f"View workflow privately at: https://labs.rowansci.com/msa/{msa_workflow.
34
34
 
35
35
  msa_result = msa_workflow.result()
36
36
 
37
- msa_result.download_files("chai", path=msa_directory)
37
+ msa_result.download_files(rowan.MSAFormat.CHAI, path=msa_directory)
38
38
 
39
39
  tar_path = next(msa_directory.glob("*.tar.gz"))
40
40
  with tarfile.open(tar_path, "r") as tar_ref:
@@ -22,7 +22,7 @@ msa_workflow = rowan.submit_msa_workflow(
22
22
  initial_protein_sequences=[
23
23
  "HPETLVKVKDAEDQLGARVGYIELDLNSGKILESFRPEERFPMMSTFKVLLCGAVLSRIDAGQEQLGRRIHYSQNDLVEYSPVTEKHLTDGMTVRELCSAAITMSDNTAANLLLTTIGGPKELTAFLHNMGDHVTRLDRWEPELNEAIPNDERDTTMPVAMATTLRKLLTGELLTLASRQQLIDWMEADKVAGPLLRSALPAGWFIADKSGAGERGSRGIIAALGPDGKPSRIVVIYTTGSQATMDERNRQIAEIGASLIKHW"
24
24
  ],
25
- output_formats=["chai"],
25
+ output_formats=[rowan.MSAFormat.CHAI],
26
26
  name="CHAI MSA Example",
27
27
  folder=folder,
28
28
  )
@@ -31,7 +31,7 @@ print(f"View workflow privately at: https://labs.rowansci.com/msa/{msa_workflow.
31
31
 
32
32
  msa_result = msa_workflow.result()
33
33
 
34
- msa_result.download_files("chai", path=msa_directory)
34
+ msa_result.download_files(rowan.MSAFormat.CHAI, path=msa_directory)
35
35
 
36
36
  tar_path = next(msa_directory.glob("*.tar.gz"))
37
37
  with tarfile.open(tar_path, "r") as tar_ref:
@@ -14,7 +14,7 @@ msa_workflow = rowan.submit_msa_workflow(
14
14
  "VLSPADKTNVKAAWGKVGAHAGEYGAEALERMFLSFPTTKTYFPHFDLSHGSAQVKGHGKKVADALTNAVAHVDDMPNALSALSDLHAHKLRVDPVNFKLLSHCLLVTLAAHLPAEFTPAVHASLDKFLASVSTVLTSKYR",
15
15
  "VHLTPEEKSAVTALWGKVNVDEVGGEALGRLLVVYPWTQRFFESFGDLSTPDAVMGNPKVKAHGKKVLGAFSDGLAHLDNLKGTFATLSELHCDKLHVDPENFRLLGNVLVCVLAHHFGKEFTPPVQAAYQKVVAGVANALAHKYH",
16
16
  ],
17
- output_formats=["colabfold"],
17
+ output_formats=[rowan.MSAFormat.COLABFOLD],
18
18
  name="Colabfold Paired MSA Example",
19
19
  folder=folder,
20
20
  )
@@ -23,7 +23,7 @@ print(f"View workflow privately at: https://labs.rowansci.com/msa/{msa_workflow.
23
23
 
24
24
  msa_result = msa_workflow.result()
25
25
 
26
- msa_result.download_files("colabfold", path=msa_directory)
26
+ msa_result.download_files(rowan.MSAFormat.COLABFOLD, path=msa_directory)
27
27
 
28
28
  tar_path = next(msa_directory.glob("*.tar.gz"))
29
29
  with tarfile.open(tar_path, "r") as tar_ref:
@@ -13,7 +13,7 @@ msa_workflow = rowan.submit_msa_workflow(
13
13
  initial_protein_sequences=[
14
14
  "HPETLVKVKDAEDQLGARVGYIELDLNSGKILESFRPEERFPMMSTFKVLLCGAVLSRIDAGQEQLGRRIHYSQNDLVEYSPVTEKHLTDGMTVRELCSAAITMSDNTAANLLLTTIGGPKELTAFLHNMGDHVTRLDRWEPELNEAIPNDERDTTMPVAMATTLRKLLTGELLTLASRQQLIDWMEADKVAGPLLRSALPAGWFIADKSGAGERGSRGIIAALGPDGKPSRIVVIYTTGSQATMDERNRQIAEIGASLIKHW"
15
15
  ],
16
- output_formats=["colabfold"],
16
+ output_formats=[rowan.MSAFormat.COLABFOLD],
17
17
  name="Colabfold MSA Example",
18
18
  folder=folder,
19
19
  )
@@ -22,7 +22,7 @@ print(f"View workflow privately at: https://labs.rowansci.com/msa/{msa_workflow.
22
22
 
23
23
  msa_result = msa_workflow.result()
24
24
 
25
- msa_result.download_files("colabfold", path=msa_directory)
25
+ msa_result.download_files(rowan.MSAFormat.COLABFOLD, path=msa_directory)
26
26
 
27
27
  tar_path = next(msa_directory.glob("*.tar.gz"))
28
28
  with tarfile.open(tar_path, "r") as tar_ref:
@@ -0,0 +1,39 @@
1
+ import rowan
2
+
3
+ # Set your API key or use the ROWAN_API_KEY environment variable
4
+ # rowan.api_key = "rowan-sk..."
5
+ folder = rowan.get_folder("examples")
6
+
7
+ # Dasatinib — redocked into its own ABL1 co-crystal structure (PDB: 2GQG)
8
+ dasatinib = rowan.Molecule.from_smiles("Cc1nc(Nc2ncc(C(=O)Nc3c(C)cccc3Cl)s2)cc(N2CCN(CCO)CC2)n1")
9
+
10
+ protein = rowan.create_protein_from_pdb_id("2GQG")
11
+ if len(protein.chains) > 1:
12
+ protein = protein.select_chains([protein.chains[0]])
13
+ protein.prepare()
14
+
15
+ # Pocket is [[center_x, center_y, center_z], [size_x, size_y, size_z]] in Å.
16
+ # For a co-crystal structure, extract these from the bound ligand's position.
17
+ # Use rowan.submit_pocket_detection_workflow() when the binding site is unknown.
18
+ center = [44.59, 79.75, 39.59]
19
+ size = [24.15, 21.33, 19.88]
20
+ workflow = rowan.submit_docking_workflow(
21
+ protein,
22
+ pocket=[center, size],
23
+ initial_molecule=dasatinib,
24
+ name="dasatinib docking (2GQG redock)",
25
+ folder=folder,
26
+ )
27
+
28
+ print(f"View workflow privately at: https://labs.rowansci.com/docking/{workflow.uuid}")
29
+
30
+ result = workflow.result()
31
+ print(result)
32
+
33
+ for i, score in enumerate(result.scores):
34
+ print(f" Pose {i}: score={score.score:.3f} posebusters_valid={score.posebusters_valid}")
35
+
36
+ # Download the top-scoring protein–ligand complex as a PDB
37
+ complex_protein = result.get_complex(0)
38
+ complex_protein.download_pdb_file("dasatinib_2GQG_complex.pdb")
39
+ print("Saved dasatinib_2GQG_complex.pdb")
@@ -19,7 +19,7 @@ workflows = []
19
19
  results = {}
20
20
 
21
21
  protein = rowan.create_protein_from_pdb_id(
22
- "CDK2", "1HCK", project_uuid=rowan.default_project().uuid
22
+ "1HCK", name="CDK2", project_uuid=rowan.default_project().uuid
23
23
  )
24
24
 
25
25
  protein.prepare()
@@ -43,11 +43,14 @@ lowest_conformer_energy = 0
43
43
  for workflow, result in workflow_results:
44
44
  for conformer_uuid in result.conformers:
45
45
  energy = rowan.retrieve_calculation_molecules(conformer_uuid)[0]["energy"]
46
- lowest_conformer_energy = min(lowest_conformer_energy, energy)
46
+ if energy is not None:
47
+ lowest_conformer_energy = min(lowest_conformer_energy, energy)
47
48
 
48
49
  sorted_scores = sorted(result.scores, key=lambda s: s.score)
49
50
  for score in sorted_scores:
50
51
  pose_energy = rowan.retrieve_calculation_molecules(score.pose)[0]["energy"]
52
+ if pose_energy is None:
53
+ continue
51
54
  strain = (pose_energy - lowest_conformer_energy) * HARTREE_TO_KCALMOL
52
55
  if score.posebusters_valid and strain < 4:
53
56
  results[workflow.name] = score
@@ -5,7 +5,7 @@ import rowan
5
5
  folder = rowan.get_folder("examples")
6
6
 
7
7
  protein = rowan.create_protein_from_pdb_id(
8
- "thymidine phosphorylase", "1OTP", project_uuid=rowan.default_project().uuid
8
+ "1OTP", name="thymidine phosphorylase", project_uuid=rowan.default_project().uuid
9
9
  )
10
10
 
11
11
  protein.prepare()
@@ -0,0 +1,39 @@
1
+ """
2
+ Resubmit a completed workflow with a perturbed structure.
3
+
4
+ Two strategies: random noise to break symmetry, or displacement along a
5
+ vibrational mode to follow a reaction coordinate or escape a stuck geometry.
6
+ """
7
+
8
+ import rowan
9
+
10
+ # Set your API key or use the ROWAN_API_KEY environment variable
11
+ # rowan.api_key = "rowan-sk..."
12
+ folder = rowan.get_folder("examples")
13
+
14
+ # --- Option 1: random noise ---
15
+ wf = rowan.retrieve_workflow("your-workflow-uuid")
16
+ mol = wf.result().molecule
17
+
18
+ perturbed_mol = mol.perturb()
19
+ resubmit = rowan.submit_multistage_optimization_workflow(
20
+ initial_molecule=perturbed_mol,
21
+ name="cyclobutane opt - perturbed resubmit",
22
+ folder=folder,
23
+ )
24
+ print(f"https://labs.rowansci.com/multistage-opt/{resubmit.uuid}")
25
+
26
+ # --- Option 2: displace along a vibrational mode ---
27
+ # Requires a prior frequency calculation. Imaginary modes have negative frequency.
28
+ ts_wf = rowan.retrieve_workflow("your-ts-freq-workflow-uuid")
29
+ ts_mol = ts_wf.result().molecule
30
+
31
+ imaginary_mode = next(m for m in ts_mol.vibrational_modes if m.frequency < 0)
32
+
33
+ displaced_mol = ts_mol.displace_along_mode(mode=imaginary_mode, displacement=0.3)
34
+ resubmit = rowan.submit_multistage_optimization_workflow(
35
+ initial_molecule=displaced_mol,
36
+ name="TS resubmit - displaced along imaginary mode",
37
+ folder=folder,
38
+ )
39
+ print(f"https://labs.rowansci.com/multistage-opt/{resubmit.uuid}")
@@ -13,13 +13,13 @@ folder = rowan.get_folder("examples")
13
13
  workflow = rowan.submit_scan_workflow(
14
14
  initial_molecule=rowan.Molecule.from_smiles("O"),
15
15
  name="Water Angle scan",
16
- scan_settings={
17
- "type": "angle",
18
- "atoms": [2, 1, 3], # 1-indexed
19
- "start": 100,
20
- "stop": 110,
21
- "num": 5,
22
- },
16
+ scan_settings=rowan.ScanSettings(
17
+ type="angle",
18
+ atoms=[2, 1, 3], # 1-indexed
19
+ start=100,
20
+ stop=110,
21
+ num=5,
22
+ ),
23
23
  calculation_method="GFN2-xTB",
24
24
  calculation_engine="xtb",
25
25
  folder=folder,
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "rowan-python"
3
- version = "3.1.3"
3
+ version = "3.1.4"
4
4
  description = "Rowan Python Library"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -2,6 +2,8 @@
2
2
  from . import constants
3
3
  from stjames import (
4
4
  Atom,
5
+ BindingPoseContact,
6
+ ConformerGenSettings,
5
7
  Constraint,
6
8
  ConstraintType,
7
9
  ConformerClusteringSettings,
@@ -14,15 +16,19 @@ from stjames import (
14
16
  KMeansClusteringSettings,
15
17
  Method,
16
18
  Mode,
19
+ MSAFormat,
17
20
  MultiStageOptSettings,
18
21
  OpenConfSettings,
19
22
  OptimizationSettings,
20
23
  PBCDFTSettings,
21
24
  PeriodicCell,
25
+ ProteinSequence,
26
+ ScanSettings,
22
27
  Settings,
23
28
  Solvent,
24
29
  SolventSettings,
25
30
  Task,
31
+ VibrationalMode,
26
32
  )
27
33
  from stjames.workflows.relative_binding_free_energy_perturbation import RBFEGraph, RBFEGraphEdge
28
34
  from stjames.excited_state_settings import TDDFTSettings
@@ -2,6 +2,7 @@
2
2
 
3
3
  from typing import Self
4
4
 
5
+ import httpx
5
6
  import stjames
6
7
  from pydantic import BaseModel, ConfigDict, Field
7
8
 
@@ -62,10 +63,13 @@ class Calculation(BaseModel):
62
63
  :param in_place: If True, update this instance in-place. If False, return new instance.
63
64
  :returns: Updated Calculation object.
64
65
  """
65
- with api_client() as client:
66
- response = client.get(f"/calculation/{self.uuid}/stjames")
67
- response.raise_for_status()
68
- data = response.json()
66
+ try:
67
+ with api_client() as client:
68
+ response = client.get(f"/calculation/{self.uuid}/stjames")
69
+ response.raise_for_status()
70
+ data = response.json()
71
+ except httpx.TimeoutException as e:
72
+ raise TimeoutError(f"Timed out fetching calculation {self.uuid}.") from e
69
73
 
70
74
  molecules = _parse_molecules(data.get("molecules", []))
71
75
 
@@ -94,11 +98,15 @@ def retrieve_calculation(uuid: str) -> Calculation:
94
98
  :param uuid: UUID of the calculation to retrieve.
95
99
  :returns: Calculation object with the fetched data.
96
100
  :raises requests.HTTPError: If the API request fails.
101
+ :raises TimeoutError: If the response times out.
97
102
  """
98
- with api_client() as client:
99
- response = client.get(f"/calculation/{uuid}/stjames")
100
- response.raise_for_status()
101
- data = response.json()
103
+ try:
104
+ with api_client() as client:
105
+ response = client.get(f"/calculation/{uuid}/stjames")
106
+ response.raise_for_status()
107
+ data = response.json()
108
+ except httpx.TimeoutException as e:
109
+ raise TimeoutError(f"Timed out fetching calculation {uuid}.") from e
102
110
 
103
111
  molecules = _parse_molecules(data.get("molecules", []))
104
112
 
@@ -1,11 +1,16 @@
1
+ from __future__ import annotations
2
+
1
3
  from datetime import datetime
2
- from typing import Any, Self
4
+ from typing import TYPE_CHECKING, Any, Self
3
5
 
4
6
  from pydantic import BaseModel
5
7
 
6
8
  from .project import default_project, retrieve_project
7
9
  from .utils import api_client, get_project_uuid
8
10
 
11
+ if TYPE_CHECKING:
12
+ from .workflows.base import Workflow
13
+
9
14
 
10
15
  class Folder(BaseModel):
11
16
  """
@@ -121,6 +126,77 @@ class Folder(BaseModel):
121
126
  """
122
127
  print_folder_tree(self.uuid, max_depth, show_uuids)
123
128
 
129
+ def children(self, size: int = 100) -> list[Folder]:
130
+ """
131
+ List all child folders directly inside this folder.
132
+
133
+ :param size: Maximum number of child folders to return.
134
+ :returns: List of child Folder objects.
135
+ :raises HTTPError: If the API request fails.
136
+ """
137
+ return list_folders(parent_uuid=self.uuid, size=size)
138
+
139
+ def workflows(self, size: int = 100) -> list[Workflow]:
140
+ """
141
+ List all workflows directly inside this folder.
142
+
143
+ :param size: Maximum number of workflows to return.
144
+ :returns: List of Workflow objects.
145
+ :raises HTTPError: If the API request fails.
146
+ """
147
+ from .workflows.base import list_workflows
148
+
149
+ return list_workflows(parent_uuid=self.uuid, size=size)
150
+
151
+ def contents(self, size: int = 100) -> list[Folder | Workflow]:
152
+ """
153
+ List everything directly inside this folder, both child folders and workflows.
154
+
155
+ Folders come first, followed by workflows. For a single type, use :func:`children`
156
+ or :func:`workflows`.
157
+
158
+ :param size: Maximum number of items of each type to return.
159
+ :returns: List of Folder and Workflow objects.
160
+ :raises HTTPError: If the API request fails.
161
+ """
162
+ return [*self.children(size=size), *self.workflows(size=size)]
163
+
164
+ def parent(self) -> Folder | None:
165
+ """
166
+ Retrieve the parent folder, or None if this is a root folder.
167
+
168
+ :returns: Parent Folder, or None if there is no parent.
169
+ :raises HTTPError: If the API request fails.
170
+ """
171
+ if self.parent_uuid is None:
172
+ return None
173
+ return retrieve_folder(self.parent_uuid)
174
+
175
+ def __truediv__(self, name: str) -> Folder:
176
+ """
177
+ Traverse into a child folder by name using the ``/`` operator.
178
+
179
+ Example::
180
+
181
+ root = rowan.root_folder()
182
+ subfolder = root / "CDK2" / "docking"
183
+
184
+ :param name: Exact name of the child folder to navigate into.
185
+ :returns: Child Folder with the given name.
186
+ :raises ValueError: If no child with that name exists, or if multiple children share
187
+ the same name (use :func:`children` and select by UUID to disambiguate).
188
+ """
189
+ matches = [f for f in self.children(size=200) if f.name == name]
190
+ if not matches:
191
+ raise ValueError(f"No child folder named {name!r} in {self.name!r} ({self.uuid})")
192
+ if len(matches) > 1:
193
+ uuids = ", ".join(f.uuid for f in matches)
194
+ raise ValueError(
195
+ f"Multiple child folders named {name!r} in {self.name!r} ({self.uuid}). "
196
+ f"Use retrieve_folder() with one of these UUIDs to disambiguate: {uuids}"
197
+ )
198
+ return matches[0]
199
+
124
200
 
125
201
  def retrieve_folder(uuid: str) -> Folder:
126
202
  """
@@ -147,6 +223,9 @@ def list_folders(
147
223
  """
148
224
  Retrieve a list of folders based on the specified criteria.
149
225
 
226
+ If no ``parent_uuid`` is given and a project is active (via :func:`set_project` or
227
+ ``rowan.project_uuid``), lists folders rooted at that project's root folder.
228
+
150
229
  :param parent_uuid: UUID of the parent folder to filter by.
151
230
  :param name_contains: Substring to search for in folder names.
152
231
  :param public: Filter folders by their public status.
@@ -156,6 +235,11 @@ def list_folders(
156
235
  :returns: List of Folder objects that match the search criteria.
157
236
  :raises requests.HTTPError: if the request to the API fails.
158
237
  """
238
+ if parent_uuid is None:
239
+ if project_uuid := get_project_uuid():
240
+ parent_uuid = retrieve_project(project_uuid).root_folder_uuid
241
+ else:
242
+ parent_uuid = default_project().root_folder_uuid
159
243
 
160
244
  params: dict[str, Any] = {
161
245
  "page": page,
@@ -189,6 +273,9 @@ def create_folder(
189
273
  """
190
274
  Create a new folder.
191
275
 
276
+ If no ``parent_uuid`` is given and a project is active (via :func:`set_project` or
277
+ ``rowan.project_uuid``), the folder is created inside that project's root folder.
278
+
192
279
  :param name: Name of the folder.
193
280
  :param parent_uuid: UUID of the parent folder.
194
281
  :param notes: Description of the folder.
@@ -196,6 +283,12 @@ def create_folder(
196
283
  :param public: Whether the folder is public.
197
284
  :returns: Newly created folder.
198
285
  """
286
+ if parent_uuid is None:
287
+ if project_uuid := get_project_uuid():
288
+ parent_uuid = retrieve_project(project_uuid).root_folder_uuid
289
+ else:
290
+ parent_uuid = default_project().root_folder_uuid
291
+
199
292
  data = {
200
293
  "name": name,
201
294
  "parent_uuid": parent_uuid,
@@ -210,6 +303,32 @@ def create_folder(
210
303
  return Folder(**folder_data)
211
304
 
212
305
 
306
+ def root_folder() -> Folder:
307
+ """
308
+ Get the root folder of the active project.
309
+
310
+ The root folder is the top of the folder tree you navigate and store workflows in. Use the
311
+ active project set via :func:`set_project` (or ``rowan.project_uuid``), falling back to the
312
+ default project.
313
+
314
+ Example::
315
+
316
+ root = rowan.root_folder()
317
+ for child in root.children():
318
+ print(child.name)
319
+ batch = root / "CDK2" / "docking"
320
+
321
+ :returns: Root Folder of the active or default project.
322
+ :raises HTTPError: If the API request fails.
323
+ """
324
+ if project_uuid := get_project_uuid():
325
+ root_uuid = retrieve_project(project_uuid).root_folder_uuid
326
+ else:
327
+ root_uuid = default_project().root_folder_uuid
328
+ assert root_uuid is not None
329
+ return retrieve_folder(root_uuid)
330
+
331
+
213
332
  def get_folder(path: str, create: bool = True) -> Folder:
214
333
  """
215
334
  Get a folder by name or nested path within the default project.
@@ -1,5 +1,7 @@
1
1
  """Molecule class for representing molecular structures and computed properties."""
2
2
 
3
+ import math
4
+ import random
3
5
  from pathlib import Path
4
6
  from typing import Any, Self
5
7
 
@@ -272,6 +274,77 @@ class Molecule(BaseModel):
272
274
  """
273
275
  return self._stjames.dihedral(i, j, k, l, degrees=degrees)
274
276
 
277
+ def perturb(self, stddev: float = 0.005) -> "Molecule":
278
+ """
279
+ Return a copy with random Gaussian noise added to each atom position.
280
+
281
+ Useful for breaking symmetry before resubmitting an optimization (e.g. when
282
+ a calculation is stuck in a saddle point or converges to an unwanted geometry).
283
+
284
+ :param stddev: standard deviation of the Gaussian displacement (Angstroms)
285
+ :returns: new Molecule with perturbed coordinates
286
+ """
287
+
288
+ def _gauss() -> float:
289
+ u1 = random.random()
290
+ u2 = random.random()
291
+ return math.sqrt(-2.0 * math.log(u1)) * math.cos(2.0 * math.pi * u2) * stddev
292
+
293
+ new_atoms = [
294
+ stjames.Atom(
295
+ atomic_number=atom.atomic_number,
296
+ position=(
297
+ atom.position[0] + _gauss(),
298
+ atom.position[1] + _gauss(),
299
+ atom.position[2] + _gauss(),
300
+ ),
301
+ mass=atom.mass,
302
+ )
303
+ for atom in self._stjames.atoms
304
+ ]
305
+ new_mol = self._stjames.model_copy(update={"atoms": new_atoms})
306
+ return Molecule(_stjames=new_mol)
307
+
308
+ def displace_along_mode(self, mode: stjames.VibrationalMode, displacement: float) -> "Molecule":
309
+ """
310
+ Return a copy with atom positions displaced along a vibrational normal mode.
311
+
312
+ Requires a prior frequency calculation. For a transition state, the imaginary
313
+ mode has a negative frequency and points toward the reactant or product.
314
+
315
+ :param mode: vibrational mode to displace along, from ``vibrational_modes``
316
+ :param displacement: displacement distance along the normalized mode (Angstroms)
317
+ :returns: new Molecule with displaced coordinates
318
+ :raises ValueError: if the mode has no displacements or zero-norm displacements
319
+ """
320
+ raw = mode.displacements
321
+ if not raw:
322
+ raise ValueError(
323
+ "Vibrational mode has no displacements. "
324
+ "Rerun the calculation with frequencies=True to displace along a mode."
325
+ )
326
+ norm = math.sqrt(sum(x**2 + y**2 + z**2 for x, y, z in raw))
327
+ if norm == 0:
328
+ raise ValueError(
329
+ "Vibrational mode has zero-norm displacements. "
330
+ "This indicates malformed frequency output, try displacing along a different mode."
331
+ )
332
+
333
+ new_atoms = [
334
+ stjames.Atom(
335
+ atomic_number=atom.atomic_number,
336
+ position=(
337
+ atom.position[0] + (dx / norm) * displacement,
338
+ atom.position[1] + (dy / norm) * displacement,
339
+ atom.position[2] + (dz / norm) * displacement,
340
+ ),
341
+ mass=atom.mass,
342
+ )
343
+ for atom, (dx, dy, dz) in zip(self._stjames.atoms, raw, strict=True)
344
+ ]
345
+ new_mol = self._stjames.model_copy(update={"atoms": new_atoms})
346
+ return Molecule(_stjames=new_mol)
347
+
275
348
 
276
349
  def load_named_ligands(path: Path | str) -> dict[str, Molecule]:
277
350
  """