rowan-python 3.2.1__tar.gz → 3.2.3__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.
- {rowan_python-3.2.1 → rowan_python-3.2.3}/PKG-INFO +2 -2
- {rowan_python-3.2.1 → rowan_python-3.2.3}/pyproject.toml +2 -2
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/__init__.py +3 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/project.py +8 -1
- rowan_python-3.2.3/rowan/spectra.py +68 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/utils.py +63 -14
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/workflows/_molecular_dynamics.py +27 -2
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/workflows/binding_affinity.py +7 -1
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/workflows/conformer_search.py +62 -10
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/workflows/protein_md.py +3 -3
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/workflows/relative_binding_free_energy_perturbation.py +67 -2
- {rowan_python-3.2.1 → rowan_python-3.2.3}/skills/computational-chemistry-and-biology/reference/binding_affinity.md +11 -1
- {rowan_python-3.2.1 → rowan_python-3.2.3}/skills/computational-chemistry-and-biology/reference/conformer_search.md +19 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/skills/computational-chemistry-and-biology/reference/protein_md.md +1 -1
- {rowan_python-3.2.1 → rowan_python-3.2.3}/tests/test_binding_affinity.py +33 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/tests/test_conformer_search.py +20 -0
- rowan_python-3.2.3/tests/test_download_presigned_files.py +68 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/tests/test_md_workflow_updates.py +24 -0
- rowan_python-3.2.3/tests/test_project.py +35 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/tests/test_rbfe_trajectory_download.py +24 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/uv.lock +5 -5
- {rowan_python-3.2.1 → rowan_python-3.2.3}/.claude-plugin/marketplace.json +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/.codex-plugin/plugin.json +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/.editorconfig +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/.envrc +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/.github/workflows/build-and-deploy-docs.yml +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/.github/workflows/publish-skill.yml +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/.github/workflows/python-publish.yml +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/.github/workflows/test.yml +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/.gitignore +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/.python-version +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/.rumdl.toml +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/LICENSE +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/README.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/api/api-keys.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/api/calculation.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/api/folder.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/api/molecule.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/api/project.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/api/protein.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/api/user.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/api/webhooks.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/api/workflow.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/images/RowanLogoLarge.png +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/images/RowanSquareLogo.png +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/images/favicon.svg +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/index.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/stylesheets/colors.css +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/workflows/admet.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/workflows/analogue-docking.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/workflows/basic-calculation.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/workflows/batch-docking.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/workflows/binding-affinity.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/workflows/bond-dissociation-energy.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/workflows/conformer-search-settings.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/workflows/conformer-search.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/workflows/covalent-inhibitor-scan.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/workflows/descriptors.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/workflows/docking.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/workflows/double-ended-ts-search.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/workflows/electronic-properties.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/workflows/fukui.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/workflows/hydrogen-bond-donor-acceptor-strength.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/workflows/interaction-energy-decomposition.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/workflows/ion-mobility.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/workflows/irc.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/workflows/logp.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/workflows/macropka.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/workflows/membrane-permeability.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/workflows/msa.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/workflows/multistage-optimization.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/workflows/nmr.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/workflows/pka.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/workflows/pocket-detection.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/workflows/pose-analysis-md.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/workflows/protein-binder-design.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/workflows/protein-cofolding.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/workflows/protein-md.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/workflows/protein-preparation.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/workflows/rbfe-graph.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/workflows/redox-potential.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/workflows/relative-binding-free-energy-perturbation.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/workflows/scan.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/workflows/settings.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/workflows/solubility.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/workflows/solvent-dependent-conformers.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/workflows/spin-states.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/workflows/strain.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/workflows/tautomer-search.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/docs/workflows/types.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/PROTAC_solubility.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/admet.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/analogue_docking.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/aqueous_solubility.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/basic_calculation.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/basic_calculation_from_json.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/basic_calculation_with_constraint.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/basic_calculation_with_solvent.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/batch_docking.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/batch_solubility.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/bde.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/binding_affinity.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/boltz_paired_msa.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/boltz_single_msa.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/chai_paired_msa.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/chai_single_msa.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/cofolding_screen.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/colabfold_paired_msa.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/colabfold_single_msa.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/conformer_dependent_redox.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/conformers.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/conformers_screen.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/covalent_docking.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/covalent_inhibitor_scan.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/data/1iep_receptorH.pdb +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/data/Al_FCC.xyz +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/data/citalopram_1iep.xyz +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/data/ibuprofen_conformers.sdf +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/data/tyk2_ligands.sdf +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/data/tyk2_structure.pdb +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/data/workflow_example.json +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/dcd_download.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/descriptors.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/docking.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/docking_screen.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/double_ended_ts_search.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/electronic_properties.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/estimate_workflow.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/fukui_index.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/hydrogen_bond_basicity.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/induced_fit_docking.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/interaction_energy_decomposition.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/ion_mobility.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/irc.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/logp.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/macropka.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/membrane_permeability.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/multistage_optimization.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/nmr.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/optimization.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/periodic_dft.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/phenol_pka.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/pka.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/pocket_detection.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/pose_analysis_md.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/project_scoped_api_key.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/protein_binder_design.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/protein_cofolding.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/protein_cofolding_modified_inputs.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/protein_cofolding_with_constraints.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/protein_cofolding_with_templates.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/protein_file_download.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/protein_md.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/protein_preparation.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/rbfe_graph.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/rbfe_resubmit.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/redox_potential.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/relative_binding_free_energy_perturbation.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/resubmit_with_perturbations.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/retrieve_workflow.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/scan.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/solvent_dependent_conformers.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/spin_states.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/strain.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/tautomer.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/template.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/temporary_workflow_sharing.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/examples/webhook.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/mkdocs.yml +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/prek.toml +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/api_keys.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/calculation.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/config.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/constants.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/folder.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/molecule.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/protein.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/py.typed +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/types.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/user.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/workflows/__init__.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/workflows/admet.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/workflows/analogue_docking.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/workflows/base.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/workflows/basic_calculation.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/workflows/batch_docking.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/workflows/bde.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/workflows/constants.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/workflows/covalent_inhibitor_scan.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/workflows/descriptors.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/workflows/docking.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/workflows/double_ended_ts_search.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/workflows/electronic_properties.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/workflows/fukui.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/workflows/hydrogen_bond_donor_acceptor_strength.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/workflows/interaction_energy_decomposition.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/workflows/ion_mobility.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/workflows/irc.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/workflows/logp.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/workflows/macropka.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/workflows/membrane_permeability.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/workflows/msa.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/workflows/multistage_optimization.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/workflows/nmr.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/workflows/pka.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/workflows/pocket_detection.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/workflows/pose_analysis_md.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/workflows/protein_binder_design.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/workflows/protein_cofolding.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/workflows/protein_preparation.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/workflows/rbfe_graph.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/workflows/redox_potential.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/workflows/scan.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/workflows/solubility.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/workflows/solvent_dependent_conformers.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/workflows/spin_states.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/workflows/strain.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/rowan/workflows/tautomer_search.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/skills/computational-chemistry-and-biology/.claude-plugin/plugin.json +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/skills/computational-chemistry-and-biology/SKILL.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/skills/computational-chemistry-and-biology/reference/admet.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/skills/computational-chemistry-and-biology/reference/analogue_docking.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/skills/computational-chemistry-and-biology/reference/basic_calculation.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/skills/computational-chemistry-and-biology/reference/batch_docking.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/skills/computational-chemistry-and-biology/reference/bde.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/skills/computational-chemistry-and-biology/reference/covalent_inhibitor_scan.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/skills/computational-chemistry-and-biology/reference/descriptors.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/skills/computational-chemistry-and-biology/reference/docking.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/skills/computational-chemistry-and-biology/reference/double_ended_ts_search.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/skills/computational-chemistry-and-biology/reference/electronic_properties.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/skills/computational-chemistry-and-biology/reference/folders_and_projects.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/skills/computational-chemistry-and-biology/reference/fukui.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/skills/computational-chemistry-and-biology/reference/hydrogen_bond_donor_acceptor_strength.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/skills/computational-chemistry-and-biology/reference/interaction_energy_decomposition.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/skills/computational-chemistry-and-biology/reference/ion_mobility.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/skills/computational-chemistry-and-biology/reference/irc.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/skills/computational-chemistry-and-biology/reference/logp.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/skills/computational-chemistry-and-biology/reference/macropka.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/skills/computational-chemistry-and-biology/reference/mcp_execution.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/skills/computational-chemistry-and-biology/reference/membrane_permeability.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/skills/computational-chemistry-and-biology/reference/msa.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/skills/computational-chemistry-and-biology/reference/multistage_optimization.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/skills/computational-chemistry-and-biology/reference/nmr.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/skills/computational-chemistry-and-biology/reference/pka.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/skills/computational-chemistry-and-biology/reference/pocket_detection.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/skills/computational-chemistry-and-biology/reference/pose_analysis_md.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/skills/computational-chemistry-and-biology/reference/protein_binder_design.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/skills/computational-chemistry-and-biology/reference/protein_cofolding.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/skills/computational-chemistry-and-biology/reference/protein_preparation.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/skills/computational-chemistry-and-biology/reference/python_sdk.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/skills/computational-chemistry-and-biology/reference/rbfe_graph.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/skills/computational-chemistry-and-biology/reference/redox_potential.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/skills/computational-chemistry-and-biology/reference/relative_binding_free_energy_perturbation.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/skills/computational-chemistry-and-biology/reference/scan.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/skills/computational-chemistry-and-biology/reference/solubility.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/skills/computational-chemistry-and-biology/reference/solvent_dependent_conformers.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/skills/computational-chemistry-and-biology/reference/spin_states.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/skills/computational-chemistry-and-biology/reference/strain.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/skills/computational-chemistry-and-biology/reference/tautomer_search.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/skills/computational-chemistry-and-biology/reference/webhooks.md +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/skills/computational-chemistry-and-biology/scripts/check_env.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/tests/test_cofolding_workflow_updates.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/tests/test_docking_workflow.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/tests/test_docking_workflow_updates.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/tests/test_macropka_nmr.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/tests/test_mango_forcefields.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/tests/test_plugin.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/tests/test_protein.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/tests/test_solvent_dependent_conformers.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/tests/test_tautomer_search.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/tests/test_utils.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/tests/test_workflow_results.py +0 -0
- {rowan_python-3.2.1 → rowan_python-3.2.3}/tests/test_workflow_submission.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: rowan-python
|
|
3
|
-
Version: 3.2.
|
|
3
|
+
Version: 3.2.3
|
|
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
|
|
@@ -12,7 +12,7 @@ Requires-Dist: httpx
|
|
|
12
12
|
Requires-Dist: nest-asyncio
|
|
13
13
|
Requires-Dist: rdkit
|
|
14
14
|
Requires-Dist: setuptools
|
|
15
|
-
Requires-Dist: stjames>=0.0.
|
|
15
|
+
Requires-Dist: stjames>=0.0.289
|
|
16
16
|
Description-Content-Type: text/markdown
|
|
17
17
|
|
|
18
18
|
# Rowan Python Library
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "rowan-python"
|
|
3
|
-
version = "3.2.
|
|
3
|
+
version = "3.2.3"
|
|
4
4
|
description = "Rowan Python Library"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "MIT"
|
|
@@ -12,7 +12,7 @@ authors = [
|
|
|
12
12
|
]
|
|
13
13
|
dependencies = [
|
|
14
14
|
"httpx",
|
|
15
|
-
"stjames>=0.0.
|
|
15
|
+
"stjames>=0.0.289",
|
|
16
16
|
"setuptools",
|
|
17
17
|
"rdkit",
|
|
18
18
|
"nest-asyncio",
|
|
@@ -15,6 +15,7 @@ from stjames import (
|
|
|
15
15
|
Constraint,
|
|
16
16
|
ConstraintType,
|
|
17
17
|
ConformerClusteringSettings,
|
|
18
|
+
ConformerProperties,
|
|
18
19
|
ConformerGenSettingsUnion,
|
|
19
20
|
Correction,
|
|
20
21
|
DNASequence,
|
|
@@ -67,6 +68,7 @@ from stjames.engine_compatibility import (
|
|
|
67
68
|
METHOD_ENGINES,
|
|
68
69
|
get_supported_corrections,
|
|
69
70
|
)
|
|
71
|
+
from stjames.pdb import ResidueRef
|
|
70
72
|
from stjames.optimization.band_method import NEBSettings
|
|
71
73
|
from stjames.optimization.interpolation import Interpolation
|
|
72
74
|
from stjames.optimization.string_method import FSMSettings, StringMethodSettings
|
|
@@ -82,5 +84,6 @@ from .types import ProteinUUID, RdkitMol, SMILES, StJamesMolecule, StructureInpu
|
|
|
82
84
|
from .workflows import *
|
|
83
85
|
from .project import *
|
|
84
86
|
from .protein import *
|
|
87
|
+
from .spectra import HARTREE_TO_EV, broaden_spectrum
|
|
85
88
|
from .user import *
|
|
86
89
|
from .utils import *
|
|
@@ -197,8 +197,15 @@ def default_project() -> Project:
|
|
|
197
197
|
|
|
198
198
|
Raises:
|
|
199
199
|
httpx.HTTPStatusError: API request fails
|
|
200
|
+
ValueError: user has no default project in the API key's organization
|
|
200
201
|
"""
|
|
201
202
|
with api_client() as client:
|
|
202
203
|
response = client.get("/user/me/default_project")
|
|
203
204
|
response.raise_for_status()
|
|
204
|
-
|
|
205
|
+
project = response.json()
|
|
206
|
+
if project is None:
|
|
207
|
+
raise ValueError(
|
|
208
|
+
"You have no default project in this organization. "
|
|
209
|
+
"Set a default project, or pass a folder or project."
|
|
210
|
+
)
|
|
211
|
+
return Project(**project)
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""Broaden stick spectra into continuous line shapes."""
|
|
2
|
+
|
|
3
|
+
import math
|
|
4
|
+
from collections.abc import Sequence
|
|
5
|
+
from typing import Literal
|
|
6
|
+
|
|
7
|
+
HARTREE_TO_EV = 27.211386245988
|
|
8
|
+
|
|
9
|
+
_GAUSSIAN_FWHM_TO_SIGMA = 1 / (2 * math.sqrt(2 * math.log(2)))
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def broaden_spectrum(
|
|
13
|
+
positions: Sequence[float],
|
|
14
|
+
intensities: Sequence[float],
|
|
15
|
+
grid: Sequence[float],
|
|
16
|
+
fwhm: float,
|
|
17
|
+
lineshape: Literal["gaussian", "lorentzian"] = "gaussian",
|
|
18
|
+
) -> list[float]:
|
|
19
|
+
"""Sum line shapes centered on each stick, each with area equal to its intensity.
|
|
20
|
+
|
|
21
|
+
Use it on the stick spectra of `ConformerSearchResult.ensemble_properties`, for
|
|
22
|
+
example excitation energies with oscillator or rotatory strengths for UV-vis or ECD,
|
|
23
|
+
or vibrational frequencies with IR intensities. `grid` and `fwhm` share the units of
|
|
24
|
+
`positions`; convert excitation energies from Hartree with `HARTREE_TO_EV` first to
|
|
25
|
+
broaden in eV.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
positions: line positions
|
|
29
|
+
intensities: line intensities, parallel to `positions`
|
|
30
|
+
grid: points at which to evaluate the spectrum
|
|
31
|
+
fwhm: full width at half maximum of each line
|
|
32
|
+
lineshape: line shape of each stick
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
spectrum at each point of `grid`
|
|
36
|
+
|
|
37
|
+
Raises:
|
|
38
|
+
ValueError: `fwhm` is not positive or `intensities` differs in length from `positions`
|
|
39
|
+
|
|
40
|
+
>>> [round(y, 4) for y in broaden_spectrum([0.0], [1.0], [0.0, 0.5], fwhm=1.0)]
|
|
41
|
+
[0.9394, 0.4697]
|
|
42
|
+
>>> [round(y, 4) for y in broaden_spectrum([0.0], [2.0], [0.0], 1.0, "lorentzian")]
|
|
43
|
+
[1.2732]
|
|
44
|
+
"""
|
|
45
|
+
if fwhm <= 0:
|
|
46
|
+
raise ValueError(f"fwhm must be positive, got {fwhm}")
|
|
47
|
+
if len(positions) != len(intensities):
|
|
48
|
+
raise ValueError(
|
|
49
|
+
f"positions ({len(positions)}) and intensities ({len(intensities)}) "
|
|
50
|
+
"must have the same length"
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
if lineshape == "gaussian":
|
|
54
|
+
sigma = fwhm * _GAUSSIAN_FWHM_TO_SIGMA
|
|
55
|
+
norm = 1 / (sigma * math.sqrt(2 * math.pi))
|
|
56
|
+
|
|
57
|
+
def shape(dx: float) -> float:
|
|
58
|
+
return norm * math.exp(-0.5 * (dx / sigma) ** 2)
|
|
59
|
+
|
|
60
|
+
else:
|
|
61
|
+
gamma = fwhm / 2
|
|
62
|
+
|
|
63
|
+
def shape(dx: float) -> float:
|
|
64
|
+
return gamma / (math.pi * (dx**2 + gamma**2))
|
|
65
|
+
|
|
66
|
+
return [
|
|
67
|
+
sum(i * shape(x - p) for p, i in zip(positions, intensities, strict=True)) for x in grid
|
|
68
|
+
]
|
|
@@ -4,7 +4,7 @@ from contextvars import ContextVar
|
|
|
4
4
|
from dataclasses import dataclass
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
from tempfile import NamedTemporaryFile
|
|
7
|
-
from typing import Any, Generator, Literal
|
|
7
|
+
from typing import Any, Callable, ContextManager, Generator, Literal
|
|
8
8
|
|
|
9
9
|
import httpx
|
|
10
10
|
import stjames
|
|
@@ -167,24 +167,14 @@ def api_client() -> Generator[httpx.Client, None, None]:
|
|
|
167
167
|
yield client
|
|
168
168
|
|
|
169
169
|
|
|
170
|
-
def
|
|
171
|
-
|
|
172
|
-
method: Literal["GET", "POST"],
|
|
173
|
-
url: str,
|
|
174
|
-
*,
|
|
175
|
-
params: dict[str, Any] | None = None,
|
|
176
|
-
json: Any = None,
|
|
177
|
-
) -> Path:
|
|
178
|
-
"""Stream an authenticated API response into one local file atomically."""
|
|
170
|
+
def _stream_to_file(path: Path, stream: Callable[[], ContextManager[httpx.Response]]) -> Path:
|
|
171
|
+
"""Write a streamed response into one local file atomically."""
|
|
179
172
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
180
173
|
temporary_path: Path | None = None
|
|
181
174
|
try:
|
|
182
175
|
with NamedTemporaryFile(dir=path.parent, prefix=f".{path.name}.", delete=False) as output:
|
|
183
176
|
temporary_path = Path(output.name)
|
|
184
|
-
with (
|
|
185
|
-
api_client() as client,
|
|
186
|
-
client.stream(method, url, params=params, json=json) as response,
|
|
187
|
-
):
|
|
177
|
+
with stream() as response:
|
|
188
178
|
response.raise_for_status()
|
|
189
179
|
for chunk in response.iter_bytes():
|
|
190
180
|
output.write(chunk)
|
|
@@ -196,6 +186,65 @@ def download_file(
|
|
|
196
186
|
return path
|
|
197
187
|
|
|
198
188
|
|
|
189
|
+
def download_file(
|
|
190
|
+
path: Path,
|
|
191
|
+
method: Literal["GET", "POST"],
|
|
192
|
+
url: str,
|
|
193
|
+
*,
|
|
194
|
+
params: dict[str, Any] | None = None,
|
|
195
|
+
json: Any = None,
|
|
196
|
+
) -> Path:
|
|
197
|
+
"""Stream an authenticated API response into one local file atomically."""
|
|
198
|
+
|
|
199
|
+
@contextmanager
|
|
200
|
+
def stream() -> Generator[httpx.Response, None, None]:
|
|
201
|
+
with (
|
|
202
|
+
api_client() as client,
|
|
203
|
+
client.stream(method, url, params=params, json=json) as response,
|
|
204
|
+
):
|
|
205
|
+
yield response
|
|
206
|
+
|
|
207
|
+
return _stream_to_file(path, stream)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def download_presigned_files(
|
|
211
|
+
directory: Path,
|
|
212
|
+
url: str,
|
|
213
|
+
*,
|
|
214
|
+
params: dict[str, Any] | None = None,
|
|
215
|
+
json: Any = None,
|
|
216
|
+
) -> list[Path]:
|
|
217
|
+
"""Fetch presigned download links from the API and stream each file into `directory`.
|
|
218
|
+
|
|
219
|
+
Links are short-lived, so an expired one is refreshed once before giving up.
|
|
220
|
+
"""
|
|
221
|
+
|
|
222
|
+
def fetch_links() -> list[dict[str, str]]:
|
|
223
|
+
with api_client() as client:
|
|
224
|
+
return client.post(url, params=params, json=json).json()
|
|
225
|
+
|
|
226
|
+
def download(link: dict[str, str]) -> Path:
|
|
227
|
+
# Plain client: the API key must never be sent to S3.
|
|
228
|
+
@contextmanager
|
|
229
|
+
def stream() -> Generator[httpx.Response, None, None]:
|
|
230
|
+
with httpx.Client(timeout=httpx.Timeout(120, read=None)) as client:
|
|
231
|
+
with client.stream("GET", link["url"]) as response:
|
|
232
|
+
yield response
|
|
233
|
+
|
|
234
|
+
return _stream_to_file(directory / Path(link["filename"]).name, stream)
|
|
235
|
+
|
|
236
|
+
links = fetch_links()
|
|
237
|
+
paths = []
|
|
238
|
+
for i, link in enumerate(links):
|
|
239
|
+
try:
|
|
240
|
+
paths.append(download(link))
|
|
241
|
+
except httpx.HTTPStatusError as error:
|
|
242
|
+
if error.response.status_code != 403:
|
|
243
|
+
raise
|
|
244
|
+
paths.append(download(fetch_links()[i]))
|
|
245
|
+
return paths
|
|
246
|
+
|
|
247
|
+
|
|
199
248
|
ATOMIC_NUMBER_TO_ATOMIC_SYMBOL = {
|
|
200
249
|
"1": "H",
|
|
201
250
|
"2": "He",
|
|
@@ -8,7 +8,7 @@ from typing import Literal, Protocol
|
|
|
8
8
|
from stjames.pdb import PDB
|
|
9
9
|
|
|
10
10
|
from rowan.protein import Protein, retrieve_protein
|
|
11
|
-
from rowan.utils import api_client, download_file
|
|
11
|
+
from rowan.utils import api_client, download_file, download_presigned_files
|
|
12
12
|
|
|
13
13
|
from .base import WorkflowResult
|
|
14
14
|
|
|
@@ -222,7 +222,9 @@ class _MolecularDynamicsResult(WorkflowResult):
|
|
|
222
222
|
name: str | None = None,
|
|
223
223
|
path: Path | str | None = None,
|
|
224
224
|
) -> Path:
|
|
225
|
-
"""Download DCD trajectory files for selected replicates.
|
|
225
|
+
"""Download DCD trajectory files for selected replicates as one `.tar.gz` archive.
|
|
226
|
+
|
|
227
|
+
Long trajectories can time out; use `download_trajectory_files` for those.
|
|
226
228
|
|
|
227
229
|
Args:
|
|
228
230
|
replicates: zero-based replicate indices
|
|
@@ -244,6 +246,29 @@ class _MolecularDynamicsResult(WorkflowResult):
|
|
|
244
246
|
json=replicates,
|
|
245
247
|
)
|
|
246
248
|
|
|
249
|
+
def download_trajectory_files(
|
|
250
|
+
self,
|
|
251
|
+
replicates: list[int],
|
|
252
|
+
path: Path | str | None = None,
|
|
253
|
+
) -> list[Path]:
|
|
254
|
+
"""Download each selected replicate's DCD file directly from storage.
|
|
255
|
+
|
|
256
|
+
Args:
|
|
257
|
+
replicates: zero-based replicate indices
|
|
258
|
+
path: output directory; defaults to the current directory
|
|
259
|
+
|
|
260
|
+
Returns:
|
|
261
|
+
paths to the downloaded `.dcd` files, one per replicate
|
|
262
|
+
|
|
263
|
+
Raises:
|
|
264
|
+
httpx.HTTPStatusError: API or storage request fails
|
|
265
|
+
"""
|
|
266
|
+
return download_presigned_files(
|
|
267
|
+
Path(path) if path is not None else Path.cwd(),
|
|
268
|
+
f"/trajectory/{self.workflow_uuid}/trajectory_dcd_urls",
|
|
269
|
+
json=replicates,
|
|
270
|
+
)
|
|
271
|
+
|
|
247
272
|
def _trajectory(self, replicate: int) -> _MolecularDynamicsTrajectory:
|
|
248
273
|
"""Return one trajectory with a consistent bounds error."""
|
|
249
274
|
trajectories = self.trajectories
|
|
@@ -153,9 +153,15 @@ def submit_binding_affinity_workflow(
|
|
|
153
153
|
binding_affinity_settings=binding_affinity_settings or SinglePointEnergySettings(),
|
|
154
154
|
)
|
|
155
155
|
|
|
156
|
+
workflow_data = workflow.model_dump(serialize_as_any=True, mode="json")
|
|
157
|
+
if isinstance(workflow.binding_affinity_settings, SinglePointEnergySettings):
|
|
158
|
+
workflow_data["binding_affinity_settings"]["truncation_radius"] = (
|
|
159
|
+
workflow.binding_affinity_settings.truncation_radius
|
|
160
|
+
)
|
|
161
|
+
|
|
156
162
|
data = {
|
|
157
163
|
"workflow_type": "binding_affinity",
|
|
158
|
-
"workflow_data":
|
|
164
|
+
"workflow_data": workflow_data,
|
|
159
165
|
"name": name,
|
|
160
166
|
"folder_uuid": folder_uuid,
|
|
161
167
|
"max_credits": max_credits,
|
|
@@ -31,6 +31,8 @@ from .base import (
|
|
|
31
31
|
)
|
|
32
32
|
from .constants import to_relative_kcal
|
|
33
33
|
|
|
34
|
+
ConformerProperties = stjames.ConformerProperties
|
|
35
|
+
|
|
34
36
|
|
|
35
37
|
@register_result("conformer_search")
|
|
36
38
|
class ConformerSearchResult(WorkflowResult):
|
|
@@ -72,22 +74,59 @@ class ConformerSearchResult(WorkflowResult):
|
|
|
72
74
|
return to_relative_kcal(energies) if relative else energies
|
|
73
75
|
|
|
74
76
|
@property
|
|
75
|
-
def radii_of_gyration(self) -> list[float]:
|
|
76
|
-
"""Radius of gyration for each conformer (A)."""
|
|
77
|
+
def radii_of_gyration(self) -> list[float | None]:
|
|
78
|
+
"""Radius of gyration for each conformer (A), None where it failed."""
|
|
77
79
|
return [p.radius_of_gyration for p in self._workflow.conformer_properties]
|
|
78
80
|
|
|
79
81
|
@property
|
|
80
|
-
def sasa(self) -> list[float]:
|
|
81
|
-
"""Solvent accessible surface area for each conformer (A^2)."""
|
|
82
|
+
def sasa(self) -> list[float | None]:
|
|
83
|
+
"""Solvent accessible surface area for each conformer (A^2), None where it failed."""
|
|
82
84
|
return [p.solvent_accessible_surface_area for p in self._workflow.conformer_properties]
|
|
83
85
|
|
|
84
86
|
@property
|
|
85
|
-
def polar_sasa(self) -> list[float]:
|
|
86
|
-
"""Polar solvent accessible surface area for each conformer (A^2)."""
|
|
87
|
+
def polar_sasa(self) -> list[float | None]:
|
|
88
|
+
"""Polar solvent accessible surface area for each conformer (A^2), None where it failed."""
|
|
87
89
|
return [
|
|
88
90
|
p.polar_solvent_accessible_surface_area for p in self._workflow.conformer_properties
|
|
89
91
|
]
|
|
90
92
|
|
|
93
|
+
@property
|
|
94
|
+
def dipoles(self) -> list[float | None]:
|
|
95
|
+
"""Dipole moment magnitude about the center of mass for each conformer (Debye).
|
|
96
|
+
|
|
97
|
+
None where the method reports no dipole.
|
|
98
|
+
"""
|
|
99
|
+
return [p.dipole for p in self._workflow.conformer_properties]
|
|
100
|
+
|
|
101
|
+
@property
|
|
102
|
+
def ensemble_properties(self) -> ConformerProperties | None:
|
|
103
|
+
"""Ensemble properties weighted by `boltzmann_weights`.
|
|
104
|
+
|
|
105
|
+
Each property is averaged over the conformers that have it: SASA, polar SASA, Rg,
|
|
106
|
+
the root-mean-square dipole (Debye), and per-atom Mulliken charges. When the run
|
|
107
|
+
computed them, it also holds stick spectra with every conformer's lines, each
|
|
108
|
+
intensity scaled by its conformer's population: `excitation_energies` (Hartree) with
|
|
109
|
+
`oscillator_strengths` (UV-vis) and `rotatory_strengths` (ECD), and
|
|
110
|
+
`vibrational_frequencies` (cm^-1) with `ir_intensities` (km/mol). Pass them to
|
|
111
|
+
`rowan.broaden_spectrum` for the spectrum. Rotatory strengths are omitted when the
|
|
112
|
+
conformers differ in stereochemistry, since mixing enantiomers cancels the ECD signal.
|
|
113
|
+
"""
|
|
114
|
+
return self._workflow.ensemble_properties
|
|
115
|
+
|
|
116
|
+
@property
|
|
117
|
+
def boltzmann_weights(self) -> list[float]:
|
|
118
|
+
"""Population of each conformer at the weighting temperature (sum to 1.0).
|
|
119
|
+
|
|
120
|
+
From free energies when every conformer has frequencies, else from ground-state
|
|
121
|
+
energies. Empty for workflows run before weights were reported.
|
|
122
|
+
"""
|
|
123
|
+
return list(self._workflow.boltzmann_weights)
|
|
124
|
+
|
|
125
|
+
@property
|
|
126
|
+
def weighting_temperature(self) -> float:
|
|
127
|
+
"""Temperature of `boltzmann_weights` and `ensemble_properties` (K)."""
|
|
128
|
+
return self._workflow.weighting_temperature
|
|
129
|
+
|
|
91
130
|
def get_conformers(self, n: int | None = None) -> list[Molecule]:
|
|
92
131
|
"""Fetch conformer molecules.
|
|
93
132
|
|
|
@@ -156,6 +195,7 @@ def _mso_for_final_method(
|
|
|
156
195
|
final_method: Method,
|
|
157
196
|
solvent: SolventInput = None,
|
|
158
197
|
transition_state: bool = False,
|
|
198
|
+
frequencies: bool = False,
|
|
159
199
|
) -> MultiStageOptSettings:
|
|
160
200
|
"""Build optimization settings for the final method.
|
|
161
201
|
|
|
@@ -163,9 +203,9 @@ def _mso_for_final_method(
|
|
|
163
203
|
without a named preset.
|
|
164
204
|
|
|
165
205
|
`solvent` and `transition_state` are merged into each stage's `Settings`
|
|
166
|
-
when supplied
|
|
167
|
-
|
|
168
|
-
toggles).
|
|
206
|
+
when supplied, and `frequencies` runs on the optimization stage; the named
|
|
207
|
+
preset itself is independent of them (mirrors how tinbergen's form keeps the
|
|
208
|
+
preset dropdown separate from the solvent / TS toggles).
|
|
169
209
|
"""
|
|
170
210
|
opt_method, sp_method = _FINAL_METHOD_TO_MSO_RECIPE.get(final_method, (final_method, None))
|
|
171
211
|
|
|
@@ -196,6 +236,7 @@ def _mso_for_final_method(
|
|
|
196
236
|
return MultiStageOptSettings(
|
|
197
237
|
optimization_settings=[opt_stage],
|
|
198
238
|
singlepoint_settings=sp_stage,
|
|
239
|
+
frequencies=frequencies,
|
|
199
240
|
)
|
|
200
241
|
|
|
201
242
|
|
|
@@ -224,6 +265,8 @@ def submit_conformer_search_workflow(
|
|
|
224
265
|
final_method: Method | str = "aimnet2_wb97md3",
|
|
225
266
|
solvent: SolventInput = None,
|
|
226
267
|
transition_state: bool = False,
|
|
268
|
+
frequencies: bool = False,
|
|
269
|
+
weighting_temperature: float = 298.15,
|
|
227
270
|
multistage_opt_settings: MultiStageOptSettings | None = None,
|
|
228
271
|
conformer_clustering_settings: ConformerClusteringSettings | None = None,
|
|
229
272
|
initial_conformers: Sequence[StructureInput] | None = None,
|
|
@@ -264,6 +307,11 @@ def submit_conformer_search_workflow(
|
|
|
264
307
|
`multistage_opt_settings` is provided
|
|
265
308
|
transition_state: whether to optimize the transition state. Ignored
|
|
266
309
|
if `multistage_opt_settings` is provided
|
|
310
|
+
frequencies: compute vibrational frequencies of each conformer at the optimization
|
|
311
|
+
level, for the ensemble IR spectrum. Ignored if `multistage_opt_settings` is
|
|
312
|
+
provided; set its `frequencies` instead
|
|
313
|
+
weighting_temperature: temperature for the conformer populations, in Kelvin. With
|
|
314
|
+
frequencies, populations use free energies at this temperature
|
|
267
315
|
multistage_opt_settings: optimization stages and singlepoint settings
|
|
268
316
|
for ranking conformers. When provided, takes precedence over
|
|
269
317
|
`final_method` / `solvent` / `transition_state`. When omitted, an MSO is
|
|
@@ -359,7 +407,10 @@ def submit_conformer_search_workflow(
|
|
|
359
407
|
if isinstance(final_method, str):
|
|
360
408
|
final_method = Method(final_method)
|
|
361
409
|
multistage_opt_settings = _mso_for_final_method(
|
|
362
|
-
final_method,
|
|
410
|
+
final_method,
|
|
411
|
+
solvent=solvent,
|
|
412
|
+
transition_state=transition_state,
|
|
413
|
+
frequencies=frequencies,
|
|
363
414
|
)
|
|
364
415
|
multistage_opt_settings = _release_ts_optimization_constraints(multistage_opt_settings)
|
|
365
416
|
|
|
@@ -370,6 +421,7 @@ def submit_conformer_search_workflow(
|
|
|
370
421
|
multistage_opt_settings=multistage_opt_settings,
|
|
371
422
|
conf_gen_settings=conf_gen_settings,
|
|
372
423
|
conformer_clustering_settings=conformer_clustering_settings,
|
|
424
|
+
weighting_temperature=weighting_temperature,
|
|
373
425
|
)
|
|
374
426
|
|
|
375
427
|
workflow_data = workflow.model_dump(serialize_as_any=True, mode="json")
|
|
@@ -149,7 +149,7 @@ def submit_protein_md_workflow(
|
|
|
149
149
|
binder: Binder | None = None,
|
|
150
150
|
protein_restraint_cutoff: float | None = None,
|
|
151
151
|
protein_restraint_constant: float = 100,
|
|
152
|
-
analysis_interval_ps: float | None =
|
|
152
|
+
analysis_interval_ps: float | None = 100,
|
|
153
153
|
clustering: KMeansClusteringSettings | GreedyClusteringSettings | None = None,
|
|
154
154
|
validate_forcefield: bool = True,
|
|
155
155
|
name: str = "Protein MD Workflow",
|
|
@@ -194,8 +194,8 @@ def submit_protein_md_workflow(
|
|
|
194
194
|
protein_restraint_cutoff: cutoff distance from the binder past which Cα atoms are
|
|
195
195
|
harmonically restrained, in Å; None disables restraints
|
|
196
196
|
protein_restraint_constant: force constant for Cα backbone restraints, in kcal/mol/Ų
|
|
197
|
-
analysis_interval_ps: interval at which to compute per-frame SASA and polar SASA
|
|
198
|
-
None disables those analyses
|
|
197
|
+
analysis_interval_ps: interval at which to compute per-frame SASA and polar SASA
|
|
198
|
+
(default 100), in ps. None disables those analyses
|
|
199
199
|
clustering: how to cluster trajectory frames. None disables clustering; pass a
|
|
200
200
|
KMeansClusteringSettings (num_clusters) or GreedyClusteringSettings (cutoff_angstrom)
|
|
201
201
|
validate_forcefield: validate the protein forcefield
|
|
@@ -12,7 +12,7 @@ from rowan.folder import Folder
|
|
|
12
12
|
from rowan.molecule import Molecule
|
|
13
13
|
from rowan.protein import Protein
|
|
14
14
|
from rowan.types import ProteinUUID
|
|
15
|
-
from rowan.utils import api_client, download_file
|
|
15
|
+
from rowan.utils import api_client, download_file, download_presigned_files
|
|
16
16
|
|
|
17
17
|
from .base import Workflow, WorkflowResult, molecule_to_dict, register_result
|
|
18
18
|
from .rbfe_graph import RelativeBindingFreeEnergyGraphEdge, RelativeBindingFreeEnergyGraphResult
|
|
@@ -143,7 +143,9 @@ class RelativeBindingFreeEnergyPerturbationResult(WorkflowResult):
|
|
|
143
143
|
*,
|
|
144
144
|
leg: Literal["complex", "solvent"] = "complex",
|
|
145
145
|
) -> Path:
|
|
146
|
-
"""Download DCD trajectory files for a specific perturbation edge.
|
|
146
|
+
"""Download DCD trajectory files for a specific perturbation edge as one `.tar.gz` archive.
|
|
147
|
+
|
|
148
|
+
Long trajectories can time out; use `download_edge_trajectory_files` for those.
|
|
147
149
|
|
|
148
150
|
Args:
|
|
149
151
|
edge_index: index of the edge (0-based, matching `edges` order)
|
|
@@ -183,6 +185,40 @@ class RelativeBindingFreeEnergyPerturbationResult(WorkflowResult):
|
|
|
183
185
|
json=lambda_vals,
|
|
184
186
|
)
|
|
185
187
|
|
|
188
|
+
def download_edge_trajectory_files(
|
|
189
|
+
self,
|
|
190
|
+
edge_index: int,
|
|
191
|
+
lambda_vals: list[float] | None = None,
|
|
192
|
+
path: Path | str | None = None,
|
|
193
|
+
*,
|
|
194
|
+
leg: Literal["complex", "solvent"] = "complex",
|
|
195
|
+
) -> list[Path]:
|
|
196
|
+
"""Download each lambda window's DCD file for one edge directly from storage.
|
|
197
|
+
|
|
198
|
+
Args:
|
|
199
|
+
edge_index: index of the edge (0-based, matching `edges` order)
|
|
200
|
+
lambda_vals: lambda values to download. Defaults to all windows
|
|
201
|
+
path: directory to save the files to. Defaults to current directory
|
|
202
|
+
leg: thermodynamic leg whose trajectories should be downloaded
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
paths to the downloaded `.dcd` files, one per lambda window
|
|
206
|
+
|
|
207
|
+
Raises:
|
|
208
|
+
IndexError: edge_index is out of range
|
|
209
|
+
httpx.HTTPStatusError: API or storage request fails
|
|
210
|
+
"""
|
|
211
|
+
edges = self.edges
|
|
212
|
+
if edge_index < 0 or edge_index >= len(edges):
|
|
213
|
+
raise IndexError(f"Edge index {edge_index} out of range (0-{len(edges) - 1})")
|
|
214
|
+
|
|
215
|
+
return download_presigned_files(
|
|
216
|
+
Path(path) if path is not None else Path.cwd(),
|
|
217
|
+
f"/trajectory/{self.workflow_uuid}/rbfe_trajectory_dcd_urls",
|
|
218
|
+
params={"edge_index": edge_index, "leg": leg},
|
|
219
|
+
json=lambda_vals,
|
|
220
|
+
)
|
|
221
|
+
|
|
186
222
|
def download_all_trajectories(
|
|
187
223
|
self,
|
|
188
224
|
path: Path | str | None = None,
|
|
@@ -205,6 +241,35 @@ class RelativeBindingFreeEnergyPerturbationResult(WorkflowResult):
|
|
|
205
241
|
self.download_edge_trajectories(i, path=path, leg=leg) for i in range(len(self.edges))
|
|
206
242
|
]
|
|
207
243
|
|
|
244
|
+
def download_all_trajectory_files(
|
|
245
|
+
self,
|
|
246
|
+
path: Path | str | None = None,
|
|
247
|
+
*,
|
|
248
|
+
leg: Literal["complex", "solvent"] = "complex",
|
|
249
|
+
) -> list[Path]:
|
|
250
|
+
"""Download every edge's DCD files directly from storage.
|
|
251
|
+
|
|
252
|
+
Files for each edge go in an `edge_<index>` subdirectory of `path`.
|
|
253
|
+
|
|
254
|
+
Args:
|
|
255
|
+
path: directory to save the files to. Defaults to current directory
|
|
256
|
+
leg: thermodynamic leg whose trajectories should be downloaded
|
|
257
|
+
|
|
258
|
+
Returns:
|
|
259
|
+
paths to all downloaded `.dcd` files, in edge order
|
|
260
|
+
|
|
261
|
+
Raises:
|
|
262
|
+
httpx.HTTPStatusError: any API or storage request fails
|
|
263
|
+
"""
|
|
264
|
+
directory = Path(path) if path is not None else Path.cwd()
|
|
265
|
+
return [
|
|
266
|
+
file
|
|
267
|
+
for i in range(len(self.edges))
|
|
268
|
+
for file in self.download_edge_trajectory_files(
|
|
269
|
+
i, path=directory / f"edge_{i}", leg=leg
|
|
270
|
+
)
|
|
271
|
+
]
|
|
272
|
+
|
|
208
273
|
@property
|
|
209
274
|
def ligand_dg_results(self) -> dict[str, RelativeBindingFreeEnergyResult] | None:
|
|
210
275
|
"""Per-ligand binding free energy results, or None if not yet computed."""
|
|
@@ -57,9 +57,19 @@ for name, score in zip(ligands.keys(), result.scores):
|
|
|
57
57
|
|
|
58
58
|
- `rowan.SinglePointEnergySettings` (default):
|
|
59
59
|
- `multistage_opt_settings` (default PM6-D3H4X/COSMO optimization + PM6-D3H4X/COSMO2 single-point in water): a `rowan.MultiStageOptSettings` controlling ligand geometry optimization and energy evaluation.
|
|
60
|
-
- `truncation_radius` (default `6.0` Å): protein residues
|
|
60
|
+
- `truncation_radius` (default `6.0` Å): selects whole protein residues with any atom within this distance of any submitted ligand atom.
|
|
61
|
+
- `truncation_residues`: nonempty list of `rowan.ResidueRef` objects identifying polymer residues by chain and residue keys in the submitted protein. Requires `truncation_radius=None`; a radius and explicit residues cannot be supplied together.
|
|
61
62
|
- `rowan.GninaAffinitySettings`, `rowan.AEVPLIGAffinitySettings`, `rowan.NessoAffinitySettings`: no tunable parameters.
|
|
62
63
|
|
|
64
|
+
For an explicit pocket, pass settings like these, using keys from your protein:
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
rowan.SinglePointEnergySettings(
|
|
68
|
+
truncation_radius=None,
|
|
69
|
+
truncation_residues=[rowan.ResidueRef("polymer", "A", "A.182")],
|
|
70
|
+
)
|
|
71
|
+
```
|
|
72
|
+
|
|
63
73
|
```python
|
|
64
74
|
# Replace the path, residue name, and SMILES with those of your holo complex.
|
|
65
75
|
holo_protein = rowan.upload_protein("Holo complex", "PATH_TO_HOLO_PDB")
|
|
@@ -27,10 +27,29 @@ best = result.get_conformers(1)[0] # lowest-energy conformer, a rowan.Molecule
|
|
|
27
27
|
|
|
28
28
|
`get_conformers(n)` returns the `n` lowest-energy conformers as `rowan.Molecule` objects (omit `n` for all), and `get_energies(relative=True)` gives their relative energies in kcal/mol. Both are energy-ordered, so the lowest-energy conformer is index 0, ready to feed into a downstream 3D workflow. `get_conformers` makes one API call per conformer, so request only as many as you need.
|
|
29
29
|
|
|
30
|
+
## Ensemble properties
|
|
31
|
+
|
|
32
|
+
`sasa`, `polar_sasa`, `radii_of_gyration`, and `dipoles` give per-conformer values in energy order (`None` where a value could not be computed); `dipoles` are magnitudes about the center of mass, in Debye. `boltzmann_weights` are the conformer populations at `weighting_temperature`, from free energies when every conformer has frequencies and from ground-state energies otherwise.
|
|
33
|
+
|
|
34
|
+
`ensemble_properties` is a `rowan.ConformerProperties` weighted by those populations over the conformers that have each value: SASA, polar SASA, Rg, the root-mean-square dipole, and per-atom Mulliken charges. When the run computed them, it also holds stick spectra with every conformer's lines, each intensity scaled by its conformer's population: `excitation_energies` (Hartree) with `oscillator_strengths` (UV-vis) and `rotatory_strengths` (ECD), and `vibrational_frequencies` (cm⁻¹) with `ir_intensities` (km/mol).
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
import rowan
|
|
38
|
+
|
|
39
|
+
props = result.ensemble_properties
|
|
40
|
+
energies_ev = [e * rowan.HARTREE_TO_EV for e in props.excitation_energies]
|
|
41
|
+
grid = [2 + 0.01 * i for i in range(600)] # eV
|
|
42
|
+
uv_vis = rowan.broaden_spectrum(energies_ev, props.oscillator_strengths, grid, fwhm=0.3)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Spectra need the matching calculations: set `frequencies=True` for IR, and give `multistage_opt_settings` a TDDFT `singlepoint_settings` (with `excited_state_settings=rowan.TDDFTSettings(...)`) for UV-vis and ECD. ECD is omitted when the conformers differ in stereochemistry, so give a SMILES with every stereocenter assigned. A TDDFT single point does not report dipoles or charges. The populations are fixed when the workflow runs; resubmit with another `weighting_temperature` for a different temperature.
|
|
46
|
+
|
|
30
47
|
## Settings
|
|
31
48
|
|
|
32
49
|
- `final_method` (default `aimnet2_wb97md3`): method used to rank the final conformers.
|
|
33
50
|
- `solvent` (default gas phase): implicit solvent; introspect `rowan.Solvent` for the available solvents.
|
|
51
|
+
- `frequencies` (default `False`): compute vibrational frequencies of each conformer at the optimization level, for the ensemble IR spectrum and free-energy populations.
|
|
52
|
+
- `weighting_temperature` (default 298.15 K): temperature for the conformer populations and their free-energy corrections.
|
|
34
53
|
- `transition_state` (default `False`): optimize each conformer toward a transition state rather than a minimum. Start from a fully optimized transition state, not a guess and not a SMILES. Conformer generation will otherwise relax the transition state to a local minimum, so pass constraints in `conf_gen_settings` to hold the reacting atoms in place during generation; the post-generation TS optimizations then run without constraints.
|
|
35
54
|
- `conf_gen_settings` (default none, currently resolves to OpenConf): override conformer generation. Pick and tune a generator by passing its settings object: `rowan.OpenConfSettings`, `rowan.ETKDGSettings`, `rowan.MonteCarloMultipleMinimumSettings` (MCMM), or `rowan.iMTDGCSettings` (iMTDGC). OpenConf (the default) is Rowan's conformer generator; ETKDG is a fast RDKit knowledge-based generator; iMTDGC runs CREST metadynamics for more thorough sampling of flexible molecules, at higher cost. Each has its own tuning fields; run `help()` on the one you want. `max_confs` is common to every generator and caps how many conformers are kept after generation.
|
|
36
55
|
- `multistage_opt_settings` (default none): override the full optimization-and-ranking stack as a `MultiStageOptSettings`. When supplied, it takes precedence over `final_method`, `solvent`, and `transition_state`, so those are ignored. Omit it and set `final_method`/`solvent`/`transition_state` instead unless you need a stack they cannot express.
|
|
@@ -61,7 +61,7 @@ result.download_trajectories([0], path=".") # save DCD trajectory files
|
|
|
61
61
|
- `binder` (default `None`): a `rowan.Binder` specifying the binder within the complex — protein/peptide chains (`chain_ids`), small molecules (`small_molecule_residues`, identified by residue-name string or 0-based non-polymer residue index), or both. Enables per-frame MM/GBSA and binder RMSD analyses (see result fields).
|
|
62
62
|
- `protein_restraint_cutoff` (default `None`): distance from the binder past which Cα atoms are harmonically restrained, in angstrom; `None` disables restraints. Useful for keeping the binding site mobile while stabilizing the rest of the protein.
|
|
63
63
|
- `protein_restraint_constant` (default `100`): force constant for the Cα backbone restraints, in kcal/mol/Ų.
|
|
64
|
-
- `analysis_interval_ps` (default `
|
|
64
|
+
- `analysis_interval_ps` (default `100`): interval at which to compute per-frame SASA and polar SASA, in ps. `None` disables those analyses.
|
|
65
65
|
- `clustering` (default `None`): cluster the trajectory frames. `None` disables it; pass `rowan.KMeansClusteringSettings(num_clusters=10)` or `rowan.GreedyClusteringSettings(cutoff_angstrom=2.0)`.
|
|
66
66
|
- `validate_forcefield` (default `True`): validate the protein forcefield before submitting; raises early if the protein cannot be parameterized or has clashing residues. When a `binder` is set, its small molecules are excluded from validation, whether keyed by residue name or by non-polymer index, since they are parameterized from their SMILES. Cofactors, metals, and glycans outside the binder are still validated — set `False` to skip the pre-check entirely.
|
|
67
67
|
|