Full Code of OpenFreeEnergy/openfe for AI

main cb10892e79a2 cached
465 files
4.5 MB
1.2M tokens
2321 symbols
1 requests
Download .txt
Showing preview only (4,794K chars total). Download the full file or copy to clipboard to get everything.
Repository: OpenFreeEnergy/openfe
Branch: main
Commit: cb10892e79a2
Files: 465
Total size: 4.5 MB

Directory structure:
gitextract_wx9d1k3u/

├── .dockerignore
├── .git-blame-ignore-revs
├── .gitattributes
├── .github/
│   ├── CONTRIBUTING.md
│   ├── PULL_REQUEST_TEMPLATE/
│   │   └── release_template.md
│   ├── pull_request_template.md
│   └── workflows/
│       ├── aws-cpu-long-tests.yaml
│       ├── aws-gpu-integration-tests.yaml
│       ├── ci.yaml
│       ├── clean-pr-caches.yaml
│       ├── cron-conda.yaml
│       ├── cron-docker.yaml
│       ├── cron-feedstock-build-tests.yaml
│       ├── cron-package-test.yaml
│       ├── griffe-api-break.yaml
│       ├── mypy.yaml
│       ├── release-docker-image.yaml
│       ├── release-installers.yaml
│       ├── release-make-condalock.yaml
│       ├── release-prep-examplenotebooks.yaml
│       └── release-prep-feedstock.yaml
├── .gitignore
├── .pre-commit-config.yaml
├── .readthedocs.yaml
├── CITATION.cff
├── Code_of_Conduct.md
├── LICENSE
├── MANIFEST.in
├── README.md
├── codecov.yml
├── devtools/
│   ├── data/
│   │   ├── fix_rbfe_results.py
│   │   └── gen_serialized_results.py
│   ├── debug_openmm.sh
│   └── installer/
│       └── construct.yaml
├── docs/
│   ├── CHANGELOG.rst
│   ├── Makefile
│   ├── _ext/
│   │   └── sass.py
│   ├── _sass/
│   │   └── deflist-flowchart.scss
│   ├── _templates/
│   │   └── autosummary/
│   │       ├── base.rst
│   │       └── class.rst
│   ├── conf.py
│   ├── cookbook/
│   │   ├── bespoke_parameters.nblink
│   │   ├── choose_protocol.nblink
│   │   ├── create_alchemical_network.nblink
│   │   ├── dumping_transformation.rst
│   │   ├── generate_ligand_network.nblink
│   │   ├── hand_write_ligand_network.nblink
│   │   ├── index.rst
│   │   ├── jq_inspection.rst
│   │   ├── ligandnetwork_vis.nblink
│   │   ├── loading_molecules.nblink
│   │   ├── network_from_orion_fepp.nblink
│   │   ├── rfe_alchemical_planners.nblink
│   │   └── user_charges.nblink
│   ├── environment.yaml
│   ├── guide/
│   │   ├── cli/
│   │   │   ├── cli_basics.rst
│   │   │   ├── cli_yaml.rst
│   │   │   └── index.rst
│   │   ├── execution/
│   │   │   ├── execution_theory.rst
│   │   │   ├── index.rst
│   │   │   └── quickrun_execution.rst
│   │   ├── index.rst
│   │   ├── introduction.rst
│   │   ├── protocols/
│   │   │   ├── absolutebinding.rst
│   │   │   ├── absolutesolvation.rst
│   │   │   ├── index.rst
│   │   │   ├── plainmd.rst
│   │   │   ├── relativehybridtopology.rst
│   │   │   └── septop.rst
│   │   ├── results/
│   │   │   ├── index.rst
│   │   │   ├── working_with_networks.rst
│   │   │   └── working_with_results.rst
│   │   ├── setup/
│   │   │   ├── alchemical_network_model.rst
│   │   │   ├── chemical_systems_and_thermodynamic_cycles.rst
│   │   │   ├── creating_atom_mappings_and_scores.rst
│   │   │   ├── creating_ligand_networks.rst
│   │   │   ├── defining_protocols.rst
│   │   │   └── index.rst
│   │   ├── troubleshooting.rst
│   │   └── under_the_hood.rst
│   ├── index.rst
│   ├── installation.rst
│   ├── make.bat
│   ├── reference/
│   │   ├── api/
│   │   │   ├── alchemical_network_planning.rst
│   │   │   ├── atom_mappers.rst
│   │   │   ├── defining_and_executing_simulations.rst
│   │   │   ├── index.rst
│   │   │   ├── ligand_network.rst
│   │   │   ├── openmm_binding_afe.rst
│   │   │   ├── openmm_md.rst
│   │   │   ├── openmm_protocol_settings.rst
│   │   │   ├── openmm_rfe.rst
│   │   │   ├── openmm_septop.rst
│   │   │   ├── openmm_solvation_afe.rst
│   │   │   └── systems_and_components.rst
│   │   ├── cli/
│   │   │   ├── charge_molecules.rst
│   │   │   ├── gather.rst
│   │   │   ├── index.rst
│   │   │   ├── plan_rbfe_network.rst
│   │   │   ├── plan_rhfe_network.rst
│   │   │   └── quickrun.rst
│   │   └── index.rst
│   └── tutorials/
│       ├── .gitignore
│       ├── abfe_analysis_tutorial.nblink
│       ├── abfe_tutorial.nblink
│       ├── ahfe_tutorial.nblink
│       ├── charge_molecules_cli_tutorial.rst
│       ├── index.rst
│       ├── md_tutorial.nblink
│       ├── plotting_with_cinnabar.nblink
│       ├── rbfe_cli_tutorial.rst
│       ├── rbfe_membrane_protein.nblink
│       ├── rbfe_python_tutorial.nblink
│       ├── septop_analysis_tutorial.nblink
│       ├── septop_tutorial.nblink
│       └── showcase_notebook.nblink
├── environment.yml
├── news/
│   └── TEMPLATE.rst
├── production/
│   ├── Dockerfile
│   └── environment.yml
├── pyproject.toml
├── rever.xsh
└── src/
    ├── openfe/
    │   ├── __init__.py
    │   ├── analysis/
    │   │   ├── __init__.py
    │   │   └── plotting.py
    │   ├── data/
    │   │   ├── __init__.py
    │   │   ├── _downloader.py
    │   │   └── _registry.py
    │   ├── due.py
    │   ├── orchestration/
    │   │   └── __init__.py
    │   ├── protocols/
    │   │   ├── __init__.py
    │   │   ├── openmm_afe/
    │   │   │   ├── __init__.py
    │   │   │   ├── abfe_units.py
    │   │   │   ├── afe_protocol_results.py
    │   │   │   ├── ahfe_units.py
    │   │   │   ├── base_afe_units.py
    │   │   │   ├── equil_afe_settings.py
    │   │   │   ├── equil_binding_afe_method.py
    │   │   │   └── equil_solvation_afe_method.py
    │   │   ├── openmm_md/
    │   │   │   ├── __init__.py
    │   │   │   ├── plain_md_methods.py
    │   │   │   └── plain_md_settings.py
    │   │   ├── openmm_rfe/
    │   │   │   ├── __init__.py
    │   │   │   ├── _rfe_utils/
    │   │   │   │   ├── __init__.py
    │   │   │   │   ├── lambdaprotocol.py
    │   │   │   │   ├── multistate.py
    │   │   │   │   ├── relative.py
    │   │   │   │   └── topologyhelpers.py
    │   │   │   ├── equil_rfe_methods.py
    │   │   │   ├── equil_rfe_settings.py
    │   │   │   ├── hybridtop_protocol_results.py
    │   │   │   ├── hybridtop_protocols.py
    │   │   │   └── hybridtop_units.py
    │   │   ├── openmm_septop/
    │   │   │   ├── __init__.py
    │   │   │   ├── base_units.py
    │   │   │   ├── equil_septop_method.py
    │   │   │   ├── equil_septop_settings.py
    │   │   │   ├── septop_protocol_results.py
    │   │   │   ├── septop_units.py
    │   │   │   └── utils.py
    │   │   ├── openmm_utils/
    │   │   │   ├── __init__.py
    │   │   │   ├── charge_generation.py
    │   │   │   ├── mdtraj_utils.py
    │   │   │   ├── multistate_analysis.py
    │   │   │   ├── omm_compute.py
    │   │   │   ├── omm_settings.py
    │   │   │   ├── serialization.py
    │   │   │   ├── settings_validation.py
    │   │   │   ├── system_creation.py
    │   │   │   └── system_validation.py
    │   │   └── restraint_utils/
    │   │       ├── __init__.py
    │   │       ├── geometry/
    │   │       │   ├── __init__.py
    │   │       │   ├── base.py
    │   │       │   ├── boresch/
    │   │       │   │   ├── __init__.py
    │   │       │   │   ├── geometry.py
    │   │       │   │   ├── guest.py
    │   │       │   │   └── host.py
    │   │       │   ├── flatbottom.py
    │   │       │   ├── harmonic.py
    │   │       │   └── utils.py
    │   │       ├── openmm/
    │   │       │   ├── __init__.py
    │   │       │   ├── omm_forces.py
    │   │       │   └── omm_restraints.py
    │   │       └── settings.py
    │   ├── setup/
    │   │   ├── __init__.py
    │   │   ├── alchemical_network_planner/
    │   │   │   ├── __init__.py
    │   │   │   ├── abstract_alchemical_network_planner.py
    │   │   │   └── relative_alchemical_network_planner.py
    │   │   ├── atom_mapping/
    │   │   │   ├── __init__.py
    │   │   │   ├── ligandatommapper.py
    │   │   │   ├── lomap_mapper.py
    │   │   │   ├── lomap_scorers.py
    │   │   │   ├── perses_mapper.py
    │   │   │   └── perses_scorers.py
    │   │   ├── chemicalsystem_generator/
    │   │   │   ├── __init__.py
    │   │   │   ├── abstract_chemicalsystem_generator.py
    │   │   │   └── easy_chemicalsystem_generator.py
    │   │   └── ligand_network_planning.py
    │   ├── storage/
    │   │   ├── __init__.py
    │   │   ├── metadatastore.py
    │   │   ├── resultclient.py
    │   │   └── resultserver.py
    │   ├── tests/
    │   │   ├── __init__.py
    │   │   ├── analysis/
    │   │   │   ├── __init__.py
    │   │   │   └── test_plotting.py
    │   │   ├── conftest.py
    │   │   ├── data/
    │   │   │   ├── 181l_only.pdb
    │   │   │   ├── CN.sdf
    │   │   │   ├── __init__.py
    │   │   │   ├── a2a/
    │   │   │   │   └── __init__.py
    │   │   │   ├── benzene_modifications.sdf
    │   │   │   ├── cdk8/
    │   │   │   │   ├── __init__.py
    │   │   │   │   ├── cdk8_ligands.sdf
    │   │   │   │   └── cdk8_protein.pdb
    │   │   │   ├── eg5/
    │   │   │   │   ├── __init__.py
    │   │   │   │   ├── eg5_cofactor.sdf
    │   │   │   │   ├── eg5_ligands.sdf
    │   │   │   │   └── eg5_protein.pdb
    │   │   │   ├── external_formats/
    │   │   │   │   ├── __init__.py
    │   │   │   │   └── somebenzenes_edges.edge
    │   │   │   ├── htf/
    │   │   │   │   ├── __init__.py
    │   │   │   │   ├── chloroethane.sdf
    │   │   │   │   ├── ethane.sdf
    │   │   │   │   ├── fluoroethane.sdf
    │   │   │   │   └── t4_lysozyme_data/
    │   │   │   │       ├── benzene.sdf
    │   │   │   │       ├── chlorobenzene.sdf
    │   │   │   │       └── fluorobenzene.sdf
    │   │   │   ├── lomap_basic/
    │   │   │   │   ├── 1,3,7-trimethylnaphthalene.mol2
    │   │   │   │   ├── 1-butyl-4-methylbenzene.mol2
    │   │   │   │   ├── 2,6-dimethylnaphthalene.mol2
    │   │   │   │   ├── 2-methyl-6-propylnaphthalene.mol2
    │   │   │   │   ├── 2-methylnaphthalene.mol2
    │   │   │   │   ├── 2-naftanol.mol2
    │   │   │   │   ├── README.md
    │   │   │   │   ├── __init__.py
    │   │   │   │   ├── methylcyclohexane.mol2
    │   │   │   │   └── toluene.mol2
    │   │   │   ├── multi_molecule.sdf
    │   │   │   ├── openmm_afe/
    │   │   │   │   ├── T4_abfe_system.xml.bz2
    │   │   │   │   └── __init__.py
    │   │   │   ├── openmm_md/
    │   │   │   │   └── __init__.py
    │   │   │   ├── openmm_rfe/
    │   │   │   │   ├── __init__.py
    │   │   │   │   ├── benzene_toluene_hybrid_top/
    │   │   │   │   │   ├── hybrid_topology_atoms.csv
    │   │   │   │   │   └── hybrid_topology_bonds.txt
    │   │   │   │   ├── charged_benzenes.sdf
    │   │   │   │   ├── dummy_charge_ligand_23.sdf
    │   │   │   │   ├── dummy_charge_ligand_55.sdf
    │   │   │   │   ├── ligand_23.sdf
    │   │   │   │   ├── ligand_55.sdf
    │   │   │   │   ├── malt1_shapefit_1832577-09-9.sdf
    │   │   │   │   ├── malt1_shapefit_Pfizer-01-01.sdf
    │   │   │   │   ├── reference.xml
    │   │   │   │   ├── vacuum_nocoord.nc
    │   │   │   │   └── vacuum_nocoord_checkpoint.nc
    │   │   │   ├── openmm_septop/
    │   │   │   │   ├── __init__.py
    │   │   │   │   └── system.xml.bz2
    │   │   │   └── serialization/
    │   │   │       ├── __init__.py
    │   │   │       ├── ethane_template.sdf
    │   │   │       └── network_template.graphml
    │   │   ├── dev/
    │   │   │   ├── __init__.py
    │   │   │   └── serialization_test_templates.py
    │   │   ├── protocols/
    │   │   │   ├── __init__.py
    │   │   │   ├── conftest.py
    │   │   │   ├── openmm_abfe/
    │   │   │   │   ├── __init__.py
    │   │   │   │   ├── conftest.py
    │   │   │   │   ├── test_abfe_energies.py
    │   │   │   │   ├── test_abfe_protocol.py
    │   │   │   │   ├── test_abfe_protocol_results.py
    │   │   │   │   ├── test_abfe_settings.py
    │   │   │   │   ├── test_abfe_slow.py
    │   │   │   │   ├── test_abfe_tokenization.py
    │   │   │   │   ├── test_abfe_validation.py
    │   │   │   │   └── utils.py
    │   │   │   ├── openmm_ahfe/
    │   │   │   │   ├── __init__.py
    │   │   │   │   ├── test_ahfe_protocol.py
    │   │   │   │   ├── test_ahfe_protocol_results.py
    │   │   │   │   ├── test_ahfe_resume.py
    │   │   │   │   ├── test_ahfe_settings.py
    │   │   │   │   ├── test_ahfe_slow.py
    │   │   │   │   ├── test_ahfe_tokenization.py
    │   │   │   │   ├── test_ahfe_validation.py
    │   │   │   │   └── utils.py
    │   │   │   ├── openmm_md/
    │   │   │   │   ├── __init__.py
    │   │   │   │   ├── test_plain_md_protocol.py
    │   │   │   │   ├── test_plain_md_resume.py
    │   │   │   │   ├── test_plain_md_slow.py
    │   │   │   │   └── test_plain_md_tokenization.py
    │   │   │   ├── openmm_rfe/
    │   │   │   │   ├── __init__.py
    │   │   │   │   ├── helpers.py
    │   │   │   │   ├── test_hybrid_factory.py
    │   │   │   │   ├── test_hybrid_top_protocol.py
    │   │   │   │   ├── test_hybrid_top_resume.py
    │   │   │   │   ├── test_hybrid_top_slow.py
    │   │   │   │   ├── test_hybrid_top_tokenization.py
    │   │   │   │   └── test_hybrid_top_validation.py
    │   │   │   ├── openmm_septop/
    │   │   │   │   ├── __init__.py
    │   │   │   │   ├── conftest.py
    │   │   │   │   ├── test_septop_protocol.py
    │   │   │   │   ├── test_septop_protocol_results.py
    │   │   │   │   ├── test_septop_resume.py
    │   │   │   │   ├── test_septop_settings.py
    │   │   │   │   ├── test_septop_slow.py
    │   │   │   │   ├── test_septop_tokenization.py
    │   │   │   │   ├── test_septop_validation.py
    │   │   │   │   └── utils.py
    │   │   │   ├── restraints/
    │   │   │   │   ├── __init__.py
    │   │   │   │   ├── test_geometry_base.py
    │   │   │   │   ├── test_geometry_boresch.py
    │   │   │   │   ├── test_geometry_boresch_guest.py
    │   │   │   │   ├── test_geometry_boresch_host.py
    │   │   │   │   ├── test_geometry_flatbottom.py
    │   │   │   │   ├── test_geometry_harmonic.py
    │   │   │   │   ├── test_geometry_utils.py
    │   │   │   │   ├── test_omm_restraints.py
    │   │   │   │   ├── test_openmm_forces.py
    │   │   │   │   └── test_settings.py
    │   │   │   ├── test_openmm_settings.py
    │   │   │   ├── test_openmmutils.py
    │   │   │   └── test_openmmutils_serialization.py
    │   │   ├── setup/
    │   │   │   ├── __init__.py
    │   │   │   ├── alchemical_network_planner/
    │   │   │   │   ├── __init__.py
    │   │   │   │   ├── edge_types.py
    │   │   │   │   └── test_relative_alchemical_network_planner.py
    │   │   │   ├── atom_mapping/
    │   │   │   │   ├── __init__.py
    │   │   │   │   ├── conftest.py
    │   │   │   │   ├── test_atommapper.py
    │   │   │   │   ├── test_lomap_atommapper.py
    │   │   │   │   ├── test_lomap_scorers.py
    │   │   │   │   ├── test_perses_atommapper.py
    │   │   │   │   └── test_perses_scorers.py
    │   │   │   ├── chemicalsystem_generator/
    │   │   │   │   ├── __init__.py
    │   │   │   │   ├── component_checks.py
    │   │   │   │   └── test_easy_chemicalsystem_generator.py
    │   │   │   └── test_network_planning.py
    │   │   ├── storage/
    │   │   │   ├── __init__.py
    │   │   │   ├── conftest.py
    │   │   │   ├── test_metadatastore.py
    │   │   │   ├── test_resultclient.py
    │   │   │   └── test_resultserver.py
    │   │   └── utils/
    │   │       ├── __init__.py
    │   │       ├── conftest.py
    │   │       ├── test_atommapping_network_plotting.py
    │   │       ├── test_duecredit.py
    │   │       ├── test_log_control.py
    │   │       ├── test_network_plotting.py
    │   │       ├── test_optional_imports.py
    │   │       ├── test_remove_oechem.py
    │   │       ├── test_system_probe.py
    │   │       └── test_visualization_3D.py
    │   └── utils/
    │       ├── __init__.py
    │       ├── atommapping_network_plotting.py
    │       ├── custom_typing.py
    │       ├── ligand_utils.py
    │       ├── logging_control.py
    │       ├── network_plotting.py
    │       ├── optional_imports.py
    │       ├── remove_oechem.py
    │       ├── silence_root_logging.py
    │       ├── system_probe.py
    │       └── visualization_3D.py
    └── openfecli/
        ├── README.md
        ├── __init__.py
        ├── cli.py
        ├── clicktypes/
        │   ├── __init__.py
        │   └── hyphenchoice.py
        ├── commands/
        │   ├── __init__.py
        │   ├── atommapping.py
        │   ├── fetch.py
        │   ├── gather.py
        │   ├── gather_abfe.py
        │   ├── gather_septop.py
        │   ├── generate_partial_charges.py
        │   ├── plan_rbfe_network.py
        │   ├── plan_rhfe_network.py
        │   ├── quickrun.py
        │   ├── test.py
        │   └── view_ligand_network.py
        ├── data/
        │   ├── __init__.py
        │   └── _registry.py
        ├── fetchables.py
        ├── fetching.py
        ├── parameters/
        │   ├── __init__.py
        │   ├── mapper.py
        │   ├── misc.py
        │   ├── mol.py
        │   ├── molecules.py
        │   ├── output.py
        │   ├── output_dir.py
        │   ├── plan_network_options.py
        │   ├── protein.py
        │   └── utils.py
        ├── plan_alchemical_networks_utils.py
        ├── plugins.py
        ├── tests/
        │   ├── __init__.py
        │   ├── clicktypes/
        │   │   └── test_hyphenchoice.py
        │   ├── commands/
        │   │   ├── __init__.py
        │   │   ├── conftest.py
        │   │   ├── test_atommapping.py
        │   │   ├── test_charge_generation.py
        │   │   ├── test_gather/
        │   │   │   ├── test_abfe_full_results_multiple_units_dg_.tsv
        │   │   │   ├── test_abfe_full_results_multiple_units_raw_.tsv
        │   │   │   ├── test_abfe_full_results_single_unit_dg_.tsv
        │   │   │   ├── test_abfe_full_results_single_unit_raw_.tsv
        │   │   │   ├── test_abfe_single_repeat_multiple_units_dg_.tsv
        │   │   │   ├── test_abfe_single_repeat_multiple_units_raw_.tsv
        │   │   │   ├── test_abfe_single_repeat_single_unit_dg_.tsv
        │   │   │   ├── test_abfe_single_repeat_single_unit_raw_.tsv
        │   │   │   ├── test_cmet_failed_edge_ddg_.tsv
        │   │   │   ├── test_cmet_failed_edge_raw_.tsv
        │   │   │   ├── test_cmet_full_results_ddg_.tsv
        │   │   │   ├── test_cmet_full_results_dg_.tsv
        │   │   │   ├── test_cmet_full_results_raw_.tsv
        │   │   │   ├── test_cmet_missing_all_complex_legs_allow_partial_ddg_.tsv
        │   │   │   ├── test_cmet_missing_all_complex_legs_fail_ddg_.tsv
        │   │   │   ├── test_cmet_missing_all_complex_legs_fail_dg_.tsv
        │   │   │   ├── test_cmet_missing_complex_leg_ddg_.tsv
        │   │   │   ├── test_cmet_missing_complex_leg_dg_.tsv
        │   │   │   ├── test_cmet_missing_complex_leg_raw_.tsv
        │   │   │   ├── test_cmet_missing_edge_ddg_.tsv
        │   │   │   ├── test_cmet_missing_edge_dg_.tsv
        │   │   │   ├── test_cmet_missing_edge_raw_.tsv
        │   │   │   ├── test_septop_full_results_ddg_current_.tsv
        │   │   │   ├── test_septop_full_results_ddg_pre_openfe_v1_11_.tsv
        │   │   │   ├── test_septop_full_results_dg_current_.tsv
        │   │   │   ├── test_septop_full_results_dg_pre_openfe_v1_11_.tsv
        │   │   │   ├── test_septop_full_results_raw_current_.tsv
        │   │   │   ├── test_septop_full_results_raw_pre_openfe_v1_11_.tsv
        │   │   │   ├── test_septop_single_repeat_ddg_current_.tsv
        │   │   │   ├── test_septop_single_repeat_ddg_pre_openfe_v1_11_.tsv
        │   │   │   ├── test_septop_single_repeat_dg_current_.tsv
        │   │   │   ├── test_septop_single_repeat_dg_pre_openfe_v1_11_.tsv
        │   │   │   ├── test_septop_single_repeat_raw_current_.tsv
        │   │   │   └── test_septop_single_repeat_raw_pre_openfe_v1_11_.tsv
        │   │   ├── test_gather.py
        │   │   ├── test_ligand_network_viewer.py
        │   │   ├── test_plan_rbfe_network.py
        │   │   ├── test_plan_rhfe_network.py
        │   │   ├── test_quickrun.py
        │   │   └── test_test.py
        │   ├── conftest.py
        │   ├── data/
        │   │   ├── __init__.py
        │   │   ├── bad_transformation.json
        │   │   ├── rbfe_tutorial/
        │   │   │   ├── __init__.py
        │   │   │   ├── tyk2_ligands.sdf
        │   │   │   └── tyk2_protein.pdb
        │   │   └── transformation.json
        │   ├── dev/
        │   │   ├── __init__.py
        │   │   └── write_transformation_json.py
        │   ├── parameters/
        │   │   ├── __init__.py
        │   │   ├── test_mapper.py
        │   │   ├── test_mol.py
        │   │   ├── test_molecules.py
        │   │   ├── test_output.py
        │   │   ├── test_output_dir.py
        │   │   ├── test_plan_network_options.py
        │   │   ├── test_protein.py
        │   │   └── test_utils.py
        │   ├── test_cli.py
        │   ├── test_fetchables.py
        │   ├── test_fetching.py
        │   ├── test_plugins.py
        │   ├── test_rbfe_tutorial.py
        │   ├── test_utils.py
        │   └── utils.py
        └── utils.py

================================================
FILE CONTENTS
================================================

================================================
FILE: .dockerignore
================================================
# Ignore everything
*

# Only allow
!production/environment.yml


================================================
FILE: .git-blame-ignore-revs
================================================
# https://github.com/OpenFreeEnergy/openfe/pull/1604 - ruff formatting part 1
3a08b6809fc57662e4146db3c7ccedfbc7c7c8df  

# https://github.com/OpenFreeEnergy/openfe/pull/1610 - ruff formatting part 2
2311a2f2d956dd30e95c180841ce19b921d89e1f

# https://github.com/OpenFreeEnergy/openfe/pull/1622 - ruff formatting part 3
d7196d119e2f65d88e488afc665f2521e4f68042

# https://github.com/OpenFreeEnergy/openfe/pull/1623 - ruff formatting part 4
036869ae81670c6dcfa2532f125ee88c3f35936c

# https://github.com/OpenFreeEnergy/openfe/pull/1665 - ruff isort
588f552ca9200a99fd77aed993ea3766b154ee53

# https://github.com/OpenFreeEnergy/openfe/pull/1667 - ruff f-strings and whitespace
18f211db974cdde38a5d88d6e74aaaf78fda8897

# https://github.com/OpenFreeEnergy/openfe/pull/1668 - ruff pycodestyle changes 
b693d37c8ac0e30283bd8b5f13386fdc98901cf8


================================================
FILE: .gitattributes
================================================


================================================
FILE: .github/CONTRIBUTING.md
================================================
# Contributing to OpenFE

Thanks for contributing to the OpenFE software project!
Read our [code of conduct](../Code_of_Conduct.md) to understand the standards you must adhere to.

## Questions

If you have any questions on using the OpenFE package, reach out on the "Discussions" tab above to start a conversation!
We are happy to get you started in using our software.

## Issues

If you think you have encountered a software issue, please raise this on the "Issues" tab in Github.
In general the more details you can provide the better, 
we recommend reading section 3.3 of [this article](https://livecomsjournal.org/index.php/livecoms/article/view/v3i1e1473)
to understand the problem solving process.

## Contributing

We welcome any fixes or code contributions.
Note that any contributions made must be made under a MIT license.
Feel free to reach out to the developer team who can assist you in this process.


================================================
FILE: .github/PULL_REQUEST_TEMPLATE/release_template.md
================================================
<!--
Checklist for releasing a new version of openfe. 
-->

Make the PR:
* [ ] Create a new release prep branch corresponding to the version name, e.g. `release/v1.2.0`.  Note: please follow [semantic versioning](https://semver.org/).
* [ ] Check that all user-relevant updates are included in the `news/` rever `.rst` files. You can backfill any additional items by making a new .rst, e.g. `backfill.rst`
* [ ] Run [rever](https://regro.github.io/rever-docs/index.html#), e.g. `rever 1.2.0`. This will auto-commit `docs/CHANGELOG.md` and remove the `.rst` files from `news/`. 
* [ ] Verify that`docs/CHANGELOG.rst` looks correct and that it renders as expected in the docs preview.
* [ ] If needed, create a release of the [example notebooks repository](https://github.com/OpenFreeEnergy/ExampleNotebooks) and update the pinned release version in the `openfe/docs/conf.py`.
* [ ] Make the PR and verify that CI/CD passes. 
  * [ ] [feedstock packaging tests](https://github.com/OpenFreeEnergy/openfe/actions/workflows/release-prep-feedstock.yaml)
  * [ ] [example notebooks](https://github.com/OpenFreeEnergy/openfe/actions/workflows/release-prep-examplenotebooks.yaml)
  * [ ] [GPU tests](https://github.com/OpenFreeEnergy/openfe/actions/workflows/aws-gpu-integration-tests.yaml)
* [ ] Merge the PR into `main`.

After Merging the PR [follow this guide](https://github.com/OpenFreeEnergy/openfe/wiki/How-to-create-a-new-release)



================================================
FILE: .github/pull_request_template.md
================================================
<!--
Thank you for pull request.
Below are a few things we ask you kindly to self-check before getting a review. Remove checks that are not relevant.
-->

<!--
Please note any issues this fixes using [closing keywords]( https://help.github.com/articles/closing-issues-using-keywords/ ):
-->

<!--
see https://regro.github.io/rever-docs/news.html for details on how to add news entry (you do not need to run the rever command)
-->

Checklist
* [ ] All new code is appropriately documented (user-facing code _must_ have complete docstrings).
* [ ] Added a ``news`` entry, or the changes are not user-facing.
* [ ] Ran pre-commit: you can run [pre-commit](https://pre-commit.com) locally or comment on this PR with `pre-commit.ci autofix`.

Manual Tests: these are slow so don't need to be run every commit, only before merging and when relevant changes are made (generally at reviewer-discretion). 
* [ ] [GPU integration tests](https://github.com/OpenFreeEnergy/openfe/actions/workflows/aws-gpu-integration-tests.yaml)
* [ ] [example notebook testing](https://github.com/OpenFreeEnergy/openfe/actions/workflows/release-prep-examplenotebooks.yaml)
* [ ] [packaging tests](https://github.com/OpenFreeEnergy/openfe/actions/workflows/cron-package-test.yaml): run this for any large feature PRs or PRs that add test data.


## Developers certificate of origin
- [ ] I certify that this contribution is covered by the MIT License [here](https://github.com/OpenFreeEnergy/openfe/blob/main/LICENSE) and the **Developer Certificate of Origin** at <https://developercertificate.org/>.


================================================
FILE: .github/workflows/aws-cpu-long-tests.yaml
================================================
name: "manual AWS: CPU long tests"
on:
  workflow_dispatch:

jobs:
  start-aws-runner:
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read
    outputs:
      mapping: ${{ steps.aws-start.outputs.mapping }}
    steps:
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::010438489691:role/GHARunnerAWS
          aws-region: us-east-2
      - name: Create cloud runner
        id: aws-start
        uses: omsf/start-aws-gha-runner@v1.0.0
        with:
          aws_image_id: ami-0b7f661c228e6a4bb
          aws_instance_type: c7i.xlarge
          aws_home_dir: /home/ubuntu
          aws_root_device_size: 125 
        env:
          GH_PAT: ${{ secrets.GH_PAT }}

  self-hosted-test:
    runs-on: self-hosted
    timeout-minutes: 720  # 12 hours  
    defaults:
      run:
        shell: bash -leo pipefail {0}
    env:
      OE_LICENSE: ${{ github.workspace }}/oe_license.txt

    needs:
      - start-aws-runner
    steps:
      - uses: actions/checkout@v4

      - name: Print disk usage
        run: "df -h"

      - name: Print Docker details
        run: "docker version || true"

      - name: "Setup Micromamba"
        uses: mamba-org/setup-micromamba@v2
        with:
          environment-file: environment.yml
          environment-name: openfe_env
          condarc: |
            channels:
              - conda-forge
              - openeye
          create-args: >-
            espaloma_charge==0.0.8
            espaloma==0.4.0
            openeye-toolkits
            python=3.12

      - name: "Check if OpenMM can get a GPU"
        run: python -m openmm.testInstallation

      - name: "Install"
        run: python -m pip install --no-deps -e .

      - name: "Test imports"
        run: |
          # if we add more to this, consider changing to for + env vars
          python -Ic "import openfe; print(openfe.__version__)"          

      - name: "Environment Information"
        run: |
          micromamba info
          micromamba list
          pip list

      - name: Test OE License & Write License to File
        env:
          OE_LICENSE_TEXT: ${{ secrets.OE_LICENSE }}
        run: |
          echo "${OE_LICENSE_TEXT}" > ${OE_LICENSE}
          python -c "import openeye; assert openeye.oechem.OEChemIsLicensed(), 'OpenEye license checks failed!'"

      - name: "Run tests"
        env:
          OFE_SLOW_TESTS: "true"
          DUECREDIT_ENABLE: 'yes'
          OFE_INTEGRATION_TESTS: FALSE
        run: |
          pytest -n logical -vv --durations=10 --runslow src/openfecli/tests/ src/openfe/tests/

  stop-aws-runner:
    runs-on: ubuntu-latest
    permissions:
        id-token: write
        contents: read
    needs:
      - start-aws-runner
      - self-hosted-test
    if: ${{ always() }}
    steps:
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::010438489691:role/GHARunnerAWS
          aws-region: us-east-2
      - name: Stop instances
        uses: omsf/stop-aws-gha-runner@v1.0.0
        with:
          instance_mapping: ${{ needs.start-aws-runner.outputs.mapping }}
        env:
          GH_PAT: ${{ secrets.GH_PAT }}


================================================
FILE: .github/workflows/aws-gpu-integration-tests.yaml
================================================
name: "manual AWS: GPU integration tests"
on:
  workflow_dispatch:

jobs:
  start-aws-runner:
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read
    outputs:
      mapping: ${{ steps.aws-start.outputs.mapping }}
    steps:
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::010438489691:role/GHARunnerAWS
          aws-region: us-east-2
      - name: Create cloud runner
        id: aws-start
        uses: omsf/start-aws-gha-runner@v1.0.0
        with:
          aws_image_id: ami-076a54ed41e67782d
          aws_instance_type: g4dn.xlarge
          aws_home_dir: /home/ubuntu
          aws_root_device_size: 125
        env:
          GH_PAT: ${{ secrets.GH_PAT }}

  self-hosted-test:
    runs-on: self-hosted
    timeout-minutes: 720  # 12 hours  
    defaults:
      run:
        shell: bash -leo pipefail {0}
    env:
      OE_LICENSE: ${{ github.workspace }}/oe_license.txt

    needs:
      - start-aws-runner
    steps:
      - uses: actions/checkout@v4

      - name: Print disk usage
        run: "df -h"

      - name: Print Docker details
        run: "docker version || true"

      - name: Check for nvidia-smi
        run: "nvidia-smi"

      - name: "Setup Micromamba"
        uses: mamba-org/setup-micromamba@v2
        with:
          environment-file: environment.yml
          environment-name: openfe_env
          condarc: |
            channels:
              - conda-forge
              - openeye
          create-args: >-
            espaloma_charge==0.0.8
            espaloma==0.4.0
            openeye-toolkits
            python=3.12
            cuda-version=12.8

      - name: "Check if OpenMM can get a GPU"
        run: python -m openmm.testInstallation

      - name: "Install"
        run: python -m pip install --no-deps -e .

      - name: "Test imports"
        run: |
          # if we add more to this, consider changing to for + env vars
          python -Ic "import openfe; print(openfe.__version__)"          

      - name: "Environment Information"
        run: |
          micromamba info
          micromamba list
          pip list

      - name: Test OE License & Write License to File
        env:
          OE_LICENSE_TEXT: ${{ secrets.OE_LICENSE }}
        run: |
          echo "${OE_LICENSE_TEXT}" > ${OE_LICENSE}
          python -c "import openeye; assert openeye.oechem.OEChemIsLicensed(), 'OpenEye license checks failed!'"

      - name: "Run tests"
        env:
          DUECREDIT_ENABLE: 'yes'
          OFE_INTEGRATION_TESTS: TRUE
        run: |
          # The -m flag will only run tests with @pytest.mark.integration
          pytest -n logical -vv --durations=10 -m integration src/openfecli/tests/ src/openfe/tests/

  stop-aws-runner:
    runs-on: ubuntu-latest
    permissions:
        id-token: write
        contents: read
    needs:
      - start-aws-runner
      - self-hosted-test
    if: ${{ always() }}
    steps:
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::010438489691:role/GHARunnerAWS
          aws-region: us-east-2
      - name: Stop instances
        uses: omsf/stop-aws-gha-runner@v1.0.0
        with:
          instance_mapping: ${{ needs.start-aws-runner.outputs.mapping }}
        env:
          GH_PAT: ${{ secrets.GH_PAT }}


================================================
FILE: .github/workflows/ci.yaml
================================================
name: "CI"
on:
  pull_request:
   # Skip CI if changed files only affect the following folders
   # - docs: documentation changes don't need code validation
   # See here for more details: https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#example-excluding-paths
    paths-ignore:
      - "docs/*"
      - "news/*"
      - ".readthedocs.yaml"
      - ".github/workflows/cpu-long-tests.yaml"
      - ".github/workflows/gpu-integration-tests.yaml"
  push:
    branches:
      - main
  schedule:
    - cron: "0 4 * * *"
  workflow_dispatch:
  release:
    types:
      - published

concurrency:
  group: "${{ github.workflow }}-${{ github.ref }}"
  cancel-in-progress: true

defaults:
  run:
    shell: bash -leo pipefail {0}

jobs:
  tests:
    runs-on: ${{ matrix.os }}
    name: "💻-${{matrix.os }} 🐍-${{ matrix.python-version }} oechem: ${{ matrix.openeye }}"
    strategy:
      fail-fast: false
      matrix:
        os: ["ubuntu-latest"]
        openeye: ["no"]
        python-version:
          - "3.11"
          - "3.12"
          - "3.13"
        include:
          - os: "ubuntu-latest"
            python-version: "3.13"
            openeye: "yes"
          - os: "macos-latest"
            python-version: "3.12"
            openeye: "no"

    env:
      OE_LICENSE: ${{ github.workspace }}/oe_license.txt

    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Get current date
        id: date
        run: echo "date=$(date +%Y-%m-%d)" >> "${GITHUB_OUTPUT}"

      - name: "Setup Micromamba"
        if: ${{ matrix.python-version != '3.13' }}
        uses: mamba-org/setup-micromamba@v2
        with:
          environment-file: environment.yml
          environment-name: openfe_env
          cache-environment: true
          cache-downloads: true
          cache-environment-key: environment-${{ steps.date.outputs.date }}
          cache-downloads-key: downloads-${{ steps.date.outputs.date }}
          create-args: >-
            espaloma=0.4.0
            python=${{ matrix.python-version }}
            pydantic=${{ matrix.pydantic-version }}
          init-shell: bash

      - name: "Setup Micromamba"
        # Can't install espaloma with python 3.13
        if: ${{ matrix.python-version == '3.13' }}
        uses: mamba-org/setup-micromamba@v2
        with:
          environment-file: environment.yml
          environment-name: openfe_env
          cache-environment: true
          cache-downloads: true
          cache-environment-key: environment-${{ steps.date.outputs.date }}
          cache-downloads-key: downloads-${{ steps.date.outputs.date }}
          create-args: >-
            python=${{ matrix.python-version }}
            pydantic=${{ matrix.pydantic-version }}
          init-shell: bash

      - name: "Install OpenEye"
        if: ${{ !github.event.pull_request.head.repo.fork
                && matrix.openeye == 'yes' }}
        env:
          OE_LICENSE_TEXT: ${{ secrets.OE_LICENSE }}
        run: |
          echo "${OE_LICENSE_TEXT}" > ${OE_LICENSE}
          micromamba install -c openeye openeye-toolkits
          python -c "import openeye; assert openeye.oechem.OEChemIsLicensed(), 'oechem license check failed!'"

      - name: "Install"
        run: python -m pip install --no-deps -e .

      - name: "Environment Information"
        run: |
          micromamba info
          micromamba list
          pip list

      - name: "Test imports"
        run: |
          # if we add more to this, consider changing to for + env vars
          python -Ic "import openfe; print(openfe.__version__)"

      - name: Cache Pooch data
        uses: actions/cache@v4
        with:
          path: |
            # linux cache location
            ~/.cache/openfe
            # osx cache location
            ~/Library/Caches/openfe
          # When files are added or changed in a pooch registry
          # bump this key to create a new cache, for example if 
          # the key is pooch-${{ matrix.os }}-1 change it to pooch-${{ matrix.os }}-2
          key: pooch-${{ matrix.os }}-1

      - name: "Run tests"
        env:
          # Set the OFE_SLOW_TESTS to True if running a Cron job
          OFE_SLOW_TESTS: ${{ fromJSON('{"false":"false","true":"true"}')[github.event_name != 'pull_request'] }}
          DUECREDIT_ENABLE: 'yes'
        run: |
          pytest -n auto -v --cov=openfe --cov=openfecli --cov-report=xml --durations=10

      - name: codecov-pr
        if: ${{ github.repository == 'OpenFreeEnergy/openfe'
                && github.event_name == 'pull_request' }}
        uses: codecov/codecov-action@v4
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          file: coverage.xml
          fail_ci_if_error: False
          verbose: True
          flags: fast-tests

      - name: codecov-merge
        # we only want to upload a slow report if
        # 1) it isn't a schedule run
        # 2) it wasn't from a PR (we don't run slow tests on PRs)
        if: ${{ github.repository == 'OpenFreeEnergy/openfe'  
                && github.event_name != 'schedule'            
                && github.event_name != 'pull_request' }}     
        uses: codecov/codecov-action@v4
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          file: coverage.xml
          fail_ci_if_error: False
          verbose: True
          flags: slow-tests


================================================
FILE: .github/workflows/clean-pr-caches.yaml
================================================
# from https://docs.github.com/en/actions/how-tos/manage-workflow-runs/manage-caches#force-deleting-cache-entries
name: "clean up github runner caches on closed pull requests"
on:
  workflow_dispatch:
  pull_request:
    types:
      - closed

jobs:
  cleanup:
    runs-on: ubuntu-latest
    permissions:
      actions: write
    steps:
      - name: Cleanup
        run: |
          echo "Fetching list of cache keys"
          cacheKeysForPR=$(gh cache list --ref $BRANCH --limit 100 --json id --jq '.[].id')

          ## Setting this to not fail the workflow while deleting cache keys.
          set +e
          echo "Deleting caches..."
          for cacheKey in $cacheKeysForPR
          do
              gh cache delete $cacheKey
          done
          echo "Done"
        env:
          GH_TOKEN: ${{ github.token }}
          GH_REPO: ${{ github.repository }}
          BRANCH: refs/pull/${{ github.event.pull_request.number }}/merge


================================================
FILE: .github/workflows/cron-conda.yaml
================================================
name: "cron: conda builds daily tests"
on:
  workflow_dispatch:
  schedule:
    # At 05:00 UTC every day
    - cron: "0 5 * * *"

concurrency:
  group: "${{ github.workflow }}-${{ github.ref }}"
  cancel-in-progress: true

defaults:
  run:
    shell: bash -leo pipefail {0}

jobs:
  condacheck:
    runs-on: ${{ matrix.OS }}
    name: "daily conda check"
    strategy:
      fail-fast: false
      matrix:
        os: ['ubuntu-latest']
        python-version:
          - "3.11"
          - "3.12"
          - "3.13"
        include:
        - os: "macos-latest"
          python-version: "3.12"
          openeye: "no"
    steps:
      - name: Checkout Code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
          path: openfe_repo

      - name: Get Latest Version
        id: latest-version
        working-directory: openfe_repo
        run: |
          REPO="${{ github.repository }}"
          VERSION=$(curl -s -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
            "https://api.github.com/repos/$REPO/releases/latest" | jq -r '.tag_name | ltrimstr("v")')
          echo $VERSION
          echo "VERSION=$VERSION" >> $GITHUB_OUTPUT

      - name: Setup Micromamba and Install openfe
        uses: mamba-org/setup-micromamba@v2
        with:
          environment-name: openfe
          create-args: >-
            python=${{ matrix.python-version }}
            openfe=${{ steps.latest-version.outputs.VERSION }}
            pytest
            pytest-xdist
          condarc: |
            channels:
              - conda-forge
          init-shell: bash


      - name: "env info"
        run: |
          micromamba info
          micromamba list

      - id: run-tests
        name: "Run tests"
        run: |
          # note: this only runs the fast tests
          pytest -n auto --pyargs openfe openfecli


================================================
FILE: .github/workflows/cron-docker.yaml
================================================
name: "cron: docker image daily tests"

on:
  push:
    branches:
      - main
  schedule:
    # nightly tests
    - cron: "0 14 * * 0"

  workflow_dispatch:

defaults:
  run:
    shell: bash -leo pipefail {0}

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: openfreeenergy/openfe

jobs:
  build-and-push-image:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Use dev tag for nightly builds
        id: latest-version
        run: |
          VERSION=dev
          echo $VERSION
          echo "VERSION=$VERSION" >> $GITHUB_OUTPUT

      - name: Print Latest Version
        run: echo ${{ steps.latest-version.outputs.VERSION }}

      - name: Create fully qualified image registry path
        id: fqirp
        run: |
          FQIRP=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.latest-version.outputs.VERSION }}
          echo "FQIRP=$FQIRP" >> $GITHUB_OUTPUT

      - name: Print FQIRP
        run: echo ${{ steps.fqirp.outputs.FQIRP  }}

      - name: Log in to the Container registry
        uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Extract metadata (tags, labels) for Docker
        id: meta
        uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          tags: |
            type=schedule,pattern=nightly,enable=true,priority=1000
            type=ref,event=branch,enable=true,priority=600
            type=ref,event=tag,enable=true,priority=600
            type=ref,event=pr,prefix=pr-,enable=true,priority=600
            type=semver,pattern={{major}}.{{minor}}
            type=semver,pattern={{version}}
            type=sha
            ${{ steps.latest-version.outputs.VERSION }}

      - name: Build and export to Docker
        uses: docker/build-push-action@v6
        with:
          context: .
          file: production/Dockerfile
          load: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}

      - name: Test image
        run: |
          docker run --rm ${{ steps.fqirp.outputs.FQIRP }} openfe --help
          docker run --rm ${{ steps.fqirp.outputs.FQIRP }} openfe --version
          docker run --rm ${{ steps.fqirp.outputs.FQIRP }} python -c "import gufe; print(f'{gufe.__version__=}')"
          docker run --rm ${{ steps.fqirp.outputs.FQIRP }} pytest --pyargs gufe -v
          docker run --rm ${{ steps.fqirp.outputs.FQIRP }} pytest --pyargs openfe -v


================================================
FILE: .github/workflows/cron-feedstock-build-tests.yaml
================================================
# tests this openfe commit and gufe main to check for 
# conda-feedstock build issues
name: "cron: weekly feedstock package build tests"

concurrency:
  group: "${{ github.workflow }}-${{ github.ref }}"
  cancel-in-progress: true

defaults:
  run:
    shell: bash -leo pipefail {0}
on:
  workflow_dispatch:
  schedule:
    # 3 am weekly on monday
    - cron: "0 3 * * MON"

jobs:
  test-conda-build:
    runs-on: ubuntu-latest
    
    steps:
      - name: Checkout openfe repository
        uses: actions/checkout@v4
        with:
          path: openfe
      
      - name: Checkout conda-forge feedstock
        uses: actions/checkout@v4
        with:
          repository: conda-forge/openfe-feedstock
          path: openfe-feedstock
      
      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.x'
      
      - name: Install conda-build dependencies
        run: |
          pip install pyyaml

      # TODO just checkout the repo where we need it?
      - name: Copy source code to recipe folder
        run: cp -r openfe openfe-feedstock/recipe/openfe_source
      
      - name: Modify feedstock to use local path
        run: |
          cd openfe-feedstock
          
          # Backup original recipe.yaml
          cp recipe/recipe.yaml recipe/recipe.yaml.bak

          # NOTE: now that we use v1 feedstock, we  can use yq to directly parse the YAML here.
          # Add path after source: and delete url line
          sed -i '/^source:/a\  path: ./openfe_source' recipe/recipe.yaml
          sed -i '/^  url:/d' recipe/recipe.yaml
          
          echo "Modified recipe.yaml:"
          cat recipe/recipe.yaml
                
      - name: Run conda-forge build test
        run: |
          cd openfe-feedstock
          python build-locally.py
        continue-on-error: true
        id: build_test

          # Uncomment if build_artifacts is needed to troubleshoot build
      
          #      - name: Upload build logs
          #        if: always()
          #        uses: actions/upload-artifact@v4
          #        with:
          #          name: conda-build-logs
          #          path: |
          #            openfe-feedstock/build_artifacts/
          #            openfe-feedstock/recipe/recipe.yaml
          #            openfe-feedstock/recipe/recipe.yaml.bak
          #          if-no-files-found: warn
      
      - name: Check build status
        if: steps.build_test.outcome == 'failure'
        run: |
          echo "❌ Conda forge build test failed. Check the uploaded logs for details."
          exit 1


================================================
FILE: .github/workflows/cron-package-test.yaml
================================================
name: "cron: package install daily tests"
on:
  workflow_dispatch:
  schedule:
    # At 03:00 UTC daily
    - cron: "0 3 * * *"

concurrency:
  group: "${{ github.workflow }}-${{ github.ref }}"
  cancel-in-progress: true

defaults:
  run:
    shell: bash -leo pipefail {0}

jobs:
  package-tests:
    runs-on: ubuntu-latest
    name: "main branch long tests"
    steps:
      - uses: actions/checkout@v4

      - name: Get current date
        id: date
        run: echo "date=$(date +%Y-%m-%d)" >> "${GITHUB_OUTPUT}"

      - name: "Setup Micromamba"
        uses: mamba-org/setup-micromamba@v2
        with:
          environment-file: environment.yml
          environment-name: openfe_env
          cache-environment: true
          cache-downloads: true
          cache-environment-key: environment-${{ steps.date.outputs.date }}
          cache-downloads-key: downloads-${{ steps.date.outputs.date }}
          create-args: >-
            python=3.12
          init-shell: bash

      - name: "install extra deps"
        run: pip install pipx wheel twine readme-renderer

      - name: "build sdist"
        run: pipx run build --sdist --outdir dist

      - name: "check package build"
        run: |
          dist=$(ls -t1 dist/openfe-*tar.gz | head -n1)
          test -n "${dist}" || { echo "no distribution found"; exit 1; }
          twine check $dist

      - name: "install from source dist"
        working-directory: ./dist
        run: python -m pip install openfe-*tar.gz

      - name: "run tests"
        working-directory: ./dist
        env:
          OFE_SLOW_TESTS: "true"
        run: |
          pytest -n auto -v --pyargs openfe.tests
          pytest -n auto -v --pyargs openfecli.tests


================================================
FILE: .github/workflows/griffe-api-break.yaml
================================================
name: "PR: griffe check for API breaks"

on:
  pull_request_target:
    branches:
      - main

jobs:
  check:
    runs-on: ubuntu-latest
    permissions:
      pull-requests: write

    steps:
    - uses: actions/checkout@v4
      with:
        fetch-depth: 0

    - run: git fetch --depth=1 --tags

    - uses: actions/setup-python@v5
      with:
        python-version: "3.13"

    - name: Check for API breaks
      continue-on-error: true
      id: check
      run: |
        pip install griffe
        griffe check "openfe" -s src --verbose -a origin/main
        griffe check "openfecli" -s src --verbose -a origin/main

    - name: Manage PR Comments
      uses: actions/github-script@v7
      with:
        script: |
          const prNumber = context.payload.pull_request.number;
          const identifier = '<!-- api-break-check -->';
          const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
          const stepUrl = `${runUrl}#step:check`;

          // Determine the outcome of the check step
          const checkStepOutcome = '${{ steps.check.outcome }}';

          // List existing comments
          const { data: comments } = await github.rest.issues.listComments({
            owner: context.repo.owner,
            repo: context.repo.repo,
            issue_number: prNumber,
          });

          // Delete previous comments from this action
          for (const comment of comments) {
            if (comment.body.includes(identifier)) {
              await github.rest.issues.deleteComment({
                owner: context.repo.owner,
                repo: context.repo.repo,
                comment_id: comment.id,
              });
            }
          }

          // Post a new comment only if the check step failed
          if (checkStepOutcome === 'failure') {
            await github.rest.issues.createComment({
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: prNumber,
              body: `${identifier}\n🚨 API breaking changes detected! 🚨\n[View logs for this step](${stepUrl})`
            });
          } else {
            await github.rest.issues.createComment({
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: prNumber,
              body: `${identifier}\nNo API break detected ✅`
            });
          }


================================================
FILE: .github/workflows/mypy.yaml
================================================
name: "PR: mypy static type checking"
on:
  pull_request:
    branches:
      - main
  push:
    branches:
      - main

concurrency:
  group: "${{ github.workflow }}-${{ github.ref }}"
  cancel-in-progress: true

defaults:
  run:
    shell: bash -leo pipefail {0}

jobs:
  mypy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Get current date
        id: date
        run: echo "date=$(date +%Y-%m-%d)" >> "${GITHUB_OUTPUT}"

      - name: "Setup Micromamba"
        uses: mamba-org/setup-micromamba@v2
        with:
          environment-file: environment.yml
          environment-name: openfe_env
          cache-environment: true
          cache-downloads: true
          cache-environment-key: environment-${{ steps.date.outputs.date }}
          cache-downloads-key: downloads-${{ steps.date.outputs.date }}
          create-args: >-
            python=3.12
            mypy>=1.17.0

          init-shell: bash

      - name: "Install steps"
        run: |
          python -m pip install --no-deps -e .

      - name: "Environment Information"
        run: |
          micromamba info
          micromamba list

      - name: "Lint with mypy"
        run: mypy 


================================================
FILE: .github/workflows/release-docker-image.yaml
================================================
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.

# GitHub recommends pinning actions to a commit SHA.
# To get a newer version, you will need to update the SHA.
# You can also reference a tag or branch, but the action may change without warning.

# Workflow to automate docker image building during the openfe release process.
name: "release: create and publish a docker image"

on:
  workflow_dispatch:

defaults:
  run:
    shell: bash -leo pipefail {0}

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: openfreeenergy/openfe

jobs:
  build-and-push-image:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write

    steps:
      # see https://dev.to/mathio/squeezing-disk-space-from-github-actions-runners-an-engineers-guide-3pjg
      - name: Aggressive cleanup
        run: |
          # remove unneeded packages to avoid running out of memory
          # Remove Java (JDKs)
          sudo rm -rf /usr/lib/jvm
      
          # Remove .NET SDKs
          sudo rm -rf /usr/share/dotnet
      
          # Remove Swift toolchain
          sudo rm -rf /usr/share/swift
      
          # Remove Haskell (GHC)
          sudo rm -rf /usr/local/.ghcup
      
          # Remove Julia
          sudo rm -rf /usr/local/julia*
      
          # Remove Android SDKs
          sudo rm -rf /usr/local/lib/android
      
          # Remove Chromium (optional if not using for browser tests)
          sudo rm -rf /usr/local/share/chromium
      
          # Remove Microsoft/Edge and Google Chrome builds
          sudo rm -rf /opt/microsoft /opt/google
      
          # Remove Azure CLI
          sudo rm -rf /opt/az
      
          # Remove PowerShell
          sudo rm -rf /usr/local/share/powershell
      
          # Remove CodeQL and other toolcaches
          sudo rm -rf /opt/hostedtoolcache
      
          docker system prune -af || true
          docker builder prune -af || true
          df -h

      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Get Latest Version
        id: latest-version
        run: |
          REPO="${{ github.repository }}"
          VERSION=$(curl -s -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
            "https://api.github.com/repos/$REPO/releases/latest" | jq -r '.tag_name | ltrimstr("v")')
          echo $VERSION
          echo "VERSION=$VERSION" >> $GITHUB_OUTPUT

      - name: Print Latest Version
        run: echo ${{ steps.latest-version.outputs.VERSION }}

      - name: Create fully qualified image registry path
        id: fqirp
        run: |
          FQIRP=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.latest-version.outputs.VERSION }}
          echo "FQIRP=$FQIRP" >> $GITHUB_OUTPUT

      - name: Print FQIRP
        run: echo ${{ steps.fqirp.outputs.FQIRP  }}

      - name: Log in to the Container registry
        uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Extract metadata (tags, labels) for Docker
        id: meta
        uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          tags: |
            type=schedule,pattern=nightly,enable=true,priority=1000
            type=ref,event=branch,enable=true,priority=600
            type=ref,event=tag,enable=true,priority=600
            type=ref,event=pr,prefix=pr-,enable=true,priority=600
            type=semver,pattern={{major}}.{{minor}}
            type=semver,pattern={{version}}
            type=sha
            ${{ steps.latest-version.outputs.VERSION }}

      - name: Build and export to Docker
        uses: docker/build-push-action@v6
        with:
          context: .
          file: production/Dockerfile
          load: true
          push: false
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          build-args: |
            VERSION=${{ steps.latest-version.outputs.VERSION }}

      - name: Test image
        run: |
          docker run --rm ${{ steps.fqirp.outputs.FQIRP }} openfe --help
          docker run --rm ${{ steps.fqirp.outputs.FQIRP }} openfe --version
          docker run --rm ${{ steps.fqirp.outputs.FQIRP }} python -c "import gufe; print(f'{gufe.__version__=}')"
          docker run --rm ${{ steps.fqirp.outputs.FQIRP }} pytest --pyargs gufe -v
          docker run --rm ${{ steps.fqirp.outputs.FQIRP }} pytest --pyargs openfe openfecli -v

      - name: Push Docker image
        uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc
        with:
          context: .
          file: production/Dockerfile
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          build-args: |
            VERSION=${{ steps.latest-version.outputs.VERSION }}

      - name: Setup Apptainer
        uses: eWaterCycle/setup-apptainer@v2
        with:
          apptainer-version: 1.3.4

      - name: Build Apptainer Image
        run: singularity build openfe_${{ steps.latest-version.outputs.VERSION }}.sif docker-daemon:${{ steps.fqirp.outputs.FQIRP }}

      - name: Test & Push Apptainer Image
        run: |
          mkdir test_apptainer
          cd test_apptainer
          singularity run ../openfe_${{ steps.latest-version.outputs.VERSION }}.sif openfe --help
          singularity run ../openfe_${{ steps.latest-version.outputs.VERSION }}.sif openfe --version
          singularity run ../openfe_${{ steps.latest-version.outputs.VERSION }}.sif pytest --pyargs openfe openfecli -v -n auto
          echo ${{ secrets.GITHUB_TOKEN }} | singularity remote login -u ${{ secrets.GHCR_USERNAME }} --password-stdin oras://ghcr.io
          singularity push ../openfe_${{ steps.latest-version.outputs.VERSION }}.sif oras://${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.latest-version.outputs.VERSION }}-apptainer
          singularity push ../openfe_${{ steps.latest-version.outputs.VERSION }}.sif oras://${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest-apptainer


================================================
FILE: .github/workflows/release-installers.yaml
================================================
name: "release: make single-file installers"

on:
  workflow_dispatch:

defaults:
  run:
    shell: bash -leo pipefail {0}

jobs:
  test:
    name: Building single file installer on ${{ matrix.os }}
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        os: [macos-latest, ubuntu-latest]

    steps:
    - name: Checkout Code
      uses: actions/checkout@v4
      with:
        fetch-depth: 0

    - name: Get Latest Version
      id: latest-version
      run: |
        REPO="${{ github.repository }}"
        VERSION=$(curl -s -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
          "https://api.github.com/repos/$REPO/releases/latest" | jq -r '.tag_name | ltrimstr("v")')
        echo $VERSION
        echo "VERSION=$VERSION" >> $GITHUB_OUTPUT

    - name: Install constructor environment with Micromamba
      uses: mamba-org/setup-micromamba@v2
      with:
        environment-name: installer
        create-args: >-
          python=3.11
          jinja2
          constructor
        init-shell: bash

    - name: Create installer
      run: VERSION=${{ steps.latest-version.outputs.VERSION }} constructor devtools/installer/

    - name: Get installer file name
      id: file-name
      run: |
        # This should work as long as we don't have any *.sh files in our root dir
        FILE_NAME=$(find * -maxdepth 0 -type f -name "*.sh")
        echo $FILE_NAME
        echo "FILE_NAME=$FILE_NAME" >> $GITHUB_OUTPUT

    - name: Test installer
      run: |
        chmod +x ${{ steps.file-name.outputs.FILE_NAME }}
        ./${{ steps.file-name.outputs.FILE_NAME }} -b
        export PATH="$HOME/openfeforge/bin:$PATH"
        OFE_SLOW_TESTS=FALSE pytest -v --pyargs openfe
        # Copy for "latest" release by removing version
        # Inspired by https://github.com/conda-forge/miniforge/blob/main/.github/workflows/ci.yml
        cp ${{ steps.file-name.outputs.FILE_NAME }} $(echo ${{ steps.file-name.outputs.FILE_NAME }} | sed -e 's/-[^-]*//')

    - uses: actions/upload-artifact@v4
      with:
        name: ${{ steps.file-name.outputs.FILE_NAME }}
        path: OpenFEforge*
        if-no-files-found: error

    - name: Upload openfe forge to release
      uses: svenstaro/upload-release-action@v2
      with:
        repo_token: ${{ secrets.GITHUB_TOKEN }}
        file: OpenFEforge*
        tag: ${{ github.ref }}
        overwrite: true
        file_glob: true
      if: startsWith(github.ref, 'refs/tags/')


================================================
FILE: .github/workflows/release-make-condalock.yaml
================================================
name: "release: create openfe conda-lock file"

on:
  workflow_dispatch:

defaults:
  run:
    shell: bash -leo pipefail {0}

jobs:
  create-conda-lock-file-and-test-linux:
    runs-on: ubuntu-latest
    
    steps:  
    - name: Install conda-lock with Micromamba
      uses: mamba-org/setup-micromamba@v2
      with:
        environment-name: conda-lock
        create-args: >-
          conda-lock

    # This saves me some time since we only need the latest tag
    - name: Get latest tag
      id: latest-version
      run: |
        REPO="${{ github.repository }}"
        VERSION=$(curl -s -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
          "https://api.github.com/repos/$REPO/releases/latest" | jq -r '.tag_name | ltrimstr("v")')
        echo $VERSION
        echo "VERSION=$VERSION" >> $GITHUB_OUTPUT

    - name: Print Latest Version
      run: echo ${{ steps.latest-version.outputs.VERSION }}
    
    - name: Create environment file to lock
      run: |
        cat > environment-to-lock.yaml << 'EOF'
        name: openfe_env
        channels:
          - conda-forge
        platforms:
          - linux-64
          - osx-arm64
        dependencies:
          - openfe==${{ steps.latest-version.outputs.VERSION }}
          - python=3.12
        EOF

    - name: Generate lock files
      run: | 
        conda lock --with-cuda 11.8 -f environment-to-lock.yaml --lockfile openfe-conda-lock.yml 
        cp openfe-conda-lock.yml openfe-${{ steps.latest-version.outputs.VERSION }}-conda-lock.yml

    - name: Test lock file (linux)
      run: |
        conda-lock install -p /home/runner/micromamba/envs/lf-test openfe-conda-lock.yml
        micromamba activate /home/runner/micromamba/envs/lf-test
        openfe test

    - name: Upload file as artifact
      uses: actions/upload-artifact@v4
      with:
        name: conda-lock-files
        path: "*conda-lock.yml"

  test-osx-lock-file:
    needs: create-conda-lock-file-and-test-linux
    runs-on: macos-latest
    steps:
    - name: Download artifact
      uses: actions/download-artifact@v4
      with:
        name: conda-lock-files

    - name: Install conda-lock with Micromamba
      uses: mamba-org/setup-micromamba@v2
      with:
        environment-name: conda-lock
        create-args: >-
          conda-lock

    - name: Test lock file (osx)
      run: |
        conda-lock install -p /Users/runner/micromamba/envs/lf-test openfe-conda-lock.yml
        micromamba activate /Users/runner/micromamba/envs/lf-test
        openfe test

    - name: Upload lock files to release
      uses: svenstaro/upload-release-action@v2
      if: startsWith(github.ref, 'refs/tags/')
      with:
        repo_token: ${{ secrets.GITHUB_TOKEN }}
        file: "*conda-lock.yml"
        tag: ${{ github.ref }}
        overwrite: true
        file_glob: true


================================================
FILE: .github/workflows/release-prep-examplenotebooks.yaml
================================================
name: "release prep: test example notebooks"

on:
  workflow_dispatch:

concurrency:
  group: "${{ github.workflow }}-${{ github.ref }}"
  cancel-in-progress: true

defaults:
  run:
    shell: bash -leo pipefail {0}

jobs:
  test-example-notebooks:
    runs-on: ubuntu-latest
    
    steps:
      - name: Checkout openfe repository
        uses: actions/checkout@v4
        with:
          path: openfe
      
      - name: Checkout example notebooks
        uses: actions/checkout@v4
        with:
          repository: openfreeenergy/ExampleNotebooks
          path: example-notebooks

      - name: Setup Micromamba
        uses: mamba-org/setup-micromamba@v2
        with:
          environment-file: openfe/environment.yml
          environment-name: openfe_env
          create-args: >-
            python=3.12
            nbval
          init-shell: bash

      - name: Install OpenFE
        run: python -m pip install --no-deps -e ./openfe

      - name: Environment Information
        run: |
          micromamba info
          micromamba list

      - name: Run example notebooks
        run: |
          cd example-notebooks
          python -m pytest -v --nbval-lax --nbval-cell-timeout=3000 -n auto --dist loadscope


================================================
FILE: .github/workflows/release-prep-feedstock.yaml
================================================
# tests this openfe commit with the latest gufe release
# meant to be used for release prep to catch feedstock issues before releasing on github
name: "release prep: test conda-forge package build"

concurrency:
  group: "${{ github.workflow }}-${{ github.ref }}"
  cancel-in-progress: true

defaults:
  run:
    shell: bash -leo pipefail {0}
on:
  workflow_dispatch:
  # TODO: run when "release prep" label is added

jobs:
  test-conda-build:
    runs-on: ubuntu-latest
    
    steps:
      - name: Checkout openfe repository
        uses: actions/checkout@v4
        with:
          path: openfe
      
      - name: Checkout conda-forge feedstock
        uses: actions/checkout@v4
        with:
          repository: conda-forge/openfe-feedstock
          path: openfe-feedstock
      
      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.x'
      
      - name: Install conda-build dependencies
        run: |
          pip install pyyaml

      # TODO just checkout the repo where we need it?
      - name: Copy source code to recipe folder
        run: cp -r openfe openfe-feedstock/recipe/openfe_source

      - name: Get Latest gufe Version
        id: latest-gufe-version
        run: |
          REPO="openfreeenergy/gufe"
          VERSION=$(curl -s -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
            "https://api.github.com/repos/$REPO/releases/latest" | jq -r '.tag_name | ltrimstr("v")')
          echo $VERSION
          echo "VERSION=$VERSION" >> $GITHUB_OUTPUT
      
      - name: Modify feedstock to use local path and latest gufe
        uses: mikefarah/yq@master
        with:
          cmd: |
            cd openfe-feedstock
            
            # Backup original recipe.yaml
            cp recipe/recipe.yaml recipe/recipe.yaml.bak

            # Add path after 'source:' and delete url line
            yq -i '.source.path="./openfe_source"' recipe/recipe.yaml
            yq -i 'del(.source.url)' recipe/recipe.yaml

            # remove existing gufe entry and add the gufe pin we want
            yq -i 'del(.outputs.[0].requirements.run[] | select(. =="*gufe*"))' recipe/recipe.yaml
            yq -i '.outputs.[0].requirements.run += "gufe==${{ steps.latest-gufe-version.outputs.VERSION }}"' recipe/recipe.yaml
            
            
            echo "Modified recipe.yaml:"
            cat recipe/recipe.yaml
                
      - name: Run conda-forge build test
        run: |
          cd openfe-feedstock
          python build-locally.py
        continue-on-error: true
        id: build_test

          # Uncomment if build_artifacts is needed to troubleshoot build
      
          #      - name: Upload build logs
          #        if: always()
          #        uses: actions/upload-artifact@v4
          #        with:
          #          name: conda-build-logs
          #          path: |
          #            openfe-feedstock/build_artifacts/
          #            openfe-feedstock/recipe/recipe.yaml
          #            openfe-feedstock/recipe/recipe.yaml.bak
          #          if-no-files-found: warn
      
      - name: Check build status
        if: steps.build_test.outcome == 'failure'
        run: |
          echo "❌ Conda forge build test failed. Check the uploaded logs for details."
          exit 1


================================================
FILE: .gitignore
================================================
# custom ignores
.duecredit.p
.xxrun
.idea/
.vscode/

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
*~

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
#  Usually these files are written by a python script from a template
#  before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/
docs/reference/api/generated
docs/tutorials/*.png

# PyBuilder
.pybuilder/
target/

# Jupyter Notebook
.ipynb_checkpoints

# IPython
profile_default/
ipython_config.py

# pyenv
#   For a library or package, you might want to ignore these files since the code is
#   intended to run in multiple environments; otherwise, check them in:
# .python-version

# pipenv
#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
#   However, in case of collaboration, if having platform-specific dependencies or dependencies
#   having no cross-platform support, pipenv may install dependencies that don't work, or not
#   install all needed dependencies.
#Pipfile.lock

# poetry
#   Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
#   This is especially recommended for binary packages to ensure reproducibility, and is more
#   commonly ignored for libraries.
#   https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock

# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/

# Celery stuff
celerybeat-schedule
celerybeat.pid

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
.dmypy.json
dmypy.json

# Pyre type checker
.pyre/

# pytype static type analyzer
.pytype/

# Cython debug symbols
cython_debug/

# PyCharm
#  JetBrains specific template is maintainted in a separate JetBrains.gitignore that can
#  be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
#  and can be added to the global gitignore or merged into this file.  For a more nuclear
#  option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

# vim
*.swp

# vscode
.vscode/

# Example notebooks
docs/ExampleNotebooks/

# duecredit
.duecredit.p

# Some charge stuff
*.model.pt

# Rever
rever/


================================================
FILE: .pre-commit-config.yaml
================================================
ci:
    autoupdate_schedule: quarterly
    # comment / label "pre-commit.ci autofix" to a pull request to manually trigger auto-fixing
    autofix_prs: false
    skip: []
    submodules: false

repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
  rev: v6.0.0
  hooks:
  - id: check-added-large-files
    args: ["--maxkb=900"]
  - id: check-case-conflict
  - id: check-executables-have-shebangs
  - id: check-symlinks
  - id: check-toml
  - id: check-yaml
    exclude: devtools/installer/construct.yaml  # not a true YAML file
  - id: debug-statements

- repo: https://github.com/tox-dev/pyproject-fmt
  rev: "v2.21.0"
  hooks:
    - id: pyproject-fmt

- repo: https://github.com/astral-sh/ruff-pre-commit
  rev: v0.15.9
  hooks:
    # Run the linter.
    - id: ruff
      args: [--fix ]
    # Run the formatter.
    - id: ruff-format


================================================
FILE: .readthedocs.yaml
================================================
version: 2

build:
  os: "ubuntu-24.04"
  tools:
    python: "miniconda3-3.12-24.9"

sphinx:
   configuration: docs/conf.py
   fail_on_warning: true

conda:
  environment: docs/environment.yaml

python:
  # Install our python package before building the docs
  install:
    - method: pip
      path: .


================================================
FILE: CITATION.cff
================================================
cff-version: 1.2.0
message: "If you use this software, please cite it as below."
authors:
- family-names: "Alibay"
  given-names: "Irfan"
  orcid: "https://orcid.org/0000-0001-5787-9130"
- family-names: "Gowers"
  given-names: "Richard J."
  orcid: "https://orcid.org/0000-0002-3241-1846"
- family-names: "Swenson"
  given-names: "David W.H."
  orcid: "https://orcid.org/0000-0001-9922-7923"
- family-names: "Henry"
  given-names: "Michael M."
  orcid: "https://orcid.org/0000-0002-3870-9993"
- family-names: "Ries"
  given-names: "Benjamin"
  orcid: "https://orcid.org/0000-0002-0945-8304"
- family-names: "Baumann"
  given-names: "Hannah M."
  orcid: "https://orcid.org/0000-0002-1736-7744"
- family-names: "Eastwood"
  given-names: "James R. B."
  orcid: "https://orcid.org/0000-0003-3895-5227"
- given-names: "Ashley"
  family-names: "Mitchell"
  orcid: 'https://orcid.org/0000-0002-8246-5113'
- given-names: "David"
  family-names: "Dotson"
  orcid: "https://orcid.org/0000-0001-5879-2942"
- given-names: Joshua T.
  family-names: Horton
  orcid: 'https://orcid.org/0000-0001-8694-7200'
- given-names: Matthew
  family-names: Thompson
  orcid: 'https://orcid.org/0000-0002-1460-3983'
- given-names: Alyssa
  family-names: Travitz
  orcid: 'https://orcid.org/0000-0001-5953-8807'
title: "The Open Free Energy library"
version: 1.7.0
date-released: 2025-04-25
url: "https://openfree.energy/"
repository-code: "https://github.com/openfreeEnergy/openfe"
doi: 10.5281/zenodo.8344247


================================================
FILE: Code_of_Conduct.md
================================================
## Code of Conduct ##

This project is dedicated to providing a welcoming and supportive environment for all people, regardless of background or identity. Members do not tolerate harassment for any reason, but especially harassment based on gender, sexual orientation, disability, physical appearance, body size, race, nationality, sex, color, ethnic or social origin, pregnancy, citizenship, familial status, veteran status, genetic information, religion or belief, political or any other opinion, membership of a national minority, property, age, or preference of text editor. 


### Expected Behavior ###

All participants in our events and communications are expected to show respect and courtesy to others. All interactions should be professional regardless of platform: either online or in-person. In order to foster a positive and professional working environment we encourage the following kinds of behaviors in all work events, activities, and platforms:

 * Use welcoming and inclusive language
 * Be respectful of different viewpoints and experiences
 * Gracefully accept constructive criticism
 * Focus on what is best for the community
 * Show courtesy and respect towards other community members

Note: See the [four social rules](https://www.recurse.com/manual#sub-sec-social-rules) for further recommendations.

### Unacceptable Behavior ###

Harassment is any form of behavior intended to exclude, intimidate, or cause discomfort. Prohibited harassing behavior includes, but is not limited to:

* written or verbal comments which have the effect of excluding people
* causing someone to fear for their safety, such as through stalking, following, or intimidating
* the display of sexual or violent images
* unwelcome sexual attention
* non-consensual or unwelcome physical contact
* sustained disruption of talks, events, or communications
* incitement to violence, suicide, or self-harm
* continuing to initiate interaction (including photography or recording) with someone after being asked to stop

and

* publication of private comment without consent

This list should not be taken to be exhaustive, but rather as a guide to make it easier to enrich our community and all those in which we participate. All interactions should be professional regardless of location: Harassment is prohibited whether it occurs on or offline, and the same standards apply to both.

Enforcement of this Code of Conduct will be respectful and not include any harassing behaviors.


You deserve sincere thanks for helping to make this a welcoming, friendly community for all.

This Code of Conduct was adapted from the [cmelab](https://github.com/cmelab/getting-started/blob/master/wiki/pages/Code_of_Conduct.md).


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2022 OpenFreeEnergy

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: MANIFEST.in
================================================
recursive-include src/openfe/tests/data/ *.sdf
recursive-include src/openfe/tests/data/ *.bz2
recursive-include src/openfe/tests/data/ *.csv
recursive-include src/openfe/tests/data/ *.pdb
recursive-include src/openfe/tests/data/ *.mol2
recursive-include src/openfe/tests/data/ *.xml
recursive-include src/openfe/tests/data/ *.graphml
recursive-include src/openfe/tests/data/ *.edge
recursive-include src/openfe/tests/data/ *.dat
recursive-include src/openfe/tests/data/ *.txt
recursive-include src/openfe/tests/data/ *.gz
recursive-include src/openfe/tests/data/ *json_results.gz
include src/openfecli/tests/data/*.json
include src/openfecli/tests/data/*.tar.gz
include src/openfecli/tests/commands/test_gather/*.tsv
recursive-include src/openfecli/tests/ *.sdf
recursive-include src/openfecli/tests/ *.pdb
include src/openfe/tests/data/openmm_rfe/vacuum_nocoord.nc


================================================
FILE: README.md
================================================
[![Logo](https://img.shields.io/badge/OSMF-OpenFreeEnergy-%23002f4a)](https://openfree.energy/)
[![build](https://github.com/OpenFreeEnergy/openfe/actions/workflows/ci.yaml/badge.svg?branch=main)](https://github.com/OpenFreeEnergy/openfe/actions/workflows/ci.yaml)
[![coverage](https://codecov.io/gh/OpenFreeEnergy/openfe/branch/main/graph/badge.svg)](https://codecov.io/gh/OpenFreeEnergy/openfe)
[![documentation](https://readthedocs.org/projects/openfe/badge/?version=stable)](https://docs.openfree.energy/en/stable/?badge=stable)
[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.8344248.svg)](https://doi.org/10.5281/zenodo.17258732)


# `openfe` - A Python package for executing alchemical free energy calculations.

The `openfe` package is the flagship project of [Open Free Energy](https://openfree.energy),
a pre competitive consortium aiming to provide robust, permissively licensed open source tools for molecular simulation in the drug discovery field.

Using `openfe` you can easily plan and execute alchemical free energy calculations.

See our [website](https://openfree.energy/) for more information on the project,
[try for yourself](https://try.openfree.energy) from the comfort of your browser,
and we have [documentation on using the package](https://docs.openfree.energy/en/latest/index.html).

## License

This library is made available under the [MIT](https://opensource.org/licenses/MIT) open source license.

## Install

### Latest release

The latest release of `openfe` can be installed via `mamba`, `docker`, or a `single file installer`. See [our installation instructions](https://docs.openfree.energy/en/stable/installation.html) for more details.
Dependencies can be installed via conda through:

### Development version

The development version of `openfe` can be installed directly from the `main` branch of this repository.

First install the package dependencies using `mamba`:

```bash
mamba env create -f environment.yml
```

The openfe library can then be installed via:

```
python -m pip install --no-deps .
```

## Authors

The OpenFE development team.

## Acknowledgements

OpenFE is an [Open Molecular Software Foundation](https://omsf.io/) hosted project.


================================================
FILE: codecov.yml
================================================
coverage:
  status:
    project: off


================================================
FILE: devtools/data/fix_rbfe_results.py
================================================
"""A script to fix up rbfe_results.tar.gz

Useful if Settings are ever changed in a backwards-incompatible way

Will expect "rbfe_results.tar.gz" in this directory, will overwrite this file
"""

import glob
import json
import os.path
import tarfile

from gufe.tokenization import JSON_HANDLER

from openfe.protocols import openmm_rfe


def untar(fn):
    """extract tarfile called *fn*"""
    with tarfile.open(fn) as f:
        f.extractall()


def retar(loc, name):
    """create tar.gz called *name* of directory *loc*"""
    with tarfile.open(name, mode="w:gz") as f:
        f.add(loc, arcname=os.path.basename(loc))


def replace_settings(fn, new_settings):
    """replace settings instances in *fn* with *new_settings*"""
    with open(fn, "r") as f:
        data = json.load(f)

    for k in data["protocol_result"]["data"]:
        data["protocol_result"]["data"][k][0]["inputs"]["settings"] = new_settings

    for k in data["unit_results"]:
        data["unit_results"][k]["inputs"]["settings"] = new_settings

    with open(fn, "w") as f:
        json.dump(data, f, cls=JSON_HANDLER.encoder)


def fix_rbfe_results():
    untar("rbfe_results.tar.gz")

    # generate valid settings as defaults
    new_settings = openmm_rfe.RelativeHybridTopologyProtocol.default_settings()

    # walk over all result jsons
    for fn in glob.glob("./results/*json"):
        # replace instances of settings within with valid settings
        replace_settings(fn, new_settings)

    retar("results", "rbfe_results.tar.gz")


if __name__ == "__main__":
    fix_rbfe_results()


================================================
FILE: devtools/data/gen_serialized_results.py
================================================
"""
Dev script to generate some result jsons that are used for testing

Generates
- ABFEProtocol_json_results.gz
  - used in abfe_results_json fixture
- SepTopProtocol_json_results.gy
  - used in septop_json fixture
- AHFEProtocol_json_results.gz
  - used in afe_solvation_json fixture
- RHFEProtocol_json_results.gz
  - used in rfe_transformation_json fixture
- MDProtocol_json_results.gz
  - used in md_json fixture
"""

import gzip
import json
import logging
import pathlib
import sys
import tempfile

import gufe
from gufe.tokenization import JSON_HANDLER
from kartograf import KartografAtomMapper
from kartograf.atom_aligner import align_mol_shape
from openff.toolkit import AmberToolsToolkitWrapper, Molecule, RDKitToolkitWrapper
from openff.toolkit.utils.toolkit_registry import ToolkitRegistry, toolkit_registry_manager
from openff.units import unit
from rdkit import Chem

import openfe
from openfe.protocols.openmm_afe import (
    AbsoluteBindingProtocol,
    AbsoluteSolvationProtocol,
)
from openfe.protocols.openmm_md.plain_md_methods import PlainMDProtocol
from openfe.protocols.openmm_rfe import RelativeHybridTopologyProtocol
from openfe.protocols.openmm_septop import SepTopProtocol
from openfecli.utils import configure_logger

# avoid problems with output not showing if queueing system kills a job
sys.stdout.reconfigure(line_buffering=True)

stdout_handler = logging.StreamHandler(sys.stdout)
configure_logger("gufekey", handler=stdout_handler)
configure_logger("gufe", handler=stdout_handler)
configure_logger("openfe", handler=stdout_handler)
configure_logger("openmmtools.multistate.multistatereporter", level=logging.DEBUG, handler=stdout_handler)  # fmt: skip
configure_logger("openmmtools.multistate.multistatesampler", level=logging.DEBUG, handler=stdout_handler)  # fmt: skip

logger = logging.getLogger(__name__)

LIGA = "[H]C([H])([H])C([H])([H])C(=O)C([H])([H])C([H])([H])[H]"
LIGB = "[H]C([H])([H])C(=O)C([H])([H])C([H])([H])C([H])([H])[H]"

amber_rdkit = ToolkitRegistry([RDKitToolkitWrapper(), AmberToolsToolkitWrapper()])


def get_molecule(smi, name):
    with toolkit_registry_manager(amber_rdkit):
        m = Molecule.from_smiles(smi)
        m.generate_conformers()
        m.assign_partial_charges(partial_charge_method="am1bcc")
    return openfe.SmallMoleculeComponent.from_openff(m, name=name)


def get_hif2a_inputs():
    with gzip.open("inputs/hif2a_protein.pdb.gz", "r") as f:
        protcomp = openfe.ProteinComponent.from_pdb_file(f, name="hif2a_prot")

    with gzip.open("inputs/hif2a_ligands.sdf.gz", "r") as f:
        smcs = [
            openfe.SmallMoleculeComponent(mol)
            for mol in list(Chem.ForwardSDMolSupplier(f, removeHs=False))
        ]

    return smcs, protcomp


def execute_and_serialize(
    dag,
    protocol,
    simname,
    new_serialization: bool = False
):  # fmt: skip
    """
    Execute & serialize a DAG

    Parameters
    ----------
    dag : gufe.ProtocolDAG
      The DAG to execute & serialize.
    protocol : gufe.Protocol
      The Protocol to which the DAG belongs.
    simname : str
      The name of the simulation, used for the serialized file name.
    new_serialization : bool
      Whether or not we should use the "new" `to_json` serialization.
      Default is False (for now).
    """
    logger.info(f"running {simname}")
    with tempfile.TemporaryDirectory() as tmpdir:
        workdir = pathlib.Path(tmpdir)
        dagres = gufe.protocols.execute_DAG(
            dag,
            shared_basedir=workdir,
            scratch_basedir=workdir,
            keep_shared=True,
            raise_error=True,
            n_retries=2,
        )
    protres = protocol.gather([dagres])

    if new_serialization:
        protres.to_json(f"{simname}_json_results.json")

    else:
        outdict = {
            "estimate": protres.get_estimate(),
            "uncertainty": protres.get_uncertainty(),
            "protocol_result": protres.to_dict(),
            "unit_results": {
                unit.key: unit.to_keyed_dict()
                for unit in dagres.protocol_unit_results
            }
        }  # fmt: skip

        with gzip.open(f"{simname}_json_results.gz", "wt") as zipfile:
            json.dump(outdict, zipfile, cls=JSON_HANDLER.encoder)


def generate_md_settings():
    settings = PlainMDProtocol.default_settings()
    settings.simulation_settings.equilibration_length_nvt = 0.01 * unit.nanosecond
    settings.simulation_settings.equilibration_length = 0.01 * unit.nanosecond
    settings.simulation_settings.production_length = 0.01 * unit.nanosecond
    settings.forcefield_settings.nonbonded_method = "nocutoff"

    return settings


def generate_md_json(smc):
    protocol = PlainMDProtocol(settings=generate_md_settings())
    system = openfe.ChemicalSystem({"ligand": smc})
    dag = protocol.create(stateA=system, stateB=system, mapping=None)

    execute_and_serialize(dag, protocol, "MDProtocol")


def generate_abfe_settings():
    settings = AbsoluteBindingProtocol.default_settings()
    settings.solvent_equil_simulation_settings.equilibration_length_nvt = 10 * unit.picosecond
    settings.solvent_equil_simulation_settings.equilibration_length = 10 * unit.picosecond
    settings.solvent_equil_simulation_settings.production_length = 10 * unit.picosecond
    settings.solvent_simulation_settings.equilibration_length = 100 * unit.picosecond
    settings.solvent_simulation_settings.production_length = 500 * unit.picosecond
    settings.solvent_simulation_settings.time_per_iteration = 2.5 * unit.ps
    settings.complex_equil_simulation_settings.equilibration_length_nvt = 10 * unit.picosecond
    settings.complex_equil_simulation_settings.equilibration_length = 10 * unit.picosecond
    settings.complex_equil_simulation_settings.production_length = 100 * unit.picosecond
    settings.complex_simulation_settings.equilibration_length = 100 * unit.picosecond
    settings.complex_simulation_settings.production_length = 500 * unit.picosecond
    settings.complex_simulation_settings.time_per_iteration = 2.5 * unit.ps
    settings.solvent_solvation_settings.box_shape = "dodecahedron"
    settings.complex_solvation_settings.box_shape = "dodecahedron"
    settings.solvent_solvation_settings.solvent_padding = 1.5 * unit.nanometer
    settings.complex_solvation_settings.solvent_padding = 1.0 * unit.nanometer
    settings.forcefield_settings.nonbonded_cutoff = 0.8 * unit.nanometer
    settings.protocol_repeats = 3
    settings.engine_settings.compute_platform = "CUDA"

    return settings


def generate_abfe_json():
    ligands, protein = get_hif2a_inputs()
    protocol = AbsoluteBindingProtocol(settings=generate_abfe_settings())
    sysA = openfe.ChemicalSystem(
        {
            "ligand": ligands[0],
            "protein": protein,
            "solvent": openfe.SolventComponent(),
        }
    )
    sysB = openfe.ChemicalSystem(
        {
            "protein": protein,
            "solvent": openfe.SolventComponent(),
        }
    )

    dag = protocol.create(stateA=sysA, stateB=sysB, mapping=None)
    execute_and_serialize(dag, protocol, "ABFEProtocol", new_serialization=True)


def generate_ahfe_settings():
    settings = AbsoluteSolvationProtocol.default_settings()
    settings.solvent_equil_simulation_settings.equilibration_length_nvt = 10 * unit.picosecond
    settings.solvent_equil_simulation_settings.equilibration_length = 10 * unit.picosecond
    settings.solvent_equil_simulation_settings.production_length = 10 * unit.picosecond
    settings.solvent_simulation_settings.equilibration_length = 10 * unit.picosecond
    settings.solvent_simulation_settings.production_length = 500 * unit.picosecond
    settings.vacuum_equil_simulation_settings.equilibration_length = 10 * unit.picosecond
    settings.vacuum_equil_simulation_settings.production_length = 10 * unit.picosecond
    settings.vacuum_simulation_settings.equilibration_length = 10 * unit.picosecond
    settings.vacuum_simulation_settings.production_length = 1000 * unit.picosecond
    settings.lambda_settings.lambda_elec = [0.0, 0.25, 0.5, 0.75, 1.0, 1.0,
                                            1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
                                            1.0]  # fmt: skip
    settings.lambda_settings.lambda_vdw = [0.0, 0.0, 0.0, 0.0, 0.0, 0.12, 0.24,
                                           0.36, 0.48, 0.6, 0.7, 0.77, 0.85,
                                           1.0]  # fmt: skip
    settings.protocol_repeats = 3
    settings.solvent_simulation_settings.n_replicas = 14
    settings.vacuum_simulation_settings.n_replicas = 14
    settings.solvent_simulation_settings.early_termination_target_error = 0.12 * unit.kilocalorie_per_mole  # fmt: skip
    settings.vacuum_simulation_settings.early_termination_target_error = 0.12 * unit.kilocalorie_per_mole  # fmt: skip
    settings.vacuum_engine_settings.compute_platform = "CPU"
    settings.solvent_engine_settings.compute_platform = "CUDA"

    return settings


def generate_ahfe_json(smc):
    protocol = AbsoluteSolvationProtocol(settings=generate_ahfe_settings())
    sysA = openfe.ChemicalSystem({"ligand": smc, "solvent": openfe.SolventComponent()})
    sysB = openfe.ChemicalSystem({"solvent": openfe.SolventComponent()})

    dag = protocol.create(stateA=sysA, stateB=sysB, mapping=None)

    execute_and_serialize(dag, protocol, "AHFEProtocol")


def generate_rfe_settings():
    settings = RelativeHybridTopologyProtocol.default_settings()
    settings.simulation_settings.equilibration_length = 10 * unit.picosecond
    settings.simulation_settings.production_length = 250 * unit.picosecond
    settings.forcefield_settings.nonbonded_method = "nocutoff"

    return settings


def generate_rfe_json(smcA, smcB):
    protocol = RelativeHybridTopologyProtocol(settings=generate_rfe_settings())

    a_smcB = align_mol_shape(smcB, ref_mol=smcA)
    mapper = KartografAtomMapper(atom_map_hydrogens=True)
    mapping = next(mapper.suggest_mappings(smcA, a_smcB))

    systemA = openfe.ChemicalSystem({"ligand": smcA})
    systemB = openfe.ChemicalSystem({"ligand": a_smcB})

    dag = protocol.create(stateA=systemA, stateB=systemB, mapping=mapping)

    execute_and_serialize(dag, protocol, "RHFEProtocol")


def generate_septop_settings():
    settings = SepTopProtocol.default_settings()
    settings.solvent_equil_simulation_settings.equilibration_length_nvt = 10 * unit.picosecond
    settings.solvent_equil_simulation_settings.equilibration_length = 10 * unit.picosecond
    settings.solvent_equil_simulation_settings.production_length = 10 * unit.picosecond
    settings.solvent_simulation_settings.equilibration_length = 10 * unit.picosecond
    settings.solvent_simulation_settings.production_length = 50 * unit.picosecond
    settings.solvent_simulation_settings.time_per_iteration = 2.5 * unit.ps
    settings.complex_equil_simulation_settings.equilibration_length_nvt = 10 * unit.picosecond
    settings.complex_equil_simulation_settings.equilibration_length = 10 * unit.picosecond
    settings.complex_equil_simulation_settings.production_length = 10 * unit.picosecond
    settings.complex_simulation_settings.equilibration_length = 10 * unit.picosecond
    settings.complex_simulation_settings.production_length = 50 * unit.picosecond
    settings.complex_simulation_settings.time_per_iteration = 2.5 * unit.ps
    settings.solvent_solvation_settings.box_shape = "dodecahedron"
    settings.complex_solvation_settings.box_shape = "dodecahedron"
    settings.solvent_solvation_settings.solvent_padding = 1.2 * unit.nanometer
    settings.complex_solvation_settings.solvent_padding = 1.0 * unit.nanometer
    settings.forcefield_settings.nonbonded_cutoff = 0.9 * unit.nanometer
    settings.protocol_repeats = 1
    settings.engine_settings.compute_platform = "CUDA"

    return settings


def generate_septop_json():
    hif2a_ligands, hif2a_protein = get_hif2a_inputs()
    protocol = SepTopProtocol(settings=generate_septop_settings())
    sysA = openfe.ChemicalSystem(
        {
            "ligand_A": hif2a_ligands[0],
            "protein": hif2a_protein,
            "solvent": openfe.SolventComponent(),
        }
    )
    sysB = openfe.ChemicalSystem(
        {
            "ligand_B": hif2a_ligands[1],
            "protein": hif2a_protein,
            "solvent": openfe.SolventComponent(),
        }
    )

    dag = protocol.create(stateA=sysA, stateB=sysB, mapping=None)
    execute_and_serialize(dag, protocol, "SepTopProtocol")


if __name__ == "__main__":
    molA = get_molecule(LIGA, "ligandA")
    molB = get_molecule(LIGB, "ligandB")
    generate_md_json(molA)
    generate_abfe_json()
    generate_ahfe_json(molA)
    generate_rfe_json(molA, molB)
    generate_septop_json()


================================================
FILE: devtools/debug_openmm.sh
================================================
#!/usr/bin/env bash

echo "Run this script with your conda env activated"
echo "Invoke the script like this: "
echo "./debug_openmm.sh | tee -a debug.log"
echo "Then send us debug.log"

set -euo pipefail

date

which -a python

conda info -a || echo "no conda"
mamba info -a || echo "no mamba"
micromamba info || echo "no micromamba"

nvidia-smi || echo "no nvidia-smi, are you on a gpu node?"

echo "test openmm"
python -m openmm.testInstallation || echo "testing openmm"

echo "checking plugin load failures"
python -c "import openmm; print(openmm.Platform.getPluginLoadFailures())" || echo "plugin load failures"

echo "checking which platforms support mixed precision"
python -c "import openmmtools; [print(_.getName()) for _ in openmmtools.utils.get_available_platforms(minimum_precision='mixed')]" || echo "openmm errors"

conda list || echo "no conda"
mamba list || echo "no mamba"
micromamba list || echo "no micromamba"


================================================
FILE: devtools/installer/construct.yaml
================================================
name: OpenFEforge
version: {{ environ["VERSION"] }}
company: OpenFE
license_file: ../../LICENSE

channels:
  - conda-forge

write_condarc: True
keep_pkgs: True
transmute_file_type: .conda

specs:
  - conda
  - jupyterlab
  - mamba
  - notebook <7
  - openfe=={{ environ["VERSION"] }}
  - pip
  - pytest
  - pytest-xdist
  # python needs to match https://github.com/googlecolab/backend-info/blob/main/os-info.txt
  # until colab pushes a fix
  - python 3.12.11

# Not building an .exe for windows or a .pkg for macOS
installer_type: sh


================================================
FILE: docs/CHANGELOG.rst
================================================
=========
Changelog
=========

.. current developments

v1.11.1
====================

**Fixed:**

* Fixed slow response time of CLI commands (`PR #1972 <https://github.com/OpenFreeEnergy/openfe/pull/1972>`_).




v1.11.0
====================

* **openfe v1.11.0** introduces support for protein-membrane systems both with the Python API and the CLI. See our tutorial `RBFE calculations of a Protein-Membrane System <https://docs.openfree.energy/en/latest/tutorials/rbfe_membrane_protein.html>`_ for details.

The `ability to resume execution of incomplete transformations <https://docs.openfree.energy/en/v1.10.0/guide/execution/quickrun_execution.html>`_ that was introduced in ``openfe v1.10.0`` is now available for the plain MD and SepTop protocols.

See below for the full changelog for this release:

**Added:**

* Added support for systems with membranes to the following protocols:
  PlainMDProtocol, RelativeHybridTopologyProtocol, SepTopProtocol, and AbsoluteBindingProtocol (`PR #1561 <https://github.com/OpenFreeEnergy/openfe/pull/1561>`_).
* Added support for membrane systems to ``openfe plan-rbfe-network``.
  Use ``--protein-membrane`` instead of the ``--protein`` argument, and see the tutorial on preparing membrane systems (`PR #1896 <https://github.com/OpenFreeEnergy/openfe/pull/1896>`_).
* Added API support for resuming the PlainMDProtocol (`PR #1884 <https://github.com/OpenFreeEnergy/openfe/pull/1884>`_).
* Added API support for resuming the SepTopProtocol. (`PR #1949 <https://github.com/OpenFreeEnergy/openfe/pull/1949>`_).
* The ``validate`` method for the SepTopProtocol has been implemented.
  This means that settings and system validation can mostly be done prior to Protocol execuation by calling ``SepTopProtocol.validate(stateA, stateB, mapping=None)`` (`PR #1946 <https://github.com/OpenFreeEnergy/openfe/pull/1946>`_).

**Changed:**

* The SepTopProtocol now has a dedicated Analysis unit.
  At the top level API, this does not change behavior, but if you are directly interfacing with th ProtocolUnits, you will have to account for this change.
  The SepTopProtocolResult now solely uses the Analysis units (`PR #1937 <https://github.com/OpenFreeEnergy/openfe/pull/1937>`_).
* Updated the chemical systems user guide and the defining protocols user guide to reflect recent protocol updates, including adding membrane support (`PR #1933 <https://github.com/OpenFreeEnergy/openfe/pull/1933>`_).
* The default value for the Hybrid TopologyProtocol setting ``turn_off_core_unique_exceptions`` has been changed to ``True``.
  This means 1-4 interactions involving the unique alchemical atoms and core regions will now be interpolated on/off accordingly by default (`PR #1856 <https://github.com/OpenFreeEnergy/openfe/pull/1856>`_).

**Deprecated:**

* Perses atom mapper and scorer functionality is deprecated, now slated to be removed in ``openfe v1.12``.
  This includes ``PersesAtomMapper`` and ``default_perses_scorer`` (`PR #1857 <https://github.com/OpenFreeEnergy/openfe/pull/1857>`_).

**Fixed:**

* Fix erroneous logging information message which would mention setting up the alchemical system when running simulation or analysis units with the hybrid topology, AHFE or ABFE Protocols (`PR #1915 <https://github.com/OpenFreeEnergy/openfe/pull/1915>`_).
* System equality checks on resuming no longer expect complete equality in the force parameters.
  This fixes a scenario where small changes in precision due to running on different machines would prevent users from restarting their simulations (`PR #1914 <https://github.com/OpenFreeEnergy/openfe/pull/1914>`_).



v1.10.0
====================

This release introduces the ability to resume execution of an incomplete transformation using ``openfe quickrun`` with the ``--resume`` flag.
See the `quickrun documentation <https://docs.openfree.energy/en/v1.10.0/guide/execution/quickrun_execution.html>`_ details.

**Added:**

* Added ``--resume`` flag to ``openfe quickrun``.
  Quickrun now temporarily caches ``protocolDAG`` information and, when used with the ``--resume`` flag, quickrun will attempt to resume execution of an incomplete transformation (`PR #1848 <https://github.com/OpenFreeEnergy/openfe/pull/1848>`_).
* Added API support to resume ``RelativeHybridTopologyProtocol`` simulations (`PR #1774 <https://github.com/OpenFreeEnergy/openfe/pull/1774>`_).
* Added API support to resume ``AbsoluteBindingProtocol`` and ``AbsoluteSolvationProtocol`` simulations (`PR #1808 <https://github.com/OpenFreeEnergy/openfe/pull/1808>`_).

**Deprecated:**

* Perses atom mapper and scorer functionality is deprecated, slated to be removed in ``openfe v2.0``.
  This includes ``PersesAtomMapper`` and ``default_perses_scorer`` (`PR #1857 <https://github.com/OpenFreeEnergy/openfe/pull/1857>`_).

**Fixed:**

* Fixed bug introduced in v1.9.0 to ``openfe gather-abfe --report=raw`` where additional unit results for Setup and Simulation units would be shown.
  This fix restores the behavior prior to v1.9.0 (`PR #1876 <https://github.com/OpenFreeEnergy/openfe/pull/1876>`_).



v1.9.1
====================

**Fixed:**

* Fixed a bug in Protocol termination for the HybridTop and AFE Protocols which would unnecessarily declare an ``UnboundLocalError``.
* Updated ``openfe_analysis`` dependency  to fix issue with RMSD analysis (`Issue #1834 <https://github.com/OpenFreeEnergy/openfe/issues/1834>`_).



v1.9.0
====================

**Added:**

* The ``validate`` method for the RelativeHybridTopologyProtocol has been implemented.
  This means that settings and system validation can mostly be done prior to Protocol execution by calling ``RelativeHybridTopologyProtocol.validate(stateA, stateB, mapping)`` (`PR #1740 <https://github.com/OpenFreeEnergy/openfe/pull/1740>`_).

* Added ``openfe test --download-only`` flag, which downloads all test data stored remotely to the local cache (`PR #1814 <https://github.com/OpenFreeEnergy/openfe/pull/1814>`_).

**Changed:**

* The absolute free energy protocols (AbsoluteBindingProtocol and AbsoluteSolvationProtocol) have been broken into multiple
  protocol units, allowing for setup, run, and analysis to happen
  separately in the future when relevant changes to protocol execution are
  made (`PR #1776 <https://github.com/OpenFreeEnergy/openfe/pull/1776>`_).
* The relative free energy protocol (RelativeHybridTopologyProtocol) has been
  broken into multiple protocol units, allowing for the setup, run, analysis to happen
  separately (`PR #1773 <https://github.com/OpenFreeEnergy/openfe/pull/1773>`_).

**Fixed:**

* Fixed bug in ligand network visualization (such as with ``openfe view-ligand-network``) so that ligand names are no longer cut off by the plot border (`PR #1822 <https://github.com/OpenFreeEnergy/openfe/pull/1822>`_).
* Endstates in the RelativeHybridTopologyProtocol are now being created
  in a manner that allows for isomorphic molecules that differ between
  endstates to have different parameters (`PR #1772 <https://github.com/OpenFreeEnergy/openfe/pull/1772>`_).



v1.8.1
====================

**Added:**

* Added a progress bar for ``openfe gather`` JSON loading (`PR #1786 <https://github.com/OpenFreeEnergy/openfe/pull/1786>`_).

**Fixed:**

* Due to issues with OpenFF's handling of toolkit registries
  with NAGL, the use of NAGL models (e.g. AshGC) when OpenEye
  is installed but not requested as the charge backend has been
  disabled (Issue #1760, `PR #1762 <https://github.com/OpenFreeEnergy/openfe/pull/1762>`_).
* Fixed bug in ligand network visualization (such as with ``openfe view-ligand-network``) so that ligand names are no longer cut off by the plot border (`PR #1822 <https://github.com/OpenFreeEnergy/openfe/pull/1822>`_).



v1.8.0
====================

**Added:**

* The ``HybridTopologyFactory`` supports building hybrid OpenMM systems which contain ``CMAPTorsionForces`` on non-alchemical atoms.
  This should allow for simulations using Amber ff19SB (`PR #1695 <https://github.com/OpenFreeEnergy/openfe/pull/1695>`_).
* Added experimental features ``openfe gather-septop`` and ``openfe gather-abfe``, which are analogous to ``openfe gather`` and allow for gathering results generated by the Separated Topologies and Absolute Binding Free Energy protocols, respectively.  These commands are experimental and are liable to be changed in a future release.
* Emit a clarifying log message when a user gets a warning from JAX (`PR #1585 <https://github.com/OpenFreeEnergy/openfe/pull/1585>`_, fixes `Issue #1499 <https://github.com/OpenFreeEnergy/openfe/issues/1499>`_).
* Disable JAX acceleration by default, see https://docs.openfree.energy/en/latest/guide/troubleshooting.html#pymbar-disable-jax for more information (`PR #1694 <https://github.com/OpenFreeEnergy/openfe/pull/1692>`_).
* New options have been added to the ``AlchemicalSettings`` of the ``SepTopProtocol``, ``AbsoluteSolvationProtocol`` and ``AbsoluteBindingProtocol``. Notably, these options allow users to control the softcore parameters as well as the use of long range dispersion corrections (`PR #1742 <https://github.com/OpenFreeEnergy/openfe/pull/1742>`_).

**Changed:**

* ``openfe gather`` is now more rigorous in extracting ligand names and run types. These are now determined directly from component attributes, rather than relying on naming conventions. (`PR #1691 <https://github.com/OpenFreeEnergy/openfe/pull/1702>`_).
* Updated installation docs to recommend ``miniconda`` with ``conda-lock`` as the preferred installation method (`PR #1692 <https://github.com/OpenFreeEnergy/openfe/pull/1692>`_).



v1.7.0
====================

This release brings several long awaited features to OpenFE, including the SepTop and ABFE Protocols, as well as the adoption of more computationally efficient settings in the CLI and across the Python API.

The v1.7.0 release also comes with some API changes and breaks, including:
  * "CUDA" is now the default platform in the settings, you will need to change this if you run on a non-NVIDIA-powered platform.
  * The default solvation cutoff is now 1.5 nm, to avoid issues with small boxes when dealing with ligands in solvent. When calculating complexes using the MD or HybridTopology Protocols with the API, you will need to reduce this value to ~ 1 nm to avoid excessively large water boxes.
  * The API has fully migrated to Pydantic V2 and the ``GufeQuantity`` scheme. This only affects Protocol developers. If needed, please see the `gufe typing documentation <https://gufe.openfree.energy/en/latest/generated/gufe.settings.typing.html>`_ for more details.

Note that if you want to use NAGL to assign partial charges, you must use ``python >= 3.11``.
Python 3.10 support is no longer maintained according to `SPEC 0 <https://scientific-python.org/specs/spec-0000/>`_ guidelines.
The openfe lock file and docker and apptainer images use Python 3.12, and so charge assignment with NAGL will work without intervention.

**Added:**

* Addition of an Absolute Binding Free Energy Protocol (`PR #1045 <https://github.com/OpenFreeEnergy/openfe/pull/1045>`_).
* Added `a cookbook for using jq to inspect JSON files <https://docs.openfree.energy/en/v1.7.0/cookbook/jq_inspection.html>`_.
* The AbsoluteSolvationProtocol now properly implements the ``validate`` method,
  allowing users to verify inputs by calling the method directly (`PR #1572 <https://github.com/OpenFreeEnergy/openfe/pull/1572>`_).
* Added a new RBFE protocol based on Separated Topologies (`PR #1057 <https://github.com/OpenFreeEnergy/openfe/pull/1057>`_).

**Changed:**

* The default atom mapper used in the CLI has been changed from ``LomapAtomMapper`` to ``KartografAtomMapper`` in line with the recommended defaults from the industry benchmarking paper. Users who wish to continue to use ``LomapAtomMapper`` can do so via the YAML configuration file. See the `documentation <https://docs.openfree.energy/en/latest/tutorials/rbfe_cli_tutorial.html#customize-your-campaign-setup>`_ for details (`PR #1530 <https://github.com/OpenFreeEnergy/openfe/pull/1530>`_).
* An improved error message is now shown when a mapping involving a changing constraint length cannot be fixed (`PR #1529 <https://github.com/OpenFreeEnergy/openfe/pull/1529>`_).
* The default platform for OpenMM-based Protocols is now CUDA and will fail by default on a non-Nvidia GPU enabled system (`PR #1576 <https://github.com/OpenFreeEnergy/openfe/pull/1576>`_).
* Remove unnecessary limit on residues ids (``resids``) when getting mappings from topology in ``topology_helpers.py`` utility module (`PR #1539 <https://github.com/OpenFreeEnergy/openfe/pull/1539>`_).
* The relative hybrid topology protocol no longer runs the FIRE minimizer when ``dry=True`` (`PR #1468 <https://github.com/OpenFreeEnergy/openfe/pull/1468>`_).
* Units must be explicitly assigned when defining ``Settings`` parameters, and values will be converted to match the default units for a given field. For example, use ``1.0 * units.bar`` or ``"1 bar"`` for pressure, and ``300 * unit.kelvin`` or ``"300 kelvin"`` for temperature.
* For protocol developers: ``FloatQuantity`` is no longer supported. Instead, use ``GufeQuantity`` and ``specify_quantity_units()`` to make a ``TypeAlias``. See the `gufe typing documentation <https://gufe.openfree.energy/en/latest/generated/gufe.settings.typing.html>`_ for more details.
* The default ``time_per_iteration`` setting of the ``MultiStateSimulationSettings`` class has been increased from 1.0 ps to 2.5 ps as part of the fast settings update (`PR #1523 <https://github.com/OpenFreeEnergy/openfe/pull/1523>`_).

* The default ``box_shape`` setting of the ``OpenMMSolvationSettings`` class has been changed from ``cubic`` to ``dodecahedron`` to improve simulation efficiency as part of the fast settings update (`PR #1523 <https://github.com/OpenFreeEnergy/openfe/pull/1523>`_).

* The default ``solvent_padding`` settings of the ``OpenMMSolvationSettings`` class has been increased from 1.2 nm to 1.5 nm to be compatible with the new ``box_shape`` default as part of the fast settings update (`PR #1523 <https://github.com/OpenFreeEnergy/openfe/pull/1523>`_).

* The default ``nonbonded_cutoff`` setting of the ``OpenMMSystemGeneratorFFSettings`` class has been decreased to 0.9 nm from 1.0 nm, in line with current force fields best practices and our newly validated fast settings (`PR #1523 <https://github.com/OpenFreeEnergy/openfe/pull/1523>`_).

* When calling the CLI ``openfe plan_rbfe_network``, the ``RelativeHybridTopologyProtocol`` settings now reflects the above "fast" settings updates. This includes;

  * Dodecahedron box solvation
  * Solvation cutoff of 1.5 nm in solvent-only legs, and 1.0 nm in complex legs
  * A replica exchange rate of 2.5 ps
  * A 0.9 nm nonbonded cutoff

**Deprecated:**

* Deprecated ``openfe.utils.visualization_3D.view_mapping_3d()``. Use the method ``LigandAtomMapping.view_3d()`` instead (`PR #1592 <https://github.com/OpenFreeEnergy/openfe/pull/1592>`_).
* Deprecated ``openfe.utils.ligand_utils.get_alchemical_charge_difference()``, which is replaced by ``LigandAtomMapping.get_alchemical_charge_difference()`` in ``gufe`` (`PR #1479 <https://github.com/OpenFreeEnergy/openfe/pull/1479>`_).

**Fixed:**

* Charged molecules are now explicitly disallowed in the
  AbsoluteSolvationProtocol(`PR #1572 <https://github.com/OpenFreeEnergy/openfe/pull/1572>`_).



v1.6.1
====================
This release includes minor fixes and updates to tests.

**Added:**

* Added a cookbook for using ``jq`` to inspect JSON files.

**Changed:**

* Remove unnecessary limit on residues ids (``resids``) when getting mappings from topology in ``topology_helpers.py`` utility module.
* The relative hybrid topology protocol no longer runs the FIRE minimizer when ``dry=True``.

**Fixed:**

* Updated tests to expect to find NAGL, now that it is supported.



v1.6.0
====================
This release adds support for OpenMM 8.3.0 and Python 3.13.

**Added:**

* Added support for openmm 8.3.0 (benchmarking results at `Issue #1377 <https://github.com/OpenFreeEnergy/openfe/issues/1377>`_.
* Added support for ``python 3.13`` (we no longer guarantee support for ``python 3.10``).
* Adds a new internal API for defining alchemical restraints (`PR #1043 <https://github.com/OpenFreeEnergy/openfe/pull/1043>`_).



v1.5.0
====================
This release includes support for openmm 8.2 and numpy v2. Checkpoint interval default frequency has changed, resulting in much smaller file sizes. There are also a few minor changes as a result of migrating to use **konnektor** as the backend for many network generators.


**Added:**

* Added support for openmm 8.2 (`PR #1366 <https://github.com/OpenFreeEnergy/openfe/pull/1366>`_)
* Added optional ``n_processes`` (number of parallel processes to use when generating the network) arguments for network planners (`PR #927 <https://github.com/OpenFreeEnergy/openfe/pull/927>`_).
* Added optional ``progress`` (whether to show progress bar) for ``openfe.setup.ligand_network_planning.generate_radial_network`` (default= ``False``, such that there is no default behavior change)(`PR #927 <https://github.com/OpenFreeEnergy/openfe/pull/927>`_).
* Added compatibility for numpy v2 (`PR #1260 <https://github.com/OpenFreeEnergy/openfe/pull/1260>`_).

**Changed:**

* The checkpoint interval default frequency has been increased to every
  nanosecond. ``real_time_analysis_interval`` no longer needs to be divisible
  by the checkpoint interval, allowing users of the ``HybridTopologyProtocol``
  and ``AbsoluteSolvationProtocol`` to write checkpoints less frequently and
  yielding smaller file sizes.
* `konnektor <https://konnektor.openfree.energy/en/latest/>`_ is now used as the backend for all network generation (`PR #927 <https://github.com/OpenFreeEnergy/openfe/pull/927>`_).
* ``openfe.setup.ligand_network_planning.generate_maximal_network`` now returns the *best* mapping for each edge, rather than *all possible* mappings for each edge. If multiple mappers are passed but no scorer, the first mapper passed will be used, and a warning will be raised (`PR #927 <https://github.com/OpenFreeEnergy/openfe/pull/927>`_).

**Fixed:**

* Absolute free energy calculations (e.g. ``AbsoluteSolvationProtocol``) now
  correctly pass the equilibrated box vectors to the alchemical simulation.
  In the past default vectors were used, which in some cases led to random
  crashes due to an abrupt volume change. We do not believe that this
  significantly affected free energy results (`PR #1275 <https://github.com/OpenFreeEnergy/openfe/pull/1275>`_).



v1.4.0
====================

This release includes significant quality of life improvements for the CLI's ``openfe gather`` command.

**Added:**

* ``openfe gather`` now accepts any number of filepaths and/or directories containing results JSON files, instead of only accepting one results directory (`PR #1212 <https://github.com/OpenFreeEnergy/openfe/pull/1212>`_).
* When running ``openfe gather --report=dg`` and result edges have fewer than 2 replicates, an error will be thrown up-front instead of failing downstream with a ``numpy.linalg.LinAlgError: SVD did not converge`` error (`PR #1243 <https://github.com/OpenFreeEnergy/openfe/pull/1243>`_).
* ``openfe gather`` includes failed simulations in its output, with ``Error`` listed in place of a computed value, instead of simply omitting those results from the output table (`PR #1227 <https://github.com/OpenFreeEnergy/openfe/pull/1227>`_).
* ``openfe gather --report=dg`` (the default) checks for connectivity of the results network and throws an error if the network is disconnected or has fewer than 3 edges (`PR #1227 <https://github.com/OpenFreeEnergy/openfe/pull/1227>`_).
* ``openfe gather`` prints warnings for all results JSONs whose simulations have failed or are otherwise invalid  (`PR #1227 <https://github.com/OpenFreeEnergy/openfe/pull/1227>`_ ).
* ``openfe gather`` now throws an error up-front if no valid results are provided, instead of returning an empty table (`PR #1245 <https://github.com/OpenFreeEnergy/openfe/pull/1245>`_).

**Changed:**

* Improved formatting of ``openfe gather`` output tables. Use ``--tsv`` to instead view the raw tsv formatted output (this was the default behavior as of v1.3.x) (`PR #1246 <https://github.com/OpenFreeEnergy/openfe/pull/1246>`_).
* Improved responsiveness of several CLI commands (`PR #1254 <https://github.com/OpenFreeEnergy/openfe/pull/1254>`_).


v1.3.1
====================
Bugfix release - Improved error handling and code cleanup.

We are also dropping official support for MacOSX-x86_64.
Any platform-specific bugs will be addressed when possible, but as a low priority.

**Added:**

* ``openfe gather`` now detects failed simulations up-front and prints warnings to stdout (`PR #1207 <https://github.com/OpenFreeEnergy/openfe/pull/1207>`_).

**Changed:**

* Temporarily disabled bootstrap uncertainties in forward/reverse analysis due to solver loop issues when dealing with too small a set of samples (`PR #1174 <https://github.com/OpenFreeEnergy/openfe/pull/1174>`_).

**Removed:**

* Dropped official support for MacOSX-x86_64. Any platform-specific bugs will be addressed when possible, but as a low priority.
* Unused trajectory handling code was removed from ``openfe.utils``, please use ``openfe-analysis`` instead (`PR #1182 <https://github.com/OpenFreeEnergy/openfe/pull/1182>`_).

**Fixed:**

* Fixed `issue #1178 <https://github.com/OpenFreeEnergy/openfe/issues/1178>`_ -- The GPU system probe is now more robust to different ways the ``nvidia-smi`` command can fail (`PR #1186 <https://github.com/OpenFreeEnergy/openfe/pull/1186>`_)
* Fixed bug where openmm protocols using default settings would re-load from JSON as a different gufe key due to unit name string representation discrepancies (`PR #1210 <https://github.com/OpenFreeEnergy/openfe/pull/1210>`_)


v1.3.0
====================

**Added:**

* Added CLI support for ``generate_lomap_network``. This option can be specified as a `YAML-defined setting <https://docs.openfree.energy/en/stable/guide/cli/cli_yaml.html>`_
* Added ``--n-protocol-repeats`` CLI option to allow user-defined number of repeats per quickrun execution. This allows for parallelizing execution of repeats by setting ``--n-protocol-repeats=1`` and calling ``quickrun`` on the same input file multiple times.
* Added a new CLI command (``charge-molecules``) to bulk assign partial charges to molecules `PR#1068 <https://github.com/OpenFreeEnergy/openfe/pull/1068>`_
* CLI setup will raise warnings for unsupported top-level YAML fields.
* OpenMMEngineSettings now has a `gpu_device_index` attribute allowing users to pass through a list of ``ints`` to select the GPU devices to run their simulations on.
* Add support for variable position/velocity trajectory writing.
* ``openfe gather`` now supports replicates that have been submitted in parallel across separate directories.

**Changed:**

* Networks planned using the CLI will now automatically use an extended protocol for transformations involving a net charge change `PR#1053 <https://github.com/OpenFreeEnergy/openfe/pull/1053>`_
* The ``plan-rhfe-network`` and ``plan-rbfe-network`` CLI commands will now assign partial charges before planning the network if charges are not present, the charge assignment method can be controlled via the yaml settings file `PR#1068 <https://github.com/OpenFreeEnergy/openfe/pull/1068>`_
* `openfe.protocols.openmm_rfe._rfe_utils.compute` has been moved to `openfe.protocols.openmm_utils.omm_compute`.
* ``openfe gather`` now includes *all* edges with missing runs (instead of just the first failing edge) when raising a "missing runs" error.
* ``openfe quickrun`` now creates the parent directory as-needed for user-defined output json paths (``-o``).
* The MBAR bootstrap (1000 iterations) error is used to estimate protocol uncertainty instead of the statistical uncertainty (one standard deviation) and pymbar3 is no longer supported `PR#1077 <https://github.com/OpenFreeEnergy/openfe/pull/1077>`_
* CLI network planners' default names use prefixes `rbfe_` or `rhfe_` , instead of `easy_rbfe` or `easy_rhfe`, to simplify default transformation names.

**Removed:**

* openfe is no longer tested against macos-12. macos support is, for now, limited to osx-arm64 (macos-14+).

**Fixed:**

* ``openfe quickrun`` now creates the parent directory as-needed for user-defined output json paths (``-o``).
* OpenMM CPU vacuum calculations now enforce the use of a single CPU to avoid large performance losses.



v1.2.0
====================

**Added:**

* New `cookbook featuring bespokefit <https://docs.openfree.energy/en/stable/cookbook/bespoke_parameters.html>`_

**Fixed:**

* Improved responsiveness of CLI calls
* Fixed bug where `openfe gather --report raw` was only including first replicates.



v1.1.0
====================

**Added:**

* Extended system solvation tooling, including support for; non-cubic boxes,
  explicitly defining the number of waters added, the box vectors, and box size
  as supported by `Modeller.addSolvent` in OpenMM 8.0 and above.

**Changed:**

* Improved documentation of the OpenMMSolvationSettings.
* The `PersesAtomMapper` now uses openff.units inline with the rest of the package.
* Structural analysis data is no longer written to `structural_analysis.json`
  but rather a 32bit numpy compressed file named `structural_analysis.npz`
  (`PR #937 <https://github.com/OpenFreeEnergy/openfe/pull/937>`_).
* Structural analysis array data is no longer directly returned in the
  RelativeHybridTopologyProtocol result dictionary. Instead it should
  be accessed from the serialized NPZ file `structural_analysis.npz`.
  The `structural_analysis` key now contains a path to the NPZ file,
  if the structural analysis did not fail (the `structural_analysis_error`
  key will instead be present on failure) (`PR #937 <https://github.com/OpenFreeEnergy/openfe/pull/937>`_).
* Add duecredit citations for pymbar when calling
  `openfe.protocols.openmm_utils.multistate_analysis`.

**Fixed:**

* 2D RMSD plotting now allows for fewer than 5 states (`PR #896 <https://github.com/OpenFreeEnergy/openfe/pull/896>`_).
* 2D RMSD plotting no longer draws empty axes when
  the number of states - 1 is not divisible by 4 (`PR #896 <https://github.com/OpenFreeEnergy/openfe/pull/896>`_).
* The RelativeHybridTopologyProtocol result unit is now much smaller,
  due to the removal of structural analysis data (`PR #937 <https://github.com/OpenFreeEnergy/openfe/pull/937>`_).



v1.0.1
====================

**Added:**

* Debug script in devtools to test OpenMM installation.
* Use rever to manage changelog.

**Changed:**

* Updated docs to reference miniforge instead of mambaforge since they are the same now, see https://github.com/conda-forge/miniforge?tab=readme-ov-file#whats-the-difference-between-mambaforge-and-miniforge.
* The LomapAtomMapper defaults have now changed to better reflect real-life usage. Key kwarg changes include; `max3d=1.0` and `shift=False`.

**Fixed:**

* Calling `get_forward_and_reverse_energy_analysis` in the RFE and AFE protocols now results a warning if any results are ``None`` due to MBAR convergence issues.
* Checkpoint interval default value has been set to 250 ps instead of 1 ps.
  This better matches the previous default for openfe versions < 1.0rc
  (See `issue #772 <https://github.com/OpenFreeEnergy/openfe/issues/772>`_ ).




================================================
FILE: docs/Makefile
================================================
# Minimal makefile for Sphinx documentation
#

# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS    ?= -v -W --keep-going
SPHINXBUILD   ?= sphinx-build
SOURCEDIR     = .
BUILDDIR      = _build

# Put it first so that "make" without argument is like "make help".
help:
	@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

.PHONY: help Makefile

# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
	@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)


================================================
FILE: docs/_ext/sass.py
================================================
"""
sphinxcontrib-sass
https://github.com/attakei-lab/sphinxcontrib-sass
Kayuza Takei
Apache 2.0

Modified to:
- Write directly to Sphinx output directory
- Infer targets if not given
- Ensure ``target: Path`` in ``configure_path()``
- Return version number and thread safety from ``setup()``
- Use compressed style by default
- More complete type checking
"""

from os import PathLike
from pathlib import Path
from typing import Optional, Union

import sass
from sphinx.application import Sphinx
from sphinx.environment import BuildEnvironment
from sphinx.util import logging

logger = logging.getLogger(__name__)


def configure_path(conf_dir: str, src: Optional[Union[PathLike, Path]]) -> Path:
    if src is None:
        target = Path(conf_dir)
    else:
        target = Path(src)
    if not target.is_absolute():
        target = Path(conf_dir) / target
    return target


def get_targets(app: Sphinx) -> dict[Path, Path]:
    src_dir = configure_path(app.confdir, app.config.sass_src_dir)
    dst_dir = configure_path(app.outdir, app.config.sass_out_dir)

    if isinstance(app.config.sass_targets, dict):
        targets = app.config.sass_targets
    else:
        targets = {
            path: path.relative_to(src_dir).with_suffix(".css")
            for path in src_dir.glob("**/[!_]*.s[ca]ss")
        }

    return {src_dir / src: dst_dir / dst for src, dst in targets.items()}


def build_sass_sources(app: Sphinx, env: BuildEnvironment):
    logger.debug("Building stylesheet files")
    include_paths = [str(p) for p in app.config.sass_include_paths]
    targets = get_targets(app)
    output_style = app.config.sass_output_style
    # Build css files
    for src, dst in targets.items():
        content = src.read_text()
        css = sass.compile(
            string=content,
            output_style=output_style,
            include_paths=[str(src.parent)] + include_paths,
        )
        dst.parent.mkdir(exist_ok=True, parents=True)
        dst.write_text(css)


def setup(app: Sphinx):
    """
    Setup function for this extension.
    """
    logger.debug(f"Using {__name__}")
    app.add_config_value("sass_include_paths", [], "html")
    app.add_config_value("sass_src_dir", None, "html")
    app.add_config_value("sass_out_dir", None, "html")
    app.add_config_value("sass_targets", None, "html")
    app.add_config_value("sass_output_style", "compressed", "html")
    app.connect("env-updated", build_sass_sources)

    return {
        "version": "0.3.4ofe",
        "parallel_read_safe": True,
        "parallel_write_safe": True,
    }


================================================
FILE: docs/_sass/deflist-flowchart.scss
================================================
:root {
    --arrow-thickness: 4px;
    --arrow-head-size: 7px;
    --arrow-length: 2em;
    --arrow-multiple-gap: 20px;
    --arrow-color: var(--pst-color-text-muted);
    --arrow-fade-dist: 0px;
    --flowchart-def-bg-color: var(--pst-color-surface);
    --flowchart-bg-color: var(--pst-color-background);
    --flowchart-def-border-color: var(--pst-color-border);
    --flowchart-unit-width: 45px;
    --flowchart-spacing: 0.5rem;
    --flowchart-column-gap: calc(1.5 * var(--flowchart-spacing));
    --flowchart-top-label-space: 26px;
}
.arrow.thick {
    --arrow-thickness: 6px;
    --arrow-head-size: 10px;
}

.deflist-flowchart ul,
ul.deflist-flowchart {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  height: 100%;
  grid-column-gap: var(--flowchart-column-gap);
  margin: 0;
  padding: 0;
}

.deflist-flowchart {
  margin: 1em 0;

  p:first-child {
    margin-top: 0;
  }

  p:last-child {
    margin-bottom: 0;
  }

  li,
  li ul
  {
    margin: 0;
    padding: 0;
  }

  li:empty:not([class])
  {
    display: None;
  }

  li {
    list-style: none;
  }

  .arrow-down::after,
  .arrow-up::after,
  .arrow-multiple.arrow-down::before,
  .arrow-multiple.arrow-up::before,
  .arrow-cycle::after,
  .arrow-cycle::before {
    content: "";
  }

  .arrow-down,
  .arrow-up,
  .arrow-cycle
  {
    --arrow-head-size-clamped: calc(min(var(--arrow-head-size), var(--arrow-length) / 2));

    display: flex;
    justify-content: center;
    align-items: center;
    flex-grow: 1;
    min-height: var(--arrow-length);
    width: 100%;
    margin: calc(2 * var(--flowchart-spacing)) auto;
    position: relative;
    z-index: 1;
    padding: calc(var(--arrow-length) / 4) 0;

    &::before, &::after {
      --actual-arrow-length: max(var(--arrow-length), 100%);
      --arrow-tail-gradient:
        linear-gradient(
          45deg,
          transparent calc(50% - var(--arrow-thickness)/2),
          var(--arrow-color) calc(50% - var(--arrow-thickness)/2),
          var(--arrow-color) calc(50% + var(--arrow-thickness)/2),
          transparent calc(50% + var(--arrow-thickness)/2)
        );
      --arrow-head-gradient:
        linear-gradient(
          -45deg,
          var(--arrow-color) var(--arrow-head-size-clamped),
          transparent var(--arrow-head-size-clamped)
        );
      height: calc(var(--actual-arrow-length)/1.4142);
      width: auto;
      aspect-ratio: 1;
      padding: 0;
      display: inline-block;
      transform: rotate(45deg);
      background-image:
        var(--arrow-tail-gradient),
        var(--arrow-head-gradient);
      position: absolute;
      top: 0;
      left: 50%;
      transform-origin: 0 0;
      z-index: -1;
    }

    &.arrow-tail {
      &::before, &::after {
        background-image:
          var(--arrow-tail-gradient);
      }
    }

    > p {
      background: linear-gradient(
        transparent,
        var(--flowchart-bg-color) var(--arrow-fade-dist),
        var(--flowchart-bg-color) calc(100% - var(--arrow-fade-dist)),
        transparent,
      );
      line-height: 1.5;
      z-index: 10;
    }
  }

  .arrow-down:not(.arrow-tail),
  .arrow-cycle {
    padding-bottom: calc(var(--arrow-head-size-clamped) + var(--arrow-length) / 4);
  }

  .arrow-up:not(.arrow-tail),
  .arrow-cycle {
    padding-top: calc(var(--arrow-head-size-clamped) + var(--arrow-length) / 4);
  }

  .arrow-cycle, .arrow-multiple {
    &::after {
      translate: calc(0.5 * var(--arrow-multiple-gap)) 0;
    }
    &::before {
      translate: calc(-0.5 * var(--arrow-multiple-gap)) 0;
    }
  }

  .arrow-up::after,
  .arrow-multiple.arrow-up::before,
  .arrow-cycle::before
  {
    transform: rotate(-135deg);
    translate: 0 calc(var(--actual-arrow-length) + 2 * var(--flowchart-spacing) + var(--arrow-head-size-clamped) / 2);
  }

  .arrow-cycle::before {
    translate:
      calc(-0.5 * var(--arrow-multiple-gap))
      140%;
  }

  .arrow-aside {
    margin-left: calc(8 * var(--arrow-head-size-clamped));
    &::after {
      left: calc(-4 * var(--arrow-head-size-clamped));
    }
  }

  .arrow-multiple-combine {
    &::before {
      content: "";
      width: var(--arrow-multiple-gap);
      border: var(--arrow-thickness) solid var(--arrow-color);
      height: calc(var(--arrow-length) / 2);
      background: var(--flowchart-bg-color);
      transform: none;
      left: auto;
      z-index: 2;
    }

    &.arrow-down {
      padding-top: calc(0.75 * var(--arrow-length) - var(--arrow-head-size-clamped) / 2);
      padding-bottom: calc(0.5 * var(--arrow-head-size-clamped) + 0.25 * var(--arrow-length));
      &::before {
        border-top: 1px solid var(--flowchart-bg-color);
      }
    }

    &.arrow-up {
      &::before {
        border-bottom: 1px solid var(--flowchart-bg-color);
        top: auto;
        bottom: -1px;
      }
    }
  }

  .arrow-tail {
    &.arrow-down {
      margin-bottom: 0;
    }
    &.arrow-up {
      margin-top: 0;
    }
  }

  .arrow-head {
    &.arrow-up {
      margin-bottom: 0;
    }
    &.arrow-down {
      margin-top: 0;
    }
  }

  .arrow-combine, .arrow-combine-left, .arrow-combine-right {
    &.arrow-down.arrow-tail, &.arrow-up.arrow-head {
      --arrow-combine-gradient-angle: 0deg;
      padding-bottom: calc(0.5 * var(--arrow-thickness));
      margin-bottom: calc(-0.5 * var(--arrow-thickness));
    }
    &.arrow-up.arrow-tail, &.arrow-down.arrow-head {
      --arrow-combine-gradient-angle: 180deg;
      padding-top: calc(0.5 * var(--arrow-thickness));
      margin-top: calc(-0.5 * var(--arrow-thickness));
    }
    background-image:
      linear-gradient(
        var(--arrow-combine-gradient-angle),
        var(--arrow-color) var(--arrow-thickness),
        transparent var(--arrow-thickness)
      );
    background-repeat: no-repeat;

    width: calc(max(100% + 2 * var(--flowchart-column-gap), var(--flowchart-unit-width)));
    margin-left: calc(-1 * var(--flowchart-column-gap));

    &.arrow-combine-left, &.arrow-combine-right {
      background-size: 50%;

      &.arrow-multiple {
        background-size: calc(50% + 0.5 * var(--arrow-multiple-gap));
      }
    }

    &.arrow-combine-right {
      background-position-x: 100%;
    }
  }

  > ul > li {
    &.arrow-down,
    &.arrow-up,
    &.arrow-cycle {
      width: calc(100% - var(--flowchart-top-label-space));
      margin-left: 0;
    }
  }
  
  dl {
    display: flex;
    flex-direction: row-reverse;
    margin: 0;
    padding: 0 var(--flowchart-spacing);
  }
  dt {
    display: inline-block;
    writing-mode: vertical-rl;
    margin-top: .25rem;
    flex-grow: 0;
    width: var(--flowchart-top-label-space);
    font-size: 1.1em;
  }
  dd {
    text-align: center;
    position: relative;
    border: 1px solid var(--flowchart-def-border-color);
    border-radius: .25rem;
    margin: 0;
    display: inline-block;
    flex-grow: 1;
    container-type: inline-size;
    container-name: flowchart;
    overflow-x: auto;
  }

  dd dl {
    background-color: var(--flowchart-def-bg-color);
    border-radius: 4px;
    box-shadow: 0 6px 10px 0 rgba(0,0,0,0.14),
                0 1px 18px 0 rgba(0,0,0,0.12),
                0 3px 5px -1px rgba(0,0,0,0.4);
    display: block;
    margin: 0 auto;
    padding: calc(var(--flowchart-spacing) / 2);
    max-width: calc(100cqw - 2 * var(--flowchart-spacing));
    min-width: calc(2 * var(--flowchart-unit-width) + var(--flowchart-column-gap));
  }
  dd dt {
    writing-mode: horizontal-tb;
    display: block;
    margin-top: 0;
    width: unset;
    font-size: unset;
  }
  dd dd {
    border: none;
    display: block;
    container-type: unset;
    overflow-x: unset;
    padding: calc(var(--flowchart-spacing) / 2);
  }

  dd > ul {
    width: fit-content;
    padding: var(--flowchart-spacing);
    margin: 0 auto;
    overflow: hidden;
  }

  dd dd > ul {
    min-width: unset;
    padding: 0;
    margin: 0;
  }

  dl a, a {
    font-weight: bold;
  }

  div.flowchart-sidebyside > ul:only-child {
    display: flex;
    flex-direction: row;
    flex-wrap: nowrap;
    justify-content: space-between;
  }

  .flowchart-spacer {
    height: 100%;
    flex-shrink: 9999;
    min-height: calc(2 * var(--flowchart-spacing))
  }

  .width-1 {
    width: calc(var(--flowchart-unit-width));
  }
  .width-2 {
    width: calc(2 * var(--flowchart-unit-width) + var(--flowchart-column-gap));
  }
  .width-3 {
    width: calc(3 * var(--flowchart-unit-width) + 2 * var(--flowchart-column-gap));
  }
  .width-4 {
    width: calc(4 * var(--flowchart-unit-width) + 3 * var(--flowchart-column-gap));
  }
  .width-5 {
    width: calc(5 * var(--flowchart-unit-width) + 4 * var(--flowchart-column-gap));
  }
  .width-6 {
    width: calc(6 * var(--flowchart-unit-width) + 5 * var(--flowchart-column-gap));
  }
  .width-7 {
    width: calc(7 * var(--flowchart-unit-width) + 6 * var(--flowchart-column-gap));
  }
  .width-8 {
    width: calc(8 * var(--flowchart-unit-width) + 7 * var(--flowchart-column-gap));
  }
  .width-9 {
    width: calc(9 * var(--flowchart-unit-width) + 8 * var(--flowchart-column-gap));
  }
  .width-10 {
    width: calc(10 * var(--flowchart-unit-width) + 9 * var(--flowchart-column-gap));
  }
  li {
    &.width-2,
    &.width-3,
    &.width-4,
    &.width-5,
    &.width-6,
    &.width-7,
    &.width-8,
    &.width-9,
    &.width-10,
    &.width-full {
      > dl {
        max-width: unset;
      }
    }
  }
}

================================================
FILE: docs/_templates/autosummary/base.rst
================================================
.. title:: {{ objname }}

.. currentmodule:: {{ module }}

.. auto{{ objtype }}:: {{ objname }}


================================================
FILE: docs/_templates/autosummary/class.rst
================================================
.. title:: {{ objname }}

.. currentmodule:: {{ module }}

.. auto{{ objtype }}:: {{ objname }}


================================================
FILE: docs/conf.py
================================================
# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html

# -- Path setup --------------------------------------------------------------

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
import os
import sys
from importlib.metadata import version
from inspect import cleandoc
from pathlib import Path

import git
import nbformat
import nbsphinx
from packaging.version import parse

sys.path.insert(0, os.path.abspath("../"))


os.environ["SPHINX"] = "True"

# -- Project information -----------------------------------------------------

project = "OpenFE"
copyright = "2022, The OpenFE Development Team"
author = "The OpenFE Development Team"
# don't include patch version (https://github.com/OpenFreeEnergy/openfe/issues/1261)
version = f"{parse(version('openfe')).major}.{parse(version('openfe')).minor}"

# -- General configuration ---------------------------------------------------

# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
    "sphinx.ext.autodoc",
    "sphinx.ext.napoleon",
    "sphinx_click.ext",
    "sphinxcontrib.autodoc_pydantic",
    "sphinx_toolbox.collapse",
    "sphinx.ext.autosectionlabel",
    "sphinx_design",
    "sphinx.ext.intersphinx",
    "sphinx.ext.autosummary",
    "docs._ext.sass",
    "myst_parser",
    "nbsphinx",
    "nbsphinx_link",
    "sphinx.ext.mathjax",
]
suppress_warnings = ["config.cache"]  # https://github.com/sphinx-doc/sphinx/issues/12300

intersphinx_mapping = {
    "python": ("https://docs.python.org/3.9", None),
    "numpy": ("https://numpy.org/doc/stable", None),
    "scikit.learn": ("https://scikit-learn.org/stable", None),
    "openmm": ("https://docs.openmm.org/latest/api-python/", None),
    "rdkit": ("https://www.rdkit.org/docs", None),
    "openeye": ("https://docs.eyesopen.com/toolkits/python/", None),
    "mdtraj": ("https://www.mdtraj.org/1.9.5/", None),
    "openff.units": ("https://docs.openforcefield.org/projects/units/en/stable", None),
    "gufe": ("https://gufe.openfree.energy/en/latest/", None),
}

autoclass_content = "both"
# Make sure labels are unique
# https://www.sphinx-doc.org/en/master/usage/extensions/autosectionlabel.html#confval-autosectionlabel_prefix_document
autosectionlabel_prefix_document = True

autodoc_pydantic_model_show_json = False

autodoc_default_options = {
    "members": True,
    "member-order": "bysource",
    "inherited-members": "GufeTokenizable,BaseModel",
    "undoc-members": True,
    "special-members": "__call__",
}
toc_object_entries_show_parents = "hide"

# Add any paths that contain templates here, relative to this directory.
templates_path = ["_templates"]

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = [
    "_build",
    "**/Thumbs.db",
    "**/.DS_Store",
    "_ext",
    "_sass",
    "**/README.md",
    "ExampleNotebooks",
]

autodoc_mock_imports = [
    "cinnabar",
    "dill",
    "MDAnalysis",
    "matplotlib",
    "mdtraj",
    "openfe_analysis",
    "openmmforcefields",
    "openmmtools",
    "pymbar",
    "openff.interchange",
    "openmmforcefields",
    "psutil",
    "py3Dmol",
    "zstandard",
]

# Extensions for the myst parser
myst_enable_extensions = [
    "dollarmath",
    "colon_fence",
    "smartquotes",
    "replacements",
    "deflist",
    "attrs_inline",
]
myst_heading_anchors = 3

# -- Options for HTML output -------------------------------------------------

# The theme to use for HTML and HTML Help pages.  See the documentation for
# a list of builtin themes.
#
html_theme = "ofe_sphinx_theme"
html_theme_options = {
    "logo": {"text": "OpenFE docs"},
    "icon_links": [
        {
            "name": "GitHub",
            "url": "https://github.com/OpenFreeEnergy/openfe",
            "icon": "fa-brands fa-square-github",
            "type": "fontawesome",
        }
    ],
    "accent_color": "cantina-purple",
    "navigation_with_keys": False,
}
html_logo = "_static/OFE-color-icon.svg"
html_favicon = "_static/OFE-color-icon.svg"
# temporary fix, see https://github.com/pydata/pydata-sphinx-theme/issues/1662
html_sidebars = {
    "installation": [],
    "CHANGELOG": [],
}
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
# html_static_path = ['_static']


# replace macros
rst_prolog = """
.. |rdkit.mol| replace:: :class:`rdkit.Chem.rdchem.Mol`
"""

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ["_static"]
html_css_files = [
    "css/custom.css",
    "css/custom-api.css",
    "css/deflist-flowchart.css",
]

# custom-api.css is compiled from custom-api.scss
sass_src_dir = "_sass"
sass_out_dir = "_static/css"

# Clone or update ExampleNotebooks
example_notebooks_path = Path("ExampleNotebooks")
try:
    if example_notebooks_path.exists():
        repo = git.Repo(example_notebooks_path)
        try:
            repo.remote("origin").pull()
        except git.exc.GitCommandError:
            # cannot pull if on a tag
            pass
    else:
        repo = git.Repo.clone_from(
            "https://github.com/OpenFreeEnergy/ExampleNotebooks.git",
            branch="2026.04.28",
            to_path=example_notebooks_path,
        )
except Exception as e:
    from sphinx.util.logging import getLogger

    filename = e.__traceback__.tb_frame.f_code.co_filename
    lineno = e.__traceback__.tb_lineno
    getLogger("sphinx.ext.openfe_git").warning(
        f"Getting ExampleNotebooks failed in {filename} line {lineno}: {e}"
    )


# First, create links at top of notebook pages
# All notebooks are in ExampleNotebooks repo, so link to that
# Finally, add sphinx reference anchor in prolog so that we can make refs
nbsphinx_prolog = cleandoc(r"""
    {%- set gh_repo = "OpenFreeEnergy/ExampleNotebooks" -%}
    {%- set gh_branch = "main" -%}
    {%- set path = env.doc2path(env.docname, base=None) -%}
    {%- if path.endswith(".nblink") -%}
        {%- set path = env.metadata[env.docname]["nbsphinx-link-target"] -%}
    {%- endif -%}
    {%- if path.startswith("ExampleNotebooks/") -%}
        {%- set path = path.replace("ExampleNotebooks/", "", 1) -%}
    {%- endif -%}
    {%- set gh_url =
        "https://www.github.com/"
        ~ gh_repo
        ~ "/blob/"
        ~ gh_branch
        ~ "/"
        ~ path
    -%}
    {%- set dl_url =
        "https://raw.githubusercontent.com/"
        ~ gh_repo
        ~ "/"
        ~ gh_branch
        ~ "/"
        ~ path
    -%}

    .. container:: ofe-top-of-notebook

        .. button-link:: {{gh_url}}
            :color: primary
            :shadow:
            :outline:

            :octicon:`mark-github` View on GitHub

        .. button-link:: {{dl_url}}
            :color: primary
            :shadow:
            :outline:

            :octicon:`download` Download Notebook

    .. _{{ env.doc2path(env.docname, base=None) }}:
""")


================================================
FILE: docs/cookbook/bespoke_parameters.nblink
================================================
{
   "path": "../ExampleNotebooks/cookbook/bespoke_parameters_showcase.ipynb"
}

================================================
FILE: docs/cookbook/choose_protocol.nblink
================================================
{
   "path": "../ExampleNotebooks/cookbook/choose_protocol.ipynb"
}


================================================
FILE: docs/cookbook/create_alchemical_network.nblink
================================================
{
   "path": "../ExampleNotebooks/cookbook/create_alchemical_network.ipynb"
}


================================================
FILE: docs/cookbook/dumping_transformation.rst
================================================
.. _dumping_transformations:

Dumping a ``Transformation`` to JSON
====================================

If you're trying to run a full campaign of simulations representing an
alchemical network, we generally recommend saving objects using our storage
tools, when avoids saving duplicate information to disk.

.. TODO: add links to storage tools once they're complete

However, there are situations where it is reasonable to serialize a single
:class:`.Transformation`. For example, this can be useful when trying to
compare results run on different machines. This also provides a trivial way
for a user to run edges in parallel, if they don't want to use the more
sophisticated techniques we have developed.

For these cases, we have made it very easy for a user to dump a
transformation to JSON. Simply use the method
:meth:`.Transformation.to_json`. For example:

.. code::

    transformation.to_json("mytransformation.json")

When you do dump a single transformation, it can be reloaded into memory
with the :meth:`.Transformation.from_json` method:

.. code::

    transformation = Transformation.from_json("mytransformation.json")

Once you've saved to it JSON, you can also run this transformation with the
``openfe`` command line tool's :ref:`cli_quickrun`, e.g.:

.. code:: bash

    $ openfe quickrun mytransformation.json -d dir_for_files -o output.json


================================================
FILE: docs/cookbook/generate_ligand_network.nblink
================================================
{
   "path": "../ExampleNotebooks/cookbook/generate_ligand_network.ipynb"
}


================================================
FILE: docs/cookbook/hand_write_ligand_network.nblink
================================================
{
   "path": "../ExampleNotebooks/cookbook/hand_write_ligand_network.ipynb"
}


================================================
FILE: docs/cookbook/index.rst
================================================
.. _cookbooks:

Cookbook
========

This section describes common tasks involving the OpenFE Python API.

The :any:`OpenFE CLI<cli-reference>` provides a simple way to perform the most common procedures for free energy calculations, but does not provide much flexibility for fine-tuning your approach or combining OpenFE with other tools. The :any:`Python API<api>` allows that flexibility, but using it is more complex. This cookbook breaks down common steps that would be implemented in Python to help navigate that complexity.

.. note:: This section is a work-in-progress.

.. module:: openfe
    :noindex:

The Basic Workflow
------------------

The typical way to use the Python API is to load a number of molecules you want to calculate free energies of, construct a :class:`LigandNetwork` connecting them in an efficient way, and then combine that with information for how each ligand should be simulated to construct an :class:`AlchemicalNetwork`, which specifies the entire simulation campaign. This provides a lot of flexibility in how molecules are specified, mapped, connected, and simulated, without exposing a great deal of complexity. OpenFE recommends this workflow for most users.

.. container:: deflist-flowchart

    * Setup
        - .. container:: flowchart-sidebyside

            -   -
                    .. rst-class:: flowchart-spacer
                -

                    .. rst-class:: arrow-down arrow-from-nothing
                - :any:`choose_protocol`

                - :class:`Protocol`
                    Simulation procedure for an alchemic mutation.

                    .. rst-class:: arrow-down arrow-tail arrow-combine-right
                -

            -   -
                    .. rst-class:: width-8
                -  Chemical component definition
                    SDF, PDB, RDKit, OpenFF Molecule, solvent spec, etc.

                - .. container:: flowchart-sidebyside

                    - .. rst-class:: width-3

                        -

                            .. rst-class:: arrow-down arrow-multiple
                        - :any:`Loading proteins`, :any:`Defining solvents`

                        - :class:`SolventComponent` and :class:`ProteinComponent`
                            Other chemical components needed to simulate the ligand.

                            .. rst-class:: arrow-down arrow-multiple arrow-tail arrow-combine
                        -

                    -   - .. container:: flowchart-sidebyside

                            - .. rst-class:: width-5

                                -
                                    .. rst-class:: arrow-down arrow-multiple
                                - :any:`Loading small molecules`


                                - :class:`SmallMoleculeComponent`
                                    The ligands that will be mutated.

                            - .. rst-class:: width-3

                                -
                                    .. rst-class:: flowchart-spacer
                                -

                                - Orion/FEP+
                                    Network from another tool.


                        - .. container:: flowchart-sidebyside

                            - .. rst-class:: width-2

                                -
                                    .. rst-class:: arrow-down arrow-multiple
                                - :any:`generate_ligand_network`

                            - .. rst-class:: width-2

                                -
                                    .. rst-class:: arrow-down arrow-multiple
                                - :any:`hand_write_ligand_network`

                            - .. rst-class:: width-1

                                -
                                    .. rst-class:: arrow-down arrow-tail arrow-multiple arrow-combine-right
                                -

                                    .. rst-class:: flowchart-spacer
                                -

                            - .. rst-class:: width-3

                                -
                                    .. rst-class:: arrow-down arrow-tail arrow-combine-left
                                -

                                    .. rst-class:: arrow-down arrow-head flowchart-spacer
                                - :any:`network_from_orion_fepp`

                        - :class:`LigandNetwork <openfe.setup.LigandNetwork>`
                            A network of ligand transformations.

                        - .. container:: flowchart-sidebyside

                            -   -
                                    .. rst-class:: arrow-down arrow-tail arrow-combine-left width-4
                                -

                            -   -
                                    .. rst-class:: arrow-cycle width-4
                                -

                                - :any:`ligandnetwork_vis`


            .. rst-class:: arrow-down arrow-head
        - :any:`create_alchemical_network`

        - :class:`AlchemicalNetwork`
            A complete simulation campaign.

      .. rst-class:: arrow-down
    * :any:`dumping_transformations`

    * Run
        - :any:`openfe quickrun <cli_quickrun>`
            OpenFE recommends using the ``openfe quickrun`` CLI command to execute a transformation.

      .. rst-class:: arrow-down
    *

    * Gather
        - :any:`openfe gather <cli_gather>`
            OpenFE recommends using the ``openfe gather`` CLI command to collect the results of a transformation.

List of Cookbooks
-----------------

.. toctree::
    :maxdepth: 1

    loading_molecules
    dumping_transformation
    jq_inspection
    choose_protocol
    generate_ligand_network
    rfe_alchemical_planners
    network_from_orion_fepp
    hand_write_ligand_network
    ligandnetwork_vis
    create_alchemical_network
    user_charges
    bespoke_parameters

    


================================================
FILE: docs/cookbook/jq_inspection.rst
================================================
.. _jq_inspection:

Using ``jq`` to inspect OpenFE JSONs
==============================================
Sometimes you may want to get a sense of the contents of JSON files, but the files are too unwieldy to inspect one-by-one in a code editor.

`jq <https://github.com/jqlang/jq>`_ is a helpful command-line tool that we recommend for for quickly inspecting JSON files.

Below is a common use-case to get you started, but you can do much more by checking out the `jq manual <https://jqlang.org/manual/>`_.

To view all the top-level JSON keys, use ``jq "keys" filename.json``, for example with a results JSON from the tutorial:

.. code:: bash

    $ jq "keys" rbfe_lig_ejm_46_solvent_lig_jmc_28_solvent.json
    [
    "estimate",
    "protocol_result",
    "uncertainty",
    "unit_results"
    ]

.. note::

    You can use ``"keys[]"`` instead of ``"keys"`` for a cleaner output.

Now that you know ``estimate`` is at the top-level of the JSON, you can use the following pattern to see the next level of keys:

.. code:: bash

    $ jq ".estimate | keys " rbfe_lig_ejm_46_solvent_lig_jmc_28_solvent.json
    {
    "magnitude",
    "unit":,
    ":is_custom:":,
    "pint_unit_registry":
    }


If you want to show all the keys _and_ their values, simply omit ``| key`` from the query:

.. code:: bash

    $ jq ".estimate" rbfe_lig_ejm_46_solvent_lig_jmc_28_solvent.json
    {
    "magnitude": 23.347074789078682,
    "unit": "kilocalorie / mole",
    ":is_custom:": true,
    "pint_unit_registry": "openff_units"
    }


This can be very helpful for quickly checking results for many files, for example:

.. code:: bash

    $ jq ".estimate.magnitude" rbfe*.json
    -14.925911852820793
    -40.72063957254803
    -27.76541486479537
    -16.023754604070007
    -57.38608716292447
    -15.748326155729705
    -39.933880531487326
    -27.780933075807425
    -16.76023951588401
    -58.36294851896545
    -19.038006312251575
    -20.26856586311034
    17.338257573349775
    15.775784163095102
    23.134622420900932
    17.071712542470248
    15.873122071409249
    23.347074789078682


================================================
FILE: docs/cookbook/ligandnetwork_vis.nblink
================================================
{
   "path": "../ExampleNotebooks/cookbook/ligandnetwork_vis.ipynb"
}


================================================
FILE: docs/cookbook/loading_molecules.nblink
================================================
{
   "path": "../ExampleNotebooks/cookbook/loading_molecules.ipynb"
}


================================================
FILE: docs/cookbook/network_from_orion_fepp.nblink
================================================
{
   "path": "../ExampleNotebooks/cookbook/network_from_orion_fepp.ipynb"
}


================================================
FILE: docs/cookbook/rfe_alchemical_planners.nblink
================================================
{
   "path": "../ExampleNotebooks/cookbook/rfe_alchemical_planners.ipynb"
}


================================================
FILE: docs/cookbook/user_charges.nblink
================================================
{
   "path": "../ExampleNotebooks/cookbook/user_charges.ipynb"
}


================================================
FILE: docs/environment.yaml
================================================
name: openfe-docs
channels:
- https://conda.anaconda.org/conda-forge

# explicit pins to speed up build:
dependencies:
- autodoc-pydantic >= 2.1
- docutils == 0.20
- gitpython
- libsass
- myst-parser
- nbsphinx
- nbsphinx-link
- openff-toolkit-base == 0.17.0
- openff-units == 0.3.1
- openmm == 8.3.1
- packaging
- pip
- plugcli >= 0.2.1
- python
- pydantic >=2.0.0, <2.12.0  # https://github.com/openforcefield/openff-interchange/issues/1346
- sphinx ==7.2.6 # TODO: debug "duplicate object" warning with later versions
- sphinx-click
- sphinx-design
- sphinx-toolbox
- threadpoolctl
- tqdm
- pip:
  - git+https://github.com/OpenFreeEnergy/gufe@main
  - git+https://github.com/OpenFreeEnergy/ofe-sphinx-theme@v0.3.1
  # pip install these so that we can make sure docs build on main while these packages' docs are under development
  - git+https://github.com/OpenFreeEnergy/kartograf@main
  - git+https://github.com/OpenFreeEnergy/konnektor@main
  - git+https://github.com/OpenFreeEnergy/lomap@main

# These are added automatically by RTD, so we include them here
# for a consistent environment.
- mock
- pillow
# - sphinx
# - sphinx_rtd_theme


================================================
FILE: docs/guide/cli/cli_basics.rst
================================================
CLI basics
==========

The ``openfe`` command consists of several subcommands. This is similar to
tools like ``gmx``, which has subcommands like ``gmx mdrun``, or ``conda``,
which has subcommands like ``conda install``.

To get a list of the subcommands and their descriptions, call ``openfe`` (or
``openfe -h``):

.. TODO autogenerate using sphinxcontrib-programoutput

.. code:: none

    Usage: openfe [OPTIONS] COMMAND [ARGS]...

      This is the command line tool to provide easy access to functionality from
      the OpenFE Python library.

    Options:
      --version   Show the version and exit.
      --log PATH  logging configuration file
      -h, --help  Show this message and exit.

    Network Planning Commands:
      plan-rhfe-network    Plan a relative hydration free energy network, saved as
                          JSON files for the quickrun command.
      plan-rbfe-network    Plan a relative binding free energy network, saved as
                          JSON files for the quickrun command.
      view-ligand-network  Visualize a ligand network

    Quickrun Executor Commands:
      gather    Gather result jsons for network of RFE results into a TSV file
      quickrun  Run a given transformation, saved as a JSON file

    Miscellaneous Commands:
      fetch             Fetch tutorial or other resource.
      charge-molecules  Generate partial charges for a set of molecules.
      test              Run the OpenFE test suite

The ``--log`` option takes a logging configuration file and sets that
logging behavior. If you use it, it must come before the subcommand name.

You can find out more about each subcommand by putting ``--help`` *after*
the subcommand name, e.g., ``openfe quickrun --help``, which returns

.. code:: none

    Usage: openfe quickrun [OPTIONS] TRANSFORMATION

      Run the transformation (edge) in the given JSON file.

      Simulation JSON files can be created with the :ref:`cli_plan-rbfe-network`
      or from Python a :class:`.Transformation` can be saved using its to_json
      method::

          transformation.to_json("filename.json")

      That will save a JSON file suitable to be input for this command.

      Running this command will execute the simulation defined in the JSON file,
      creating a directory for each individual task (``Unit``) in the workflow.
      For example, when running the OpenMM HREX Protocol a directory will be
      created for each repeat of the sampling process (by default 3).

    Options:
      -d, --work-dir DIRECTORY  Directory in which to store files in (defaults to
                                current directory). If the directory does not
                                exist, it will be created at runtime.
      -o PATH                   Filepath at which to create and write the JSON-
                                formatted results.
      --resume                  Attempt to resume this transformation's execution
                                using the cache.
      -h, --help                Show this message and exit.

For more details on various commands, see the :ref:`cli-reference`.


================================================
FILE: docs/guide/cli/cli_yaml.rst
================================================
.. _userguide_cli_yaml_interface:

Customising CLI planning with YAML settings
===========================================

The planning commands in the CLI can be made more powerful by supplying
YAML-formatted files to customise the planning algorithms.

This settings file has a series of sections for customising the different algorithms.
For example, the settings file which re-specifies the default behaviour would look like ::

  network:
    method: generate_minimal_spanning_network
  mapper:
    method: LomapAtomMapper
    settings:
      time: 1
      threed: True
      max3d: 0.95
      element_change: True
  partial_charge:
    method: am1bcc
    settings:
      off_toolkit_backend: ambertools

The name of the algorithm is given behind the ``method:`` key and the arguments to the
algorithm are then optionally given behind the ``settings:`` key.
All sections of the file ``network:``, ``mapper:``  and ``partial_charge:`` are optional.

The settings YAML file is then provided to the ``-s`` option of ``openfe plan-rbfe-network``: ::

  openfe plan-rbfe-network -M molecules.sdf -p protein.pdb -s settings.yaml

Customising the atom mapper
---------------------------

There is a choice to be made as to which atom mapper is used,
currently included are the :class:`.LomapAtomMapper` and the :class:`.KartografAtomMapper` (full details in the `Kartograf documentation`_.)

.. _Kartograf documentation: https://kartograf.readthedocs.io/en/latest/api/kartograf.mappers.html#kartograf.atom_mapper.KartografAtomMapper

For example, to switch to using the ``Kartograf`` atom mapper, this settings YAML could be used ::

  mapper:
    method: KartografAtomMapper
    settings:
      atom_max_distance: 0.95
      atom_map_hydrogens: True
      map_hydrogens_on_hydrogens_only: False
      map_exact_ring_matches_only: True


Customising the network planner
-------------------------------

There are a variety of network planning options available, including
:func:`.generate_radial_network`,
:func:`.generate_minimal_spanning_network`, and
:func:`.generate_minimal_redundant_network`.

For example, to plan a radial network using a ligand called 'CHEMBL1078774' as the central ligand, this settings YAML
could be given ::

  network:
    method: generate_radial_network
    settings:
      central_ligand: CHEMBL1078774

Where the required ``central_ligand`` argument has been passed inside the ``settings:`` section.

Note that there is a subtle distinction when ligand names could be interpreted as integers.
To select the first ligand, the **integer** 0 can be given ::

  network:
    method: generate_radial_network
    settings:
      central_ligand: 0

Whereas if we wanted to specify the ligand named "0", we would instead explicitly pass this as **a string** to the YAML
settings file ::

  network:
    method: generate_radial_network
    settings:
      central_ligand: '0'

Customising the partial charge generation
-----------------------------------------

There are a range of partial charge generation schemes available, including

* ``am1bcc``
* ``am1bccelf10`` (only possible if ``off_toolkit_backend`` in settings is set to ``openeye``)
* ``nagl`` (must have ``openff-nagl`` installed)
* ``espaloma`` (must have ``espaloma_charge`` installed)

The following settings can also be set

* ``off_toolkit_backend`` The backend to use for partial charge generation. Choose from  ``ambertools`` (default), ``openeye`` or ``rdkit``.
* ``number_of_conformers`` The number of conformers to use for partial charge generation. If unset (default), the input conformer will be used.
* ``nagl_model``: The NAGL model to use. If unset (default), the latest available production charge model will be used.

For example, to generate the partial charges using the ``am1bccelf10`` method from ``openeye`` the following should be added to the YAML settings file ::

 partial_charge:
   method: am1bccelf10
   settings:
     off_toolkit_backend: openeye

For more information on the different options, please refer to the :class:`.OpenFFPartialChargeSettings`.


================================================
FILE: docs/guide/cli/index.rst
================================================
.. _userguide_cli_interface:

CLI Interface
=============

In addition to the powerful Python API, OpenFE provides a simple command
line interface to facilitate some more common (and less complicated) tasks.
The Python API tries to be as easy to use as possible, but the CLI provides
wrappers around some parts of the Python API to make it easier to integrate
into non-Python workflows.


.. toctree::
   cli_basics
   cli_yaml


================================================
FILE: docs/guide/execution/execution_theory.rst
================================================
.. _userguide_execution_theory:

Protocols and the Execution Model Theory
========================================

Protocols in OpenFE are built on a flexible execution model. 
Result objects are shaped by this model, and therefore some basic
background on it can be useful when looking into the details of simulation
results. In general, most users don't need to work with the details of the
execution model, but the general ideas can be useful.

.. TODO figure showing an example dag

Each protocol involves a number of steps (called ``ProtocolUnit``\ s) which occur in
some order. Formally, this is described by a directed acyclic graph (DAG),
so the collection of steps to run is called a ``ProtocolDAG``. A
:class:`.Protocol` creates the ``ProtocolDAG``, and a single ``ProtocolDAG``
should give information necessary to obtain an estimate of the desired
thermodynamic observable. Over the course of a campaign, a single
:class:`.Protocol` may create multiple ``ProtocolDAG``\ s, e.g., to extend a
simulation. NB: While independent runs can be created as separate
``ProtocolDAG``\ s, the recommend way to do independent runs is as a
``repeats`` part of the settings for the protocol, which puts the
independent runs in a single ``ProtocolDAG``.

.. TODO review recommendation for repeats in context of NEQ protocol

There are results objects at each level of this: so the
:class:`.ProtocolResult` is associated with the :class:`.Protocol`. Just as
the :class:`.Protocol` may create one or more ``ProtocolDAG``\ s, the
:class:`.ProtocolResult` will be made from one or more
:class:`.ProtocolDAGResult`\ s.  Finally, each :class:`.ProtocolDAGResult`
may carry information about multiple :class:`.ProtocolUnitResult`\ s, just a
single ``ProtocolDAG`` may involve multiple ``ProtocolUnit``\ s.

.. TODO FUTURE: figure showing the relations of protocol objects and result
   objects

.. TODO FUTURE: add information about scratch/shared/permanent storage
   once that becomes relevant


================================================
FILE: docs/guide/execution/index.rst
================================================
.. _userguide_execution:

Execution
=========

With a :class:`.Transformation` defined, the next step is to execute this.
The easiest way to run it is to use the :ref:`quickrun CLI tool <userguide_quickrun>`.
More advanced options are available through first considering the
:ref:`theory of the execution model<userguide_execution_theory>`
then :ref:`reading on the available Python functions<reference_execution>`.

.. toctree::
   quickrun_execution
   execution_theory


================================================
FILE: docs/guide/execution/quickrun_execution.rst
================================================
.. _userguide_quickrun:

Execution with Quickrun
=======================

The planning and preparation of a campaign of alchemical simulations using ``openfe`` is intended to be achievable on a local workstation in a matter of minutes.
The *execution* of these simulations however requires a large amount of computational power, and beyond running single calculations locally, is intended to be distributed across a HPC environment.
Doing this requires storing and sending the details of the simulation from the local workstation to a HPC environment, which can be done via the :func:`.Transformation.to_json` function which :ref:`creates a saved JSON version of the data<dumping_transformations>`.
These serialized JSON files are the currency of executing a campaign of simulations and contain all the information required to execute a single simulation.

To read the ``Transformation`` information and execute the simulation, the command line interface provides the ``openfe quickrun`` command, the full details of which are given in :ref:`the CLI reference section<cli_quickrun>`.


Basic quickrun usage
--------------------

The ``quickrun`` command takes in the ``Transformation`` information represented as JSON, then executes a simulation according to those specifications.
For example, the following command executes a simulation defined by ``transformation.json`` and produces a results file named ``results.json``.

::

  > openfe quickrun transformation.json -d workdir/ -o workdir/results.json

The ``-d`` / ``--work-dir`` flag controls where working files (checkpoints, trajectory data, etc...) are written.
If it is omitted, the current directory will be used.

The ``-o`` flag controls where the results file will be written.
If it is omitted, results are written to a file named ``<transformation_key>_results.json`` in the working directory, where ``<transformation_key>`` is a unique identifier.


Resuming a halted job
---------------------

When ``openfe quickrun`` starts, it saves a plan of the simulation to a cache file before execution begins:

.. code:: bash

    <work-dir>/quickrun_cache/dag-cache-<key>.json

Where ``<key>`` is a unique identifier based on the ``-o`` file path and Transformation.
This cache is automatically removed once the job completes.

If a job is interrupted (e.g. due to a wall-time limit, node failure, or manual cancellation), you can resume the interrupted job by passing the ``--resume`` flag:

.. code:: bash

    > openfe quickrun transformation.json -d workdir/ -o workdir/results.json --resume

The planned simulation cache will be used to identify where in the simulation process it left off and, if supported by the Transformation Protocol, how to resume.

.. note::

    The same ``-d`` / ``--work-dir`` and ``-o`` flag arguments used in the
    original run must be specified so that ``quickrun`` can locate the cache file.

If you pass ``--resume`` but no cache file is found (e.g. the job never started), the following warning is printed and a fresh execution begins.

.. code:: bash

    openfe quickrun was run with --resume, but no cached results found at
    <path-to-cache-file>. Starting new execution.

If the cache file is corrupted (e.g. due to an incomplete write at the moment of interruption), ``quickrun --resume`` will raise an error with instructions to rerun the simulation:

.. code:: bash

    Recovery failed, please remove <work-dir>/quickrun_cache/dag-cache-<key>.json
    before executing a new transformation simulation.

If you do not pass the ``--resume`` flag, the code will detect the partially complete transformation and prevent you from accidentally starting a duplicate run.
The following error will be raised:

.. code:: bash

    Transformation has been started but is incomplete. Please remove
    <work-dir>/quickrun_cache/dag-cache-<key>.json and rerun, or resume
    execution using the ``--resume`` flag.


Executing within a job submission script
----------------------------------------

You may need to submit computational jobs to a queueing engine, such as Slurm.
The ``openfe quickrun`` command can be used within a submission script as follows:

.. code-block:: bash

  #!/bin/bash

  #SBATCH --job-name="openfe job"
  #SBATCH --mem-per-cpu=2G

  # activate an appropriate conda environment, or any "module load" commands required to
  conda activate openfe_env

  openfe quickrun transformation.json -d workdir/ -o workdir/results.json


Parallel execution of repeats with Quickrun
===========================================

Serial execution of multiple repeats of a transformation can be inefficient when simulation times are long.
Higher throughput can be achieved with parallel execution by running one repeat per HPC job.
Most protocols are set up to run three repeats in serial by default, but this can be changed by either:

1. Defining the protocol setting ``protocol_repeats`` - see the :ref:`protocol configuration guide <cookbook/choose_protocol.nblink>` for more details.
2. Using the ``openfe plan-rhfe-network`` (or ``plan-rbfe-network``) command line flag ``--n-protocol-repeats``.

Each transformation can then be executed multiple times via the ``openfe quickrun`` command to produce a set of repeats.
However, **you must use unique results files for each repeat to ensure they don't overwrite each other**.
We recommend using folders named ``results_x`` where x is 0-2 to store the repeated calculations as our :ref:`openfe gather <cli_gather>` command also supports this file structure.

Below is an example of a simple script that will create and submit a separate job script (``\*.job`` named file) for every alchemical transformation (for the simplest SLURM use case) in a network running each repeat in parallel and writing the results to a unique folder:

.. code-block:: bash

   for file in network_setup/transformations/*.json; do
     relpath="${file:30}"  # strip off "network_setup/"
     dirpath=${relpath%.*}  # strip off final ".json"
     jobpath="network_setup/transformations/${dirpath}.job"
     if [ -f "${jobpath}" ]; then
       echo "${jobpath} already exists"
       exit 1
     fi
     for repeat in {0..2}; do
       cmd="openfe quickrun ${file} -o results_${repeat}/${relpath} -d results_${repeat}/${dirpath} --n-protocol-repeats 1"
       echo -e "#!/usr/bin/env bash\n${cmd}" > "${jobpath}"
       sbatch "${jobpath}"
     done
   done

This should result in the following file structure after execution:

::

    results_parallel
    ├── results_0
    │   ├── rbfe_lig_ejm_31_complex_lig_ejm_42_complex.json
    │   ├── shared_HybridTopologyMultiStateAnalysisUnit-5e0825de1dd045818cdc3428205c1cf7_attempt_0
    │   │   ├── forward_reverse_convergence.png
    │   │   ├── ligand_RMSD.png
    │   │   ├── mbar_overlap_matrix.png
    │   │   ├── replica_exchange_matrix.png
    │   │   ├── replica_state_timeseries.png
    │   │   └── structural_analysis.npz
    │   ├── shared_HybridTopologyMultiStateSimulationUnit-144be594cf024cb19152cfe5e0b3fb7d_attempt_0
    │   │   ├── checkpoint.chk
    │   │   ├── simulation.nc
    │   │   └── simulation_real_time_analysis.yaml
    │   └── shared_HybridTopologySetupUnit-01b5afe1972c4e2f9d0943da43b4b19c_attempt_0
    │       ├── A_db.json
    │       ├── B_db.json
    │       ├── hybrid_positions.npy
    │       ├── hybrid_system.pdb
    │       └── hybrid_system.xml.bz2
    ├── results_1
    │   ├── rbfe_lig_ejm_31_complex_lig_ejm_42_complex.json
    │   ├── shared_HybridTopologyMultiStateAnalysisUnit-7986bec616a74929aee85e900535f4a2_attempt_0
    │   │   ├── forward_reverse_convergence.png
    │   │   ├── ligand_RMSD.png
    │   │   ├── mbar_overlap_matrix.png
    │   │   ├── replica_exchange_matrix.png
    │   │   ├── replica_state_timeseries.png
    │   │   └── structural_analysis.npz
    │   ├── shared_HybridTopologyMultiStateSimulationUnit-18eb295b7123444f9ac66ff3caffcab8_attempt_0
    │   │   ├── checkpoint.chk
    │   │   ├── simulation.nc
    │   │   └── simulation_real_time_analysis.yaml
    │   └── shared_HybridTopologySetupUnit-3d8ccb1ef5124bd4ba20e0047aad0b5f_attempt_0
    │       ├── A_db.json
    │       ├── B_db.json
    │       ├── hybrid_positions.npy
    │       ├── hybrid_system.pdb
    │       └── hybrid_system.xml.bz2
    └── results_2
        ├── rbfe_lig_ejm_31_complex_lig_ejm_42_complex.json
        ├── shared_HybridTopologyMultiStateAnalysisUnit-ac5fad8ad1fb49598f80018713dce070_attempt_0
        │   ├── forward_reverse_convergence.png
        │   ├── ligand_RMSD.png
        │   ├── mbar_overlap_matrix.png
        │   ├── replica_exchange_matrix.png
        │   ├── replica_state_timeseries.png
        │   └── structural_analysis.npz
        ├── shared_HybridTopologyMultiStateSimulationUnit-73abea21b423444881bd8f21415c937f_attempt_0
        │   ├── checkpoint.chk
        │   ├── simulation.nc
        │   └── simulation_real_time_analysis.yaml
        └── shared_HybridTopologySetupUnit-79bc9b63321945338a3b69d9f94ee15b_attempt_0
            ├── A_db.json
            ├── B_db.json
            ├── hybrid_positions.npy
            ├── hybrid_system.pdb
            └── hybrid_system.xml.bz2

The results of which can be gathered from the CLI using the ``openfe gather`` command, in this case you should direct
it to the root directory which includes the repeat results and it will automatically collate the information

::

  > openfe gather results_parallel

Optimizing GPU performance with NVIDIA MPS
==========================================

You can further optimize execution of ``openfe quickrun`` using NVIDIA's Multi-Process Service (MPS).
See NVIDIA's documentation on `MPS for OpenFE free energy calculations <https://developer.nvidia.com/blog/maximizing-openmm-molecular-dynamics-throughput-with-nvidia-multi-process-service/?ref=blog.omsf.io#mps_for_openfe_free_energy_calculations>`_ for details.

See Also
--------

- :ref:`userguide_results` - details on inspecting these results.
- :ref:`cli-reference` - full CLI reference for ``openfe quickrun``
- :ref:`rbfe_cli_tutorial` - a tutorial on how to use the CLI to run hybrid topology relative binding free energy calculations.


================================================
FILE: docs/guide/index.rst
================================================
User\ |nbsp|\ Guide
===================

.. toctree::
    :maxdepth: 2

    introduction
    setup/index
    execution/index
    results/index
    cli/index
    protocols/index
    under_the_hood
    troubleshooting

.. |nbsp| unicode:: 0xA0 .. copyright sign
    :trim:

================================================
FILE: docs/guide/introduction.rst
================================================
.. _guide-introduction:

Introduction 
============

Here we present an overview of the workflow for calculating free energies in
OpenFE in the broadest strokes possible. This workflow is reflected in both
the Python API and in the command line interface, and so we have a section
for each.

Workflow overview
-----------------

The overall workflow of OpenFE involves three stages:

1. :ref:`Simulation setup <userguide_setup>`: Defining the simulation campaign you are going to run.
2. :ref:`Execution <userguide_execution>`: Running and performing initial analysis of your
   simulation campaign.
3. :ref:`Gather results <userguide_results>`: Assembling the results from the simulation
   campaign for further analysis.

In many use cases, these stages may be done on different machines. For
example, you are likely to make use of HPC or cloud computing resources to
run the simulation campaign. Because of this, each stage has a defined output which 
is then the input for the next stage:

.. TODO make figure
.. .. figure:: ???
    :alt: Setup -> (AlchemicalNetwork) -> Execution -> (ProtocolResults) -> Gather

    The main stages of a free energy calculation in OpenFE, and the intermediates between them.

The output of the :ref:`simulation setup <userguide_setup>` stage is an :class:`.AlchemicalNetwork`. This contains all
the information about what is being simulated (e.g., what ligands, host proteins, solvation details, etc.) and the
information about how to perform the simulation (the Protocol).

The output of the :ref:`execution <userguide_execution>` stage is the basic results from each edge.
This can depend of the specific analysis intended, but will either involve a
:class:`.ProtocolResult` representing the calculated :math:`\Delta G` for
each edge or the :class:`.ProtocolDAGResult` linked to the data needed to
calculate that :math:`\Delta G`.

The :ref:`gather results <userguide_results>` stage aggregates the individual results for further analysis. For example, the CLI's ``gather`` command will create a
table of the :math:`\Delta G` for each leg.

For more workflow details, see :ref:`under-the-hood`.

.. TODO: Should the CLI workflow be moved to under "CLI Interface"?

CLI Workflow
------------

We have separate CLI commands for each stage of setup, execution, and
gathering results. With the CLI, the Python objects of
:class:`.AlchemicalNetwork` and :class:`.ProtocolResult` are stored to disk
in an intermediate representation between the commands.

.. TODO make figure
.. .. figure:: ???
   :alt: [NetworkPlanner -> AlchemicalNetwork] -> Transformation JSON -> quickrun -> Result JSON -> gather

   The CLI workflow, with intermediates. The setup stage uses a network
   planner to generate the network, before saving each transformation as a
   JSON file.

The commands used to generate an :class:`.AlchemicalNetwork` using the CLI are:

* :ref:`cli_plan-rbfe-network`
* :ref:`cli_plan-rhfe-network`

.. note::

   To ensure a consistent set of partial charges are used for each molecule across different transformations, the CLI
   network planners will now automatically generate charges ahead of planning the network. The partial charge generation
   scheme can be configured using the :ref:`YAML settings <userguide_cli_yaml_interface>`. We also provide tooling to
   generate the partial charges as a separate CLI step which can be run before network planning, see the :ref:`tutorial <charge_molecules_cli_tutorial>`
   for more details.


For example, you can create a relative binding free energy (RBFE) network using

.. code:: bash

    $ openfe plan-rbfe-network -p protein.pdb -M dir_with_sdfs/

This will save the alchemical network represented as a JSON file for each
edge of the :class:`.AlchemicalNetwork` (i.e., each leg of the alchemical cycle).

To run a given transformation, use the :ref:`cli_quickrun`; for example:

.. code:: bash

    $ openfe quickrun mytransformation.json -d dir_for_files -o output.json

In many cases, you will want to create a job script for a queuing system
(e.g., SLURM) that wraps that command. You can do this for all JSON files
from the network planning command with something like this:

.. TODO Link to example here. I think this is waiting on the CLI example
   being merged into example notebooks?

Finally, assuming all results (and only results) are in the `results/` directory,
use the :ref:`cli_gather` to generate a summary table:

.. code:: bash

    $ openfe gather ./results/ -o final_results.tsv

This will output a tab-separated file with the ligand pair, the estimated
:math:`\Delta G` and the uncertainty in that estimate.

The CLI provides a very straightforward user experience that works with the
most simple use cases. For use cases that need more workflow customization,
the Python API makes it relatively straightforward to define exactly the
simulation you want to run. The next sections of this user guide will
illustrate how to customize the behavior to your needs.


================================================
FILE: docs/guide/protocols/absolutebinding.rst
================================================
.. _userguide_abfe_protocol:

Absolute Binding Protocol
=========================

Overview
--------

The :class:`AbsoluteBindingProtocol <.AbsoluteBindingProtocol>` calculates the absolute binding free energy,
which is the free energy difference between a ligand in solution and the ligand bound to a protein.

The absolute binding free energy is calculated through a thermodynamic cycle.
In this cycle, the interactions of the molecule are decoupled, meaning turned off,
using a partial annihilation scheme (see below) both in the solvent and in the complex phases.

Restraints are required to keep the weakly
coupled and fully decoupled ligand in the binding site region and thereby reduce the phase
space that needs to be sampled. In the :class:`AbsoluteBindingProtocol <.AbsoluteBindingProtocol>`
we apply orientational, or Boresch-style, restraints, as described below.

The absolute binding free energy is then obtained via summation of free energy differences along the thermodynamic cycle.

.. figure:: img/abfe-cycle.png
   :scale: 50%

   Thermodynamic cycle for the absolute binding free energy protocol.

Scientific Details
------------------

Orientational restraints
~~~~~~~~~~~~~~~~~~~~~~~~

Orientational, or Boresch-style, restraints are automatically (unless manually specified) applied between three
protein and three ligand atoms using one bond, two angle, and three dihedral restraints.
Reference atoms are picked based on different criteria, such as the root mean squared
fluctuation of the atoms in a short MD simulation, the secondary structure of the protein,
and the distance between atoms, based on heuristics from Baumann et al. [1]_ and Alibay et al. [2]_.
Two strategies for selecting protein atoms are available, either picking atoms that are bonded to each other or that can span multiple residues.
This can be specified using the ``restraint_settings.anchor_finding_strategy`` settings.

Partial annihilation scheme
~~~~~~~~~~~~~~~~~~~~~~~~~~~

In the :class:`.AbsoluteBindingProtocol` the coulombic interactions of the molecule are fully turned off (annihilated).
The Lennard-Jones interactions are instead decoupled, meaning the intermolecular interactions are turned off, keeping the intramolecular Lennard-Jones interactions.

The lambda schedule
~~~~~~~~~~~~~~~~~~~

Molecular interactions are turned off during an alchemical path using a discrete set of lambda windows.
For the transformation in the binding site, the following steps are carried out, starting with the ligand fully interacting in the binding site.

1. Restrain the ligand using orientational restraints.
2. Turn off the electrostatic interactions of the ligand.
3. Decouple Lennard-Jones interactions of the ligand.
4. Release the restraints of the now dummy ligand analytically.

The lambda schedule in the solvent phase is similar to the one in the complex, except that no restraints are applied.
A soft-core potential is applied to the Lennard-Jones potential to avoid instablilites in intermediate lambda windows.
The soft-core potential function from Beutler et al. [3]_ is used by default.
The lambda schedule is defined in the ``lambda_settings`` objects ``lambda_elec``, ``lambda_vdw``, and ``lambda_restraints``.

Simulation overview
~~~~~~~~~~~~~~~~~~~

The :class:`.ProtocolDAG` of the :class:`.AbsoluteBindingProtocol` contains :class:`.ProtocolUnit`\ s from both the complex and solvent transformations.
This means that both legs of the thermodynamic cycle are constructed and run concurrently in the same :class:`.ProtocolDAG`.
This is different from the :class:`.RelativeHybridTopologyProtocol` where the :class:`.ProtocolDAG` only runs a single leg of a thermodynamic cycle.
If multiple ``protocol_repeats`` are run (default: ``protocol_repeats=3``), the :class:`.ProtocolDAG` contains multiple :class:`.ProtocolUnit`\ s of both complex and solvent transformations.

Simulation steps
""""""""""""""""

Each :class:`.ProtocolUnit` (whether complex or solvent) carries out the following steps:

1. Parameterize the system using `OpenMMForceFields <https://github.com/openmm/openmmforcefields>`_ and `Open Force Field <https://github.com/openforcefield/openff-forcefields>`_.
2. Equilibrate the fully interacting system using a short MD simulation using the same approach as the :class:`.PlainMDProtocol` (including rounds of NVT and NPT equilibration).
3. Create an alchemical system.
4. Add orientational restraints to the complex system.
5. Minimize the alchemical system.
6. Equilibrate and production simulate the alchemical system using the chosen multistate sampling method (under NPT conditions).
7. Analyze results for the transformation.

.. note:: Three different types of multistate sampling (i.e. replica swapping between lambda states) methods can be chosen; HREX, SAMS, and independent (no lambda swaps attempted).
          By default the HREX approach is selected, this can be altered using ``solvent_simulation_settings.sampler_method`` or ``complex_simulation_settings.sampler_method`` (default: ``repex``).

Simulation details
""""""""""""""""""

Here are some details of how the simulation is carried out which are not detailed in the :class:`.AbsoluteBindingSettings`:

* The protocol applies a `LangevinMiddleIntegrator <https://openmmtools.readthedocs.io/en/latest/api/generated/openmmtools.mcmc.LangevinDynamicsMove.html>`_ which uses Langevin dynamics, with the LFMiddle discretization [4]_.
* A MonteCarloBarostat is used in the NPT ensemble to maintain constant pressure.

Getting the free energy estimate
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The free energy differences are obtained from simulation data using the `MBAR estimator <https://www.alchemistry.org/wiki/Multistate_Bennett_Acceptance_Ratio>`_ (multistate Bennett acceptance ratio estimator) as implemented in the `PyMBAR package <https://pymbar.readthedocs.io/en/master/mbar.html>`_.
Both the MBAR estimates of the two legs of the thermodynamic cycle, and the overall absolute binding free energy (of the entire cycle) are obtained,
which is different compared to the results in the :class:`.RelativeHybridTopologyProtocol` where results from two legs of the thermodynamic cycle are obtained separately.

In addition to the estimates of the free energy changes and their uncertainty, the protocol also returns some metrics to help assess convergence of the results, these are detailed in the :ref:`multistate analysis section <multistate_analysis>`.

See Also
--------

**Setting up AFE calculations**

* :ref:`Defining the Protocol <defining-protocols>`


**Tutorials**

* :any:`Absolute Binding Free Energies tutorial <../../tutorials/abfe_tutorial>`

**Cookbooks**

:ref:`Cookbooks <cookbooks>`

**API Documentation**

* :ref:`OpenMM Absolute Binding Free Energy <afe binding protocol api>`
* :ref:`OpenMM Protocol Settings <openmm protocol settings api>`

References
----------

* `pymbar <https://pymbar.readthedocs.io/en/stable/>`_
* `yank <http://getyank.org/latest/>`_
* `OpenMMTools <https://openmmtools.readthedocs.io/en/stable/>`_
* `OpenMM <https://openmm.org/>`_

.. [1] Broadening the Scope of Binding Free Energy Calculations Using a Separated Topologies Approach, H. Baumann, E. Dybeck, C. McClendon, F. Pickard IV, V. Gapsys, L. Pérez-Benito, D. Hahn, G. Tresadern, A. Mathiowetz, D. Mobley, J. Chem. Theory Comput., 2023, 19, 15, 5058–5076
.. [2] Evaluating the use of absolute binding free energy in the fragment optimisation process, I. Alibay, A. Magarkar, D. Seeliger, P. Biggin, Commun Chem 5, 105 (2022)
.. [3] Avoiding singularities and numerical instabilities in free energy calculations based on molecular simulations, T.C. Beutler, A.E. Mark, R.C. van Schaik, P.R. Greber, and W.F. van Gunsteren, Chem. Phys. Lett., 222 529–539 (1994)
.. [4] Unified Efficient Thermostat Scheme for the Canonical Ensemble with Holonomic or Isokinetic Constraints via Molecular Dynamics, Zhijun Zhang, Xinzijian Liu, Kangyu Yan, Mark E. Tuckerman, and Jian Liu, J. Phys. Chem. A 2019, 123, 28, 6056-6079


================================================
FILE: docs/guide/protocols/absolutesolvation.rst
================================================
Absolute Solvation Protocol
===========================

Overview
--------

The :class:`AbsoluteSolvationProtocol <.AbsoluteSolvationProtocol>` calculates the free energy change 
associate with transferring a molecule from vacuum into a solvent.

.. note::
   Currently, water is the only supported solvent, however, more solvents might be possible in the future.

The absolute solvation free energy is calculated through a thermodynamic cycle. 
In this cycle, the interactions of the molecule are decoupled, meaning turned off, using a partial annihilation scheme (see below) both in the solvent and in the vacuum phases.
The absolute solvation free energy is then obtained via summation of free energy differences along the thermodynamic cycle.

.. figure:: img/ahfe_thermocycle.png
   :scale: 80%

   Thermodynamic cycle for the absolute solvation free energy protocol.

Scientific Details
------------------

Partial annihilation scheme
~~~~~~~~~~~~~~~~~~~~~~~~~~~

In the :class:`.AbsoluteSolvationProtocol` the coulombic interactions of the molecule are fully turned off (annihilated). 
The Lennard-Jones interactions are instead decoupled, meaning the intermolecular interactions turned off, keeping the intramolecular Lennard-Jones interactions.

The lambda schedule
~~~~~~~~~~~~~~~~~~~

Molecular interactions are turned off during an alchemical path using a discrete set of lambda windows. The electrostatic interactions are turned off first, followed by the decoupling of the Lennard-Jones interactions. 
A soft-core potential is applied to the Lennard-Jones potential to avoid instablilites in intermediate lambda windows. 
Both the soft-core potential functions from Beutler et al. [1]_ and from Gapsys et al. [2]_ are available and can be specified in the ``alchemical_settings.softcore_LJ`` settings
(default: ``gapsys``).
The lambda schedule is defined in the ``lambda_settings`` objects ``lambda_elec`` and ``lambda_vdw``. Note that the ``lambda_restraints`` setting is ignored for the :class:`.AbsoluteSolvationProtocol`.

Simulation overview
~~~~~~~~~~~~~~~~~~~

The :class:`.ProtocolDAG` of the :class:`.AbsoluteSolvationProtocol` contains :class:`.ProtocolUnit`\ s from both the vacuum and solvent transformations.
This means that both legs of the thermodynamic cycle are constructed and run concurrently in the same :class:`.ProtocolDAG`. This is different from the :class:`.RelativeHybridTopologyProtocol` where the :class:`.ProtocolDAG` only runs a single leg of a thermodynamic cycle.
If multiple ``protocol_repeats`` are run (default: ``protocol_repeats=3``), the :class:`.ProtocolDAG` contains multiple :class:`.ProtocolUnit`\ s of both vacuum and solvent transformations.

Simulation steps
""""""""""""""""

Each :class:`.ProtocolUnit` (whether vacuum or solvent) carries out the following steps:

1. Parameterize the system using `OpenMMForceFields <https://github.com/openmm/openmmforcefields>`_ and `Open Force Field <https://github.com/openforcefield/openff-forcefields>`_.
2. Equilibrate the fully interacting system using a short MD simulation using the same approach as the :class:`.PlainMDProtocol` (in the solvent leg this will include rounds of NVT and NPT equilibration).
3. Create an alchemical system.
4. Minimize the alchemical system.
5. Equilibrate and production simulate the alchemical system using the chosen multistate sampling method (under NPT conditions if solvent is present).
6. Analyze results for the transformation.

Note: three different types of multistate sampling (i.e. replica swapping between lambda states) methods can be chosen; HREX, SAMS, and independent (no lambda swaps attempted). By default the HREX approach is selected, this can be altered using ``solvent_simulation_settings.sampler_method`` or ``vacuum_simulation_settings.sampler_method`` (default: ``repex``).

Simulation details
""""""""""""""""""

Here are some details of how the simulation is carried out which are not detailed in the :class:`.AbsoluteSolvationSettings`:

* The protocol applies a `LangevinMiddleIntegrator <https://openmmtools.readthedocs.io/en/latest/api/generated/openmmtools.mcmc.LangevinDynamicsMove.html>`_ which uses Langevin dynamics, with the LFMiddle discretization [3]_.
* A MonteCarloBarostat is used in the NPT ensemble to maintain constant pressure.

Getting the free energy estimate
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The free energy differences are obtained from simulation data using the `MBAR estimator <https://www.alchemistry.org/wiki/Multistate_Bennett_Acceptance_Ratio>`_ (multistate Bennett acceptance ratio estimator) as implemented in the `PyMBAR package <https://pymbar.readthedocs.io/en/master/mbar.html>`_.
Both the MBAR estimates of the two legs of the thermodynamic cycle, and the overall absolute solvation free energy (of the entire cycle) are obtained,
which is different compared to the results in the :class:`.RelativeHybridTopologyProtocol` where results from two legs of the thermodynamic cycle are obtained separately.

In addition to the estimates of the free energy changes and their uncertainty, the protocol also returns some metrics to help assess convergence of the results, these are detailed in the :ref:`multistate analysis section <multistate_analysis>`.

.. todo: issue 792 change this reference to point to the new results section


See Also
--------

**Setting up AFE calculations**

* :ref:`Defining the Protocol <defining-protocols>`

..
  To be added: Setting up AHFE calculations

**Tutorials**

* :any:`Absolute Hydration Free Energies tutorial <../../tutorials/ahfe_tutorial>`

**Cookbooks**

:ref:`Cookbooks <cookbooks>`

**API Documentation**

* :ref:`OpenMM Absolute Solvation Free Energy <afe solvation protocol api>`
* :ref:`OpenMM Protocol Settings <openmm protocol settings api>`

References
----------

* `pymbar <https://pymbar.readthedocs.io/en/stable/>`_
* `yank <http://getyank.org/latest/>`_
* `OpenMMTools <https://openmmtools.readthedocs.io/en/stable/>`_
* `OpenMM <https://openmm.org/>`_

.. [1] Avoiding singularities and numerical instabilities in free energy calculations based on molecular simulations, T.C. Beutler, A.E. Mark, R.C. van Schaik, P.R. Greber, and W.F. van Gunsteren, Chem. Phys. Lett., 222 529–539 (1994)
.. [2] New Soft-Core Potential Function for Molecular Dynamics Based Alchemical Free Energy Calculations, V. Gapsys, D. Seeliger, and B.L. de Groot, J. Chem. Theor. Comput., 8 2373-2382 (2012)
.. [3] Unified Efficient Thermostat Scheme for the Canonical Ensemble with Holonomic or Isokinetic Constraints via Molecular Dynamics, Zhijun Zhang, Xinzijian Liu, Kangyu Yan, Mark E. Tuckerman, and Jian Liu, J. Phys. Chem. A 2019, 123, 28, 6056-6079


================================================
FILE: docs/guide/protocols/index.rst
================================================
.. _userguide_protocols:

Details of Specific Protocols
=============================

Details on the theory and behaviour of different Protocols are listed here.

.. toctree::
   relativehybridtopology
   absolutebinding
   absolutesolvation
   septop
   plainmd


================================================
FILE: docs/guide/protocols/plainmd.rst
================================================
Plain MD Protocol
=================

Overview
--------

The :class:`.PlainMDProtocol` enables the user to run a Molecular Dynamics (MD) simulation of a :class:`.ChemicalSystem`, which can contain e.g. a solvated protein-ligand complex, a molecule and water, or a molecule in vacuum.

.. todo: Later add ref to ChemicalSystem section

Scientific Details
------------------

The :class:`.PlainMDProtocol` runs MD simulations of a system either in solvent or vacuum, depending on the input provided by the user in the :class:`.ChemicalSystem`.
The protocol applies a 
`LangevinMiddleIntegrator <http://docs.openmm.org/development/api-python/generated/openmm.openmm.LangevinMiddleIntegrator.html>`_ 
which uses Langevin dynamics, with the LFMiddle discretization [1]_.  

Simulation Steps and Outputs
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

If there is a ``SolventComponent`` in the :class:`.ChemicalSystem`, the each :class:`.ProtocolUnit` carries out the following steps:

.. list-table:: 
  :widths: 50 50
  :header-rows: 1

  * - Step
    - Outputs (with default names)
  * - 1. Parameterize the system using `OpenMMForceFields <https://github.com/openmm/openmmforcefields>`_ and `Open Force Field <https://github.com/openforcefield/openff-forcefields>`_
    - Forcefield cache (``db.json``)
  * - 2. OpenMM object creation
    - Structure of the full system (``system.pdb``)
  * - 3. Minimize the system
    - Minimized Structure (``minimized.pdb``)
  * - 4. Equilibrate in the canonical (NVT) ensemble
    - NVT equilibrated structure (``equil_nvt.pdb``)
  * - 5. Equilibrate the system under isobaric-isothermal (NPT) conditions
    - NPT equilibrated structure (``equil_npt.pdb``)
  * - 6. Production simulate the system under isobaric-isothermal (NPT) conditions
    - Simulation trajectory (``simulation.xtc``), Checkpoint file (``checkpoint.chk``), Log output (``simulation.log``)

A MonteCarloBarostat is used in the NPT ensemble to maintain constant pressure.
Relevant settings under solvent conditions include the solvation settings that control the ``solvent_model`` and ``solvent_padding``.

If the :class:`.ChemicalSystem` does not contain a ``SolventComponent``, the protocol runs an MD simulation in vacuum. After a minimization, the protocol performs an equilibration, followed by a production run with no periodic boundary conditions and infinite cutoffs. Settings that control the barostat or the solvation are ignored for vacuum MD simulations.

Performance consideration for gas phase MD simulations
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

For gas phase MD simulations, we suggest setting ``OPENMM_CPU_THREADS=1`` to obtain good performance.

See Also
--------

**Tutorials**

* :any:`MD tutorial <../../tutorials/md_tutorial>`

**API Documentation**

* :ref:`OpenMM plain MD protocol <md protocol api>`
* :ref:`OpenMM Protocol Settings <openmm protocol settings api>`

References
----------
* `OpenMMTools <https://openmmtools.readthedocs.io/en/stable/>`_
* `OpenMM <https://openmm.org/>`_

.. [1] Unified Efficient Thermostat Scheme for the Canonical Ensemble with Holonomic or Isokinetic Constraints via Molecular Dynamics, Zhijun Zhang, Xinzijian Liu, Kangyu Yan, Mark E. Tuckerman, and Jian Liu, J. Phys. Chem. A 2019, 123, 28, 6056-6079


================================================
FILE: docs/guide/protocols/relativehybridtopology.rst
================================================
.. _userguide_relative_hybrid_topology_protocol:

Relative Hybrid Topology Protocol
=================================

Overview
--------

The relative free energy calculation approach calculates the difference in 
free energy between two similar ligands. Depending on the :class:`.ChemicalSystem` 
provided, the protocol either calculates the relative binding free energy 
(RBFE), or the relative hydration free energy (RHFE).
Further information on constructing chemical systems to define thermodynamic cycles,
see :ref:`userguide_chemicalsystems_and_components`

In a thermodynamic 
cycle, one ligand is converted into the other ligand by alchemically 
transforming the atoms that vary between the two ligands. The 
transformation is carried out in both environments, meaning both in the 
solvent (ΔG\ :sub:`solv`\) and in the binding site (ΔG\ :sub:`site`\) for RBFE calculations 
and in the solvent (ΔG\ :sub:`solv`\) and vacuum (ΔG\ :sub:`vacuum`\) for RHFE calculations.

.. _label: Thermodynamic cycle for the relative binding free energy protocol
.. figure:: img/rbfe_thermocycle.png
   :scale: 50%

   Thermodynamic cycle for the relative binding free energy protocol.
   
Scientific Details
------------------

This :class:`.RelativeHybridTopologyProtocol` is based off the `Perses implementation <https://perses.readthedocs.io/en/latest/>`_.

The Hybrid Topology approach
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The :class:`.RelativeHybridTopologyProtocol` uses a hybrid topology approach to represent the two
ligands, meaning that a single set of coordinates is used to represent the
common core of the two ligands while the atoms that differ between the two
ligands are represented separately. An atom map defines which atoms belong
to the core (mapped atoms) and which atoms are unique and represented
separately (see :ref:`Creating atom mappings <Creating Atom Mappings>`). During the alchemical transformation, mapped atoms are interpolated
from their type in the ligand at state A to the type in the other ligand at state B, while unique atoms
atoms (commonly known as dummy atoms) are switched, inserted or uncoupled, depending on which ligand they belong to. By default all nonbonded interactions between the
dummy region and the core region are removed to avoid coupling their motion.

.. note:: In this hybrid topology approach, all bonded interactions between the dummy region and the core region are kept. 
          As pointed out by Fleck et al. [1]_, this can lead to systematic errors if the contribution of the dummy group does not cancel out
          in the thermodynamic cycle (no separability of the partition function). We are currently working on fixing this issue.

The lambda schedule
~~~~~~~~~~~~~~~~~~~

The protocol interpolates molecular interactions between the initial and final state of the perturbation using a discrete set of lambda windows. A function describes how the different lambda components (bonded and nonbonded terms) are interpolated.
Only parameters that differ between state A (``lambda=0``) and state B (``lambda=1``) are interpolated. 
In the default lambda function in the :class:`.RelativeHybridTopologyProtocol`, first the electrostatic interactions of state A are turned off while simultaneously turning on the steric interactions of state B. Then, the steric interactions of state A are turned off while simultaneously turning on the electrostatic interactions of state B. Bonded interactions are interpolated linearly between ``lambda=0`` and ``lambda=1``. The ``lambda_settings`` ``lambda_functions`` and ``lambda_windows`` define the alchemical pathway.
A soft-core potential is applied to the Lennard-Jones potential to avoid instablilites in intermediate lambda windows.
Both the soft-core potential functions from Beutler et al. [2]_ and from Gapsys et al. [3]_ are available and can be specified in the ``alchemical_settings.softcore_LJ`` settings
(default: ``gapsys``).

Simulation overview
~~~~~~~~~~~~~~~~~~~

The :class:`.ProtocolDAG` of the :class:`.RelativeHybridTopologyProtocol` contains the :class:`.ProtocolUnit`\ s from one leg of the thermodynamic
cycle. 
This means that each :class:`.ProtocolDAG` only runs a single leg of a thermodynamic cycle and therefore two Protocol instances need to be run to get the overall relative free energy difference, ΔΔG. 
If multiple ``protocol_repeats`` are run (default: ``protocol_repeats=3``), the :class:`.ProtocolDAG` contains multiple :class:`.ProtocolUnit`\ s of both vacuum and solvent transformations.

Simulation Steps
""""""""""""""""

Each :class:`.ProtocolUnit` carries out the following steps:

1. Parameterize the system using `OpenMMForceFields <https://github.com/openmm/openmmforcefields>`_ and `Open Force Field <https://github.com/openforcefield/openff-forcefields>`_.
2. Create an alchemical system (hybrid topology).
3. Minimize the alchemical system.
4. Equilibrate and production simulate the alchemical system using the chosen multistate sampling method (under NPT conditions if solvent is present).

.. note::
   **Equilibration method:**
   The current implementation uses a simple equilibration protocol **without any positional restraints** or **temperature annealing**.
   The system is equilibrated directly under the target thermodynamic conditions, therefore the input structures should be stable under these conditions.

5. Analyze results for the transformation (for a single leg in the thermodynamic cycle).

Note: three different types of multistate sampling (i.e. replica swapping between lambda states) methods can be chosen; HREX, SAMS, and independent (no lambda swaps attempted). By default the HREX approach is selected, this can be altered using ``simulation_settings.sampler_method`` (default: ``repex``).

Simulation details
""""""""""""""""""

Here are some details of how the simulation is carried out which are not detailed in the :class:`.RelativeHybridTopologySettings`:

* The protocol applies a `LangevinMiddleIntegrator <https://openmmtools.readthedocs.io/en/latest/api/generated/openmmtools.mcmc.LangevinDynamicsMove.html>`_ which uses Langevin dynamics, with the LFMiddle discretization [4]_.
* A MonteCarloBarostat is used in the NPT ensemble to maintain constant pressure.

Getting the free energy estimate
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The free energy differences are obtained from simulation data using the `MBAR estimator <https://www.alchemistry.org/wiki/Multistate_Bennett_Acceptance_Ratio>`_ (multistate Bennett acceptance ratio estimator)
as implemented in the `PyMBAR package <https://pymbar.readthedocs.io/en/master/mbar.html>`_.
In addition to the MBAR estimates of the two legs of the thermodynamic cycle and the overall relative binding free energy difference,
the protocol also returns some metrics to help assess convergence of the results,
these are detailed in the :ref:`multistate analysis section <multistate_analysis>`.

.. note:: The MBAR uncertainty of each individual transformation is estimated using bootstrapping for 1000 iterations,
          this leads to larger errors compared to the previous error estimate method. The only exception are
          the forward and reverse convergence plots which use the MBAR analytical error.

.. todo: issue 792, consolidate this page into its own analysis page and link both RBFE and AFE pages to it
.. _multistate_analysis:

Analysis
~~~~~~~~

As standard, some analysis of the each simulation repeat is performed.
This analysis is made available through either the dictionary of results in the execution output,
or through some ready-made plots for quick inspection.
This analysis can be categorised as relating
to the energetics of the different lambda states that were sampled,
or to the analysis of the change in structural conformation over time in each state.

Energetic and replica exchange analysis
"""""""""""""""""""""""""""""""""""""""

These analyses consider the swapping and energetic overlap between the
different simulated states to help assess the convergence and correctness of the estimate of free energy
difference produced.

.. list-table:: Energetic Analysis examples
  :widths: 75 25
  :header-rows: 1

  * - Description
    - Example
  * - **MBAR overlap matrix.**

      This plot is used to assess if the different lambda states simulated overlapped energetically.
      Each matrix element represents the probability of a sample from a given row state being observable in a given column
      state.
      Since the accuracy of the MBAR estimator depends on sufficient overlap between lambda states, this is a very
      important metric.
      This plot should show that the diagonal of the matrix has some "width" so that the two end states are connected,
      with elements adjacent to the diagonal being at least 0.03 [5]_.
    - .. image:: img/mbar_overlap_matrix.png
  * - **Replica exchange probability matrix** (for replica exchange sampler simulations only).

      Similar to the MBAR overlap matrix, this shows the probability of a given lambda state being exchanged with another.
      Again, the diagonal of this matrix should be at least tridiagonal wide for the two end states to be connected.
    - .. image:: img/replica_exchange_matrix.png
  * - **Forward and reverse convergence of free energy estimates.**

      Using increasingly larger portions of the total data,
      this analysis calculates the free energy difference, both in forward and backward directions.
      In this analysis, forward and backward estimates that agree within error using only a fraction of the total data
      suggest convergence [5]_. Note: the error bars reported in this plot are
      MBAR analytical errors instead of bootstrap errors.
    - .. image:: img/forward_reverse_convergence.png
  * - **Timeseries of replica states.**

      This plot shows the time evolution of the different system configurations as they are
      exchanged between different lambda states.
      This plot should show that the states are freely mixing and that there are no cliques forming.
    - .. image:: img/replica_state_timeseries.png

Structural analysis
"""""""""""""""""""

If a protein was present, these analyses first center and align the system so that
the protein is considered the frame of reference.
Further analysis can be performed by inspecting the ``simulation.nc`` and ``hybrid_system.pdb`` files,
which contain a multistate trajectory and topology for the hybrid system respectively.
These files can be loaded into an MDAnalysis Universe object using the `openfe_analysis`_ package.

.. list-table:: Structural Analysis examples
  :widths: 75 25
  :header-rows: 1

  * - Description
    - Example
  * - **Ligand RMSD.**

      This produces a plot called ``ligand_RMSD.png`` and a results entry ``ligand_RMSD`` which gives the
      RMSD of the ligand molecule over time relative to the first frame of the production phase, for each simulated state.
      Large RMSD values, e.g. greater than 5 angstrom (system dependent), would indicate an unstable ligand binding mode.
    - .. image:: img/ligand_RMSD.png
  * - **Ligand COM drift.**

      For simulations with a protein present, this metric gives the total distance of the ligand COM
      from its initial starting (docked) position.  If this metric increases over the course of the simulation (beyond 5
      angstrom) it indicates that the ligand drifted from the binding pocket, and the simulation is unreliable.
      This produces a plot called ``ligand_COM_drift.png`` and a results entry ``ligand_COM_drift``.
    - .. image:: img/ligand_COM_drift.png
  * - **Protein 2D RMSD.**

      For simulations with a protein present, this metric gives, for each lambda state, the RMSD of the
      protein structure over time, using each frame analysed as a reference frame, to produce a 2 dimensional heatmap.
      This plot should show no significant spikes in RMSD (which will appear as brightly coloured areas).
    - .. image:: img/protein_2D_RMSD.png


See Also
--------

**Setting up RFE calculations**

* :ref:`Setting up alchemical networks <alchemical_network_creation>`

**Tutorials**

* :any:`Relative Free Energies with the OpenFE CLI <rbfe_cli_tutorial>`
* :any:`Relative Free Energies with the OpenFE Python API <tutorials/rbfe_python_tutorial.nblink>`

**Cookbooks**

:ref:`Cookbooks <cookbooks>`

**API Documentation**

* :ref:`OpenMM Relative Hybrid Topology Protocol <
Download .txt
gitextract_wx9d1k3u/

├── .dockerignore
├── .git-blame-ignore-revs
├── .gitattributes
├── .github/
│   ├── CONTRIBUTING.md
│   ├── PULL_REQUEST_TEMPLATE/
│   │   └── release_template.md
│   ├── pull_request_template.md
│   └── workflows/
│       ├── aws-cpu-long-tests.yaml
│       ├── aws-gpu-integration-tests.yaml
│       ├── ci.yaml
│       ├── clean-pr-caches.yaml
│       ├── cron-conda.yaml
│       ├── cron-docker.yaml
│       ├── cron-feedstock-build-tests.yaml
│       ├── cron-package-test.yaml
│       ├── griffe-api-break.yaml
│       ├── mypy.yaml
│       ├── release-docker-image.yaml
│       ├── release-installers.yaml
│       ├── release-make-condalock.yaml
│       ├── release-prep-examplenotebooks.yaml
│       └── release-prep-feedstock.yaml
├── .gitignore
├── .pre-commit-config.yaml
├── .readthedocs.yaml
├── CITATION.cff
├── Code_of_Conduct.md
├── LICENSE
├── MANIFEST.in
├── README.md
├── codecov.yml
├── devtools/
│   ├── data/
│   │   ├── fix_rbfe_results.py
│   │   └── gen_serialized_results.py
│   ├── debug_openmm.sh
│   └── installer/
│       └── construct.yaml
├── docs/
│   ├── CHANGELOG.rst
│   ├── Makefile
│   ├── _ext/
│   │   └── sass.py
│   ├── _sass/
│   │   └── deflist-flowchart.scss
│   ├── _templates/
│   │   └── autosummary/
│   │       ├── base.rst
│   │       └── class.rst
│   ├── conf.py
│   ├── cookbook/
│   │   ├── bespoke_parameters.nblink
│   │   ├── choose_protocol.nblink
│   │   ├── create_alchemical_network.nblink
│   │   ├── dumping_transformation.rst
│   │   ├── generate_ligand_network.nblink
│   │   ├── hand_write_ligand_network.nblink
│   │   ├── index.rst
│   │   ├── jq_inspection.rst
│   │   ├── ligandnetwork_vis.nblink
│   │   ├── loading_molecules.nblink
│   │   ├── network_from_orion_fepp.nblink
│   │   ├── rfe_alchemical_planners.nblink
│   │   └── user_charges.nblink
│   ├── environment.yaml
│   ├── guide/
│   │   ├── cli/
│   │   │   ├── cli_basics.rst
│   │   │   ├── cli_yaml.rst
│   │   │   └── index.rst
│   │   ├── execution/
│   │   │   ├── execution_theory.rst
│   │   │   ├── index.rst
│   │   │   └── quickrun_execution.rst
│   │   ├── index.rst
│   │   ├── introduction.rst
│   │   ├── protocols/
│   │   │   ├── absolutebinding.rst
│   │   │   ├── absolutesolvation.rst
│   │   │   ├── index.rst
│   │   │   ├── plainmd.rst
│   │   │   ├── relativehybridtopology.rst
│   │   │   └── septop.rst
│   │   ├── results/
│   │   │   ├── index.rst
│   │   │   ├── working_with_networks.rst
│   │   │   └── working_with_results.rst
│   │   ├── setup/
│   │   │   ├── alchemical_network_model.rst
│   │   │   ├── chemical_systems_and_thermodynamic_cycles.rst
│   │   │   ├── creating_atom_mappings_and_scores.rst
│   │   │   ├── creating_ligand_networks.rst
│   │   │   ├── defining_protocols.rst
│   │   │   └── index.rst
│   │   ├── troubleshooting.rst
│   │   └── under_the_hood.rst
│   ├── index.rst
│   ├── installation.rst
│   ├── make.bat
│   ├── reference/
│   │   ├── api/
│   │   │   ├── alchemical_network_planning.rst
│   │   │   ├── atom_mappers.rst
│   │   │   ├── defining_and_executing_simulations.rst
│   │   │   ├── index.rst
│   │   │   ├── ligand_network.rst
│   │   │   ├── openmm_binding_afe.rst
│   │   │   ├── openmm_md.rst
│   │   │   ├── openmm_protocol_settings.rst
│   │   │   ├── openmm_rfe.rst
│   │   │   ├── openmm_septop.rst
│   │   │   ├── openmm_solvation_afe.rst
│   │   │   └── systems_and_components.rst
│   │   ├── cli/
│   │   │   ├── charge_molecules.rst
│   │   │   ├── gather.rst
│   │   │   ├── index.rst
│   │   │   ├── plan_rbfe_network.rst
│   │   │   ├── plan_rhfe_network.rst
│   │   │   └── quickrun.rst
│   │   └── index.rst
│   └── tutorials/
│       ├── .gitignore
│       ├── abfe_analysis_tutorial.nblink
│       ├── abfe_tutorial.nblink
│       ├── ahfe_tutorial.nblink
│       ├── charge_molecules_cli_tutorial.rst
│       ├── index.rst
│       ├── md_tutorial.nblink
│       ├── plotting_with_cinnabar.nblink
│       ├── rbfe_cli_tutorial.rst
│       ├── rbfe_membrane_protein.nblink
│       ├── rbfe_python_tutorial.nblink
│       ├── septop_analysis_tutorial.nblink
│       ├── septop_tutorial.nblink
│       └── showcase_notebook.nblink
├── environment.yml
├── news/
│   └── TEMPLATE.rst
├── production/
│   ├── Dockerfile
│   └── environment.yml
├── pyproject.toml
├── rever.xsh
└── src/
    ├── openfe/
    │   ├── __init__.py
    │   ├── analysis/
    │   │   ├── __init__.py
    │   │   └── plotting.py
    │   ├── data/
    │   │   ├── __init__.py
    │   │   ├── _downloader.py
    │   │   └── _registry.py
    │   ├── due.py
    │   ├── orchestration/
    │   │   └── __init__.py
    │   ├── protocols/
    │   │   ├── __init__.py
    │   │   ├── openmm_afe/
    │   │   │   ├── __init__.py
    │   │   │   ├── abfe_units.py
    │   │   │   ├── afe_protocol_results.py
    │   │   │   ├── ahfe_units.py
    │   │   │   ├── base_afe_units.py
    │   │   │   ├── equil_afe_settings.py
    │   │   │   ├── equil_binding_afe_method.py
    │   │   │   └── equil_solvation_afe_method.py
    │   │   ├── openmm_md/
    │   │   │   ├── __init__.py
    │   │   │   ├── plain_md_methods.py
    │   │   │   └── plain_md_settings.py
    │   │   ├── openmm_rfe/
    │   │   │   ├── __init__.py
    │   │   │   ├── _rfe_utils/
    │   │   │   │   ├── __init__.py
    │   │   │   │   ├── lambdaprotocol.py
    │   │   │   │   ├── multistate.py
    │   │   │   │   ├── relative.py
    │   │   │   │   └── topologyhelpers.py
    │   │   │   ├── equil_rfe_methods.py
    │   │   │   ├── equil_rfe_settings.py
    │   │   │   ├── hybridtop_protocol_results.py
    │   │   │   ├── hybridtop_protocols.py
    │   │   │   └── hybridtop_units.py
    │   │   ├── openmm_septop/
    │   │   │   ├── __init__.py
    │   │   │   ├── base_units.py
    │   │   │   ├── equil_septop_method.py
    │   │   │   ├── equil_septop_settings.py
    │   │   │   ├── septop_protocol_results.py
    │   │   │   ├── septop_units.py
    │   │   │   └── utils.py
    │   │   ├── openmm_utils/
    │   │   │   ├── __init__.py
    │   │   │   ├── charge_generation.py
    │   │   │   ├── mdtraj_utils.py
    │   │   │   ├── multistate_analysis.py
    │   │   │   ├── omm_compute.py
    │   │   │   ├── omm_settings.py
    │   │   │   ├── serialization.py
    │   │   │   ├── settings_validation.py
    │   │   │   ├── system_creation.py
    │   │   │   └── system_validation.py
    │   │   └── restraint_utils/
    │   │       ├── __init__.py
    │   │       ├── geometry/
    │   │       │   ├── __init__.py
    │   │       │   ├── base.py
    │   │       │   ├── boresch/
    │   │       │   │   ├── __init__.py
    │   │       │   │   ├── geometry.py
    │   │       │   │   ├── guest.py
    │   │       │   │   └── host.py
    │   │       │   ├── flatbottom.py
    │   │       │   ├── harmonic.py
    │   │       │   └── utils.py
    │   │       ├── openmm/
    │   │       │   ├── __init__.py
    │   │       │   ├── omm_forces.py
    │   │       │   └── omm_restraints.py
    │   │       └── settings.py
    │   ├── setup/
    │   │   ├── __init__.py
    │   │   ├── alchemical_network_planner/
    │   │   │   ├── __init__.py
    │   │   │   ├── abstract_alchemical_network_planner.py
    │   │   │   └── relative_alchemical_network_planner.py
    │   │   ├── atom_mapping/
    │   │   │   ├── __init__.py
    │   │   │   ├── ligandatommapper.py
    │   │   │   ├── lomap_mapper.py
    │   │   │   ├── lomap_scorers.py
    │   │   │   ├── perses_mapper.py
    │   │   │   └── perses_scorers.py
    │   │   ├── chemicalsystem_generator/
    │   │   │   ├── __init__.py
    │   │   │   ├── abstract_chemicalsystem_generator.py
    │   │   │   └── easy_chemicalsystem_generator.py
    │   │   └── ligand_network_planning.py
    │   ├── storage/
    │   │   ├── __init__.py
    │   │   ├── metadatastore.py
    │   │   ├── resultclient.py
    │   │   └── resultserver.py
    │   ├── tests/
    │   │   ├── __init__.py
    │   │   ├── analysis/
    │   │   │   ├── __init__.py
    │   │   │   └── test_plotting.py
    │   │   ├── conftest.py
    │   │   ├── data/
    │   │   │   ├── 181l_only.pdb
    │   │   │   ├── CN.sdf
    │   │   │   ├── __init__.py
    │   │   │   ├── a2a/
    │   │   │   │   └── __init__.py
    │   │   │   ├── benzene_modifications.sdf
    │   │   │   ├── cdk8/
    │   │   │   │   ├── __init__.py
    │   │   │   │   ├── cdk8_ligands.sdf
    │   │   │   │   └── cdk8_protein.pdb
    │   │   │   ├── eg5/
    │   │   │   │   ├── __init__.py
    │   │   │   │   ├── eg5_cofactor.sdf
    │   │   │   │   ├── eg5_ligands.sdf
    │   │   │   │   └── eg5_protein.pdb
    │   │   │   ├── external_formats/
    │   │   │   │   ├── __init__.py
    │   │   │   │   └── somebenzenes_edges.edge
    │   │   │   ├── htf/
    │   │   │   │   ├── __init__.py
    │   │   │   │   ├── chloroethane.sdf
    │   │   │   │   ├── ethane.sdf
    │   │   │   │   ├── fluoroethane.sdf
    │   │   │   │   └── t4_lysozyme_data/
    │   │   │   │       ├── benzene.sdf
    │   │   │   │       ├── chlorobenzene.sdf
    │   │   │   │       └── fluorobenzene.sdf
    │   │   │   ├── lomap_basic/
    │   │   │   │   ├── 1,3,7-trimethylnaphthalene.mol2
    │   │   │   │   ├── 1-butyl-4-methylbenzene.mol2
    │   │   │   │   ├── 2,6-dimethylnaphthalene.mol2
    │   │   │   │   ├── 2-methyl-6-propylnaphthalene.mol2
    │   │   │   │   ├── 2-methylnaphthalene.mol2
    │   │   │   │   ├── 2-naftanol.mol2
    │   │   │   │   ├── README.md
    │   │   │   │   ├── __init__.py
    │   │   │   │   ├── methylcyclohexane.mol2
    │   │   │   │   └── toluene.mol2
    │   │   │   ├── multi_molecule.sdf
    │   │   │   ├── openmm_afe/
    │   │   │   │   ├── T4_abfe_system.xml.bz2
    │   │   │   │   └── __init__.py
    │   │   │   ├── openmm_md/
    │   │   │   │   └── __init__.py
    │   │   │   ├── openmm_rfe/
    │   │   │   │   ├── __init__.py
    │   │   │   │   ├── benzene_toluene_hybrid_top/
    │   │   │   │   │   ├── hybrid_topology_atoms.csv
    │   │   │   │   │   └── hybrid_topology_bonds.txt
    │   │   │   │   ├── charged_benzenes.sdf
    │   │   │   │   ├── dummy_charge_ligand_23.sdf
    │   │   │   │   ├── dummy_charge_ligand_55.sdf
    │   │   │   │   ├── ligand_23.sdf
    │   │   │   │   ├── ligand_55.sdf
    │   │   │   │   ├── malt1_shapefit_1832577-09-9.sdf
    │   │   │   │   ├── malt1_shapefit_Pfizer-01-01.sdf
    │   │   │   │   ├── reference.xml
    │   │   │   │   ├── vacuum_nocoord.nc
    │   │   │   │   └── vacuum_nocoord_checkpoint.nc
    │   │   │   ├── openmm_septop/
    │   │   │   │   ├── __init__.py
    │   │   │   │   └── system.xml.bz2
    │   │   │   └── serialization/
    │   │   │       ├── __init__.py
    │   │   │       ├── ethane_template.sdf
    │   │   │       └── network_template.graphml
    │   │   ├── dev/
    │   │   │   ├── __init__.py
    │   │   │   └── serialization_test_templates.py
    │   │   ├── protocols/
    │   │   │   ├── __init__.py
    │   │   │   ├── conftest.py
    │   │   │   ├── openmm_abfe/
    │   │   │   │   ├── __init__.py
    │   │   │   │   ├── conftest.py
    │   │   │   │   ├── test_abfe_energies.py
    │   │   │   │   ├── test_abfe_protocol.py
    │   │   │   │   ├── test_abfe_protocol_results.py
    │   │   │   │   ├── test_abfe_settings.py
    │   │   │   │   ├── test_abfe_slow.py
    │   │   │   │   ├── test_abfe_tokenization.py
    │   │   │   │   ├── test_abfe_validation.py
    │   │   │   │   └── utils.py
    │   │   │   ├── openmm_ahfe/
    │   │   │   │   ├── __init__.py
    │   │   │   │   ├── test_ahfe_protocol.py
    │   │   │   │   ├── test_ahfe_protocol_results.py
    │   │   │   │   ├── test_ahfe_resume.py
    │   │   │   │   ├── test_ahfe_settings.py
    │   │   │   │   ├── test_ahfe_slow.py
    │   │   │   │   ├── test_ahfe_tokenization.py
    │   │   │   │   ├── test_ahfe_validation.py
    │   │   │   │   └── utils.py
    │   │   │   ├── openmm_md/
    │   │   │   │   ├── __init__.py
    │   │   │   │   ├── test_plain_md_protocol.py
    │   │   │   │   ├── test_plain_md_resume.py
    │   │   │   │   ├── test_plain_md_slow.py
    │   │   │   │   └── test_plain_md_tokenization.py
    │   │   │   ├── openmm_rfe/
    │   │   │   │   ├── __init__.py
    │   │   │   │   ├── helpers.py
    │   │   │   │   ├── test_hybrid_factory.py
    │   │   │   │   ├── test_hybrid_top_protocol.py
    │   │   │   │   ├── test_hybrid_top_resume.py
    │   │   │   │   ├── test_hybrid_top_slow.py
    │   │   │   │   ├── test_hybrid_top_tokenization.py
    │   │   │   │   └── test_hybrid_top_validation.py
    │   │   │   ├── openmm_septop/
    │   │   │   │   ├── __init__.py
    │   │   │   │   ├── conftest.py
    │   │   │   │   ├── test_septop_protocol.py
    │   │   │   │   ├── test_septop_protocol_results.py
    │   │   │   │   ├── test_septop_resume.py
    │   │   │   │   ├── test_septop_settings.py
    │   │   │   │   ├── test_septop_slow.py
    │   │   │   │   ├── test_septop_tokenization.py
    │   │   │   │   ├── test_septop_validation.py
    │   │   │   │   └── utils.py
    │   │   │   ├── restraints/
    │   │   │   │   ├── __init__.py
    │   │   │   │   ├── test_geometry_base.py
    │   │   │   │   ├── test_geometry_boresch.py
    │   │   │   │   ├── test_geometry_boresch_guest.py
    │   │   │   │   ├── test_geometry_boresch_host.py
    │   │   │   │   ├── test_geometry_flatbottom.py
    │   │   │   │   ├── test_geometry_harmonic.py
    │   │   │   │   ├── test_geometry_utils.py
    │   │   │   │   ├── test_omm_restraints.py
    │   │   │   │   ├── test_openmm_forces.py
    │   │   │   │   └── test_settings.py
    │   │   │   ├── test_openmm_settings.py
    │   │   │   ├── test_openmmutils.py
    │   │   │   └── test_openmmutils_serialization.py
    │   │   ├── setup/
    │   │   │   ├── __init__.py
    │   │   │   ├── alchemical_network_planner/
    │   │   │   │   ├── __init__.py
    │   │   │   │   ├── edge_types.py
    │   │   │   │   └── test_relative_alchemical_network_planner.py
    │   │   │   ├── atom_mapping/
    │   │   │   │   ├── __init__.py
    │   │   │   │   ├── conftest.py
    │   │   │   │   ├── test_atommapper.py
    │   │   │   │   ├── test_lomap_atommapper.py
    │   │   │   │   ├── test_lomap_scorers.py
    │   │   │   │   ├── test_perses_atommapper.py
    │   │   │   │   └── test_perses_scorers.py
    │   │   │   ├── chemicalsystem_generator/
    │   │   │   │   ├── __init__.py
    │   │   │   │   ├── component_checks.py
    │   │   │   │   └── test_easy_chemicalsystem_generator.py
    │   │   │   └── test_network_planning.py
    │   │   ├── storage/
    │   │   │   ├── __init__.py
    │   │   │   ├── conftest.py
    │   │   │   ├── test_metadatastore.py
    │   │   │   ├── test_resultclient.py
    │   │   │   └── test_resultserver.py
    │   │   └── utils/
    │   │       ├── __init__.py
    │   │       ├── conftest.py
    │   │       ├── test_atommapping_network_plotting.py
    │   │       ├── test_duecredit.py
    │   │       ├── test_log_control.py
    │   │       ├── test_network_plotting.py
    │   │       ├── test_optional_imports.py
    │   │       ├── test_remove_oechem.py
    │   │       ├── test_system_probe.py
    │   │       └── test_visualization_3D.py
    │   └── utils/
    │       ├── __init__.py
    │       ├── atommapping_network_plotting.py
    │       ├── custom_typing.py
    │       ├── ligand_utils.py
    │       ├── logging_control.py
    │       ├── network_plotting.py
    │       ├── optional_imports.py
    │       ├── remove_oechem.py
    │       ├── silence_root_logging.py
    │       ├── system_probe.py
    │       └── visualization_3D.py
    └── openfecli/
        ├── README.md
        ├── __init__.py
        ├── cli.py
        ├── clicktypes/
        │   ├── __init__.py
        │   └── hyphenchoice.py
        ├── commands/
        │   ├── __init__.py
        │   ├── atommapping.py
        │   ├── fetch.py
        │   ├── gather.py
        │   ├── gather_abfe.py
        │   ├── gather_septop.py
        │   ├── generate_partial_charges.py
        │   ├── plan_rbfe_network.py
        │   ├── plan_rhfe_network.py
        │   ├── quickrun.py
        │   ├── test.py
        │   └── view_ligand_network.py
        ├── data/
        │   ├── __init__.py
        │   └── _registry.py
        ├── fetchables.py
        ├── fetching.py
        ├── parameters/
        │   ├── __init__.py
        │   ├── mapper.py
        │   ├── misc.py
        │   ├── mol.py
        │   ├── molecules.py
        │   ├── output.py
        │   ├── output_dir.py
        │   ├── plan_network_options.py
        │   ├── protein.py
        │   └── utils.py
        ├── plan_alchemical_networks_utils.py
        ├── plugins.py
        ├── tests/
        │   ├── __init__.py
        │   ├── clicktypes/
        │   │   └── test_hyphenchoice.py
        │   ├── commands/
        │   │   ├── __init__.py
        │   │   ├── conftest.py
        │   │   ├── test_atommapping.py
        │   │   ├── test_charge_generation.py
        │   │   ├── test_gather/
        │   │   │   ├── test_abfe_full_results_multiple_units_dg_.tsv
        │   │   │   ├── test_abfe_full_results_multiple_units_raw_.tsv
        │   │   │   ├── test_abfe_full_results_single_unit_dg_.tsv
        │   │   │   ├── test_abfe_full_results_single_unit_raw_.tsv
        │   │   │   ├── test_abfe_single_repeat_multiple_units_dg_.tsv
        │   │   │   ├── test_abfe_single_repeat_multiple_units_raw_.tsv
        │   │   │   ├── test_abfe_single_repeat_single_unit_dg_.tsv
        │   │   │   ├── test_abfe_single_repeat_single_unit_raw_.tsv
        │   │   │   ├── test_cmet_failed_edge_ddg_.tsv
        │   │   │   ├── test_cmet_failed_edge_raw_.tsv
        │   │   │   ├── test_cmet_full_results_ddg_.tsv
        │   │   │   ├── test_cmet_full_results_dg_.tsv
        │   │   │   ├── test_cmet_full_results_raw_.tsv
        │   │   │   ├── test_cmet_missing_all_complex_legs_allow_partial_ddg_.tsv
        │   │   │   ├── test_cmet_missing_all_complex_legs_fail_ddg_.tsv
        │   │   │   ├── test_cmet_missing_all_complex_legs_fail_dg_.tsv
        │   │   │   ├── test_cmet_missing_complex_leg_ddg_.tsv
        │   │   │   ├── test_cmet_missing_complex_leg_dg_.tsv
        │   │   │   ├── test_cmet_missing_complex_leg_raw_.tsv
        │   │   │   ├── test_cmet_missing_edge_ddg_.tsv
        │   │   │   ├── test_cmet_missing_edge_dg_.tsv
        │   │   │   ├── test_cmet_missing_edge_raw_.tsv
        │   │   │   ├── test_septop_full_results_ddg_current_.tsv
        │   │   │   ├── test_septop_full_results_ddg_pre_openfe_v1_11_.tsv
        │   │   │   ├── test_septop_full_results_dg_current_.tsv
        │   │   │   ├── test_septop_full_results_dg_pre_openfe_v1_11_.tsv
        │   │   │   ├── test_septop_full_results_raw_current_.tsv
        │   │   │   ├── test_septop_full_results_raw_pre_openfe_v1_11_.tsv
        │   │   │   ├── test_septop_single_repeat_ddg_current_.tsv
        │   │   │   ├── test_septop_single_repeat_ddg_pre_openfe_v1_11_.tsv
        │   │   │   ├── test_septop_single_repeat_dg_current_.tsv
        │   │   │   ├── test_septop_single_repeat_dg_pre_openfe_v1_11_.tsv
        │   │   │   ├── test_septop_single_repeat_raw_current_.tsv
        │   │   │   └── test_septop_single_repeat_raw_pre_openfe_v1_11_.tsv
        │   │   ├── test_gather.py
        │   │   ├── test_ligand_network_viewer.py
        │   │   ├── test_plan_rbfe_network.py
        │   │   ├── test_plan_rhfe_network.py
        │   │   ├── test_quickrun.py
        │   │   └── test_test.py
        │   ├── conftest.py
        │   ├── data/
        │   │   ├── __init__.py
        │   │   ├── bad_transformation.json
        │   │   ├── rbfe_tutorial/
        │   │   │   ├── __init__.py
        │   │   │   ├── tyk2_ligands.sdf
        │   │   │   └── tyk2_protein.pdb
        │   │   └── transformation.json
        │   ├── dev/
        │   │   ├── __init__.py
        │   │   └── write_transformation_json.py
        │   ├── parameters/
        │   │   ├── __init__.py
        │   │   ├── test_mapper.py
        │   │   ├── test_mol.py
        │   │   ├── test_molecules.py
        │   │   ├── test_output.py
        │   │   ├── test_output_dir.py
        │   │   ├── test_plan_network_options.py
        │   │   ├── test_protein.py
        │   │   └── test_utils.py
        │   ├── test_cli.py
        │   ├── test_fetchables.py
        │   ├── test_fetching.py
        │   ├── test_plugins.py
        │   ├── test_rbfe_tutorial.py
        │   ├── test_utils.py
        │   └── utils.py
        └── utils.py
Download .txt
Showing preview only (219K chars total). Download the full file or copy to clipboard to get everything.
SYMBOL INDEX (2321 symbols across 196 files)

FILE: devtools/data/fix_rbfe_results.py
  function untar (line 18) | def untar(fn):
  function retar (line 24) | def retar(loc, name):
  function replace_settings (line 30) | def replace_settings(fn, new_settings):
  function fix_rbfe_results (line 45) | def fix_rbfe_results():

FILE: devtools/data/gen_serialized_results.py
  function get_molecule (line 61) | def get_molecule(smi, name):
  function get_hif2a_inputs (line 69) | def get_hif2a_inputs():
  function execute_and_serialize (line 82) | def execute_and_serialize(
  function generate_md_settings (line 134) | def generate_md_settings():
  function generate_md_json (line 144) | def generate_md_json(smc):
  function generate_abfe_settings (line 152) | def generate_abfe_settings():
  function generate_abfe_json (line 177) | def generate_abfe_json():
  function generate_ahfe_settings (line 198) | def generate_ahfe_settings():
  function generate_ahfe_json (line 226) | def generate_ahfe_json(smc):
  function generate_rfe_settings (line 236) | def generate_rfe_settings():
  function generate_rfe_json (line 245) | def generate_rfe_json(smcA, smcB):
  function generate_septop_settings (line 260) | def generate_septop_settings():
  function generate_septop_json (line 285) | def generate_septop_json():

FILE: docs/_ext/sass.py
  function configure_path (line 28) | def configure_path(conf_dir: str, src: Optional[Union[PathLike, Path]]) ...
  function get_targets (line 38) | def get_targets(app: Sphinx) -> dict[Path, Path]:
  function build_sass_sources (line 53) | def build_sass_sources(app: Sphinx, env: BuildEnvironment):
  function setup (line 70) | def setup(app: Sphinx):

FILE: src/openfe/analysis/plotting.py
  function plot_lambda_transition_matrix (line 13) | def plot_lambda_transition_matrix(matrix: npt.NDArray) -> Axes:
  function plot_convergence (line 141) | def plot_convergence(
  function plot_replica_timeseries (line 239) | def plot_replica_timeseries(
  function plot_2D_rmsd (line 279) | def plot_2D_rmsd(data: list[list[float]], vmax=5.0) -> plt.Figure:
  function plot_ligand_COM_drift (line 336) | def plot_ligand_COM_drift(time: list[float], data: list[list[float]]):
  function plot_ligand_RMSD (line 350) | def plot_ligand_RMSD(time: list[float], data: list[list[float]]):

FILE: src/openfe/data/_downloader.py
  function retrieve_registry_data (line 6) | def retrieve_registry_data(zenodo_registry: list[dict], path: str) -> None:

FILE: src/openfe/due.py
  class InactiveDueCreditCollector (line 30) | class InactiveDueCreditCollector(object):
    method _donothing (line 33) | def _donothing(self, *args, **kwargs):
    method dcite (line 37) | def dcite(self, *args, **kwargs):
    method __repr__ (line 48) | def __repr__(self):
  function _donothing_func (line 52) | def _donothing_func(*args, **kwargs):

FILE: src/openfe/protocols/openmm_afe/abfe_units.py
  class ComplexComponentsMixin (line 47) | class ComplexComponentsMixin:
    method _get_components (line 48) | def _get_components(self):
  class ComplexSettingsMixin (line 83) | class ComplexSettingsMixin:
    method _get_settings (line 84) | def _get_settings(self) -> dict[str, SettingsBaseModel]:
  class ABFEComplexSetupUnit (line 126) | class ABFEComplexSetupUnit(ComplexComponentsMixin, ComplexSettingsMixin,...
    method _get_mda_universe (line 135) | def _get_mda_universe(
    method _get_idxs_from_residxs (line 182) | def _get_idxs_from_residxs(
    method _get_boresch_restraint (line 215) | def _get_boresch_restraint(
    method _add_restraints (line 270) | def _add_restraints(
  class ABFEComplexSimUnit (line 403) | class ABFEComplexSimUnit(
  class ABFEComplexAnalysisUnit (line 415) | class ABFEComplexAnalysisUnit(ComplexSettingsMixin, BaseAbsoluteMultiSta...
  class SolventComponentsMixin (line 424) | class SolventComponentsMixin:
    method _get_components (line 425) | def _get_components(self):
  class SolventSettingsMixin (line 453) | class SolventSettingsMixin:
    method _get_settings (line 454) | def _get_settings(self) -> dict[str, SettingsBaseModel]:
  class ABFESolventSetupUnit (line 494) | class ABFESolventSetupUnit(SolventComponentsMixin, SolventSettingsMixin,...
  class ABFESolventSimUnit (line 503) | class ABFESolventSimUnit(
  class ABFESolventAnalysisUnit (line 515) | class ABFESolventAnalysisUnit(SolventSettingsMixin, BaseAbsoluteMultiSta...

FILE: src/openfe/protocols/openmm_afe/afe_protocol_results.py
  class AbsoluteProtocolResultMixin (line 33) | class AbsoluteProtocolResultMixin:
    method __init__ (line 37) | def __init__(self, **data):
    method get_forward_and_reverse_energy_analysis (line 48) | def get_forward_and_reverse_energy_analysis(
    method get_overlap_matrices (line 104) | def get_overlap_matrices(self) -> dict[str, list[dict[str, npt.NDArray...
    method get_replica_transition_statistics (line 135) | def get_replica_transition_statistics(self) -> dict[str, list[dict[str...
    method get_replica_states (line 173) | def get_replica_states(self) -> dict[str, list[npt.NDArray]]:
    method equilibration_iterations (line 224) | def equilibration_iterations(self) -> dict[str, list[float]]:
    method production_iterations (line 247) | def production_iterations(self) -> dict[str, list[float]]:
    method selection_indices (line 272) | def selection_indices(self) -> dict[str, list[Optional[npt.NDArray]]]:
  class AbsoluteSolvationProtocolResult (line 297) | class AbsoluteSolvationProtocolResult(gufe.ProtocolResult, AbsoluteProto...
    method get_individual_estimates (line 305) | def get_individual_estimates(self) -> dict[str, list[tuple[Quantity, Q...
    method get_estimate (line 328) | def get_estimate(self):
    method get_uncertainty (line 352) | def get_uncertainty(self):
  class AbsoluteBindingProtocolResult (line 379) | class AbsoluteBindingProtocolResult(gufe.ProtocolResult, AbsoluteProtoco...
    method get_individual_estimates (line 387) | def get_individual_estimates(
    method _add_complex_standard_state_corr (line 434) | def _add_complex_standard_state_corr(
    method get_estimate (line 472) | def get_estimate(self) -> Quantity:
    method get_uncertainty (line 501) | def get_uncertainty(self) -> Quantity:
    method restraint_geometries (line 533) | def restraint_geometries(self) -> list[BoreschRestraintGeometry]:

FILE: src/openfe/protocols/openmm_afe/ahfe_units.py
  class VacuumComponentsMixin (line 27) | class VacuumComponentsMixin:
    method _get_components (line 28) | def _get_components(self):
  class VacuumSettingsMixin (line 61) | class VacuumSettingsMixin:
    method _get_settings (line 62) | def _get_settings(self) -> dict[str, SettingsBaseModel]:
  class AHFEVacuumSetupUnit (line 102) | class AHFEVacuumSetupUnit(VacuumComponentsMixin, VacuumSettingsMixin, Ba...
  class AHFEVacuumSimUnit (line 111) | class AHFEVacuumSimUnit(
  class AHFEVacuumAnalysisUnit (line 123) | class AHFEVacuumAnalysisUnit(VacuumSettingsMixin, BaseAbsoluteMultiState...
  class SolventComponentsMixin (line 132) | class SolventComponentsMixin:
    method _get_components (line 133) | def _get_components(self):
  class SolventSettingsMixin (line 162) | class SolventSettingsMixin:
    method _get_settings (line 163) | def _get_settings(self) -> dict[str, SettingsBaseModel]:
  class AHFESolventSetupUnit (line 203) | class AHFESolventSetupUnit(SolventComponentsMixin, SolventSettingsMixin,...
  class AHFESolventSimUnit (line 212) | class AHFESolventSimUnit(
  class AHFESolventAnalysisUnit (line 224) | class AHFESolventAnalysisUnit(SolventSettingsMixin, BaseAbsoluteMultiSta...

FILE: src/openfe/protocols/openmm_afe/base_afe_units.py
  class AbsoluteUnitMixin (line 95) | class AbsoluteUnitMixin:
    method _prepare (line 96) | def _prepare(
    method _get_settings (line 127) | def _get_settings(self) -> dict[str, SettingsBaseModel]:
    method _verify_execution_environment (line 154) | def _verify_execution_environment(
  class BaseAbsoluteSetupUnit (line 174) | class BaseAbsoluteSetupUnit(gufe.ProtocolUnit, AbsoluteUnitMixin):
    method _get_components (line 180) | def _get_components(
    method _get_alchemical_indices (line 198) | def _get_alchemical_indices(
    method _pre_equilibrate (line 233) | def _pre_equilibrate(
    method _assign_partial_charges (line 352) | def _assign_partial_charges(
    method _get_system_generator (line 379) | def _get_system_generator(
    method _get_modeller (line 424) | def _get_modeller(
    method _get_omm_objects (line 468) | def _get_omm_objects(
    method _add_restraints (line 537) | def _add_restraints(
    method _get_alchemical_system (line 555) | def _get_alchemical_system(
    method _subsample_topology (line 622) | def _subsample_topology(
    method run (line 659) | def run(
    method _execute (line 795) | def _execute(
  class BaseAbsoluteMultiStateSimulationUnit (line 815) | class BaseAbsoluteMultiStateSimulationUnit(gufe.ProtocolUnit, AbsoluteUn...
    method _check_restart (line 817) | def _check_restart(output_settings: SettingsBaseModel, shared_path: pa...
    method _get_components (line 861) | def _get_components(
    method _get_lambda_schedule (line 878) | def _get_lambda_schedule(
    method _get_states (line 913) | def _get_states(
    method _get_integrator (line 994) | def _get_integrator(
    method _get_reporter (line 1051) | def _get_reporter(
    method _get_sampler (line 1123) | def _get_sampler(
    method _run_simulation (line 1275) | def _run_simulation(
    method run (line 1351) | def run(
    method _execute (line 1515) | def _execute(
  class BaseAbsoluteMultiStateAnalysisUnit (line 1556) | class BaseAbsoluteMultiStateAnalysisUnit(gufe.ProtocolUnit, AbsoluteUnit...
    method _analyze_multistate_energies (line 1558) | def _analyze_multistate_energies(
    method run (line 1604) | def run(
    method _execute (line 1663) | def _execute(

FILE: src/openfe/protocols/openmm_afe/equil_afe_settings.py
  class AlchemicalSettings (line 44) | class AlchemicalSettings(SettingsBaseModel):
  class LambdaSettings (line 90) | class LambdaSettings(SettingsBaseModel):
    method must_be_between_0_and_1 (line 147) | def must_be_between_0_and_1(cls, v):
    method must_be_monotonic (line 157) | def must_be_monotonic(cls, v):
  class ABFEPreEquilOutputSettings (line 171) | class ABFEPreEquilOutputSettings(MDOutputSettings):
    method must_be_all (line 207) | def must_be_all(cls, v):
  class AbsoluteSolvationSettings (line 218) | class AbsoluteSolvationSettings(SettingsBaseModel):
    method must_be_positive (line 235) | def must_be_positive(cls, v):
  class AbsoluteBindingSettings (line 329) | class AbsoluteBindingSettings(SettingsBaseModel):
    method must_be_positive (line 346) | def must_be_positive(cls, v):

FILE: src/openfe/protocols/openmm_afe/equil_binding_afe_method.py
  class AbsoluteBindingProtocol (line 99) | class AbsoluteBindingProtocol(gufe.Protocol):
    method _default_settings (line 117) | def _default_settings(cls):
    method _adaptive_settings (line 214) | def _adaptive_settings(
    method _validate_endstates (line 254) | def _validate_endstates(
    method _validate_lambda_schedule (line 320) | def _validate_lambda_schedule(
    method _validate (line 381) | def _validate(
    method _create (line 472) | def _create(
    method _gather (line 543) | def _gather(

FILE: src/openfe/protocols/openmm_afe/equil_solvation_afe_method.py
  class AbsoluteSolvationProtocol (line 106) | class AbsoluteSolvationProtocol(gufe.Protocol):
    method _default_settings (line 124) | def _default_settings(cls):
    method _validate_endstates (line 206) | def _validate_endstates(
    method _validate_lambda_schedule (line 290) | def _validate_lambda_schedule(
    method _validate (line 363) | def _validate(
    method _create (line 435) | def _create(
    method _gather (line 507) | def _gather(

FILE: src/openfe/protocols/openmm_md/plain_md_methods.py
  class PlainMDProtocolResult (line 69) | class PlainMDProtocolResult(gufe.ProtocolResult):
    method __init__ (line 77) | def __init__(self, **data):
    method get_estimate (line 83) | def get_estimate(self):
    method get_uncertainty (line 93) | def get_uncertainty(self):
    method get_traj_filename (line 98) | def get_traj_filename(self) -> list[pathlib.Path]:
    method get_pdb_filename (line 111) | def get_pdb_filename(self) -> list[pathlib.Path]:
  class PlainMDProtocol (line 125) | class PlainMDProtocol(gufe.Protocol):
    method _default_settings (line 142) | def _default_settings(cls):
    method _validate (line 173) | def _validate(
    method _create (line 225) | def _create(
    method _gather (line 276) | def _gather(self, protocol_dag_results: Iterable[gufe.ProtocolDAGResul...
  class PlainMDUnitMixin (line 299) | class PlainMDUnitMixin:
    method _prepare (line 300) | def _prepare(
  class PlainMDSetupUnit (line 331) | class PlainMDSetupUnit(PlainMDUnitMixin, gufe.ProtocolUnit):
    method _assign_partial_charges (line 337) | def _assign_partial_charges(
    method run (line 362) | def run(
    method _execute (line 528) | def _execute(
  class PlainMDSimulationUnit (line 548) | class PlainMDSimulationUnit(PlainMDUnitMixin, gufe.ProtocolUnit):
    method _check_restart (line 554) | def _check_restart(output_settings: MDOutputSettings, shared_path: pat...
    method _verify_execution_environment (line 576) | def _verify_execution_environment(
    method _save_pdb_subset (line 596) | def _save_pdb_subset(
    method _run_dynamics (line 619) | def _run_dynamics(
    method _get_remaining_steps (line 658) | def _get_remaining_steps(
    method _run_MD (line 696) | def _run_MD(
    method run (line 911) | def run(
    method _execute (line 1057) | def _execute(

FILE: src/openfe/protocols/openmm_md/plain_md_settings.py
  class PlainMDProtocolSettings (line 25) | class PlainMDProtocolSettings(Settings):
    method must_be_positive (line 34) | def must_be_positive(cls, v):

FILE: src/openfe/protocols/openmm_rfe/_rfe_utils/lambdaprotocol.py
  class LambdaProtocol (line 15) | class LambdaProtocol(object):
    method __init__ (line 46) | def __init__(self, functions='default', windows=10, lambda_schedule=No...
    method _validate_schedule (line 155) | def _validate_schedule(schedule, windows):
    method _validate_functions (line 194) | def _validate_functions(self, n=10):
    method _check_for_naked_charges (line 232) | def _check_for_naked_charges(self):
    method get_functions (line 262) | def get_functions(self):
    method plot_functions (line 265) | def plot_functions(self, lambda_schedule=None):
  class RelativeAlchemicalState (line 292) | class RelativeAlchemicalState(AlchemicalState):
    class _LambdaParameter (line 315) | class _LambdaParameter(AlchemicalState._LambdaParameter):
    method set_alchemical_parameters (line 327) | def set_alchemical_parameters(self, global_lambda,

FILE: src/openfe/protocols/openmm_rfe/_rfe_utils/multistate.py
  class HybridCompatibilityMixin (line 29) | class HybridCompatibilityMixin:
    method __init__ (line 35) | def __init__(
    method setup (line 46) | def setup(self, reporter, lambda_protocol,
  class HybridRepexSampler (line 169) | class HybridRepexSampler(HybridCompatibilityMixin,
    method __init__ (line 176) | def __init__(
  class HybridSAMSSampler (line 191) | class HybridSAMSSampler(HybridCompatibilityMixin, sams.SAMSSampler):
    method __init__ (line 197) | def __init__(
  class HybridMultiStateSampler (line 212) | class HybridMultiStateSampler(HybridCompatibilityMixin,
    method __init__ (line 218) | def __init__(
  function create_endstates (line 233) | def create_endstates(first_thermostate, last_thermostate):
  function minimize (line 306) | def minimize(thermodynamic_state: states.ThermodynamicState,

FILE: src/openfe/protocols/openmm_rfe/_rfe_utils/relative.py
  class HybridTopologyFactory (line 24) | class HybridTopologyFactory:
    method __init__ (line 85) | def __init__(self,
    method _verify_cmap_compatibility (line 236) | def _verify_cmap_compatibility(
    method _handle_cmap_torsion_force (line 287) | def _handle_cmap_torsion_force(self):
    method _check_bounds (line 385) | def _check_bounds(value, varname, minmax=(0, 1)):
    method _invert_dict (line 407) | def _invert_dict(dictionary):
    method _set_mappings (line 418) | def _set_mappings(self, old_to_new_map, core_old_to_new_map):
    method _check_and_store_system_forces (line 465) | def _check_and_store_system_forces(self):
    method _add_particles (line 501) | def _add_particles(self):
    method _handle_box (line 548) | def _handle_box(self):
    method _set_atom_classes (line 570) | def _set_atom_classes(self):
    method _generate_dict_from_exceptions (line 613) | def _generate_dict_from_exceptions(force):
    method _validate_disjoint_sets (line 637) | def _validate_disjoint_sets(self):
    method _handle_constraints (line 673) | def _handle_constraints(self):
    method _copy_threeparticleavg (line 715) | def _copy_threeparticleavg(atm_map, env_atoms, vs):
    method _handle_virtual_sites (line 747) | def _handle_virtual_sites(self):
    method _add_bond_force_terms (line 803) | def _add_bond_force_terms(self):
    method _add_angle_force_terms (line 838) | def _add_angle_force_terms(self):
    method _add_torsion_force_terms (line 878) | def _add_torsion_force_terms(self):
    method _nonbonded_custom (line 922) | def _nonbonded_custom(v2):
    method _nonbonded_custom_sterics_common (line 958) | def _nonbonded_custom_sterics_common():
    method _nonbonded_custom_mixing_rules (line 984) | def _nonbonded_custom_mixing_rules():
    method _translate_nonbonded_method_to_custom (line 1011) | def _translate_nonbonded_method_to_custom(standard_nonbonded_method):
    method _add_nonbonded_force_terms (line 1041) | def _add_nonbonded_force_terms(self):
    method _find_bond_parameters (line 1162) | def _find_bond_parameters(bond_force, index1, index2):
    method _handle_harmonic_bonds (line 1190) | def _handle_harmonic_bonds(self):
    method _find_angle_parameters (line 1325) | def _find_angle_parameters(angle_force, indices):
    method _handle_harmonic_angles (line 1356) | def _handle_harmonic_angles(self):
    method _find_torsion_parameters (line 1527) | def _find_torsion_parameters(torsion_force, indices):
    method _handle_periodic_torsion_force (line 1561) | def _handle_periodic_torsion_force(self):
    method _handle_nonbonded (line 1680) | def _handle_nonbonded(self):
    method _handle_interaction_groups (line 1842) | def _handle_interaction_groups(self):
    method _handle_hybrid_exceptions (line 1892) | def _handle_hybrid_exceptions(self):
    method _find_exception (line 2009) | def _find_exception(force, index1, index2):
    method _handle_original_exceptions (line 2037) | def _handle_original_exceptions(self):
    method _handle_old_new_exceptions (line 2221) | def _handle_old_new_exceptions(self):
    method _compute_hybrid_positions (line 2363) | def _compute_hybrid_positions(self):
    method _create_mdtraj_topology (line 2402) | def _create_mdtraj_topology(self):
    method _create_hybrid_topology (line 2497) | def _create_hybrid_topology(self):
    method old_positions (line 2578) | def old_positions(self, hybrid_positions):
    method new_positions (line 2605) | def new_positions(self, hybrid_positions):
    method hybrid_system (line 2633) | def hybrid_system(self):
    method new_to_hybrid_atom_map (line 2645) | def new_to_hybrid_atom_map(self):
    method old_to_hybrid_atom_map (line 2657) | def old_to_hybrid_atom_map(self):
    method hybrid_positions (line 2669) | def hybrid_positions(self):
    method hybrid_topology (line 2684) | def hybrid_topology(self):
    method omm_hybrid_topology (line 2698) | def omm_hybrid_topology(self):
    method has_virtual_sites (line 2716) | def has_virtual_sites(self):

FILE: src/openfe/protocols/openmm_rfe/_rfe_utils/topologyhelpers.py
  function _get_ion_and_water_parameters (line 29) | def _get_ion_and_water_parameters(
  function _fix_alchemical_water_atom_mapping (line 96) | def _fix_alchemical_water_atom_mapping(
  function handle_alchemical_waters (line 125) | def handle_alchemical_waters(
  function get_alchemical_waters (line 225) | def get_alchemical_waters(
  function combined_topology (line 297) | def combined_topology(topology1: app.Topology,
  function _get_indices (line 385) | def _get_indices(topology, resids):
  function _remove_constraints (line 406) | def _remove_constraints(old_to_new_atom_map, old_system, old_topology,
  function get_system_mappings (line 525) | def get_system_mappings(old_to_new_atom_map,
  function set_and_check_new_positions (line 668) | def set_and_check_new_positions(mapping, old_topology, new_topology,

FILE: src/openfe/protocols/openmm_rfe/equil_rfe_settings.py
  class LambdaSettings (line 34) | class LambdaSettings(SettingsBaseModel):
  class AlchemicalSettings (line 51) | class AlchemicalSettings(SettingsBaseModel):
  class RelativeHybridTopologyProtocolSettings (line 105) | class RelativeHybridTopologyProtocolSettings(Settings):
    method must_be_positive (line 114) | def must_be_positive(cls, v):

FILE: src/openfe/protocols/openmm_rfe/hybridtop_protocol_results.py
  class RelativeHybridTopologyProtocolResult (line 22) | class RelativeHybridTopologyProtocolResult(gufe.ProtocolResult):
    method __init__ (line 27) | def __init__(self, **data):
    method compute_mean_estimate (line 35) | def compute_mean_estimate(dGs: list[Quantity]) -> Quantity:
    method get_estimate (line 43) | def get_estimate(self) -> Quantity:
    method compute_uncertainty (line 57) | def compute_uncertainty(dGs: list[Quantity]) -> Quantity:
    method get_uncertainty (line 65) | def get_uncertainty(self) -> Quantity:
    method get_individual_estimates (line 73) | def get_individual_estimates(self) -> list[tuple[Quantity, Quantity]]:
    method get_forward_and_reverse_energy_analysis (line 90) | def get_forward_and_reverse_energy_analysis(
    method get_overlap_matrices (line 137) | def get_overlap_matrices(self) -> list[dict[str, npt.NDArray]]:
    method get_replica_transition_statistics (line 157) | def get_replica_transition_statistics(self) -> list[dict[str, npt.NDAr...
    method get_replica_states (line 184) | def get_replica_states(self) -> list[npt.NDArray]:
    method equilibration_iterations (line 215) | def equilibration_iterations(self) -> list[float]:
    method production_iterations (line 230) | def production_iterations(self) -> list[float]:

FILE: src/openfe/protocols/openmm_rfe/hybridtop_protocols.py
  class RelativeHybridTopologyProtocol (line 85) | class RelativeHybridTopologyProtocol(gufe.Protocol):
    method _default_settings (line 105) | def _default_settings(cls):
    method _adaptive_settings (line 138) | def _adaptive_settings(
    method _validate_endstates (line 211) | def _validate_endstates(
    method _validate_mapping (line 255) | def _validate_mapping(
    method _validate_smcs (line 318) | def _validate_smcs(
    method _validate_charge_difference (line 371) | def _validate_charge_difference(
    method _validate_simulation_settings (line 448) | def _validate_simulation_settings(
    method _validate (line 514) | def _validate(
    method _create (line 600) | def _create(
    method _gather (line 660) | def _gather(self, protocol_dag_results: Iterable[gufe.ProtocolDAGResul...

FILE: src/openfe/protocols/openmm_rfe/hybridtop_units.py
  class HybridTopologyUnitMixin (line 84) | class HybridTopologyUnitMixin:
    method _prepare (line 85) | def _prepare(
    method _get_settings (line 116) | def _get_settings(
    method _verify_execution_environment (line 147) | def _verify_execution_environment(
  class HybridTopologySetupUnit (line 167) | class HybridTopologySetupUnit(gufe.ProtocolUnit, HybridTopologyUnitMixin):
    method _get_components (line 173) | def _get_components(
    method _assign_partial_charges (line 208) | def _assign_partial_charges(
    method _get_system_generator (line 235) | def _get_system_generator(
    method _create_stateA_system (line 280) | def _create_stateA_system(
    method _create_stateB_system (line 337) | def _create_stateB_system(
    method _handle_net_charge (line 385) | def _handle_net_charge(
    method _get_omm_objects (line 431) | def _get_omm_objects(
    method _get_alchemical_system (line 581) | def _get_alchemical_system(
    method _subsample_topology (line 644) | def _subsample_topology(
    method run (line 704) | def run(
    method _execute (line 825) | def _execute(
  class HybridTopologyMultiStateSimulationUnit (line 843) | class HybridTopologyMultiStateSimulationUnit(gufe.ProtocolUnit, HybridTo...
    method _check_restart (line 850) | def _check_restart(output_settings: SettingsBaseModel, shared_path: pa...
    method _get_integrator (line 893) | def _get_integrator(
    method _get_reporter (line 948) | def _get_reporter(
    method _get_sampler (line 1014) | def _get_sampler(
    method _run_simulation (line 1181) | def _run_simulation(
    method run (line 1265) | def run(
    method _execute (line 1417) | def _execute(
  class HybridTopologyMultiStateAnalysisUnit (line 1449) | class HybridTopologyMultiStateAnalysisUnit(gufe.ProtocolUnit, HybridTopo...
    method _analyze_multistate_energies (line 1455) | def _analyze_multistate_energies(
    method _structural_analysis (line 1502) | def _structural_analysis(
    method run (line 1569) | def run(
    method _execute (line 1649) | def _execute(

FILE: src/openfe/protocols/openmm_septop/base_units.py
  function _pre_equilibrate (line 78) | def _pre_equilibrate(
  class SepTopUnitMixin (line 233) | class SepTopUnitMixin:
    method _prepare (line 238) | def _prepare(
    method _get_settings (line 269) | def _get_settings(self) -> dict[str, SettingsBaseModel]:
    method _verify_execution_environment (line 296) | def _verify_execution_environment(
  class BaseSepTopSetupUnit (line 316) | class BaseSepTopSetupUnit(gufe.ProtocolUnit, SepTopUnitMixin):
    method _get_alchemical_system (line 321) | def _get_alchemical_system(
    method _get_components (line 400) | def _get_components(
    method _get_system_generator (line 417) | def _get_system_generator(
    method _assign_partial_charges (line 455) | def _assign_partial_charges(
    method _get_modeller (line 480) | def _get_modeller(
    method _get_omm_objects (line 541) | def _get_omm_objects(
    method _get_atom_indices (line 584) | def _get_atom_indices(
    method get_smc_comps (line 614) | def get_smc_comps(
    method get_system (line 637) | def get_system(
    method _subsample_topology (line 685) | def _subsample_topology(
    method _execute (line 722) | def _execute(
  class BaseSepTopRunUnit (line 742) | class BaseSepTopRunUnit(gufe.ProtocolUnit, SepTopUnitMixin):
    method _check_restart (line 748) | def _check_restart(output_settings: SettingsBaseModel, shared_path: pa...
    method _get_components (line 792) | def _get_components(
    method _get_lambda_schedule (line 810) | def _get_lambda_schedule(
    method _get_states (line 831) | def _get_states(
    method _get_integrator (line 891) | def _get_integrator(
    method _get_reporter (line 942) | def _get_reporter(
    method _get_sampler (line 1014) | def _get_sampler(
    method _run_simulation (line 1165) | def _run_simulation(
    method run (line 1246) | def run(
    method _execute (line 1425) | def _execute(
  class BaseSepTopAnalysisUnit (line 1458) | class BaseSepTopAnalysisUnit(gufe.ProtocolUnit, SepTopUnitMixin):
    method _analyze_multistate_energies (line 1460) | def _analyze_multistate_energies(
    method run (line 1506) | def run(
    method _execute (line 1565) | def _execute(

FILE: src/openfe/protocols/openmm_septop/equil_septop_method.py
  function _check_alchemical_charge_difference (line 110) | def _check_alchemical_charge_difference(
  class SepTopProtocol (line 142) | class SepTopProtocol(gufe.Protocol):
    method _default_settings (line 162) | def _default_settings(cls):
    method _adaptive_settings (line 273) | def _adaptive_settings(
    method _validate_endstates (line 313) | def _validate_endstates(
    method _validate_lambda_schedule (line 385) | def _validate_lambda_schedule(
    method _validate (line 461) | def _validate(
    method _create (line 514) | def _create(
    method _gather (line 594) | def _gather(

FILE: src/openfe/protocols/openmm_septop/equil_septop_settings.py
  class LambdaSettings (line 40) | class LambdaSettings(SettingsBaseModel):
    method must_be_between_0_and_1 (line 139) | def must_be_between_0_and_1(cls, v):
    method must_be_monotonically_increasing_A (line 153) | def must_be_monotonically_increasing_A(cls, v):
    method must_be_monotonically_decreasing_B (line 172) | def must_be_monotonically_decreasing_B(cls, v):
  class SepTopEquilOutputSettings (line 187) | class SepTopEquilOutputSettings(MDOutputSettings):
    method must_be_all (line 239) | def must_be_all(cls, v):
  class SepTopSettings (line 246) | class SepTopSettings(SettingsBaseModel):
    method must_be_positive (line 263) | def must_be_positive(cls, v):

FILE: src/openfe/protocols/openmm_septop/septop_protocol_results.py
  class SepTopProtocolResult (line 30) | class SepTopProtocolResult(gufe.ProtocolResult):
    method __init__ (line 33) | def __init__(self, **data):
    method get_individual_estimates (line 44) | def get_individual_estimates(
    method _add_complex_standard_state_corr (line 101) | def _add_complex_standard_state_corr(
    method _add_solvent_standard_state_corr (line 145) | def _add_solvent_standard_state_corr(
    method get_estimate (line 183) | def get_estimate(self) -> Quantity:
    method get_uncertainty (line 219) | def get_uncertainty(self) -> Quantity:
    method get_forward_and_reverse_energy_analysis (line 256) | def get_forward_and_reverse_energy_analysis(
    method get_overlap_matrices (line 310) | def get_overlap_matrices(self) -> dict[str, list[dict[str, npt.NDArray...
    method get_replica_transition_statistics (line 339) | def get_replica_transition_statistics(
    method get_replica_states (line 377) | def get_replica_states(self) -> dict[str, list[npt.NDArray]]:
    method equilibration_iterations (line 423) | def equilibration_iterations(self) -> dict[str, list[float]]:
    method production_iterations (line 444) | def production_iterations(self) -> dict[str, list[float]]:
    method restraint_geometries (line 467) | def restraint_geometries(
    method selection_indices (line 495) | def selection_indices(self) -> dict[str, list[Optional[npt.NDArray]]]:

FILE: src/openfe/protocols/openmm_septop/septop_units.py
  class SepTopComplexMixin (line 66) | class SepTopComplexMixin:
    method _get_components (line 71) | def _get_components(self):
    method _get_settings (line 102) | def _get_settings(self) -> dict[str, SettingsBaseModel]:
  class SepTopSolventMixin (line 150) | class SepTopSolventMixin:
    method _get_components (line 155) | def _get_components(self):
    method _get_settings (line 188) | def _get_settings(self) -> dict[str, SettingsBaseModel]:
  class SepTopComplexSetupUnit (line 236) | class SepTopComplexSetupUnit(SepTopComplexMixin, BaseSepTopSetupUnit):
    method get_system_AB (line 243) | def get_system_AB(
    method _get_selection_atom_indices (line 301) | def _get_selection_atom_indices(
    method _update_positions (line 335) | def _update_positions(
    method _get_mda_universe (line 375) | def _get_mda_universe(
    method _get_boresch_restraint (line 423) | def _get_boresch_restraint(
    method _add_restraints (line 477) | def _add_restraints(
    method run (line 638) | def run(
  class SepTopSolventSetupUnit (line 876) | class SepTopSolventSetupUnit(SepTopSolventMixin, BaseSepTopSetupUnit):
    method _update_positions (line 884) | def _update_positions(
    method _add_restraints (line 930) | def _add_restraints(
    method run (line 1014) | def run(
  class SepTopSolventRunUnit (line 1152) | class SepTopSolventRunUnit(SepTopSolventMixin, BaseSepTopRunUnit):
    method _get_lambda_schedule (line 1159) | def _get_lambda_schedule(
  class SepTopComplexRunUnit (line 1187) | class SepTopComplexRunUnit(SepTopComplexMixin, BaseSepTopRunUnit):
    method _get_lambda_schedule (line 1194) | def _get_lambda_schedule(
  class SepTopSolventAnalysisUnit (line 1223) | class SepTopSolventAnalysisUnit(SepTopSolventMixin, BaseSepTopAnalysisUn...
  class SepTopComplexAnalysisUnit (line 1231) | class SepTopComplexAnalysisUnit(SepTopComplexMixin, BaseSepTopAnalysisUn...

FILE: src/openfe/protocols/openmm_septop/utils.py
  class SepTopParameterState (line 5) | class SepTopParameterState(GlobalParameterState):
    class _LambdaParameter (line 53) | class _LambdaParameter(states.GlobalParameterState.GlobalParameter):
      method __init__ (line 57) | def __init__(self, parameter_name):
      method lambda_validator (line 61) | def lambda_validator(self, instance, parameter_value):

FILE: src/openfe/protocols/openmm_utils/charge_generation.py
  function assign_offmol_espaloma_charges (line 74) | def assign_offmol_espaloma_charges(offmol: OFFMol, toolkit_registry: Too...
  function assign_offmol_nagl_charges (line 112) | def assign_offmol_nagl_charges(
  function assign_offmol_am1bcc_charges (line 164) | def assign_offmol_am1bcc_charges(
  function _generate_offmol_conformers (line 207) | def _generate_offmol_conformers(
  function assign_offmol_partial_charges (line 286) | def assign_offmol_partial_charges(
  function bulk_assign_partial_charges (line 441) | def bulk_assign_partial_charges(

FILE: src/openfe/protocols/openmm_utils/mdtraj_utils.py
  function mdtraj_from_openmm (line 10) | def mdtraj_from_openmm(

FILE: src/openfe/protocols/openmm_utils/multistate_analysis.py
  class MultistateEquilFEAnalysis (line 61) | class MultistateEquilFEAnalysis:
    method __init__ (line 87) | def __init__(
    method plot (line 105) | def plot(self, filepath: Path, filename_prefix: str):
    method _analyze (line 156) | def _analyze(self, forward_reverse_samples: int):
    method _get_free_energy (line 198) | def _get_free_energy(
    method get_equil_free_energy (line 266) | def get_equil_free_energy(self) -> tuple[Quantity, Quantity]:
    method get_forward_and_reverse_analysis (line 285) | def get_forward_and_reverse_analysis(
    method get_overlap_matrix (line 378) | def get_overlap_matrix(self) -> dict[str, npt.NDArray]:
    method get_exchanges (line 394) | def get_exchanges(self) -> dict[str, npt.NDArray]:
    method replica_states (line 417) | def replica_states(self):
    method equilibration_iterations (line 424) | def equilibration_iterations(self):
    method production_iterations (line 431) | def production_iterations(self):
    method free_energy (line 438) | def free_energy(self):
    method free_energy_error (line 445) | def free_energy_error(self):
    method forward_and_reverse_free_energies (line 452) | def forward_and_reverse_free_energies(self):
    method free_energy_overlaps (line 460) | def free_energy_overlaps(self):
    method replica_exchange_statistics (line 468) | def replica_exchange_statistics(self):
    method unit_results_dict (line 483) | def unit_results_dict(self):
    method close (line 498) | def close(self):

FILE: src/openfe/protocols/openmm_utils/omm_compute.py
  function get_openmm_platform (line 12) | def get_openmm_platform(

FILE: src/openfe/protocols/openmm_utils/omm_settings.py
  class BaseSolvationSettings (line 47) | class BaseSolvationSettings(SettingsBaseModel):
  class OpenMMSolvationSettings (line 55) | class OpenMMSolvationSettings(BaseSolvationSettings):
    method supported_vectors (line 184) | def supported_vectors(cls, v):
    method is_positive_distance (line 192) | def is_positive_distance(cls, v):
    method positive_solvent_number (line 206) | def positive_solvent_number(cls, v):
    method box_size_properties (line 217) | def box_size_properties(cls, v):
  class BasePartialChargeSettings (line 228) | class BasePartialChargeSettings(SettingsBaseModel):
  class OpenFFPartialChargeSettings (line 236) | class OpenFFPartialChargeSettings(BasePartialChargeSettings):
  class OpenMMEngineSettings (line 319) | class OpenMMEngineSettings(SettingsBaseModel):
    method supported_sampler (line 350) | def supported_sampler(cls, v):
  class IntegratorSettings (line 358) | class IntegratorSettings(SettingsBaseModel):
    method must_be_positive_or_zero (line 414) | def must_be_positive_or_zero(cls, v):
    method must_be_positive (line 424) | def must_be_positive(cls, v):
    method is_time (line 431) | def is_time(cls, v):
    method must_be_inverse_time (line 438) | def must_be_inverse_time(cls, v):
    method validate_surface_tension (line 444) | def validate_surface_tension(self):
  class OutputSettings (line 457) | class OutputSettings(SettingsBaseModel):
    method must_be_positive (line 487) | def must_be_positive(cls, v):
  class MultiStateOutputSettings (line 494) | class MultiStateOutputSettings(OutputSettings):
    method must_be_positive (line 533) | def must_be_positive(cls, v):
  class SimulationSettings (line 543) | class SimulationSettings(SettingsBaseModel):
    method is_time (line 564) | def is_time(cls, v):
    method must_be_positive (line 571) | def must_be_positive(cls, v):
  class MultiStateSimulationSettings (line 578) | class MultiStateSimulationSettings(SimulationSettings):
    method supported_flatness (line 661) | def supported_flatness(cls, v):
    method supported_sampler (line 669) | def supported_sampler(cls, v):
    method must_be_positive (line 677) | def must_be_positive(cls, v):
    method must_be_zero_or_positive (line 689) | def must_be_zero_or_positive(cls, v):
  class MDSimulationSettings (line 699) | class MDSimulationSettings(SimulationSettings):
  class MDOutputSettings (line 715) | class MDOutputSettings(OutputSettings):

FILE: src/openfe/protocols/openmm_utils/serialization.py
  function serialize (line 13) | def serialize(item, filename: pathlib.Path):
  function deserialize (line 43) | def deserialize(filename: pathlib.Path):
  function make_vec3_box (line 75) | def make_vec3_box(dimensions: NanometerArrayQuantity) -> Vec3:

FILE: src/openfe/protocols/openmm_utils/settings_validation.py
  function validate_openmm_solvation_settings (line 20) | def validate_openmm_solvation_settings(settings: OpenMMSolvationSettings...
  function validate_timestep (line 55) | def validate_timestep(hmass: float, timestep: Quantity):
  function get_simsteps (line 80) | def get_simsteps(sim_length: Quantity, timestep: Quantity, mc_steps: int...
  function divmod_time (line 117) | def divmod_time(
  function divmod_time_and_check (line 146) | def divmod_time_and_check(
  function convert_checkpoint_interval_to_iterations (line 184) | def convert_checkpoint_interval_to_iterations(
  function convert_steps_per_iteration (line 220) | def convert_steps_per_iteration(
  function convert_real_time_analysis_iterations (line 244) | def convert_real_time_analysis_iterations(
  function convert_target_error_from_kcal_per_mole_to_kT (line 287) | def convert_target_error_from_kcal_per_mole_to_kT(

FILE: src/openfe/protocols/openmm_utils/system_creation.py
  function get_system_generator (line 35) | def get_system_generator(
  function get_omm_modeller (line 154) | def get_omm_modeller(

FILE: src/openfe/protocols/openmm_utils/system_validation.py
  function get_alchemical_components (line 30) | def get_alchemical_components(
  function validate_solvent (line 88) | def validate_solvent(state: ChemicalSystem, nonbonded_method: str):
  function validate_protein (line 143) | def validate_protein(state: ChemicalSystem):
  function validate_barostat (line 165) | def validate_barostat(state: ChemicalSystem, barostat: str):
  function get_components (line 215) | def get_components(state: ChemicalSystem) -> ParseCompRet:
  function assert_multistate_system_equality (line 250) | def assert_multistate_system_equality(
  function validate_chemical_system (line 353) | def validate_chemical_system(system: ChemicalSystem):

FILE: src/openfe/protocols/restraint_utils/geometry/base.py
  class BaseRestraintGeometry (line 16) | class BaseRestraintGeometry(BaseModel, abc.ABC):
  class HostGuestRestraintGeometry (line 24) | class HostGuestRestraintGeometry(BaseRestraintGeometry):
    method positive_idxs (line 46) | def positive_idxs(cls, v):

FILE: src/openfe/protocols/restraint_utils/geometry/boresch/geometry.py
  class BoreschRestraintGeometry (line 31) | class BoreschRestraintGeometry(HostGuestRestraintGeometry):
  function _get_restraint_distances (line 74) | def _get_restraint_distances(
  function find_boresch_restraint (line 136) | def find_boresch_restraint(

FILE: src/openfe/protocols/restraint_utils/geometry/boresch/guest.py
  function _sort_by_distance_from_atom (line 26) | def _sort_by_distance_from_atom(
  function _bonded_angles_from_pool (line 59) | def _bonded_angles_from_pool(
  function _get_guest_atom_pool (line 125) | def _get_guest_atom_pool(
  function find_guest_atom_candidates (line 173) | def find_guest_atom_candidates(

FILE: src/openfe/protocols/restraint_utils/geometry/boresch/host.py
  function _host_atoms_search (line 35) | def _host_atoms_search(
  function find_host_atom_candidates (line 84) | def find_host_atom_candidates(
  class EvaluateBoreschAtoms (line 200) | class EvaluateBoreschAtoms(AnalysisBase):
    method __init__ (line 215) | def __init__(
    method _prepare (line 236) | def _prepare(self):
    method _single_frame (line 246) | def _single_frame(self):
    method _conclude (line 281) | def _conclude(self):
  class EvaluateHostAtoms1 (line 335) | class EvaluateHostAtoms1(AnalysisBase):
    method __init__ (line 354) | def __init__(
    method _prepare (line 375) | def _prepare(self):
    method _single_frame (line 390) | def _single_frame(self):
    method _conclude (line 420) | def _conclude(self):
  class EvaluateHostAtoms2 (line 464) | class EvaluateHostAtoms2(EvaluateHostAtoms1):
    method _prepare (line 483) | def _prepare(self):
    method _single_frame (line 498) | def _single_frame(self):
    method _conclude (line 527) | def _conclude(self):
  function _get_lowest_variance_restraint_hostanchor (line 554) | def _get_lowest_variance_restraint_hostanchor(
  function find_host_anchor_bonded (line 622) | def find_host_anchor_bonded(
  function find_host_anchor_multi (line 697) | def find_host_anchor_multi(

FILE: src/openfe/protocols/restraint_utils/geometry/flatbottom.py
  class FlatBottomDistanceGeometry (line 24) | class FlatBottomDistanceGeometry(DistanceRestraintGeometry):
  class COMDistanceAnalysis (line 33) | class COMDistanceAnalysis(AnalysisBase):
    method __init__ (line 47) | def __init__(self, group1, group2, **kwargs):
    method _prepare (line 53) | def _prepare(self):
    method _single_frame (line 56) | def _single_frame(self):
    method _conclude (line 64) | def _conclude(self):
  function get_flatbottom_distance_restraint (line 68) | def get_flatbottom_distance_restraint(

FILE: src/openfe/protocols/restraint_utils/geometry/harmonic.py
  class DistanceRestraintGeometry (line 20) | class DistanceRestraintGeometry(HostGuestRestraintGeometry):
  function get_distance_restraint (line 26) | def get_distance_restraint(
  function get_molecule_centers_restraint (line 69) | def get_molecule_centers_restraint(

FILE: src/openfe/protocols/restraint_utils/geometry/utils.py
  function _get_mda_selection (line 35) | def _get_mda_selection(
  function get_aromatic_rings (line 76) | def get_aromatic_rings(rdmol: Chem.Mol) -> list[set[int]]:
  function get_aromatic_atom_idxs (line 110) | def get_aromatic_atom_idxs(rdmol: Chem.Mol) -> list[int]:
  function get_heavy_atom_idxs (line 129) | def get_heavy_atom_idxs(rdmol: Chem.Mol) -> list[int]:
  function get_central_atom_idx (line 146) | def get_central_atom_idx(rdmol: Chem.Mol) -> int:
  function is_collinear (line 189) | def is_collinear(
  function _wrap_angle (line 249) | def _wrap_angle(angle: Quantity) -> Quantity:
  function check_angle_not_flat (line 271) | def check_angle_not_flat(
  function check_dihedral_bounds (line 318) | def check_dihedral_bounds(
  function check_angular_variance (line 353) | def check_angular_variance(
  class CentroidDistanceSort (line 391) | class CentroidDistanceSort(AnalysisBase):
    method __init__ (line 418) | def __init__(
    method _prepare (line 437) | def _prepare(self):
    method _single_frame (line 440) | def _single_frame(self):
    method _conclude (line 447) | def _conclude(self):
  class FindHostAtoms (line 452) | class FindHostAtoms(AnalysisBase):
    method __init__ (line 476) | def __init__(
    method _prepare (line 496) | def _prepare(self):
    method _single_frame (line 499) | def _single_frame(self):
    method _conclude (line 515) | def _conclude(self):
  function get_local_rmsf (line 520) | def get_local_rmsf(atomgroup: mda.AtomGroup):  # -> ArrayQuantity:
  function _atomgroup_has_bonds (line 553) | def _atomgroup_has_bonds(atomgroup: Union[mda.AtomGroup, mda.Universe]) ...
  function stable_secondary_structure_selection (line 577) | def stable_secondary_structure_selection(
  function protein_chain_selection (line 718) | def protein_chain_selection(

FILE: src/openfe/protocols/restraint_utils/openmm/omm_forces.py
  function get_boresch_energy_function (line 15) | def get_boresch_energy_function(
  function get_periodic_boresch_energy_function (line 44) | def get_periodic_boresch_energy_function(
  function get_custom_compound_bond_force (line 74) | def get_custom_compound_bond_force(
  function add_force_in_separate_group (line 92) | def add_force_in_separate_group(

FILE: src/openfe/protocols/restraint_utils/openmm/omm_restraints.py
  class RestraintParameterState (line 54) | class RestraintParameterState(GlobalParameterState):
    method lambda_restraints (line 83) | def lambda_restraints(self, instance, new_value):
  class BaseHostGuestRestraints (line 91) | class BaseHostGuestRestraints(abc.ABC):
    method __init__ (line 107) | def __init__(
    method _verify_inputs (line 114) | def _verify_inputs(self):
    method _verify_geometry (line 122) | def _verify_geometry(self, geometry):
    method add_force (line 131) | def add_force(
    method get_standard_state_correction (line 154) | def get_standard_state_correction(
    method _get_force (line 180) | def _get_force(
  class SingleBondMixin (line 192) | class SingleBondMixin:
    method _verify_geometry (line 198) | def _verify_geometry(self, geometry: HostGuestRestraintGeometry):
  class BaseRadiallySymmetricRestraintForce (line 209) | class BaseRadiallySymmetricRestraintForce(BaseHostGuestRestraints):
    method add_force (line 220) | def add_force(
    method get_standard_state_correction (line 248) | def get_standard_state_correction(
    method _get_force (line 279) | def _get_force(self, geometry, controlling_parameter_name: str):
  class HarmonicBondRestraint (line 283) | class HarmonicBondRestraint(SingleBondMixin, BaseRadiallySymmetricRestra...
    method _get_force (line 297) | def _get_force(
  class FlatBottomBondRestraint (line 329) | class FlatBottomBondRestraint(SingleBondMixin, BaseRadiallySymmetricRest...
    method _get_force (line 346) | def _get_force(
  class CentroidHarmonicRestraint (line 380) | class CentroidHarmonicRestraint(BaseRadiallySymmetricRestraintForce):
    method _get_force (line 394) | def _get_force(
  class CentroidFlatBottomRestraint (line 426) | class CentroidFlatBottomRestraint(BaseRadiallySymmetricRestraintForce):
    method _get_force (line 440) | def _get_force(
  class BoreschRestraint (line 476) | class BoreschRestraint(BaseHostGuestRestraints):
    method add_force (line 538) | def add_force(
    method _get_force (line 569) | def _get_force(
    method get_standard_state_correction (line 629) | def get_standard_state_correction(

FILE: src/openfe/protocols/restraint_utils/settings.py
  class BaseRestraintSettings (line 27) | class BaseRestraintSettings(SettingsBaseModel):
  class DistanceRestraintSettings (line 35) | class DistanceRestraintSettings(BaseRestraintSettings):
    method positive_idxs (line 65) | def positive_idxs(cls, v):
  class FlatBottomRestraintSettings (line 72) | class FlatBottomRestraintSettings(DistanceRestraintSettings):
    method positive_value (line 85) | def positive_value(cls, v):
  class BoreschRestraintSettings (line 92) | class BoreschRestraintSettings(BaseRestraintSettings):

FILE: src/openfe/setup/alchemical_network_planner/abstract_alchemical_network_planner.py
  class AbstractAlchemicalNetworkPlanner (line 10) | class AbstractAlchemicalNetworkPlanner(abc.ABC):
    method __call__ (line 16) | def __call__(self, *args, **kwargs) -> AlchemicalNetwork:

FILE: src/openfe/setup/alchemical_network_planner/relative_alchemical_network_planner.py
  class RelativeAlchemicalNetworkPlanner (line 41) | class RelativeAlchemicalNetworkPlanner(AbstractAlchemicalNetworkPlanner,...
    method __init__ (line 44) | def __init__(
    method __call__ (line 92) | def __call__(self, *args, **kwargs) -> AlchemicalNetwork: ...  # -no-cov-
    method mappers (line 95) | def mappers(self) -> Iterable[LigandAtomMapper]:
    method mapping_scorer (line 99) | def mapping_scorer(self) -> Callable:
    method ligand_network_planner (line 103) | def ligand_network_planner(self) -> Callable:
    method transformation_protocol (line 107) | def transformation_protocol(self) -> Protocol:
    method chemical_system_generator_type (line 111) | def chemical_system_generator_type(
    method _construct_ligand_network (line 116) | def _construct_ligand_network(self, ligands: Iterable[SmallMoleculeCom...
    method _build_transformations (line 123) | def _build_transformations(
    method _build_transformation (line 181) | def _build_transformation(
  class RHFEAlchemicalNetworkPlanner (line 220) | class RHFEAlchemicalNetworkPlanner(RelativeAlchemicalNetworkPlanner):
    method __init__ (line 229) | def __init__(
    method __call__ (line 245) | def __call__(
  class RBFEAlchemicalNetworkPlanner (line 284) | class RBFEAlchemicalNetworkPlanner(RelativeAlchemicalNetworkPlanner):
    method __init__ (line 293) | def __init__(
    method _build_transformation (line 309) | def _build_transformation(
    method __call__ (line 345) | def __call__(

FILE: src/openfe/setup/atom_mapping/ligandatommapper.py
  class LigandAtomMapper (line 12) | class LigandAtomMapper(gufe.AtomMapper):
    method _mappings_generator (line 21) | def _mappings_generator(
    method suggest_mappings (line 41) | def suggest_mappings(

FILE: src/openfe/setup/atom_mapping/perses_mapper.py
  class PersesAtomMapper (line 28) | class PersesAtomMapper(LigandAtomMapper):
    method _to_dict (line 34) | def _to_dict(self) -> dict:
    method _from_dict (line 45) | def _from_dict(cls, dct: dict):
    method _defaults (line 52) | def _defaults(cls):
    method __init__ (line 56) | def __init__(
    method _mappings_generator (line 89) | def _mappings_generator(self, componentA, componentB):

FILE: src/openfe/setup/atom_mapping/perses_scorers.py
  function _get_all_mapped_atoms_with (line 21) | def _get_all_mapped_atoms_with(
  function default_perses_scorer (line 39) | def default_perses_scorer(

FILE: src/openfe/setup/chemicalsystem_generator/abstract_chemicalsystem_generator.py
  class RFEComponentLabels (line 12) | class RFEComponentLabels(str, Enum):
  class AbstractChemicalSystemGenerator (line 19) | class AbstractChemicalSystemGenerator(abc.ABC):
    method __call__ (line 25) | def __call__(self, *args, **kwargs) -> Iterable[ChemicalSystem]:

FILE: src/openfe/setup/chemicalsystem_generator/easy_chemicalsystem_generator.py
  class EasyChemicalSystemGenerator (line 21) | class EasyChemicalSystemGenerator(AbstractChemicalSystemGenerator):
    method __init__ (line 22) | def __init__(
    method __call__ (line 73) | def __call__(self, component: SmallMoleculeComponent) -> Iterable[Chem...

FILE: src/openfe/setup/ligand_network_planning.py
  function _hasten_lomap (line 27) | def _hasten_lomap(mapper, ligands):
  function generate_radial_network (line 49) | def generate_radial_network(
  function generate_maximal_network (line 157) | def generate_maximal_network(
  function generate_minimal_spanning_network (line 208) | def generate_minimal_spanning_network(
  function generate_minimal_redundant_network (line 252) | def generate_minimal_redundant_network(
  function generate_network_from_names (line 306) | def generate_network_from_names(
  function generate_network_from_indices (line 346) | def generate_network_from_indices(
  function load_orion_network (line 381) | def load_orion_network(
  function load_fepplus_network (line 427) | def load_fepplus_network(

FILE: src/openfe/storage/metadatastore.py
  class MetadataStore (line 12) | class MetadataStore(collections.abc.Mapping):
    method __init__ (line 13) | def __init__(self, external_store):
    method store_metadata (line 18) | def store_metadata(self, location: str, metadata: Metadata):
    method load_all_metadata (line 22) | def load_all_metadata(self) -> Dict[str, Metadata]:
    method __delitem__ (line 26) | def __delitem__(self, location):
    method __getitem__ (line 29) | def __getitem__(self, location):
    method __iter__ (line 32) | def __iter__(self):
    method __len__ (line 35) | def __len__(self):
  class JSONMetadataStore (line 39) | class JSONMetadataStore(MetadataStore):
    method _dump_file (line 43) | def _dump_file(self):
    method store_metadata (line 48) | def store_metadata(self, location: str, metadata: Metadata):
    method load_all_metadata (line 52) | def load_all_metadata(self):
    method __delitem__ (line 63) | def __delitem__(self, location):
  class PerFileJSONMetadataStore (line 68) | class PerFileJSONMetadataStore(MetadataStore):
    method _metadata_path (line 71) | def _metadata_path(self, location):
    method store_metadata (line 74) | def store_metadata(self, location: str, metadata: Metadata):
    method load_all_metadata (line 84) | def load_all_metadata(self):
    method __delitem__ (line 98) | def __delitem__(self, location):

FILE: src/openfe/storage/resultclient.py
  class _ResultContainer (line 21) | class _ResultContainer(abc.ABC):
    method __init__ (line 26) | def __init__(self, parent, path_component):
    method __eq__ (line 31) | def __eq__(self, other):
    method _to_path_component (line 35) | def _to_path_component(item: Any) -> str:
    method __getitem__ (line 45) | def __getitem__(self, item):
    method __truediv__ (line 58) | def __truediv__(self, item):
    method _load_next_level (line 62) | def _load_next_level(self, item):
    method __iter__ (line 65) | def __iter__(self):
    method load_stream (line 70) | def load_stream(self, location, *, allow_changed=False):
    method load_bytes (line 73) | def load_bytes(self, location, *, allow_changed=False):
    method path (line 80) | def path(self):
    method result_server (line 84) | def result_server(self):
    method __repr__ (line 87) | def __repr__(self):
  class ResultClient (line 92) | class ResultClient(_ResultContainer):
    method __init__ (line 93) | def __init__(self, external_store):
    method delete (line 101) | def delete(self, location):
    method _gufe_key_to_storage_key (line 105) | def _gufe_key_to_storage_key(prefix: str, key: str):
    method _store_gufe_tokenizable (line 130) | def _store_gufe_tokenizable(self, prefix, obj):
    method store_transformation (line 143) | def store_transformation(self, transformation):
    method store_network (line 153) | def store_network(self, network):
    method _load_gufe_tokenizable (line 163) | def _load_gufe_tokenizable(self, prefix, gufe_key):
    method load_transformation (line 207) | def load_transformation(self, key: str):
    method load_network (line 222) | def load_network(self, key: str):
    method _load_next_level (line 237) | def _load_next_level(self, transformation):
    method path (line 243) | def path(self):
    method result_server (line 247) | def result_server(self):
  class TransformationResult (line 251) | class TransformationResult(_ResultContainer):
    method __init__ (line 252) | def __init__(self, parent, transformation):
    method _load_next_level (line 256) | def _load_next_level(self, clone):
  class CloneResult (line 260) | class CloneResult(_ResultContainer):
    method __init__ (line 261) | def __init__(self, parent, clone):
    method _to_path_component (line 266) | def _to_path_component(item):
    method _load_next_level (line 269) | def _load_next_level(self, extension):
  class ExtensionResult (line 273) | class ExtensionResult(_ResultContainer):
    method __init__ (line 274) | def __init__(self, parent, extension):
    method _to_path_component (line 279) | def _to_path_component(item):
    method __getitem__ (line 282) | def __getitem__(self, filename):
    method _load_next_level (line 286) | def _load_next_level(self, filename):

FILE: src/openfe/storage/resultserver.py
  class ResultServer (line 9) | class ResultServer:
    method __init__ (line 16) | def __init__(self, external_store, metadata_store):
    method _store_metadata (line 20) | def _store_metadata(self, location):
    method store_bytes (line 24) | def store_bytes(self, location, byte_data):
    method store_path (line 28) | def store_path(self, location, path):
    method delete (line 32) | def delete(self, location):
    method validate (line 36) | def validate(self, location, allow_changed=False):
    method __iter__ (line 51) | def __iter__(self):
    method find_missing_files (line 54) | def find_missing_files(self):
    method load_stream (line 58) | def load_stream(self, location, allow_changed=False):

FILE: src/openfe/tests/analysis/test_plotting.py
  function test_mbar_overlap_plot_high_warn (line 111) | def test_mbar_overlap_plot_high_warn(matrix):
  function test_mbar_overlap_plot (line 155) | def test_mbar_overlap_plot():
  function test_plot_2D_rmsd (line 161) | def test_plot_2D_rmsd(num):

FILE: src/openfe/tests/conftest.py
  class SlowTests (line 31) | class SlowTests:
    method __init__ (line 65) | def __init__(self, config):
    method _modify_slow (line 69) | def _modify_slow(items, config):
    method _modify_integration (line 80) | def _modify_integration(items, config):
    method pytest_collection_modifyitems (line 90) | def pytest_collection_modifyitems(self, items, config):
  function pytest_addoption (line 108) | def pytest_addoption(parser):
  function pytest_configure (line 113) | def pytest_configure(config):
  function mol_from_smiles (line 119) | def mol_from_smiles(smiles: str) -> Chem.Mol:
  function simple_mapping (line 127) | def simple_mapping():
  function other_mapping (line 143) | def other_mapping():
  function lomap_basic_test_files_dir (line 159) | def lomap_basic_test_files_dir(tmp_path_factory):
  function atom_mapping_basic_test_files (line 176) | def atom_mapping_basic_test_files():
  function lomap_old_mapper (line 199) | def lomap_old_mapper() -> AtomMapper:
  function benzene_toluene_topology (line 217) | def benzene_toluene_topology():
  function benzene_modifications (line 226) | def benzene_modifications():
  function charged_benzene_modifications (line 237) | def charged_benzene_modifications():
  function T4L_septop_reference_xml (line 248) | def T4L_septop_reference_xml():
  function serialization_template (line 255) | def serialization_template():
  function benzene_transforms (line 265) | def benzene_transforms():
  function T4_protein_component_pdb (line 277) | def T4_protein_component_pdb() -> str:
  function T4_protein_component (line 283) | def T4_protein_component(T4_protein_component_pdb) -> gufe.ProteinCompon...
  function a2a_protein_membrane_pdb (line 288) | def a2a_protein_membrane_pdb():
  function a2a_protein_membrane_component (line 294) | def a2a_protein_membrane_component(a2a_protein_membrane_pdb) -> openfe.P...
  function eg5_protein_pdb (line 299) | def eg5_protein_pdb():
  function eg5_ligands_sdf (line 305) | def eg5_ligands_sdf():
  function eg5_cofactor_sdf (line 311) | def eg5_cofactor_sdf():
  function eg5_protein (line 317) | def eg5_protein(eg5_protein_pdb) -> openfe.ProteinComponent:
  function eg5_ligands (line 322) | def eg5_ligands(eg5_ligands_sdf) -> list[SmallMoleculeComponent]:
  function eg5_cofactor (line 327) | def eg5_cofactor(eg5_cofactor_sdf) -> SmallMoleculeComponent:
  function a2a_ligands_sdf (line 332) | def a2a_ligands_sdf():
  function a2a_ligands (line 338) | def a2a_ligands(a2a_ligands_sdf):
  function orion_network (line 345) | def orion_network():
  function fepplus_network (line 351) | def fepplus_network():
  function CN_molecule (line 357) | def CN_molecule() -> list[SmallMoleculeComponent]:
  function am1bcc_ref_charges (line 371) | def am1bcc_ref_charges():
  function chlorobenzene (line 409) | def chlorobenzene():
  function fluorobenzene (line 416) | def fluorobenzene():
  function ethane (line 423) | def ethane():
  function chloroethane (line 430) | def chloroethane():
  function fluoroethane (line 437) | def fluoroethane():
  function benzene (line 444) | def benzene():
  function chlorobenzene_to_fluorobenzene_mapping (line 451) | def chlorobenzene_to_fluorobenzene_mapping(chlorobenzene, fluorobenzene):
  function chloroethane_to_ethane_mapping (line 475) | def chloroethane_to_ethane_mapping(chloroethane, ethane):
  function chloroethane_to_fluoroethane_mapping (line 494) | def chloroethane_to_fluoroethane_mapping(chloroethane, fluoroethane):
  function chlorobenzene_to_benzene_mapping (line 514) | def chlorobenzene_to_benzene_mapping(chlorobenzene, benzene):
  function t4_lysozyme_solvated (line 537) | def t4_lysozyme_solvated():
  function apply_box_vectors_and_fix_nb_force (line 544) | def apply_box_vectors_and_fix_nb_force(
  function htf_cmap_chlorobenzene_to_fluorobenzene (line 594) | def htf_cmap_chlorobenzene_to_fluorobenzene(
  function htf_chloro_fluoroethane (line 634) | def htf_chloro_fluoroethane(chloroethane, fluoroethane, chloroethane_to_...
  function htf_chloro_ethane (line 669) | def htf_chloro_ethane(chloroethane, ethane, chloroethane_to_ethane_mappi...
  function htf_chlorobenzene_fluorobenzene (line 705) | def htf_chlorobenzene_fluorobenzene(
  function htf_chlorobenzene_benzene (line 749) | def htf_chlorobenzene_benzene(

FILE: src/openfe/tests/dev/serialization_test_templates.py
  function mol_from_smiles (line 28) | def mol_from_smiles(smiles: str) -> Chem.Mol:

FILE: src/openfe/tests/protocols/conftest.py
  function benzene_vacuum_system (line 31) | def benzene_vacuum_system(benzene_modifications):
  function benzene_system (line 38) | def benzene_system(benzene_modifications):
  function benzene_complex_system (line 52) | def benzene_complex_system(benzene_modifications, T4_protein_component):
  function toluene_vacuum_system (line 67) | def toluene_vacuum_system(benzene_modifications):
  function toluene_system (line 74) | def toluene_system(benzene_modifications):
  function toluene_complex_system (line 86) | def toluene_complex_system(benzene_modifications, T4_protein_component):
  function benzene_to_toluene_mapping (line 99) | def benzene_to_toluene_mapping(benzene_modifications):
  function benzene_charges (line 109) | def benzene_charges():
  function benzene_to_benzoic_mapping (line 120) | def benzene_to_benzoic_mapping(benzene_charges):
  function benzoic_to_benzene_mapping (line 128) | def benzoic_to_benzene_mapping(benzene_charges):
  function benzene_to_aniline_mapping (line 136) | def benzene_to_aniline_mapping(benzene_charges):
  function aniline_to_benzene_mapping (line 144) | def aniline_to_benzene_mapping(benzene_charges):
  function aniline_to_benzoic_mapping (line 152) | def aniline_to_benzoic_mapping(benzene_charges):
  function benzene_many_solv_system (line 160) | def benzene_many_solv_system(benzene_modifications):
  function toluene_many_solv_system (line 190) | def toluene_many_solv_system(benzene_modifications):
  function rfe_transformation_json (line 219) | def rfe_transformation_json() -> str:
  function afe_solv_transformation_json (line 231) | def afe_solv_transformation_json() -> str:
  function abfe_transformation_json_path (line 245) | def abfe_transformation_json_path() -> str:
  function md_json (line 258) | def md_json() -> str:
  function septop_json (line 272) | def septop_json() -> str:
  function industry_benchmark_files (line 295) | def industry_benchmark_files():
  function t4_lysozyme_trajectory_dir (line 314) | def t4_lysozyme_trajectory_dir():
  function simulation_nc (line 323) | def simulation_nc():
  function htop_trajectory_path (line 339) | def htop_trajectory_path():
  function htop_checkpoint_path (line 348) | def htop_checkpoint_path():
  function ahfe_vac_trajectory_path (line 357) | def ahfe_vac_trajectory_path():
  function vac_checkpoint_path (line 366) | def vac_checkpoint_path():
  function ahfe_solv_trajectory_path (line 375) | def ahfe_solv_trajectory_path():
  function ahfe_solv_checkpoint_path (line 384) | def ahfe_solv_checkpoint_path():
  function septop_solv_trajectory_path (line 393) | def septop_solv_trajectory_path():
  function septop_solv_checkpoint_path (line 402) | def septop_solv_checkpoint_path():
  function plain_md_checkpoint_path (line 418) | def plain_md_checkpoint_path():
  function available_platforms (line 424) | def available_platforms() -> set[str]:
  function get_available_openmm_platforms (line 432) | def get_available_openmm_platforms() -> set[str]:
  class ModGufeTokenizableTestsMixin (line 460) | class ModGufeTokenizableTestsMixin(GufeTokenizableTestsMixin):
    method test_repr (line 466) | def test_repr(self, instance):
  function compute_energy (line 474) | def compute_energy(

FILE: src/openfe/tests/protocols/openmm_abfe/conftest.py
  function benzene_complex_dag (line 10) | def benzene_complex_dag(benzene_modifications, T4_protein_component):

FILE: src/openfe/tests/protocols/openmm_abfe/test_abfe_energies.py
  class AlchemStateRest (line 27) | class AlchemStateRest(AlchemicalState):
  function get_alchemical_energy_components (line 38) | def get_alchemical_energy_components(alchemical_system, alchemical_state...
  class TestT4EnergiesRegression (line 105) | class TestT4EnergiesRegression:
    method t4_reference_system (line 113) | def t4_reference_system(self):
    method t4_validation_data (line 120) | def t4_validation_data(self, benzene_modifications, T4_protein_compone...
    method get_energy_components (line 158) | def get_energy_components(
    method test_energies_regression (line 182) | def test_energies_regression(self, lambda_val, t4_reference_system, t4...
    method test_lambda_scale (line 209) | def test_lambda_scale(self, t4_validation_data):

FILE: src/openfe/tests/protocols/openmm_abfe/test_abfe_protocol.py
  function default_settings (line 49) | def default_settings():
  function benzene_wcharges (line 54) | def benzene_wcharges(benzene_modifications):
  function test_create_default_protocol (line 60) | def test_create_default_protocol(default_settings):
  function test_serialize_protocol (line 68) | def test_serialize_protocol(default_settings):
  function test_repeat_units (line 78) | def test_repeat_units(benzene_modifications, T4_protein_component):
  function test_create_independent_repeat_ids (line 127) | def test_create_independent_repeat_ids(benzene_modifications, T4_protein...
  function test_mda_universe_error (line 165) | def test_mda_universe_error():
  class TestT4LysozymeDryRun (line 176) | class TestT4LysozymeDryRun:
    method set_platform (line 191) | def set_platform(self, get_available_openmm_platforms):
    method protocol (line 219) | def protocol(self, settings):
    method settings (line 225) | def settings(self):
    method dag (line 239) | def dag(self, protocol, benzene_wcharges, T4_protein_component):
    method complex_setup_units (line 262) | def complex_setup_units(self, dag):
    method complex_sim_units (line 266) | def complex_sim_units(self, dag):
    method solvent_setup_units (line 270) | def solvent_setup_units(self, dag):
    method solvent_sim_units (line 274) | def solvent_sim_units(self, dag):
    method test_number_of_units (line 277) | def test_number_of_units(
    method _assert_force_num (line 289) | def _assert_force_num(self, system, forcetype, number):
    method _assert_expected_alchemical_forces (line 293) | def _assert_expected_alchemical_forces(self, system, phase: str, setti...
    method _assert_expected_nonalchemical_forces (line 337) | def _assert_expected_nonalchemical_forces(self, system, phase: str, se...
    method _verify_sampler (line 376) | def _verify_sampler(self, sampler, phase: str, settings):
    method _check_box_vectors (line 391) | def _check_box_vectors(self, system):
    method _test_dodecahedron_vectors (line 395) | def _test_dodecahedron_vectors(system):
    method _test_cubic_vectors (line 414) | def _test_cubic_vectors(system):
    method _test_energies (line 431) | def _test_energies(reference_system, alchemical_system, alchemical_reg...
    method test_complex_dry_run (line 453) | def test_complex_dry_run(self, complex_setup_units, complex_sim_units,...
    method test_solvent_dry_run (line 514) | def test_solvent_dry_run(self, solvent_setup_units, solvent_sim_units,...
  class TestT4LysozymeTIP4PExtraSettingsDryRun (line 581) | class TestT4LysozymeTIP4PExtraSettingsDryRun(TestT4LysozymeDryRun):
    method settings (line 590) | def settings(self):
  function test_user_charges (line 617) | def test_user_charges(benzene_modifications, T4_protein_component, tmp_p...
  class TestA2AMembraneDryRun (line 690) | class TestA2AMembraneDryRun(TestT4LysozymeDryRun):
    method settings (line 716) | def settings(self):
    method dag (line 727) | def dag(self, settings, a2a_ligands, a2a_protein_membrane_component):
    method _check_box_vectors (line 757) | def _check_box_vectors(self, system):
    method _test_orthogonal_vectors (line 761) | def _test_orthogonal_vectors(system):

FILE: src/openfe/tests/protocols/openmm_abfe/test_abfe_protocol_results.py
  function patcher (line 23) | def patcher():
  function test_gather (line 86) | def test_gather(benzene_complex_dag, patcher, tmp_path):
  function test_unit_tagging (line 104) | def test_unit_tagging(benzene_complex_dag, patcher, tmp_path):
  class TestProtocolResult (line 145) | class TestProtocolResult:
    method protocolresult (line 147) | def protocolresult(self, abfe_transformation_json_path):
    method test_reload_protocol_result (line 153) | def test_reload_protocol_result(self, afe_solv_transformation_json):
    method test_get_estimate (line 160) | def test_get_estimate(self, protocolresult):
    method test_get_uncertainty (line 168) | def test_get_uncertainty(self, protocolresult):
    method test_get_individual (line 176) | def test_get_individual(self, protocolresult):
    method test_get_forwards_etc (line 188) | def test_get_forwards_etc(self, key, protocolresult):
    method test_get_frwd_reverse_none_return (line 211) | def test_get_frwd_reverse_none_return(self, key, protocolresult):
    method test_get_overlap_matrices (line 223) | def test_get_overlap_matrices(self, key, n_rep, protocolresult):
    method test_get_replica_transition_statistics (line 235) | def test_get_replica_transition_statistics(self, n_rep, key, protocolr...
    method test_equilibration_iterations (line 248) | def test_equilibration_iterations(self, key, protocolresult):
    method test_production_iterations (line 257) | def test_production_iterations(self, key, protocolresult):
    method test_filenotfound_replica_states (line 265) | def test_filenotfound_replica_states(self, protocolresult):
    method test_restraint_geometry (line 271) | def test_restraint_geometry(self, protocolresult):
    method test_selection_indices (line 292) | def test_selection_indices(self, key, protocolresult, expected_size):

FILE: src/openfe/tests/protocols/openmm_abfe/test_abfe_settings.py
  function default_settings (line 13) | def default_settings():
  function test_create_default_settings (line 17) | def test_create_default_settings():
  function test_negative_repeats_settings (line 22) | def test_negative_repeats_settings(default_settings):
  function test_incorrect_window_settings (line 34) | def test_incorrect_window_settings(val, default_settings):
  function test_monotonic_lambda_windows (line 63) | def test_monotonic_lambda_windows(val, default_settings):
  function test_equil_not_all_complex (line 73) | def test_equil_not_all_complex(default_settings):
  function test_equil_not_all_solvent (line 78) | def test_equil_not_all_solvent(default_settings):
  function test_adaptive_settings_no_protein_membrane (line 83) | def test_adaptive_settings_no_protein_membrane(toluene_complex_system, d...
  function test_adaptive_settings_with_protein_membrane (line 95) | def test_adaptive_settings_with_protein_membrane(a2a_protein_membrane_co...

FILE: src/openfe/tests/protocols/openmm_abfe/test_abfe_slow.py
  function test_openmm_run_engine (line 21) | def test_openmm_run_engine(

FILE: src/openfe/tests/protocols/openmm_abfe/test_abfe_tokenization.py
  function protocol (line 23) | def protocol():
  function protocol_units (line 28) | def protocol_units(protocol, benzene_complex_system, T4_protein_component):
  function _filter_units (line 41) | def _filter_units(pus, classtype):
  function complex_protocol_setup_unit (line 48) | def complex_protocol_setup_unit(protocol_units):
  function complex_protocol_sim_unit (line 53) | def complex_protocol_sim_unit(protocol_units):
  function complex_protocol_analysis_unit (line 58) | def complex_protocol_analysis_unit(protocol_units):
  function solvent_protocol_setup_unit (line 63) | def solvent_protocol_setup_unit(protocol_units):
  function solvent_protocol_sim_unit (line 68) | def solvent_protocol_sim_unit(protocol_units):
  function solvent_protocol_analysis_unit (line 73) | def solvent_protocol_analysis_unit(protocol_units):
  function protocol_result (line 78) | def protocol_result(abfe_transformation_json_path):
  class TestAbsoluteBindingProtocol (line 84) | class TestAbsoluteBindingProtocol(ModGufeTokenizableTestsMixin):
    method instance (line 90) | def instance(self, protocol):
  class TestABFESolventSetupUnit (line 94) | class TestABFESolventSetupUnit(ModGufeTokenizableTestsMixin):
    method instance (line 100) | def instance(self, solvent_protocol_setup_unit):
  class TestABFESolventSimUnit (line 104) | class TestABFESolventSimUnit(ModGufeTokenizableTestsMixin):
    method instance (line 110) | def instance(self, solvent_protocol_sim_unit):
  class TestABFESolventAnalysisUnit (line 114) | class TestABFESolventAnalysisUnit(ModGufeTokenizableTestsMixin):
    method instance (line 120) | def instance(self, solvent_protocol_analysis_unit):
  class TestABFEComplexSetupUnit (line 124) | class TestABFEComplexSetupUnit(ModGufeTokenizableTestsMixin):
    method instance (line 130) | def instance(self, complex_protocol_setup_unit):
  class TestABFEComplexSimUnit (line 134) | class TestABFEComplexSimUnit(ModGufeTokenizableTestsMixin):
    method instance (line 140) | def instance(self, complex_protocol_sim_unit):
  class TestABFEComplexAnalysisUnit (line 144) | class TestABFEComplexAnalysisUnit(ModGufeTokenizableTestsMixin):
    method instance (line 150) | def instance(self, complex_protocol_analysis_unit):
  class TestAbsoluteBindingProtocolResult (line 154) | class TestAbsoluteBindingProtocolResult(ModGufeTokenizableTestsMixin):
    method instance (line 160) | def instance(self, protocol_result):

FILE: src/openfe/tests/protocols/openmm_abfe/test_abfe_validation.py
  function default_settings (line 13) | def default_settings():
  function test_validate_complex_lambda_schedule_naked_charge (line 23) | def test_validate_complex_lambda_schedule_naked_charge(val, default_sett...
  function test_validate_solvent_lambda_schedule_naked_charge (line 47) | def test_validate_solvent_lambda_schedule_naked_charge(val, default_sett...
  function test_validate_lambda_schedule_nreplicas (line 71) | def test_validate_lambda_schedule_nreplicas(val, default_settings):
  function test_validate_lambda_schedule_nwindows (line 95) | def test_validate_lambda_schedule_nwindows(val, default_settings):
  function test_validate_no_protcomp (line 115) | def test_validate_no_protcomp(
  function test_validate_endstates_nosolvcomp_stateA (line 134) | def test_validate_endstates_nosolvcomp_stateA(benzene_modifications, T4_...
  function test_validate_endstates_nosolvcomp_stateB (line 154) | def test_validate_endstates_nosolvcomp_stateB(benzene_modifications, T4_...
  function test_validate_endstates_multiple_uniqueA (line 174) | def test_validate_endstates_multiple_uniqueA(benzene_modifications, T4_p...
  function test_validate_solvent_endstates_solvent_dissapearing (line 195) | def test_validate_solvent_endstates_solvent_dissapearing(
  function test_validate_endstates_unique_stateB (line 220) | def test_validate_endstates_unique_stateB(benzene_modifications, T4_prot...
  function test_charged_endstate (line 241) | def test_charged_endstate(charged_benzene_modifications, T4_protein_comp...
  function test_validate_fail_extends (line 262) | def test_validate_fail_extends(benzene_modifications, T4_protein_compone...
  function test_high_timestep (line 287) | def test_high_timestep(benzene_modifications, T4_protein_component):
  function test_validate_warnings (line 312) | def test_validate_warnings(benzene_modifications, T4_protein_component, ...

FILE: src/openfe/tests/protocols/openmm_abfe/utils.py
  function _get_units (line 26) | def _get_units(protocol_units, unit_type):

FILE: src/openfe/tests/protocols/openmm_ahfe/test_ahfe_protocol.py
  function protocol_dry_settings (line 41) | def protocol_dry_settings():
  function default_settings (line 50) | def default_settings():
  function test_create_default_protocol (line 54) | def test_create_default_protocol(default_settings):
  function test_serialize_protocol (line 60) | def test_serialize_protocol(default_settings):
  function test_bad_sampler (line 68) | def test_bad_sampler():
  function test_repeat_units (line 86) | def test_repeat_units(benzene_system):
  function test_create_independent_repeat_ids (line 120) | def test_create_independent_repeat_ids(benzene_system):
  function _assert_num_forces (line 149) | def _assert_num_forces(system, forcetype, number):
  function _verify_alchemical_sterics_force_parameters (line 158) | def _verify_alchemical_sterics_force_parameters(
  function test_setup_dry_sim_vac_benzene (line 198) | def test_setup_dry_sim_vac_benzene(benzene_system, method, protocol_dry_...
  function test_alchemical_settings_setup_vacuum (line 279) | def test_alchemical_settings_setup_vacuum(
  function test_confgen_fail_AFE (line 341) | def test_confgen_fail_AFE(benzene_system, protocol_dry_settings, tmp_path):
  function test_setup_solv_benzene (line 369) | def test_setup_solv_benzene(benzene_system, protocol_dry_settings, tmp_p...
  function test_dry_run_vsite_fail (line 412) | def test_dry_run_vsite_fail(benzene_system, tmp_path, protocol_dry_setti...
  function test_setup_dry_sim_solv_benzene_tip4p (line 460) | def test_setup_dry_sim_solv_benzene_tip4p(benzene_system, protocol_dry_s...
  function test_dry_run_solv_benzene_noncubic (line 509) | def test_dry_run_solv_benzene_noncubic(benzene_system, protocol_dry_sett...
  function test_dry_run_solv_user_charges_benzene (line 547) | def test_dry_run_solv_user_charges_benzene(benzene_modifications, protoc...
  function test_dry_run_charge_backends (line 647) | def test_dry_run_charge_backends(
  function benzene_solvation_dag (line 693) | def benzene_solvation_dag(benzene_system, protocol_dry_settings):
  function test_dry_run_vacuum_write_frequency (line 712) | def test_dry_run_vacuum_write_frequency(

FILE: src/openfe/tests/protocols/openmm_ahfe/test_ahfe_protocol_results.py
  function protocol_dry_settings (line 22) | def protocol_dry_settings():
  function benzene_solvation_dag (line 31) | def benzene_solvation_dag(benzene_system, protocol_dry_settings):
  function patcher (line 43) | def patcher():
  function test_gather (line 106) | def test_gather(benzene_solvation_dag, patcher, tmp_path):
  function test_unit_tagging (line 124) | def test_unit_tagging(benzene_solvation_dag, patcher, tmp_path):
  class TestProtocolResult (line 165) | class TestProtocolResult:
    method protocolresult (line 167) | def protocolresult(self, afe_solv_transformation_json):
    method test_reload_protocol_result (line 174) | def test_reload_protocol_result(self, afe_solv_transformation_json):
    method test_get_estimate (line 181) | def test_get_estimate(self, protocolresult):
    method test_get_uncertainty (line 189) | def test_get_uncertainty(self, protocolresult):
    method test_get_individual (line 197) | def test_get_individual(self, protocolresult):
    method test_get_forwards_etc (line 209) | def test_get_forwards_etc(self, key, protocolresult):
    method test_get_frwd_reverse_none_return (line 224) | def test_get_frwd_reverse_none_return(self, key, protocolresult):
    method test_get_overlap_matrices (line 236) | def test_get_overlap_matrices(self, key, protocolresult):
    method test_get_replica_transition_statistics (line 248) | def test_get_replica_transition_statistics(self, key, protocolresult):
    method test_equilibration_iterations (line 261) | def test_equilibration_iterations(self, key, protocolresult):
    method test_production_iterations (line 270) | def test_production_iterations(self, key, protocolresult):
    method test_filenotfound_replica_states (line 278) | def test_filenotfound_replica_states(self, protocolresult):

FILE: src/openfe/tests/protocols/openmm_ahfe/test_ahfe_resume.py
  function protocol_settings (line 29) | def protocol_settings():
  function test_verify_execution_environment (line 51) | def test_verify_execution_environment():
  function test_verify_execution_environment_fail (line 62) | def test_verify_execution_environment_fail():
  function test_verify_execution_env_missing_key (line 74) | def test_verify_execution_env_missing_key():
  function test_solvent_check_restart (line 90) | def test_solvent_check_restart(protocol_settings, ahfe_solv_trajectory_p...
  function test_vacuum_check_restart (line 106) | def test_vacuum_check_restart(protocol_settings, ahfe_vac_trajectory_path):
  function test_check_restart_one_file_missing (line 122) | def test_check_restart_one_file_missing(protocol_settings, ahfe_vac_traj...
  class TestCheckpointResuming (line 133) | class TestCheckpointResuming:
    method protocol_dag (line 135) | def protocol_dag(
    method _check_sampler (line 160) | def _check_sampler(sampler, num_iterations: int):
    method _get_positions (line 171) | def _get_positions(dataset):
    method _copy_simfiles (line 179) | def _copy_simfiles(cwd: pathlib.Path, filepath):
    method test_resume (line 183) | def test_resume(
    method test_resume_fail_particles (line 271) | def test_resume_fail_particles(
    method test_resume_fail_constraints (line 311) | def test_resume_fail_constraints(
    method test_resume_fail_forces (line 353) | def test_resume_fail_forces(
    method test_resume_differ_barostat (line 393) | def test_resume_differ_barostat(
    method test_resume_differ_forces (line 449) | def test_resume_differ_forces(
    method test_resume_bad_files (line 512) | def test_resume_bad_files(

FILE: src/openfe/tests/protocols/openmm_ahfe/test_ahfe_settings.py
  function default_settings (line 12) | def default_settings():
  function test_create_default_settings (line 16) | def test_create_default_settings():
  function test_invalid_protocol_repeats (line 21) | def test_invalid_protocol_repeats():
  function test_incorrect_window_settings (line 34) | def test_incorrect_window_settings(val, default_settings):
  function test_monotonic_lambda_windows (line 49) | def test_monotonic_lambda_windows(val, default_settings):

FILE: src/openfe/tests/protocols/openmm_ahfe/test_ahfe_slow.py
  function test_openmm_run_engine (line 20) | def test_openmm_run_engine(

FILE: src/openfe/tests/protocols/openmm_ahfe/test_ahfe_tokenization.py
  function protocol (line 23) | def protocol():
  function protocol_units (line 30) | def protocol_units(protocol, benzene_system):
  function _filter_units (line 39) | def _filter_units(pus, classtype):
  function solvent_protocol_setup_unit (line 46) | def solvent_protocol_setup_unit(protocol_units):
  function solvent_protocol_sim_unit (line 51) | def solvent_protocol_sim_unit(protocol_units):
  function solvent_protocol_analysis_unit (line 56) | def solvent_protocol_analysis_unit(protocol_units):
  function vacuum_protocol_setup_unit (line 61) | def vacuum_protocol_setup_unit(protocol_units):
  function vacuum_protocol_sim_unit (line 66) | def vacuum_protocol_sim_unit(protocol_units):
  function vacuum_protocol_analysis_unit (line 71) | def vacuum_protocol_analysis_unit(protocol_units):
  function protocol_result (line 76) | def protocol_result(afe_solv_transformation_json):
  class TestAbsoluteSolvationProtocol (line 82) | class TestAbsoluteSolvationProtocol(ModGufeTokenizableTestsMixin):
    method instance (line 88) | def instance(self, protocol):
  class TestAHFESolventSetupUnit (line 92) | class TestAHFESolventSetupUnit(ModGufeTokenizableTestsMixin):
    method instance (line 98) | def instance(self, solvent_protocol_setup_unit):
  class TestAHFESolventSimUnit (line 102) | class TestAHFESolventSimUnit(ModGufeTokenizableTestsMixin):
    method instance (line 108) | def instance(self, solvent_protocol_sim_unit):
  class TestAHFESolventAnalysisUnit (line 112) | class TestAHFESolventAnalysisUnit(ModGufeTokenizableTestsMixin):
    method instance (line 118) | def instance(self, solvent_protocol_analysis_unit):
  class TestAHFEVacuumSetupUnit (line 122) | class TestAHFEVacuumSetupUnit(ModGufeTokenizableTestsMixin):
    method instance (line 128) | def instance(self, vacuum_protocol_setup_unit):
  class TestAHFEVacuumSimUnit (line 132) | class TestAHFEVacuumSimUnit(ModGufeTokenizableTestsMixin):
    method instance (line 138) | def instance(self, vacuum_protocol_sim_unit):
  class TestAHFEVacuumAnalysisUnit (line 142) | class TestAHFEVacuumAnalysisUnit(ModGufeTokenizableTestsMixin):
    method instance (line 148) | def instance(self, vacuum_protocol_analysis_unit):
  class TestAbsoluteSolvationProtocolResult (line 152) | class TestAbsoluteSolvationProtocolResult(ModGufeTokenizableTestsMixin):
    method instance (line 158) | def instance(self, protocol_result):

FILE: src/openfe/tests/protocols/openmm_ahfe/test_ahfe_validation.py
  function default_settings (line 16) | def default_settings():
  function stateA (line 21) | def stateA(benzene_modifications):
  function stateB (line 28) | def stateB():
  function test_validate_lambda_schedule_naked_charge (line 38) | def test_validate_lambda_schedule_naked_charge(val, default_settings):
  function test_validate_lambda_schedule_nreplicas (line 68) | def test_validate_lambda_schedule_nreplicas(val, default_settings):
  function test_validate_lambda_schedule_nwindows (line 91) | def test_validate_lambda_schedule_nwindows(val, default_settings):
  function test_validate_lambda_schedule_nonzero_restraints (line 116) | def test_validate_lambda_schedule_nonzero_restraints(val, default_settin...
  function test_validate_endstates_protcomp (line 133) | def test_validate_endstates_protcomp(benzene_modifications, T4_protein_c...
  function test_validate_endstates_nosolvcomp_stateA (line 154) | def test_validate_endstates_nosolvcomp_stateA(benzene_modifications, T4_...
  function test_validate_endstates_nosolvcomp_stateB (line 173) | def test_validate_endstates_nosolvcomp_stateB(benzene_modifications, T4_...
  function test_validate_alchem_comps_appearingB (line 192) | def test_validate_alchem_comps_appearingB(benzene_modifications):
  function test_validate_alchem_comps_multi (line 208) | def test_validate_alchem_comps_multi(benzene_modifications):
  function test_validate_alchem_nonsmc (line 227) | def test_validate_alchem_nonsmc(benzene_modifications):
  function test_charged_alchem_comp (line 246) | def test_charged_alchem_comp(charged_benzene_modifications):
  function test_extends_error (line 266) | def test_extends_error(default_settings, stateA, stateB):
  function test_vac_bad_nonbonded (line 276) | def test_vac_bad_nonbonded(stateA, stateB):
  function test_vac_nvt_error (line 285) | def test_vac_nvt_error(stateA, stateB):
  function test_mapping_warning (line 294) | def test_mapping_warning(benzene_modifications, default_settings, stateA...
  function test_high_timestep (line 308) | def test_high_timestep(phase, stateA, stateB):

FILE: src/openfe/tests/protocols/openmm_ahfe/utils.py
  function _get_units (line 27) | def _get_units(protocol_units, unit_type):

FILE: src/openfe/tests/protocols/openmm_md/test_plain_md_protocol.py
  function vac_settings (line 40) | def vac_settings():
  function test_get_remaining_steps (line 58) | def test_get_remaining_steps(inputs, expected):
  function test_create_default_settings (line 66) | def test_create_default_settings():
  function test_create_default_protocol (line 72) | def test_create_default_protocol():
  function test_invalid_protocol_repeats (line 81) | def test_invalid_protocol_repeats():
  function test_serialize_protocol (line 87) | def test_serialize_protocol():
  function test_create_independent_repeat_ids (line 99) | def test_create_independent_repeat_ids(benzene_system):
  function test_dry_run_default_vacuum (line 127) | def test_dry_run_default_vacuum(benzene_vacuum_system, vac_settings, tmp...
  function test_dry_run_logger_output (line 155) | def test_dry_run_logger_output(benzene_vacuum_system, vac_settings, tmp_...
  function test_dry_run_ffcache_none_vacuum (line 206) | def test_dry_run_ffcache_none_vacuum(benzene_vacuum_system, vac_settings...
  function test_dry_run_gaff_vacuum (line 224) | def test_dry_run_gaff_vacuum(benzene_vacuum_system, vac_settings, tmp_pa...
  function test_dry_run_espaloma_vacuum_user_charges (line 243) | def test_dry_run_espaloma_vacuum_user_charges(benzene_modifications, vac...
  function test_dry_run_charge_backends (line 303) | def test_dry_run_charge_backends(
  function test_dry_many_molecules_solvent (line 332) | def test_dry_many_molecules_solvent(benzene_many_solv_system, tmp_path):
  function test_dry_run_ligand_tip4p (line 418) | def test_dry_run_ligand_tip4p(benzene_system, tmp_path):
  function test_dry_run_complex (line 450) | def test_dry_run_complex(benzene_complex_system, tmp_path):
  function test_hightimestep (line 485) | def test_hightimestep(benzene_vacuum_system, tmp_path):
  function test_vaccuum_PME_error (line 501) | def test_vaccuum_PME_error(benzene_vacuum_system):
  function test_multiple_basesolvents_error (line 515) | def test_multiple_basesolvents_error(a2a_protein_membrane_component):
  function test_states_not_matching_error (line 534) | def test_states_not_matching_error(benzene_vacuum_system, toluene_vacuum...
  function test_mapping_warning (line 545) | def test_mapping_warning(benzene_vacuum_system, tmp_path):
  function solvent_protocol_dag (line 564) | def solvent_protocol_dag(benzene_system):
  function test_unit_tagging (line 576) | def test_unit_tagging(benzene_system, tmp_path):
  function test_gather (line 611) | def test_gather(solvent_protocol_dag, tmp_path):
  class TestProtocolResult (line 636) | class TestProtocolResult:
    method protocolresult (line 638) | def protocolresult(self, md_json):
    method test_reload_protocol_result (line 645) | def test_reload_protocol_result(self, md_json):
    method test_get_estimate (line 652) | def test_get_estimate(self, protocolresult):
    method test_get_uncertainty (line 657) | def test_get_uncertainty(self, protocolresult):
    method test_get_traj_filename (line 662) | def test_get_traj_filename(self, protocolresult):
    method test_get_pdb_filename (line 668) | def test_get_pdb_filename(self, protocolresult):

FILE: src/openfe/tests/protocols/openmm_md/test_plain_md_resume.py
  function vacuum_protocol_settings (line 27) | def vacuum_protocol_settings():
  function test_verify_execution_environment (line 41) | def test_verify_execution_environment():
  function test_verify_execution_environment_fail (line 52) | def test_verify_execution_environment_fail():
  function test_verify_execution_env_missing_key (line 64) | def test_verify_execution_env_missing_key():
  function test_check_restart (line 80) | def test_check_restart(vacuum_protocol_settings, plain_md_checkpoint_path):
  class TestPlainMDResume (line 98) | class TestPlainMDResume:
    method protocol_dag (line 100) | def protocol_dag(self, vacuum_protocol_settings, benzene_vacuum_system):
    method test_resume (line 108) | def test_resume(

FILE: src/openfe/tests/protocols/openmm_md/test_plain_md_slow.py
  function test_vacuum_sim (line 14) | def test_vacuum_sim(
  function test_complex_solvent_sim_gpu (line 81) | def test_complex_solvent_sim_gpu(

FILE: src/openfe/tests/protocols/openmm_md/test_plain_md_tokenization.py
  function protocol (line 13) | def protocol():
  function protocol_units (line 18) | def protocol_units(protocol, benzene_system):
  function protocol_setup_unit (line 28) | def protocol_setup_unit(protocol, protocol_units):
  function protocol_simulation_unit (line 35) | def protocol_simulation_unit(protocol, protocol_units):
  function protocol_result (line 42) | def protocol_result(md_json):
  class TestPlainMDProtocol (line 48) | class TestPlainMDProtocol(GufeTokenizableTestsMixin):
    method instance (line 54) | def instance(self, protocol):
    method test_repr (line 57) | def test_repr(self, instance):
  class TestPlainMDSetupUnit (line 65) | class TestPlainMDSetupUnit(GufeTokenizableTestsMixin):
    method instance (line 71) | def instance(self, protocol_setup_unit):
    method test_repr (line 74) | def test_repr(self, instance):
  class TestPlainMDSimulationUnit (line 82) | class TestPlainMDSimulationUnit(GufeTokenizableTestsMixin):
    method instance (line 88) | def instance(self, protocol_simulation_unit):
    method test_repr (line 91) | def test_repr(self, instance):
  class TestPlainMDProtocolResult (line 96) | class TestPlainMDProtocolResult(GufeTokenizableTestsMixin):
    method instance (line 102) | def instance(self, protocol_result):
    method test_repr (line 105) | def test_repr(self, instance):

FILE: src/openfe/tests/protocols/openmm_rfe/helpers.py
  function make_htf (line 15) | def make_htf(
  function _make_system_with_cmap (line 137) | def _make_system_with_cmap(

FILE: src/openfe/tests/protocols/openmm_rfe/test_hybrid_factory.py
  function test_cmap_system_no_dummy_pme_energy (line 13) | def test_cmap_system_no_dummy_pme_energy(htf_cmap_chlorobenzene_to_fluor...
  function test_cmap_missing_cmap_error (line 68) | def test_cmap_missing_cmap_error():
  function test_verify_cmap_incompatible_maps_error (line 89) | def test_verify_cmap_incompatible_maps_error():
  function test_verify_cmap_incompatible_torsions_error (line 103) | def test_verify_cmap_incompatible_torsions_error():
  function test_cmap_maps_incompatible_error (line 120) | def test_cmap_maps_incompatible_error():
  function test_cmap_torsions_incompatible_error (line 143) | def test_cmap_torsions_incompatible_error():
  function test_cmap_map_index_incompatible_error (line 184) | def test_cmap_map_index_incompatible_error():
  function test_cmap_in_alchemical_region_error (line 213) | def test_cmap_in_alchemical_region_error():
  function test_softcore_parameters (line 235) | def test_softcore_parameters(chloroethane_to_fluoroethane_mapping, softc...
  function test_particles_mass_no_dummy (line 257) | def test_particles_mass_no_dummy(htf_chloro_fluoroethane):
  function test_particle_mass_dummy (line 277) | def test_particle_mass_dummy(htf_chloro_ethane):
  function test_constraints_count_no_dummy (line 299) | def test_constraints_count_no_dummy(htf_chloro_fluoroethane):
  function test_constraints_count_dummy (line 304) | def test_constraints_count_dummy(htf_chloro_ethane):
  function test_hybrid_forces_no_dummy (line 309) | def test_hybrid_forces_no_dummy(htf_chloro_fluoroethane):
  function test_hybrid_forces_dummy (line 328) | def test_hybrid_forces_dummy(htf_chloro_ethane):
  function test_bond_force_no_dummy (line 344) | def test_bond_force_no_dummy(htf_chloro_fluoroethane):
  function test_bond_force_dummy (line 383) | def test_bond_force_dummy(htf_chloro_ethane):
  function test_angle_force_no_dummy (line 433) | def test_angle_force_no_dummy(htf_chloro_fluoroethane):
  function test_angle_force_dummy (line 473) | def test_angle_force_dummy(htf_chloro_ethane):
  function test_torsion_force_no_dummy (line 538) | def test_torsion_force_no_dummy(htf_chloro_fluoroethane):
  function test_torsion_force_dummy (line 619) | def test_torsion_force_dummy(htf_chloro_ethane):
  function test_nonbonded_force_no_dummy (line 700) | def test_nonbonded_force_no_dummy(htf_chloro_fluoroethane):
  function test_nonbonded_force_dummy (line 736) | def test_nonbonded_force_dummy(htf_chloro_ethane):
  function test_nonbonded_offsets_no_dummy (line 792) | def test_nonbonded_offsets_no_dummy(htf_chloro_fluoroethane):
  function test_nonbonded_offsets_dummy (line 821) | def test_nonbonded_offsets_dummy(htf_chloro_ethane):
  function test_nonbonded_exceptions_no_dummy (line 858) | def test_nonbonded_exceptions_no_dummy(htf_chloro_fluoroethane):
  function test_nonbonded_exceptions_dummy (line 919) | def test_nonbonded_exceptions_dummy(htf_chloro_ethane):
  function test_nonbonded_exception_offsets_no_dummy (line 1001) | def test_nonbonded_exception_offsets_no_dummy(htf_chloro_fluoroethane):
  function test_nonbonded_exception_offsets_dummy (line 1095) | def test_nonbonded_exception_offsets_dummy(htf_chloro_ethane):
  function test_custom_nb_force_no_dummy (line 1197) | def test_custom_nb_force_no_dummy(htf_chloro_fluoroethane):
  function test_custom_nb_force_dummy (line 1243) | def test_custom_nb_force_dummy(htf_chloro_ethane):
  function test_custom_nb_exclusions_no_dummy (line 1309) | def test_custom_nb_exclusions_no_dummy(htf_chloro_fluoroethane):
  function test_custom_nb_exclusions_dummy (line 1333) | def test_custom_nb_exclusions_dummy(htf_chloro_ethane):
  function test_custom_nb_interation_groups_no_dummy (line 1359) | def test_custom_nb_interation_groups_no_dummy(htf_chloro_fluoroethane):
  function test_custom_nb_interation_groups_dummy (line 1391) | def test_custom_nb_interation_groups_dummy(htf_chloro_ethane):
  function test_custom_sterics_force_no_dummy (line 1425) | def test_custom_sterics_force_no_dummy(htf_chloro_fluoroethane):
  function test_custom_sterics_force_dummy (line 1450) | def test_custom_sterics_force_dummy(htf_chloro_ethane):
  function test_vacuum_system_energy_no_dummy (line 1551) | def test_vacuum_system_energy_no_dummy(htf_chloro_fluoroethane):
  function test_vacuum_system_energy_dummy (line 1608) | def test_vacuum_system_energy_dummy(htf_chloro_ethane):
  function test_system_energy_pme_no_dummy (line 1683) | def test_system_energy_pme_no_dummy(htf_chlorobenzene_fluorobenzene):
  function test_system_interaction_groups_no_dummy (line 1732) | def test_system_interaction_groups_no_dummy(htf_chlorobenzene_fluorobenz...
  function test_system_interaction_groups_dummy (line 1764) | def test_system_interaction_groups_dummy(htf_chlorobenzene_benzene):
  function test_system_energy_pme_dummy (line 1796) | def test_system_energy_pme_dummy(htf_chlorobenzene_benzene):

FILE: src/openfe/tests/protocols/openmm_rfe/test_hybrid_top_protocol.py
  function _get_units (line 54) | def _get_units(protocol_units, unit_type):
  function vac_settings (line 62) | def vac_settings():
  function solv_settings (line 71) | def solv_settings():
  function test_compute_platform_warn (line 78) | def test_compute_platform_warn():
  function test_append_topology (line 83) | def test_append_topology(benzene_complex_system, toluene_complex_system):
  function test_append_topology_no_exclude (line 112) | def test_append_topology_no_exclude(benzene_complex_system, toluene_comp...
  function test_create_default_settings (line 141) | def test_create_default_settings():
  function test_adaptive_settings_no_initial (line 147) | def test_adaptive_settings_no_initial(benzene_system, toluene_system, be...
  function test_create_default_protocol (line 159) | def test_create_default_protocol():
  function test_invalid_protocol_repeats (line 168) | def test_invalid_protocol_repeats():
  function test_serialize_protocol (line 174) | def test_serialize_protocol():
  function test_repeat_units (line 186) | def test_repeat_units(benzene_system, toluene_system, benzene_to_toluene...
  function test_create_independent_repeat_ids (line 222) | def test_create_independent_repeat_ids(benzene_system, toluene_system, b...
  function test_bad_sampler (line 250) | def test_bad_sampler():
  function test_setup_dry_sim_default_vacuum (line 272) | def test_setup_dry_sim_default_vacuum(
  function test_setup_gaff_vacuum (line 355) | def test_setup_gaff_vacuum(
  function test_dry_many_molecules_solvent (line 380) | def test_dry_many_molecules_solvent(
  function test_setup_core_element_change (line 471) | def test_setup_core_element_change(vac_settings, tmp_path):
  function test_dry_run_ligand (line 504) | def test_dry_run_ligand(
  function test_confgen_mocked_fail (line 547) | def test_confgen_mocked_fail(
  function tip4p_hybrid_factory (line 567) | def tip4p_hybrid_factory(
  function test_tip4p_particle_count (line 610) | def test_tip4p_particle_count(tip4p_hybrid_factory):
  function test_tip4p_num_waters (line 625) | def test_tip4p_num_waters(tip4p_hybrid_factory):
  function test_tip4p_check_vsite_parameters (line 642) | def test_tip4p_check_vsite_parameters(tip4p_hybrid_factory):
  function test_setup_ligand_system_cutoff (line 692) | def test_setup_ligand_system_cutoff(
  function test_setup_charge_backends (line 755) | def test_setup_charge_backends(
  function test_setup_same_mol_different_charges (line 817) | def test_setup_same_mol_different_charges(benzene_modifications, vac_set...
  function test_setup_user_charges (line 881) | def test_setup_user_charges(benzene_modifications, vac_settings, tmp_path):
  function test_virtual_sites_no_reassign (line 1001) | def test_virtual_sites_no_reassign(
  function test_setup_dodecahdron_ligand_box (line 1046) | def test_setup_dodecahdron_ligand_box(
  function test_dry_run_complex (line 1083) | def test_dry_run_complex(
  function test_dry_run_membrane_complex (line 1129) | def test_dry_run_membrane_complex(
  function test_lambda_schedule_default (line 1218) | def test_lambda_schedule_default():
  function test_lambda_schedule (line 1224) | def test_lambda_schedule(windows):
  function test_setup_ligand_overlap_warning (line 1231) | def test_setup_ligand_overlap_warning(
  function solvent_protocol_dag (line 1271) | def solvent_protocol_dag(benzene_system, toluene_system, benzene_to_tolu...
  function unit_mock_patcher (line 1284) | def unit_mock_patcher():
  function test_unit_tagging (line 1325) | def test_unit_tagging(solvent_protocol_dag, unit_mock_patcher, tmp_path):
  function test_gather (line 1364) | def test_gather(solvent_protocol_dag, unit_mock_patcher, tmp_path):
  class TestConstraintRemoval (line 1382) | class TestConstraintRemoval:
    method make_systems (line 1384) | def make_systems(
    method test_remove_constraints_lengthchange (line 1443) | def test_remove_constraints_lengthchange(self, benzene_modifications, ...
    method test_constraint_to_harmonic (line 1492) | def test_constraint_to_harmonic(self, benzene_modifications, reverse):
    method test_constraint_to_harmonic_nitrile (line 1527) | def test_constraint_to_harmonic_nitrile(self, benzene_modifications, r...
    method test_non_H_constraint_fail (line 1564) | def test_non_H_constraint_fail(self, benzene_modifications, reverse):
    method test_double_constraint_fail (line 1606) | def test_double_constraint_fail(self):
  function tyk2_xml (line 1641) | def tyk2_xml(tmp_path_factory):
  function tyk2_reference_xml (line 1687) | def tyk2_reference_xml():
  class TestTyk2XmlRegression (line 1696) | class TestTyk2XmlRegression:
    method test_particles (line 1700) | def test_particles(tyk2_xml, tyk2_reference_xml):
    method test_constraints (line 1711) | def test_constraints(tyk2_xml, tyk2_reference_xml):
  class TestProtocolResult (line 1723) | class TestProtocolResult:
    method protocolresult (line 1725) | def protocolresult(self, rfe_transformation_json):
    method test_reload_protocol_result (line 1732) | def test_reload_protocol_result(self, rfe_transformation_json):
    method test_get_estimate (line 1739) | def test_get_estimate(self, protocolresult):
    method test_get_uncertainty (line 1747) | def test_get_uncertainty(self, protocolresult):
    method test_get_individual (line 1755) | def test_get_individual(self, protocolresult):
    method test_get_forwards_etc (line 1764) | def test_get_forwards_etc(self, protocolresult):
    method test_none_foward_reverse_energies (line 1779) | def test_none_foward_reverse_energies(self, protocolresult):
    method test_get_overlap_matrices (line 1790) | def test_get_overlap_matrices(self, protocolresult):
    method test_get_replica_transition_statistics (line 1800) | def test_get_replica_transition_statistics(self, protocolresult):
    method test_equilibration_iterations (line 1811) | def test_equilibration_iterations(self, protocolresult):
    method test_production_iterations (line 1818) | def test_production_iterations(self, protocolresult):
    method test_filenotfound_replica_states (line 1825) | def test_filenotfound_replica_states(self, protocolresult):
  function benzene_solvent_openmm_system (line 1833) | def benzene_solvent_openmm_system(benzene_modifications):
  function benzene_tip4p_solvent_openmm_system (line 1867) | def benzene_tip4p_solvent_openmm_system(benzene_modifications):
  function benzene_self_system_mapping (line 1907) | def benzene_self_system_mapping(benzene_solvent_openmm_system):
  function test_get_ion_water_parameters_unknownresname (line 1942) | def test_get_ion_water_parameters_unknownresname(ion, water, benzene_sol...
  function test_get_alchemical_waters_no_waters (line 1953) | def test_get_alchemical_waters_no_waters(
  function test_handle_alchemwats_incorrect_count (line 1966) | def test_handle_alchemwats_incorrect_count(
  function test_handle_alchemwats_too_many_nbf (line 1987) | def test_handle_alchemwats_too_many_nbf(
  function test_handle_alchemwats_vsite_water (line 2011) | def test_handle_alchemwats_vsite_water(
  function test_handle_alchemwats_incorrect_atom (line 2033) | def test_handle_alchemwats_incorrect_atom(
  function test_handle_alchemical_wats (line 2061) | def test_handle_alchemical_wats(
  function _assert_total_charge (line 2107) | def _assert_total_charge(system, atom_classes, chgA, chgB):
  function test_dry_run_alchemwater_solvent (line 2147) | def test_dry_run_alchemwater_solvent(benzene_to_benzoic_mapping, solv_se...
  function test_setup_complex_alchemwater_totcharge (line 2196) | def test_setup_complex_alchemwater_totcharge(
  function test_structural_analysis_error (line 2253) | def test_structural_analysis_error(tmp_path):
  function test_dry_run_vacuum_write_frequency (line 2273) | def test_dry_run_vacuum_write_frequency(

FILE: src/openfe/tests/protocols/openmm_rfe/test_hybrid_top_resume.py
  function protocol_settings (line 37) | def protocol_settings():
  function test_verify_execution_environment (line 51) | def test_verify_execution_environment():
  function test_verify_execution_environment_fail (line 62) | def test_verify_execution_environment_fail():
  function test_verify_execution_env_missing_key (line 74) | def test_verify_execution_env_missing_key():
  function test_check_restart (line 90) | def test_check_restart(protocol_settings, htop_trajectory_path):
  class TestCheckpointResuming (line 106) | class TestCheckpointResuming:
    method protocol_dag (line 108) | def protocol_dag(
    method _check_sampler (line 118) | def _check_sampler(sampler, num_iterations: int):
    method _get_positions (line 129) | def _get_positions(dataset):
    method _copy_simfiles (line 137) | def _copy_simfiles(basedir: pathlib.Path, filepath):
    method test_resume (line 141) | def test_resume(self, protocol_dag, htop_trajectory_path, htop_checkpo...
    method test_resume_fail_particles (line 223) | def test_resume_fail_particles(
    method test_resume_fail_constraints (line 257) | def test_resume_fail_constraints(
    method test_resume_fail_forces (line 297) | def test_resume_fail_forces(
    method test_resume_differ_barostat (line 335) | def test_resume_differ_barostat(
    method test_resume_differ_forces (line 380) | def test_resume_differ_forces(
    method test_resume_bad_files (line 428) | def test_resume_bad_files(
    method test_missing_file (line 468) | def test_missing_file(

FILE: src/openfe/tests/protocols/openmm_rfe/test_hybrid_top_slow.py
  function test_openmm_run_engine (line 19) | def test_openmm_run_engine(
  function test_run_eg5_sim (line 131) | def test_run_eg5_sim(eg5_protein, eg5_ligands, eg5_cofactor, tmp_path):
  function test_run_dodecahedron_sim (line 176) | def test_run_dodecahedron_sim(benzene_system, toluene_system, benzene_to...

FILE: src/openfe/tests/protocols/openmm_rfe/test_hybrid_top_tokenization.py
  function rfe_protocol (line 24) | def rfe_protocol():
  function rfe_protocol_other_input_units (line 31) | def rfe_protocol_other_input_units():
  function protocol_units (line 39) | def protocol_units(rfe_protocol, benzene_system, toluene_system, benzene...
  function protocol_setup_unit (line 49) | def protocol_setup_unit(protocol_units):
  function protocol_simulation_unit (line 56) | def protocol_simulation_unit(protocol_units):
  function protocol_analysis_unit (line 63) | def protocol_analysis_unit(protocol_units):
  class TestRelativeHybridTopologyProtocolResult (line 70) | class TestRelativeHybridTopologyProtocolResult(GufeTokenizableTestsMixin):
    method instance (line 76) | def instance(self):
  class TestRelativeHybridTopologyProtocolOtherInputUnits (line 80) | class TestRelativeHybridTopologyProtocolOtherInputUnits(GufeTokenizableT...
    method instance (line 86) | def instance(self, rfe_protocol_other_input_units):
    method test_repr (line 89) | def test_repr(self, instance):
  class TestRelativeHybridTopologyProtocol (line 97) | class TestRelativeHybridTopologyProtocol(GufeTokenizableTestsMixin):
    method instance (line 103) | def instance(self, rfe_protocol):
    method test_repr (line 106) | def test_repr(self, instance):
  class TestHybridTopologySetupUnit (line 114) | class TestHybridTopologySetupUnit(GufeTokenizableTestsMixin):
    method instance (line 120) | def instance(self, protocol_setup_unit):
    method test_key_stable (line 123) | def test_key_stable(self):
    method test_repr (line 126) | def test_repr(self, instance):
  class TestHybridTopologyMultiStateSimulationUnit (line 134) | class TestHybridTopologyMultiStateSimulationUnit(GufeTokenizableTestsMix...
    method instance (line 140) | def instance(self, protocol_simulation_unit):
    method test_key_stable (line 143) | def test_key_stable(self):
    method test_repr (line 146) | def test_repr(self, instance):
  class TestHybridTopologyMultiStateAnalysisUnit (line 154) | class TestHybridTopologyMultiStateAnalysisUnit(GufeTokenizableTestsMixin):
    method instance (line 160) | def instance(self, protocol_analysis_unit):
    method test_key_stable (line 163) | def test_key_stable(self):
    method test_repr (line 166) | def test_repr(self, instance):

FILE: src/openfe/tests/protocols/openmm_rfe/test_hybrid_top_validation.py
  function vac_settings (line 14) | def vac_settings():
  function solv_settings (line 23) | def solv_settings():
  function test_invalid_protocol_repeats (line 30) | def test_invalid_protocol_repeats():
  function test_endstate_two_alchemcomp_stateA (line 37) | def test_endstate_two_alchemcomp_stateA(state, benzene_modifications):
  function test_endstates_not_smc (line 62) | def test_endstates_not_smc(state, benzene_modifications):
  function test_validate_mapping_none_mapping (line 86) | def test_validate_mapping_none_mapping():
  function test_validate_mapping_multi_mapping (line 92) | def test_validate_mapping_multi_mapping(benzene_to_toluene_mapping):
  function test_validate_mapping_alchem_not_in (line 101) | def test_validate_mapping_alchem_not_in(state, benzene_to_toluene_mapping):
  function test_vaccuum_PME_error (line 116) | def test_vaccuum_PME_error(
  function test_multiple_basesolvents_error (line 130) | def test_multiple_basesolvents_error(a2a_protein_membrane_component, a2a...
  function test_smcs_same_charge_passes (line 160) | def test_smcs_same_charge_passes(charge, benzene_modifications):
  function test_smcs_different_charges_none_not_none (line 174) | def test_smcs_different_charges_none_not_none(benzene_modifications):
  function test_smcs_different_charges_all (line 190) | def test_smcs_different_charges_all(benzene_modifications):
  function test_smcs_different_charges_different_endstates (line 206) | def test_smcs_different_charges_different_endstates(benzene_modifications):
  function test_solvent_nocutoff_error (line 223) | def test_solvent_nocutoff_error(
  function test_nonwater_solvent_error (line 241) | def test_nonwater_solvent_error(
  function test_too_many_solv_comps_error (line 268) | def test_too_many_solv_comps_error(
  function test_bad_solv_settings (line 301) | def test_bad_solv_settings(
  function test_too_many_prot_comps_error (line 322) | def test_too_many_prot_comps_error(
  function test_element_change_warning (line 359) | def test_element_change_warning(atom_mapping_basic_test_files):
  function test_charge_difference_no_corr (line 381) | def test_charge_difference_no_corr(benzene_to_benzoic_mapping):
  function test_charge_difference_no_solvent (line 396) | def test_charge_difference_no_solvent(benzene_to_benzoic_mapping):
  function test_charge_difference_no_pme (line 408) | def test_charge_difference_no_pme(benzene_to_benzoic_mapping):
  function test_greater_than_one_charge_difference_error (line 420) | def test_greater_than_one_charge_difference_error(aniline_to_benzoic_map...
  function test_get_charge_difference (line 440) | def test_get_charge_difference(mapping_name, result, request, caplog):
  function test_hightimestep (line 461) | def test_hightimestep(
  function test_time_per_iteration_divmod (line 482) | def test_time_per_iteration_divmod(
  function test_simsteps_not_timestep_divisible (line 505) | def test_simsteps_not_timestep_divisible(
  function test_simsteps_not_mcstep_divisible (line 527) | def test_simsteps_not_mcstep_divisible(
  function test_checkpoint_interval_not_divisible_time_per_iter (line 548) | def test_checkpoint_interval_not_divisible_time_per_iter(
  function test_pos_vel_write_frequency_not_divisible (line 570) | def test_pos_vel_write_frequency_not_divisible(
  function test_real_time_analysis_not_divisible (line 594) | def test_real_time_analysis_not_divisible(
  function test_n_replicas_not_n_windows (line 615) | def test_n_replicas_not_n_windows(

FILE: src/openfe/tests/protocols/openmm_septop/conftest.py
  function protocol_dry_settings (line 10) | def protocol_dry_settings():
  function benzene_toluene_dag (line 19) | def benzene_toluene_dag(

FILE: src/openfe/tests/protocols/openmm_septop/test_septop_protocol.py
  function default_settings (line 67) | def default_settings():
  function test_create_default_protocol (line 72) | def test_create_default_protocol(default_settings):
  function test_serialize_protocol (line 81) | def test_serialize_protocol(default_settings):
  function test_repeat_units (line 91) | def test_repeat_units(benzene_complex_system, toluene_complex_system, de...
  function test_create_independent_repeat_ids (line 132) | def test_create_independent_repeat_ids(
  function compute_interaction_energy (line 167) | def compute_interaction_energy(
  function three_particle_system (line 187) | def three_particle_system():
  class TestNonbondedInteractions (line 225) | class TestNonbondedInteractions:
    method test_one_ligand (line 226) | def test_one_ligand(self, three_particle_system):
    method test_two_ligands (line 278) | def test_two_ligands(self, three_particle_system):
    method test_two_ligands_charges (line 337) | def test_two_ligands_charges(self, three_particle_system):
  function test_dry_run_benzene_toluene (line 365) | def test_dry_run_benzene_toluene(benzene_toluene_dag, tmp_path):
  function test_dry_run_methods (line 461) | def test_dry_run_methods(
  function test_dry_run_ligand_system_pressure (line 518) | def test_dry_run_ligand_system_pressure(
  function test_virtual_sites_no_reassign (line 562) | def test_virtual_sites_no_reassign(
  function test_dry_run_ligand_system_cutoff (line 615) | def test_dry_run_ligand_system_cutoff(
  function test_dry_run_benzene_toluene_tip4p (line 655) | def test_dry_run_benzene_toluene_tip4p(
  function test_dry_run_benzene_toluene_noncubic (line 706) | def test_dry_run_benzene_toluene_noncubic(
  function test_dry_run_solv_user_charges_benzene_toluene (line 755) | def test_dry_run_solv_user_charges_benzene_toluene(
  function test_high_timestep (line 863) | def test_high_timestep(
  function test_bad_sampler (line 886) | def test_bad_sampler():
  function T4L_xml (line 905) | def T4L_xml(
  class TestT4LXmlRegression (line 935) | class TestT4LXmlRegression:
    method test_particles (line 939) | def test_particles(T4L_xml, T4L_septop_reference_xml):
    method test_constraints (line 953) | def test_constraints(T4L_xml, T4L_septop_reference_xml):
  class TestA2AMembraneDryRun (line 973) | class TestA2AMembraneDryRun:
    method settings (line 981) | def settings(self):
    method dag (line 992) | def dag(self, settings, a2a_ligands, a2a_protein_membrane_component):
    method complex_setup_units (line 1024) | def complex_setup_units(self, dag):
    method complex_run_units (line 1028) | def complex_run_units(self, dag):
    method complex_analysis_unit (line 1032) | def complex_analysis_unit(self, dag):
    method solvent_setup_units (line 1036) | def solvent_setup_units(self, dag):
    method solvent_run_units (line 1040) | def solvent_run_units(self, dag):
    method solvent_analysis_unit (line 1044) | def solvent_analysis_unit(self, dag):
    method test_number_of_units (line 1047) | def test_number_of_units(
    method _assert_force_num (line 1056) | def _assert_force_num(self, system, forcetype, number):
    method _assert_expected_alchemical_forces (line 1060) | def _assert_expected_alchemical_forces(self, system, complexed: bool, ...
    method _assert_expected_nonalchemical_forces (line 1103) | def _assert_expected_nonalchemical_forces(self, system, complexed: boo...
    method _verify_sampler (line 1139) | def _verify_sampler(self, sampler, complexed: bool, settings):
    method _test_orthogonal_vectors (line 1158) | def _test_orthogonal_vectors(system):
    method _test_cubic_vectors (line 1183) | def _test_cubic_vectors(system):
    method test_complex_dry_run (line 1199) | def test_complex_dry_run(self, complex_setup_units, complex_run_units,...
    method test_solvent_dry_run (line 1244) | def test_solvent_dry_run(self, solvent_setup_units, solvent_run_units,...

FILE: src/openfe/tests/protocols/openmm_septop/test_septop_protocol_results.py
  function patcher (line 60) | def patcher():
  function test_unit_tagging (line 129) | def test_unit_tagging(benzene_toluene_dag, patcher, tmp_path):
  function test_gather (line 171) | def test_gather(benzene_toluene_dag, patcher, tmp_path):
  class TestProtocolResult (line 189) | class TestProtocolResult:
    method protocolresult (line 191) | def protocolresult(self, septop_json):
    method test_reload_protocol_result (line 198) | def test_reload_protocol_result(self, septop_json):
    method test_get_estimate (line 205) | def test_get_estimate(self, protocolresult):
    method test_get_uncertainty (line 213) | def test_get_uncertainty(self, protocolresult):
    method test_get_individual (line 220) | def test_get_individual(self, protocolresult):
    method test_get_forwards_etc (line 231) | def test_get_forwards_etc(self, protocolresult):
    method test_get_overlap_matrices (line 253) | def test_get_overlap_matrices(self, key, protocolresult):
    method test_get_replica_transition_statistics (line 269) | def test_get_replica_transition_statistics(self, key, protocolresult):
    method test_equilibration_iterations (line 286) | def test_equilibration_iterations(self, key, protocolresult):
    method test_production_iterations (line 295) | def test_production_iterations(self, key, protocolresult):
    method test_selection_indices (line 310) | def test_selection_indices(self, key, protocolresult, expected_size):
    method test_filenotfound_replica_states (line 319) | def test_filenotfound_replica_states(self, protocolresult):
    method test_restraint_geometry (line 325) | def test_restraint_geometry(self, protocolresult):

FILE: src/openfe/tests/protocols/openmm_septop/test_septop_resume.py
  function protocol_settings (line 35) | def protocol_settings():
  function test_verify_execution_environment (line 59) | def test_verify_execution_environment():
  function test_verify_execution_environment_fail (line 70) | def test_verify_execution_environment_fail():
  function test_verify_execution_env_missing_key (line 82) | def test_verify_execution_env_missing_key():
  function test_check_restart (line 98) | def test_check_restart(protocol_settings, septop_solv_trajectory_path):
  function test_check_restart_one_file_missing (line 114) | def test_check_restart_one_file_missing(protocol_settings, septop_solv_t...
  class TestCheckpointResuming (line 125) | class TestCheckpointResuming:
    method protocol_dag (line 127) | def protocol_dag(
    method protocol_units (line 142) | def protocol_units(self, protocol_dag):
    method setup_results (line 150) | def setup_results(self, protocol_units, tmp_path):
    method pdb_file (line 160) | def pdb_file(self, setup_results):
    method _check_sampler (line 164) | def _check_sampler(sampler, num_iterations: int):
    method _get_positions (line 175) | def _get_positions(dataset):
    method _copy_simfiles (line 184) | def _copy_simfiles(cwd: pathlib.Path, filepath):
    method test_resume (line 188) | def test_resume(
    method test_resume_fail_particles (line 271) | def test_resume_fail_particles(
    method test_resume_fail_constraints (line 309) | def test_resume_fail_constraints(
    method test_resume_fail_forces (line 349) | def test_resume_fail_forces(
    method test_resume_differ_barostat (line 387) | def test_resume_differ_barostat(
    method test_resume_differ_forces (line 437) | def test_resume_differ_forces(
    method test_resume_bad_files (line 494) | def test_resume_bad_files(

FILE: src/openfe/tests/protocols/openmm_septop/test_septop_settings.py
  function protocol_dry_settings (line 14) | def protocol_dry_settings():
  function default_settings (line 23) | def default_settings():
  function test_create_default_settings (line 28) | def test_create_default_settings():
  function test_incorrect_window_settings (line 41) | def test_incorrect_window_settings(val, default_settings):
  function test_monotonic_lambda_windows_A (line 70) | def test_monotonic_lambda_windows_A(val, default_settings):
  function test_monotonic_lambda_windows_B (line 100) | def test_monotonic_lambda_windows_B(val, default_settings):
  function test_output_induces_not_all (line 110) | def test_output_induces_not_all(default_settings):
  function test_adaptive_settings_no_protein_membrane (line 117) | def test_adaptive_settings_no_protein_membrane(toluene_complex_system, d...
  function test_adaptive_settings_with_protein_membrane (line 127) | def test_adaptive_settings_with_protein_membrane(a2a_protein_membrane_co...

FILE: src/openfe/tests/protocols/openmm_septop/test_septop_slow.py
  function default_settings (line 26) | def default_settings():
  function compare_energies (line 32) | def compare_energies(alchemical_system, positions):
  function test_lambda_energies (line 104) | def test_lambda_energies(
  function test_openmm_run_engine (line 214) | def test_openmm_run_engine(
  function test_restraints_solvent (line 297) | def test_restraints_solvent(

FILE: src/openfe/tests/protocols/openmm_septop/test_septop_tokenization.py
  function protocol (line 13) | def protocol():
  function protocol_units (line 18) | def protocol_units(protocol, benzene_complex_system, toluene_complex_sys...
  function solvent_setup_protocol_unit (line 28) | def solvent_setup_protocol_unit(protocol_units):
  function solvent_run_protocol_unit (line 35) | def solvent_run_protocol_unit(protocol_units):
  function solvent_analysis_protocol_unit (line 42) | def solvent_analysis_protocol_unit(protocol_units):
  function complex_setup_protocol_unit (line 49) | def complex_setup_protocol_unit(protocol_units):
  function complex_run_protocol_unit (line 56) | def complex_run_protocol_unit(protocol_units):
  function complex_analysis_protocol_unit (line 63) | def complex_analysis_protocol_unit(protocol_units):
  function protocol_result (line 70) | def protocol_result(septop_json):
  class TestSepTopProtocol (line 79) | class TestSepTopProtocol(GufeTokenizableTestsMixin):
    method instance (line 85) | def instance(self, protocol):
    method test_repr (line 88) | def test_repr(self, instance):
  class TestSepTopSolventSetupUnit (line 96) | class TestSepTopSolventSetupUnit(GufeTokenizableTestsMixin):
    method instance (line 104) | def instance(self, solvent_setup_protocol_unit):
    method test_repr (line 107) | def test_repr(self, instance):
  class TestSepTopSolventRunUnit (line 115) | class TestSepTopSolventRunUnit(GufeTokenizableTestsMixin):
    method instance (line 121) | def instance(self, solvent_run_protocol_unit):
    method test_repr (line 124) | def test_repr(self, instance):
  class TestSepTopSolventAnalysisUnit (line 132) | class TestSepTopSolventAnalysisUnit(GufeTokenizableTestsMixin):
    method instance (line 138) | def instance(self, solvent_analysis_protocol_unit):
    method test_repr (line 141) | def test_repr(self, instance):
  class TestSepTopComplexSetupUnit (line 149) | class TestSepTopComplexSetupUnit(GufeTokenizableTestsMixin):
    method instance (line 157) | def instance(self, complex_setup_protocol_unit):
    method test_repr (line 160) | def test_repr(self, instance):
  class TestSepTopComplexRunUnit (line 168) | class TestSepTopComplexRunUnit(GufeTokenizableTestsMixin):
    method instance (line 174) | def instance(self, complex_run_protocol_unit):
    method test_repr (line 177) | def test_repr(self, instance):
  class TestSepTopComplexAnalysisUnit (line 185) | class TestSepTopComplexAnalysisUnit(GufeTokenizableTestsMixin):
    method instance (line 191) | def instance(self, complex_analysis_protocol_unit):
    method test_repr (line 194) | def test_repr(self, instance):
  class TestSepTopProtocolResult (line 202) | class TestSepTopProtocolResult(GufeTokenizableTestsMixin):
    method instance (line 208) | def instance(self, protocol_result):
    method test_repr (line 211) | def test_repr(self, instance):

FILE: src/openfe/tests/protocols/openmm_septop/test_septop_validation.py
  function default_settings (line 17) | def default_settings():
  function test_validate_lambda_schedule_nreplicas (line 35) | def test_validate_lambda_schedule_nreplicas(val, default_settings):
  function test_validate_lambda_schedule_nwindows (line 61) | def test_validate_lambda_schedule_nwindows(val, default_settings):
  function test_validate_lambda_schedule_nakedcharge (line 91) | def test_validate_lambda_schedule_nakedcharge(val, default_settings):
  function test_check_alchem_charge_diff (line 118) | def test_check_alchem_charge_diff(charged_benzene_modifications):
  function test_charge_error_create (line 127) | def test_charge_error_create(charged_benzene_modifications, T4_protein_c...
  function test_validate_endstates_protcomp (line 162) | def test_validate_endstates_protcomp(request, system_A, system_B, fail_e...
  function T4L_benzene_vacuum (line 171) | def T4L_benzene_vacuum(benzene_modifications, T4_protein_component):
  function test_validate_endstates_nosolvcomp (line 187) | def test_validate_endstates_nosolvcomp(
  function T4L_system (line 201) | def T4L_system(T4_protein_component):
  function test_validate_alchem_comps_missing (line 217) | def test_validate_alchem_comps_missing(
  function test_validate_alchem_comps_toomanyA (line 238) | def test_validate_alchem_comps_toomanyA(
  function test_validate_alchem_nonsmc (line 273) | def test_validate_alchem_nonsmc(

FILE: src/openfe/tests/protocols/openmm_septop/utils.py
  function _get_units (line 26) | def _get_units(protocol_units, unit_type):

FILE: src/openfe/tests/protocols/restraints/test_geometry_base.py
  function test_hostguest_geometry (line 9) | def test_hostguest_geometry():
  function test_hostguest_positiveidxs_validator (line 18) | def test_hostguest_positiveidxs_validator():

FILE: src/openfe/tests/protocols/restraints/test_geometry_boresch.py
  function eg5_protein_ligand_universe (line 21) | def eg5_protein_ligand_universe(eg5_protein_pdb, eg5_ligands):
  function test_get_boresch_missing_atoms (line 29) | def test_get_boresch_missing_atoms(eg5_protein_ligand_universe, eg5_liga...
  function test_boresch_too_few_host_atoms_found (line 47) | def test_boresch_too_few_host_atoms_found(eg5_protein_ligand_universe, e...
  function test_boresch_restraint_user_defined (line 64) | def test_boresch_restraint_user_defined(eg5_protein_ligand_universe, eg5...
  function test_boresch_no_guest_atoms_found_ethane (line 95) | def test_boresch_no_guest_atoms_found_ethane(eg5_protein_pdb):
  function test_boresch_no_guest_atoms_found_collinear (line 117) | def test_boresch_no_guest_atoms_found_collinear(eg5_protein_pdb):
  function test_boresch_no_host_atom_pool (line 135) | def test_boresch_no_host_atom_pool(eg5_protein_ligand_universe, eg5_liga...
  function test_boresch_no_host_anchor (line 153) | def test_boresch_no_host_anchor(eg5_protein_ligand_universe, eg5_ligands):
  function test_get_boresch_restraint_single_frame (line 175) | def test_get_boresch_restraint_single_frame(eg5_protein_ligand_universe,...
  function test_get_boresch_restraint_dssp (line 205) | def test_get_boresch_restraint_dssp(eg5_protein_ligand_universe, eg5_lig...
  function test_get_boresch_restrain_industry_benchmark_systems (line 283) | def test_get_boresch_restrain_industry_benchmark_systems(system, industr...

FILE: src/openfe/tests/protocols/restraints/test_geometry_boresch_guest.py
  function test_get_bond_angle_aromatic (line 31) | def test_get_bond_angle_aromatic(eg5_ligands, aromatic, expected):
  function test_sort_by_distance (line 45) | def test_sort_by_distance(eg5_ligands):
  function test_get_guest_atom_pool (line 51) | def test_get_guest_atom_pool(eg5_ligands):
  function test_get_guest_atom_pool_all_heavy (line 66) | def test_get_guest_atom_pool_all_heavy(eg5_ligands):
  function test_get_guest_atom_pool_no_atoms (line 85) | def test_get_guest_atom_pool_no_atoms(eg5_ligands):
  function test_find_guest_atoms_normal (line 97) | def test_find_guest_atoms_normal(eg5_ligands):
  function test_find_guest_atoms_no_atom_pool (line 108) | def test_find_guest_atoms_no_atom_pool():

FILE: src/openfe/tests/protocols/restraints/test_geometry_boresch_host.py
  function eg5_protein_ligand_universe (line 31) | def eg5_protein_ligand_universe(eg5_protein_pdb, eg5_ligands):
  function eg5_protein_ligand_universe_bonded (line 40) | def eg5_protein_ligand_universe_bonded(eg5_protein_pdb, eg5_ligands):
  function test_host_atom_candidates_dssp (line 50) | def test_host_atom_candidates_dssp(eg5_protein_ligand_universe):
  function test_host_atom_candidates_dssp_too_few_atoms (line 75) | def test_host_atom_candidates_dssp_too_few_atoms(eg5_protein_ligand_univ...
  function test_host_atom_candidate_small_search (line 96) | def test_host_atom_candidate_small_search(eg5_protein_ligand_universe):
  function test_evaluate_host1_bad_ref (line 110) | def test_evaluate_host1_bad_ref(eg5_protein_ligand_universe):
  function test_evaluate_host1_good (line 121) | def test_evaluate_host1_good(eg5_protein_ligand_universe):
  function test_evaluate_host2_good (line 156) | def test_evaluate_host2_good(eg5_protein_ligand_universe):
  class TestFindAnchorMulti (line 185) | class TestFindAnchorMulti:
    method universe (line 195) | def universe(self, eg5_protein_ligand_universe):
    method host_anchor (line 199) | def host_anchor(self, universe):
    method test_anchor_regression (line 209) | def test_anchor_regression(self, host_anchor):
    method test_host_guest_bond_distance (line 213) | def test_host_guest_bond_distance(self, host_anchor, universe):
    method test_host_distances (line 223) | def test_host_distances(self, host_anchor, universe):
    method test_not_collinear (line 237) | def test_not_collinear(self, host_anchor, universe):
    method test_angles (line 250) | def test_angles(self, host_anchor, universe):
    method test_dihedrals (line 271) | def test_dihedrals(self, host_anchor, universe):
  class TestFindAnchorBonded (line 299) | class TestFindAnchorBonded(TestFindAnchorMulti):
    method universe (line 307) | def universe(self, eg5_protein_ligand_universe_bonded):
    method host_anchor (line 311) | def host_anchor(self, universe):
  class TestFindAnchorBondedTrajectory (line 326) | class TestFindAnchorBondedTrajectory(TestFindAnchorMulti):
    method universe (line 335) | def universe(self, t4_lysozyme_trajectory_dir):
    method host_anchor (line 346) | def host_anchor(self, universe):
  function test_boresch_evaluation_noatomgroup_error (line 356) | def test_boresch_evaluation_noatomgroup_error():
  function test_boresch_evaluation_incorrectnumber_error (line 366) | def test_boresch_evaluation_incorrectnumber_error(eg5_protein_ligand_uni...
  function test_find_host_anchor_multi_none (line 378) | def test_find_host_anchor_multi_none(eg5_protein_ligand_universe):
  function test_find_host_anchor_bonded_none (line 394) | def test_find_host_anchor_bonded_none(eg5_protein_ligand_universe_bonded):
  function test_find_host_anchor_bonded_nobonds_none (line 410) | def test_find_host_anchor_bonded_nobonds_none(eg5_protein_ligand_universe):

FILE: src/openfe/tests/protocols/restraints/test_geometry_flatbottom.py
  function eg5_protein_ligand_universe (line 15) | def eg5_protein_ligand_universe(eg5_protein_pdb, eg5_ligands):
  function test_no_atoms_found (line 23) | def test_no_atoms_found(eg5_protein_ligand_universe):
  function test_get_flatbottom_restraint_from_selection (line 41) | def test_get_flatbottom_restraint_from_selection(eg5_protein_ligand_univ...
  function test_get_flatbottom_restraint_from_atoms (line 61) | def test_get_flatbottom_restraint_from_atoms(eg5_protein_ligand_universe):

FILE: src/openfe/tests/protocols/restraints/test_geometry_harmonic.py
  function eg5_protein_ligand_universe (line 15) | def eg5_protein_ligand_universe(eg5_protein_pdb, eg5_ligands):
  function test_hostguest_geometry (line 23) | def test_hostguest_geometry():
  function test_get_distance_restraint_selection (line 32) | def test_get_distance_restraint_selection(eg5_protein_ligand_universe):
  function test_get_distance_restraint_atom_list (line 51) | def test_get_distance_restraint_atom_list(eg5_protein_ligand_universe):
  function test_get_molecule_centers_restraint (line 69) | def test_get_molecule_centers_restraint(eg5_ligands):

FILE: src/openfe/tests/protocols/restraints/test_geometry_utils.py
  function eg5_pdb_universe (line 38) | def eg5_pdb_universe(eg5_protein_pdb):
  function eg5_protein_ligand_universe (line 43) | def eg5_protein_ligand_universe(eg5_protein_pdb, eg5_ligands):
  function beta_barrel_universe (line 52) | def beta_barrel_universe():
  function test_mda_selection_none_error (line 58) | def test_mda_selection_none_error(eg5_pdb_universe):
  function test_mda_selection_both_args_error (line 63) | def test_mda_selection_both_args_error(eg5_pdb_universe):
  function test_mda_selection_universe_atom_list (line 68) | def test_mda_selection_universe_atom_list(eg5_pdb_universe):
  function test_mda_selection_atomgroup_string (line 73) | def test_mda_selection_atomgroup_string(eg5_pdb_universe):
  function test_aromatic_rings (line 94) | def test_aromatic_rings(smiles, expected):
  function test_heavy_atoms (line 137) | def test_heavy_atoms(smiles, nheavy, nlight):
  function test_central_idx (line 168) | def test_central_idx(smiles, idx):
  function test_central_atom_disconnected (line 176) | def test_central_atom_disconnected():
  function test_collinear_too_few_atoms (line 183) | def test_collinear_too_few_atoms():
  function test_collinear_index_match_error_length (line 188) | def test_collinear_index_match_error_length():
  function test_collinear_index_match_error_index (line 196) | def test_collinear_index_match_error_index():
  function test_is_collinear_three_atoms (line 220) | def test_is_collinear_three_atoms(arr, thresh, truth):
  function test_is_collinear_three_atoms_dimensions (line 242) | def test_is_collinear_three_atoms_dimensions(arr, truth, dims):
  function test_is_collinear_four_atoms (line 276) | def test_is_collinear_four_atoms(arr, tresh, truth):
  function test_wrap_angle_degrees (line 287) | def test_wrap_angle_degrees():
  function test_wrap_angle_radians (line 307) | def test_wrap_angle_radians(angle, expected):
  function test_angle_not_flat (line 319) | def test_angle_not_flat(limit, force, temperature):
  function test_check_dihedral_bounds (line 344) | def test_check_dihedral_bounds(dihed, expected):
  function test_check_dihedral_bounds_defined (line 357) | def test_check_dihedral_bounds_defined(dihed, lower, upper, expected):
  function test_angular_variance (line 362) | def test_angular_variance():
  function test_atomgroup_has_bonds (line 384) | def test_atomgroup_has_bonds(eg5_protein_pdb):
  function t4_lysozyme_trajectory_universe (line 405) | def t4_lysozyme_trajectory_universe(t4_lysozyme_trajectory_dir):
  function test_atomgroup_has_bonds_ions (line 418) | def test_atomgroup_has_bonds_ions(t4_lysozyme_trajectory_universe):
  function test_centroid_distance_sort (line 432) | def test_centroid_distance_sort(eg5_protein_ligand_universe):
  function test_find_host_atoms (line 444) | def test_find_host_atoms(eg5_protein_ligand_universe):
  function test_get_rmsf_single_frame (line 464) | def test_get_rmsf_single_frame(eg5_protein_ligand_universe):
  function test_get_rmsf_trajectory (line 475) | def test_get_rmsf_trajectory(t4_lysozyme_trajectory_universe):
  class TestStableSelection (line 509) | class TestStableSelection:
    method test_stable_ss_selection (line 510) | def test_stable_ss_selection(self, t4_lysozyme_trajectory_universe):
    method test_small_chain (line 525) | def test_small_chain(self, t4_lysozyme_trajectory_universe):
    method test_bad_dssp (line 537) | def test_bad_dssp(self, t4_lysozyme_trajectory_universe):
    method test_beta_dssp (line 554) | def test_beta_dssp(self, beta_barrel_universe):
  function test_protein_chain_selection (line 565) | def test_protein_chain_selection(eg5_protein_ligand_universe):
  function test_protein_chain_selection_subchain (line 580) | def test_protein_chain_selection_subchain(eg5_pdb_universe):
  function test_protein_chain_selection_nochains (line 593) | def test_protein_chain_selection_nochains(eg5_pdb_universe):
  function test_protein_chain_selection_trim_too_large (line 607) | def test_protein_chain_selection_trim_too_large(eg5_pdb_universe):

FILE: src/openfe/tests/protocols/restraints/test_omm_restraints.py
  function test_parameter_state_default (line 33) | def test_parameter_state_default():
  function test_parameter_state_suffix (line 40) | def test_parameter_state_suffix(suffix, lambda_var):
  function test_single_bond_mixin (line 67) | def test_single_bond_mixin(restraint, geometry_settings):
  function test_verify_inputs (line 80) | def test_verify_inputs():
  function test_verify_geometry (line 89) | def test_verify_geometry():
  function tyk2_protein_ligand_system (line 101) | def tyk2_protein_ligand_system(industry_benchmark_files):
  function tyk2_rdkit_ligand (line 109) | def tyk2_rdkit_ligand(industry_benchmark_files):
  function test_harmonic_add_force (line 120) | def test_harmonic_add_force(tyk2_protein_ligand_system):
  function test_flatbottom_add_force (line 153) | def test_flatbottom_add_force(tyk2_protein_ligand_system):
  function test_centriod_harmonic_add_force (line 191) | def test_centriod_harmonic_add_force(tyk2_protein_ligand_system):
  function test_centroid_flat_bottom_add_force (line 229) | def test_centroid_flat_bottom_add_force(tyk2_protein_ligand_system):
  function test_add_boresch_force (line 272) | def test_add_boresch_force(tyk2_protein_ligand_system, tyk2_rdkit_ligand):
  function test_get_boresch_state_correction (line 316) | def test_get_boresch_state_correction(tyk2_protein_ligand_system, tyk2_r...

FILE: src/openfe/tests/protocols/restraints/test_openmm_forces.py
  function test_boresch_energy_function (line 17) | def test_boresch_energy_function(param):
  function test_periodic_boresch_energy_function (line 35) | def test_periodic_boresch_energy_function(param):
  function test_custom_compound_force (line 53) | def test_custom_compound_force(num_atoms):
  function test_add_force_in_separate_group (line 74) | def test_add_force_in_separate_group(groups, expected):
  function test_add_too_many_force_groups (line 105) | def test_add_too_many_force_groups():

FILE: src/openfe/tests/protocols/restraints/test_settings.py
  function test_distance_restraint_settings_default (line 17) | def test_distance_restraint_settings_default():
  function test_distance_restraint_negative_idxs (line 28) | def test_distance_restraint_negative_idxs():
  function test_flatbottom_restraint_settings_default (line 41) | def test_flatbottom_restraint_settings_default():
  function test_flatbottom_restraint_negative_well (line 52) | def test_flatbottom_restraint_negative_well():
  function test_boresch_restraint_settings_default (line 64) | def test_boresch_restraint_settings_default():

FILE: src/openfe/tests/protocols/test_openmm_settings.py
  class TestOMMSettingsFromStrings (line 14) | class TestOMMSettingsFromStrings:
    method test_system_settings (line 16) | def test_system_settings(self):
    method test_solvation_settings (line 23) | def test_solvation_settings(self):
    method test_alchemical_sampler_settings (line 30) | def test_alchemical_sampler_settings(self):
    method test_integator_settings (line 34) | def test_integator_settings(self):
    method test_simulation_settings (line 47) | def test_simulation_settings(self):
  class TestEquilRFESettingsFromString (line 60) | class TestEquilRFESettingsFromString:
    method test_alchemical_settings (line 61) | def test_alchemical_settings(self):
  class TestOpenMMSolvationSettings (line 69) | class TestOpenMMSolvationSettings:
    method test_unreduced_box_vectors (line 70) | def test_unreduced_box_vectors(self):
    method test_non_positive_solvent (line 90) | def test_non_positive_solvent(self, n_solv):
    method test_box_size_properties_non_1d (line 96) | def test_box_size_properties_non_1d(self):
  class TestOpenMMEngineSettings (line 103) | class TestOpenMMEngineSettings:
    method test_ok_platforms (line 105) | def test_ok_platforms(self, platform):
    method test_fail_platform (line 109) | def test_fail_platform(self):

FILE: src/openfe/tests/protocols/test_openmmutils.py
  function test_validate_ommsolvation_settings_unique_settings (line 60) | def test_validate_ommsolvation_settings_unique_settings(
  function test_validate_ommsolvation_settings_shape_conflicts (line 82) | def test_validate_ommsolvation_settings_shape_conflicts(
  function test_validate_timestep (line 98) | def test_validate_timestep():
  function test_get_simsteps (line 111) | def test_get_simsteps(s, ts, mc, es):
  function test_get_simsteps_indivisible_simtime (line 117) | def test_get_simsteps_indivisible_simtime():
  function test_mc_indivisible (line 124) | def test_mc_indivisible():
  function test_get_alchemical_components (line 131) | def test_get_alchemical_components(benzene_modifications, T4_protein_com...
  function test_duplicate_chemical_components (line 159) | def test_duplicate_chemical_components(benzene_modifications):
  function test_validate_solvent_nocutoff (line 174) | def test_validate_solvent_nocutoff(benzene_modifications):
  function test_validate_solvent_multiple_solvent (line 183) | def test_validate_solvent_multiple_solvent(benzene_modifications):
  function test_validate_solvent_multiple_solvated (line 196) | def test_validate_solvent_multiple_solvated(benzene_modifications, a2a_p...
  function test_not_water_solvent (line 209) | def test_not_water_solvent(benzene_modifications):
  function test_multiple_proteins (line 218) | def test_multiple_proteins(T4_protein_component):
  function test_membrane_protein_warns_with_non_membrane_barostat (line 225) | def test_membrane_protein_warns_with_non_membrane_barostat(a2a_protein_m...
  function test_non_membrane_protein_warns_with_membrane_barostat (line 234) | def test_non_membrane_protein_warns_with_membrane_barostat(T4_protein_co...
  function test_get_components_gas (line 243) | def test_get_components_gas(benzene_modifications):
  function test_components_solvent (line 258) | def test_components_solvent(benzene_modifications):
  function test_components_complex (line 274) | def test_components_complex(T4_protein_component, benzene_modifications):
  function test_validate_chemical_system (line 291) | def test_validate_chemical_system(a2a_protein_membrane_pdb):
  function get_settings (line 316) | def get_settings():
  class TestFEAnalysis (line 327) | class TestFEAnalysis:
    method reporter (line 330) | def reporter(self):
    method analyzer (line 344) | def analyzer(self, reporter):
    method test_free_energies (line 351) | def test_free_energies(self, analyzer):
    method test_plots (line 411) | def test_plots(self, analyzer, tmp_path):
    method test_plot_convergence_bad_units (line 418) | def test_plot_convergence_bad_units(self, analyzer):
    method test_analyze_unknown_method_warning_and_error (line 425) | def test_analyze_unknown_method_warning_and_error(self, reporter):
  class TestSystemCreation (line 437) | class TestSystemCreation:
    method test_system_generator_nosolv_nocache (line 438) | def test_system_generator_nosolv_nocache(self, get_settings):
    method test_system_generator_solv_cache (line 460) | def test_system_generator_solv_cache(self, get_settings):
    method test_system_generator_membrane (line 484) | def test_system_generator_membrane(self, get_settings):
    method test_get_omm_modeller_complex (line 506) | def test_get_omm_modeller_complex(
    method test_get_omm_modeller_membrane_box (line 538) | def test_get_omm_modeller_membrane_box(
    method ligand_mol_and_generator (line 594) | def ligand_mol_and_generator(self, get_settings):
    method test_get_omm_modeller_ligand_no_neutralize (line 609) | def test_get_omm_modeller_ligand_no_neutralize(self, ligand_mol_and_ge...
    method test_omm_modeller_ligand_n_solv (line 642) | def test_omm_modeller_ligand_n_solv(
    method test_omm_modeller_box_size (line 669) | def test_omm_modeller_box_size(self, ligand_mol_and_generator):
    method test_omm_modeller_box_vectors (line 695) | def test_omm_modeller_box_vectors(self, ligand_mol_and_generator):
  function test_convert_steps_per_iteration (line 722) | def test_convert_steps_per_iteration():
  function test_convert_steps_per_iteration_failure (line 735) | def test_convert_steps_per_iteration_failure():
  function test_convert_real_time_analysis_iterations (line 747) | def test_convert_real_time_analysis_iterations():
  function test_convert_real_time_analysis_iterations_interval_fail (line 761) | def test_convert_real_time_analysis_iterations_interval_fail():
  function test_convert_real_time_analysis_iterations_min_interval_fail (line 775) | def test_convert_real_time_analysis_iterations_min_interval_fail():
  function test_convert_real_time_analysis_iterations_None (line 789) | def test_convert_real_time_analysis_iterations_None():
  function test_convert_target_error_from_kcal_per_mole_to_kT (line 804) | def test_convert_target_error_from_kcal_per_mole_to_kT():
  function test_convert_target_error_from_kcal_per_mole_to_kT_zero (line 813) | def test_convert_target_error_from_kcal_per_mole_to_kT_zero():
  class TestOFFPartialCharge (line 823) | class TestOFFPartialCharge:
    method uncharged_mol (line 825) | def uncharged_mol(self, CN_molecule):
    method test_offmol_chg_gen_charged_overwrite (line 829) | def test_offmol_chg_gen_charged_overwrite(self, overwrite, uncharged_m...
    method test_unknown_method (line 845) | def test_unknown_method(self, uncharged_mol):
    method test_incompatible_backend_am1bcc (line 865) | def test_incompatible_backend_am1bcc(self, method, backend, uncharged_...
    method test_no_conformers (line 876) | def test_no_conformers(self, uncharged_mol):
    method test_too_many_existing_conformers (line 889) | def test_too_many_existing_conformers(self, uncharged_mol):
    method test_too_many_requested_conformers (line 906) | def test_too_many_requested_conformers(self, uncharged_mol):
    method test_am1bcc_no_conformer (line 917) | def test_am1bcc_no_conformer(self, uncharged_mol):
    method test_am1bcc_conformer_nochange (line 928) | def test_am1bcc_conformer_nochange(self, eg5_ligands):
    method test_latest_production_nagl (line 968) | def test_latest_production_nagl(self, uncharged_mol):
    method test_no_production_nagl (line 983) | def test_no_production_nagl(self, uncharged_mol):
    method test_am1bcc_reference (line 1076) | def test_am1bcc_reference(
    method test_nagl_oechem_not_openeye_error (line 1105) | def test_nagl_oechem_not_openeye_error(self, uncharged_mol):
    method test_nagl_import_error (line 1120) | def test_nagl_import_error(self, monkeypatch, uncharged_mol):
    method test_espaloma_import_error (line 1137) | def test_espaloma_import_error(self, monkeypatch, uncharged_mol):
    method test_openeye_import_error (line 1154) | def test_openeye_import_error(self, monkeypatch, uncharged_mol):
  function test_forward_backwards_failure (line 1177) | def test_forward_backwards_failure(simulation_nc):

FILE: src/openfe/tests/protocols/test_openmmutils_serialization.py
  function test_serialize_creates_parent_directory (line 11) | def test_serialize_creates_parent_directory(tmp_path):
  function test_serialize_xml (line 20) | def test_serialize_xml(tmp_path):
  function test_serialize_bz2 (line 29) | def test_serialize_bz2(tmp_path):
  function test_deserialize_xml (line 41) | def test_deserialize_xml(tmp_path):
  function test_deserialize_bz2 (line 52) | def test_deserialize_bz2(tmp_path):

FILE: src/openfe/tests/setup/alchemical_network_planner/edge_types.py
  function both_states_proteinC_edge (line 10) | def both_states_proteinC_edge(edge: Transformation) -> bool:
  function both_states_solventC_edge (line 14) | def both_states_solventC_edge(edge: Transformation) -> bool:
  function both_states_ligandC_edge (line 18) | def both_states_ligandC_edge(edge: Transformation) -> bool:
  function r_vacuum_edge (line 22) | def r_vacuum_edge(edge: Transformation) -> bool:
  function r_solvent_edge (line 30) | def r_solvent_edge(edge: Transformation) -> bool:
  function r_complex_edge (line 38) | def r_complex_edge(edge: Transformation) -> bool:

FILE: src/openfe/tests/setup/alchemical_network_planner/test_relative_alchemical_network_planner.py
  function test_rhfe_alchemical_network_planner_init (line 15) | def test_rhfe_alchemical_network_planner_init():
  function test_rbfe_alchemical_network_planner_init (line 20) | def test_rbfe_alchemical_network_planner_init():
  function test_rbfe_alchemical_network_planner_call (line 25) | def test_rbfe_alchemical_network_planner_call(atom_mapping_basic_test_fi...
  function test_rhfe_alchemical_network_planner_call_multigraph (line 74) | def test_rhfe_alchemical_network_planner_call_multigraph(atom_mapping_ba...
  function test_rhfe_alchemical_network_planner_call (line 105) | def test_rhfe_alchemical_network_planner_call(atom_mapping_basic_test_fi...

FILE: src/openfe/tests/setup/atom_mapping/conftest.py
  function _translate_lomap_mapping (line 16) | def _translate_lomap_mapping(atom_mapping_str: str) -> Dict[int, int]:
  function _get_atom_mapping_dict (line 21) | def _get_atom_mapping_dict(lomap_atom_mappings) -> Dict[Tuple[int, int],...
  function gufe_atom_mapping_matrix (line 29) | def gufe_atom_mapping_matrix(
  function mol_pair_to_shock_perses_mapper (line 50) | def mol_pair_to_shock_perses_mapper() -> Tuple[SmallMoleculeComponent, S...

FILE: src/openfe/tests/setup/atom_mapping/test_atommapper.py
  class TestAtomMapper (line 7) | class TestAtomMapper:
    method test_abstract_error (line 8) | def test_abstract_error(self, simple_mapping):
    method test_concrete_mapper (line 17) | def test_concrete_mapper(self, simple_mapping, other_mapping):
    method test_alchemical_charge_deprecation_warning (line 47) | def test_alchemical_charge_deprecation_warning(self, simple_mapping):

FILE: src/openfe/tests/setup/atom_mapping/test_lomap_atommapper.py
  function test_simple (line 14) | def test_simple(atom_mapping_basic_test_files, lomap_old_mapper):
  function test_distances (line 29) | def test_distances(atom_mapping_basic_test_files, lomap_old_mapper):
  function test_generator_length (line 51) | def test_generator_length(atom_mapping_basic_test_files, lomap_old_mapper):
  function test_bad_mapping (line 66) | def test_bad_mapping(atom_mapping_basic_test_files, lomap_old_mapper):
  function test_simple_no_element_changes (line 78) | def test_simple_no_element_changes(atom_mapping_basic_test_files, lomap_...
  function test_bas_mapping_no_element_changes (line 93) | def test_bas_mapping_no_element_changes(atom_mapping_basic_test_files, l...

FILE: src/openfe/tests/setup/atom_mapping/test_lomap_scorers.py
  function toluene_to_cyclohexane (line 21) | def toluene_to_cyclohexane(atom_mapping_basic_test_files):
  function toluene_to_methylnaphthalene (line 30) | def toluene_to_methylnaphthalene(atom_mapping_basic_test_files):
  function toluene_to_heptane (line 39) | def toluene_to_heptane(atom_mapping_basic_test_files):
  function methylnaphthalene_to_naphthol (line 51) | def methylnaphthalene_to_naphthol(atom_mapping_basic_test_files):
  function test_mcsr_zero (line 71) | def test_mcsr_zero(toluene_to_cyclohexane):
  function test_mcsr_nonzero (line 78) | def test_mcsr_nonzero(toluene_to_methylnaphthalene):
  function test_mcsr_custom_beta (line 84) | def test_mcsr_custom_beta(toluene_to_methylnaphthalene):
  function test_mcnar_score_pass (line 90) | def test_mcnar_score_pass(toluene_to_cyclohexane):
  function test_mcnar_score_fail (line 96) | def test_mcnar_score_fail(toluene_to_heptane):
  function test_atomic_number_score_pass (line 102) | def test_atomic_number_score_pass(toluene_to_cyclohexane):
  function test_atomic_number_score_fail (line 108) | def test_atomic_number_score_fail(methylnaphthalene_to_naphthol):
  function test_atomic_number_score_weights (line 115) | def test_atomic_number_score_weights(methylnaphthalene_to_naphthol):
  class TestSulfonamideRule (line 126) | class TestSulfonamideRule:
    method ethylbenzene (line 129) | def ethylbenzene():
    method sulfonamide (line 136) | def sulfonamide():
    method from_sulf_mapping (line 144) | def from_sulf_mapping():
    method test_sulfonamide_hit_backwards (line 171) | def test_sulfonamide_hit_backwards(ethylbenzene, sulfonamide, from_sul...
    method test_sulfonamide_hit_forwards (line 184) | def test_sulfonamide_hit_forwards(ethylbenzene, sulfonamide, from_sulf...
  function test_heterocycle_score (line 210) | def test_heterocycle_score(base, other, name, hit):
  function test_lomap_individual_scores (line 249) | def test_lomap_individual_scores(params, atom_mapping_basic_test_files):
  function test_lomap_regression (line 274) | def test_lomap_regression(
  function test_transmuting_methyl_into_ring_score (line 317) | def test_transmuting_methyl_into_ring_score():

FILE: src/openfe/tests/setup/atom_mapping/test_perses_atommapper.py
  function test_simple (line 12) | def test_simple(atom_mapping_basic_test_files):
  function test_generator_length (line 29) | def test_generator_length(atom_mapping_basic_test_files):
  function test_empty_atommappings (line 46) | def test_empty_atommappings(mol_pair_to_shock_perses_mapper):
  function test_dict_round_trip (line 62) | def test_dict_round_trip():

FILE: src/openfe/tests/setup/atom_mapping/test_perses_scorers.py
  function test_perses_normalization_not_using_positions (line 19) | def test_perses_normalization_not_using_positions(gufe_atom_mapping_matr...
  function test_perses_not_implemented_position_using (line 44) | def test_perses_not_implemented_position_using(gufe_atom_mapping_matrix):
  function test_perses_regression (line 61) | def test_perses_regression(gufe_atom_mapping_matrix):

FILE: src/openfe/tests/setup/chemicalsystem_generator/component_checks.py
  function ligandC_in_chem_sys (line 7) | def ligandC_in_chem_sys(chemical_system: ChemicalSystem) -> bool:
  function solventC_in_chem_sys (line 11) | def solventC_in_chem_sys(chemical_system: ChemicalSystem) -> bool:
  function proteinC_in_chem_sys (line 15) | def proteinC_in_chem_sys(chemical_system: ChemicalSystem) -> bool:
  function cofactorC_in_chem_sys (line 19) | def cofactorC_in_chem_sys(chemical_system: ChemicalSystem) -> bool:

FILE: src/openfe/tests/setup/chemicalsystem_generator/test_easy_chemicalsystem_generator.py
  function test_easy_chemical_system_generator_init (line 20) | def test_easy_chemical_system_generator_init(T4_protein_component):
  function test_build_vacuum_chemical_system (line 40) | def test_build_vacuum_chemical_system(ethane):
  function test_build_solvent_chemical_system (line 51) | def test_build_solvent_chemical_system(ethane):
  function test_build_protein_chemical_system (line 62) | def test_build_protein_chemical_system(ethane, T4_protein_component):
  function test_build_cofactor_chemical_system (line 76) | def test_build_cofactor_chemical_system(eg5_cofactor, eg5_ligands, eg5_p...
  function test_build_hydr_scenario_chemical_systems (line 88) | def test_build_hydr_scenario_chemical_systems(ethane):
  function test_build_binding_scenario_chemical_systems (line 100) | def test_build_binding_scenario_chemical_systems(ethane, T4_protein_comp...
  function test_build_hbinding_scenario_chemical_systems (line 115) | def test_build_hbinding_scenario_chemical_systems(ethane, T4_protein_com...

FILE: src/openfe/tests/setup/test_network_planning.py
  class BadMapper (line 13) | class BadMapper(openfe.setup.atom_mapping.LigandAtomMapper):
    method _defaults (line 15) | def _defaults(cls):
    method _to_dict (line 18) | def _to_dict(self):
    method _from_dict (line 22) | def _from_dict(cls, d):
    method _mappings_generator (line 25) | def _mappings_generator(self, molA, molB):
  function toluene_vs_others (line 30) | def toluene_vs_others(atom_mapping_basic_test_files):
  function simple_scorer (line 41) | def simple_scorer() -> Callable:
  function deterministic_toluene_mst_scorer (line 50) | def deterministic_toluene_mst_scorer() -> Callable:
  function deterministic_minimal_spanning_network (line 77) | def deterministic_minimal_spanning_network(
  function deterministic_minimal_redundant_network (line 93) | def deterministic_minimal_redundant_network(
  class TestRadialNetworkGenerator (line 109) | class TestRadialNetworkGenerator:
    method test_radial_network (line 111) | def test_radial_network(
    method test_radial_network_central_ligand_int_str (line 148) | def test_radial_network_central_ligand_int_str(
    method test_radial_network_bad_name (line 182) | def test_radial_network_bad_name(self, toluene_vs_others, lomap_old_ma...
    method test_radial_network_multiple_str (line 195) | def test_radial_network_multiple_str(self, toluene_vs_others, lomap_ol...
    method test_radial_network_index_error (line 208) | def test_radial_network_index_error(self, toluene_vs_others, lomap_old...
    method test_radial_network_self_central (line 221) | def test_radial_network_self_central(self, toluene_vs_others, lomap_ol...
    method test_radial_network_with_scorer (line 249) | def test_radial_network_with_scorer(self, toluene_vs_others, lomap_old...
    method test_radial_network_multiple_mappers_no_scorer (line 279) | def test_radial_network_multiple_mappers_no_scorer(self, toluene_vs_ot...
    method test_radial_network_no_mapping_failure (line 304) | def test_radial_network_no_mapping_failure(self, toluene_vs_others, lo...
  function test_generate_maximal_network (line 323) | def test_generate_maximal_network(
  class TestMinimalSpanningNetworkGenerator (line 369) | class TestMinimalSpanningNetworkGenerator:
    method test_minimal_spanning_network (line 371) | def test_minimal_spanning_network(
    method test_minimal_spanning_network_no_scorer_error (line 407) | def test_minimal_spanning_network_no_scorer_error(self, toluene_vs_oth...
    method test_minimal_spanning_network_connectedness (line 420) | def test_minimal_spanning_network_connectedness(self, deterministic_mi...
    method test_minimal_spanning_network_regression (line 430) | def test_minimal_spanning_network_regression(self, deterministic_minim...
    method test_minimal_spanning_network_unreachable (line 449) | def test_minimal_spanning_network_unreachable(
  class TestMinimalRedundantNetworkGenerator (line 468) | class TestMinimalRedundantNetworkGenerator:
    method test_minimal_redundant_network (line 469) | def test_minimal_redundant_network(
    method test_minimal_redundant_network_connectedness (line 488) | def test_minimal_redundant_network_connectedness(self, deterministic_m...
    method test_redundant_vs_spanning_network (line 499) | def test_redundant_vs_spanning_network(
    method test_minimal_redundant_network_edges (line 521) | def test_minimal_redundant_network_edges(self, deterministic_minimal_r...
    method test_minimal_redundant_network_redundant (line 548) | def test_minimal_redundant_network_redundant(self, deterministic_minim...
    method test_minimal_redundant_network_unreachable (line 554) | def test_minimal_redundant_network_unreachable(
  class TestGenerateNetworkFromNames (line 571) | class TestGenerateNetworkFromNames:
    method test_generate_network_from_names (line 572) | def test_generate_network_from_names(self, atom_mapping_basic_test_fil...
    method test_generate_network_from_names_bad_name_error (line 597) | def test_generate_network_from_names_bad_name_error(
    method test_generate_network_from_names_duplicate_name (line 614) | def test_generate_network_from_names_duplicate_name(
  class TestNetworkFromIndices (line 633) | class TestNetworkFromIndices:
    method test_network_from_indices (line 634) | def test_network_from_indices(self, atom_mapping_basic_test_files, lom...
    method test_network_from_indices_indexerror (line 657) | def test_network_from_indices_indexerror(self, atom_mapping_basic_test...
    method test_network_from_indices_disconnected_warning (line 669) | def test_network_from_indices_disconnected_warning(
  function test_network_from_external (line 690) | def test_network_from_external(file_fixture, loader, request, benzene_mo...
  function test_network_from_external_unknown_edge (line 722) | def test_network_from_external_unknown_edge(file_fixture, loader, reques...
  function test_bad_orion_network (line 746) | def test_bad_orion_network(benzene_modifications, tmp_path):
  function test_bad_edges_network (line 768) | def test_bad_edges_network(benzene_modifications, tmp_path):

FILE: src/openfe/tests/storage/conftest.py
  function solv_comp (line 9) | def solv_comp():
  function solvated_complex (line 14) | def solvated_complex(T4_protein_component, benzene_transforms, solv_comp):
  function solvated_ligand (line 25) | def solvated_ligand(benzene_transforms, solv_comp):
  function absolute_transformation (line 35) | def absolute_transformation(solvated_ligand, solvated_complex):
  function complex_equilibrium (line 45) | def complex_equilibrium(solvated_complex):
  function benzene_variants_star_map (line 53) | def benzene_variants_star_map(benzene_transforms, solv_comp, T4_protein_...

FILE: src/openfe/tests/storage/test_metadatastore.py
  function json_metadata (line 13) | def json_metadata(tmp_path):
  function per_file_metadata (line 23) | def per_file_metadata(tmp_path):
  class MetadataTests (line 36) | class MetadataTests:
    method test_store_metadata (line 39) | def test_store_metadata(self, metadata):
    method test_load_all_metadata (line 42) | def test_load_all_metadata(self):
    method test_delete (line 45) | def test_delete(self):
    method _test_load_all_metadata (line 48) | def _test_load_all_metadata(self, metadata):
    method _test_delete (line 54) | def _test_delete(self, metadata):
    method _test_iter (line 61) | def _test_iter(self, metadata):
    method _test_len (line 64) | def _test_len(self, metadata):
    method _test_getitem (line 67) | def _test_getitem(self, metadata):
  class TestJSONMetadataStore (line 71) | class TestJSONMetadataStore(MetadataTests):
    method test_store_metadata (line 72) | def test_store_metadata(self, json_metadata):
    method test_load_all_metadata (line 87) | def test_load_all_metadata(self, json_metadata):
    method test_load_all_metadata_nofile (line 90) | def test_load_all_metadata_nofile(self, tmp_path):
    method test_delete (line 97) | def test_delete(self, json_metadata):
    method test_iter (line 100) | def test_iter(self, json_metadata):
    method test_len (line 103) | def test_len(self, json_metadata):
    method test_getitem (line 106) | def test_getitem(self, json_metadata):
  class TestPerFileJSONMetadataStore (line 110) | class TestPerFileJSONMetadataStore(MetadataTests):
    method test_store_metadata (line 111) | def test_store_metadata(self, per_file_metadata):
    method test_load_all_metadata (line 123) | def test_load_all_metadata(self, per_file_metadata):
    method test_delete (line 126) | def test_delete(self, per_file_metadata):
    method test_iter (line 130) | def test_iter(self, per_file_metadata):
    method test_len (line 133) | def test_len(self, per_file_metadata):
    method test_getitem (line 136) | def test_getitem(self, per_file_metadata):
    method test_bad_metadata_contents (line 139) | def test_bad_metadata_contents(self, tmp_path):

FILE: src/openfe/tests/storage/test_resultclient.py
  function result_client (line 17) | def result_client():
  function _make_mock_transformation (line 42) | def _make_mock_transformation(hash_str):
  function test_load_file (line 48) | def test_load_file(result_client):
  class _ResultContainerTest (line 54) | class _ResultContainerTest:
    method get_container (line 56) | def get_container(result_client):
    method _getitem_object (line 59) | def _getitem_object(self, container):
    method test_iter (line 62) | def test_iter(self, result_client):
    method _get_key (line 66) | def _get_key(self, as_object, container):
    method test_getitem (line 77) | def test_getitem(self, as_object, result_client):
    method test_div (line 83) | def test_div(self, as_object, result_client):
    method test_caching (line 89) | def test_caching(self, result_client, load_with):
    method test_load_stream (line 109) | def test_load_stream(self, result_client):
    method test_load_bytes (line 115) | def test_load_bytes(self, result_client):
    method test_path (line 120) | def test_path(self, result_client):
    method test_result_server (line 124) | def test_result_server(self, result_client):
  class TestResultClient (line 129) | class TestResultClient(_ResultContainerTest):
    method get_container (line 140) | def get_container(result_client):
    method _getitem_object (line 143) | def _getitem_object(self, container):
    method test_store_protocol_dag_result (line 149) | def test_store_protocol_dag_result(self):
    method _test_store_load_same_process (line 153) | def _test_store_load_same_process(obj, store_func_name, load_func_name):
    method _test_store_load_different_process (line 165) | def _test_store_load_different_process(obj, store_func_name, load_func...
    method test_store_load_transformation_same_process (line 186) | def test_store_load_transformation_same_process(self, request, fixture):
    method test_store_load_transformation_different_process (line 198) | def test_store_load_transformation_different_process(self, request, fi...
    method test_store_load_network_same_process (line 207) | def test_store_load_network_same_process(self, request, fixture):
    method test_store_load_network_different_process (line 212) | def test_store_load_network_different_process(self, request, fixture):
    method test_delete (line 216) | def test_delete(self, result_client):
  class TestTransformationResults (line 224) | class TestTransformationResults(_ResultContainerTest):
    method get_container (line 234) | def get_container(result_client):
    method _getitem_object (line 242) | def _getitem_object(self, container):
  class TestCloneResults (line 246) | class TestCloneResults(_ResultContainerTest):
    method get_container (line 255) | def get_container(result_client):
    method _getitem_object (line 261) | def _getitem_object(self, container):
  class TestExtensionResults (line 265) | class TestExtensionResults(_ResultContainerTest):
    method get_container (line 273) | def get_container(result_client):
    method _get_key (line 279) | def _get_key(self, as_object, container):
    method test_div (line 287) | def test_div(self, result_client):
    method test_getitem (line 292) | def test_getitem(self, result_client):
    method test_caching (line 297) | def test_caching(self, result_client):

FILE: src/openfe/tests/storage/test_resultserver.py
  function result_server (line 14) | def result_server(tmp_path):
  class TestResultServer (line 22) | class TestResultServer:
    method test_store_bytes (line 23) | def test_store_bytes(self, result_server):
    method test_store_path (line 49) | def test_store_path(self, result_server, tmp_path):
    method test_iter (line 76) | def test_iter(self, result_server):
    method test_find_missing_files (line 79) | def test_find_missing_files(self, result_server):
    method test_load_stream (line 85) | def test_load_stream(self, result_server):
    method test_delete (line 91) | def test_delete(self, result_server, tmp_path):
    method test_load_stream_missing (line 100) | def test_load_stream_missing(self, result_server):
    method test_load_stream_error_bad_hash (line 104) | def test_load_stream_error_bad_hash(self, result_server):
    method test_load_stream_allow_bad_hash (line 110) | def test_load_stream_allow_bad_hash(self, result_server):

FILE: src/openfe/tests/utils/conftest.py
  class _NetworkTestContainer (line 14) | class _NetworkTestContainer(NamedTuple):
  function mols (line 25) | def mols():
  function std_edges (line 33) | def std_edges(mols):
  function simple_network (line 42) | def simple_network(mols, std_edges):
  function benzene_transforms (line 55) | def benzene_transforms():

FILE: src/openfe/tests/utils/test_atommapping_network_plotting.py
  function bound_args (line 18) | def bound_args(func, args, kwargs):
  function network_drawing (line 42) | def network_drawing(simple_network):
  function default_edge (line 58) | def default_edge(network_drawing):
  function default_node (line 64) | def default_node(network_drawing):
  class TestAtomMappingEdge (line 69) | class TestAtomMappingEdge:
    method test_draw_mapped_molecule (line 70) | def test_draw_mapped_molecule(self, default_edge):
    method test_get_image_extents (line 82) | def test_get_image_extents(self, default_edge):
    method test_select (line 87) | def test_select(self, default_edge, network_drawing):
    method test_select_mock_drawing (line 105) | def test_select_mock_drawing(self, edge_str, left_right, molA_to_molB,...
    method test_unselect (line 141) | def test_unselect(self, default_edge, network_drawing):
  class TestLigandNode (line 159) | class TestLigandNode:
    method setup_method (line 160) | def setup_method(self):
    method teardown_method (line 163) | def teardown_method(self):
    method test_register_artist (line 166) | def test_register_artist(self, default_node):
    method test_extent (line 172) | def test_extent(self, default_node):
    method test_xy (line 179) | def test_xy(self, default_node):
  function test_plot_atommapping_network (line 186) | def test_plot_atommapping_network(simple_network):

FILE: src/openfe/tests/utils/test_duecredit.py
  class TestDuecredit (line 15) | class TestDuecredit:
    method test_duecredit_protocol_collection (line 60) | def test_duecredit_protocol_collection(self, module, dois):
    method test_duecredit_active (line 65) | def test_duecredit_active(self):

FILE: src/openfe/tests/utils/test_log_control.py
  function logger (line 14) | def logger():
  class Test_MsgIncludesStringFilter (line 39) | class Test_MsgIncludesStringFilter:
    method test_single_string_blocks_matching_message (line 42) | def test_single_string_blocks_matching_message(self):
    method test_single_string_allows_non_matching_message (line 56) | def test_single_string_allows_non_matching_message(self):
    method test_list_of_strings_blocks_any_match (line 70) | def test_list_of_strings_blocks_any_match(self):
    method test_list_allows_non_matching_messages (line 96) | def test_list_allows_non_matching_messages(self):
    method test_substring_matching (line 110) | def test_substring_matching(self):
    method test_case_sensitive_matching (line 124) | def test_case_sensitive_matching(self):
  class Test_AppendMsgFilter (line 151) | class Test_AppendMsgFilter:
    method test_single_suffix_appends (line 154) | def test_single_suffix_appends(self):
    method test_multiple_suffixes_append_in_order (line 170) | def test_multiple_suffixes_append_in_order(self):
    method test_idempotent_single_suffix (line 185) | def test_idempotent_single_suffix(self):
    method test_idempotent_multiple_suffixes (line 200) | def test_idempotent_multiple_suffixes(self):
    method test_always_returns_true (line 215) | def test_always_returns_true(self):
  class Testlogging_control (line 230) | class Testlogging_control:
    method test__silence_message_single_string_single_logger (line 233) | def test__silence_message_single_string_single_logger(self, logger):
    method test__silence_message_multiple_strings_single_logger (line 245) | def test__silence_message_multiple_strings_single_logger(self, logger):
    method test__silence_message_single_string_multiple_loggers (line 258) | def test__silence_message_single_string_multiple_loggers(self):
    method test__silence_message_multiple_strings_multiple_loggers (line 278) | def test__silence_message_multiple_strings_multiple_loggers(self):
    method test__silence_logger_single (line 298) | def test__silence_logger_single(self, logger):
    method test__silence_logger_multiple (line 312) | def test__silence_logger_multiple(self):
    method test__silence_logger_custom_level (line 329) | def test__silence_logger_custom_level(self, logger):
    method test__append_logger_single_suffix_single_logger (line 344) | def test__append_logger_single_suffix_single_logger(self, logger):
    method test__append_logger_multiple_suffixes (line 355) | def test__append_logger_multiple_suffixes(self, logger):
    method test__append_logger_multiple_loggers (line 368) | def test__append_logger_multiple_loggers(self):
    method test_pymbar_example (line 387) | def test_pymbar_example(self, logger):
    method test_combining_multiple_controls (line 401) | def test_combining_multiple_controls(self, logger):
  class TestBaseLogFilter (line 415) | class TestBaseLogFilter:
    method test_cannot_instantiate_abstract_class (line 418) | def test_cannot_instantiate_abstract_class(self):
    method test_subclass_must_implement_filter (line 423) | def test_subclass_must_implement_filter(self):
    method test_subclass_with_filter_works (line 432) | def test_subclass_with_filter_works(self):
    method test_string_conversion_to_list (line 442) | def test_string_conversion_to_list(self):
    method test_list_stays_as_list (line 448) | def test_list_stays_as_list(self):
  class TestEdgeCases (line 455) | class TestEdgeCases:
    method test_empty_string (line 458) | def test_empty_string(self, logger):
    method test_empty_list (line 469) | def test_empty_list(self, logger):
    method test_special_characters_in_message (line 480) | def test_special_characters_in_message(self, logger):
    method test_unicode_characters (line 494) | def test_unicode_characters(self, logger):
    method test_very_long_message (line 507) | def test_very_long_message(self, logger):

FILE: src/openfe/tests/utils/test_network_plotting.py
  function _get_fig_ax (line 12) | def _get_fig_ax(fig):
  function mock_event (line 24) | def mock_event(event_name, xdata, ydata, fig=None):
  function make_mock_graph (line 42) | def make_mock_graph(fig=None):
  class TestNode (line 66) | class TestNode:
    method setup_method (line 67) | def setup_method(self):
    method teardown_method (line 72) | def teardown_method(self):
    method test_register_artist (line 75) | def test_register_artist(self):
    method test_extent (line 84) | def test_extent(self):
    method test_xy (line 87) | def test_xy(self):
    method test_unselect (line 90) | def test_unselect(self):
    method test_edge_select (line 98) | def test_edge_select(self):
    method test_update_location (line 105) | def test_update_location(self):
    method test_contains (line 119) | def test_contains(self, point, expected):
    method test_on_mousedown_in_rect (line 123) | def test_on_mousedown_in_rect(self):
    method test_on_mousedown_in_axes (line 134) | def test_on_mousedown_in_axes(self):
    method test_on_mousedown_out_axes (line 144) | def test_on_mousedown_out_axes(self):
    method test_on_drag (line 159) | def test_on_drag(self):
    method test_on_drag_do_nothing (line 180) | def test_on_drag_do_nothing(self):
    method test_on_drag_no_mousedown (line 189) | def test_on_drag_no_mousedown(self):
    method test_on_mouseup (line 199) | def test_on_mouseup(self):
    method test_blitting (line 209) | def test_blitting(self):
  class TestEdge (line 213) | class TestEdge:
    method setup_method (line 214) | def setup_method(self):
    method teardown_method (line 223) | def teardown_method(self):
    method test_register_artist (line 226) | def test_register_artist(self):
    method test_contains (line 242) | def test_contains(self, point, expected):
    method test_edge_xs_ys (line 246) | def test_edge_xs_ys(self):
    method _get_colors (line 249) | def _get_colors(self):
    method test_unselect (line 254) | def test_unselect(self):
    method test_select (line 271) | def test_select(self):
    method test_update_locations (line 287) | def test_update_locations(self):
  class TestEventHandler (line 297) | class TestEventHandler:
    method setup_method (line 298) | def setup_method(self):
    method teardown_method (line 311) | def teardown_method(self):
    method _mock_for_connections (line 314) | def _mock_for_connections(self):
    method test_connect (line 320) | def test_connect(self, event_type):
    method test_disconnect (line 343) | def test_disconnect(self, event_type):
    method _mock_contains (line 365) | def _mock_contains(self, mock_objs):
    method test_get_event_container_select_node (line 375) | def test_get_event_container_select_node(self, hit):
    method test_on_mousedown (line 397) | def test_on_mousedown(self, hit):
    method test_on_drag (line 413) | def test_on_drag(self, is_active):
    method test_on_mouseup_click_select (line 428) | def test_on_mouseup_click_select(self, has_selected):
    method test_on_mouseup_click_not_select (line 457) | def test_on_mouseup_click_not_select(self, has_selected):
    method test_on_mouseup_drag (line 479) | def test_on_mouseup_drag(self, has_selected):
  class TestGraphDrawing (line 504) | class TestGraphDrawing:
    method setup_method (line 505) | def setup_method(self):
    method test_init (line 517) | def test_init(self):
    method test_init_custom_ax (line 526) | def test_init_custom_ax(self):
    method test_register_node_error (line 533) | def test_register_node_error(self):
    method test_edges_for_node (line 548) | def test_edges_for_node(self, node, edges):
    method test_get_nodes_extent (line 552) | def test_get_nodes_extent(self):
    method test_reset_bounds (line 555) | def test_reset_bounds(self):
    method test_draw (line 564) | def test_draw(self):

FILE: src/openfe/tests/utils/test_optional_imports.py
  function the_answer (line 10) | def the_answer():
  function test_requires_decorator (line 14) | def test_requires_decorator():

FILE: src/openfe/tests/utils/test_remove_oechem.py
  function test_remove_oechem (line 8) | def test_remove_oechem():

FILE: src/openfe/tests/utils/test_system_probe.py
  function fake_disk_usage (line 127) | def fake_disk_usage(path):
  function patch_system (line 135) | def patch_system():
  function test_get_hostname (line 219) | def test_get_hostname():
  function test_get_gpu_info (line 226) | def test_get_gpu_info():
  function test_get_psutil_info (line 253) | def test_get_psutil_info():
  function test_get_disk_usage (line 295) | def test_get_disk_usage():
  function test_get_disk_usage_with_path (line 318) | def test_get_disk_usage_with_path():
  function test_probe_system (line 340) | def test_probe_system():
  function test_probe_system_smoke_test (line 423) | def test_probe_system_smoke_test():
  function test_log_system_probe_unconfigured (line 428) | def test_log_system_probe_unconfigured():
  function test_log_system_probe (line 454) | def test_log_system_probe(caplog):
  function test_nvidia_smi_error (line 488) | def test_nvidia_smi_error(error_type, expected, caplog):

FILE: src/openfe/tests/utils/test_visualization_3D.py
  function maps (line 9) | def maps():
  function benzene_phenol_mapping (line 18) | def benzene_phenol_mapping(benzene_transforms, maps):
  function test_visualize_component_coords_give_iterable (line 26) | def test_visualize_component_coords_give_iterable(benzene_transforms):
  function test_visualize_component_coords_give_iterable_shift (line 35) | def test_visualize_component_coords_give_iterable_shift(benzene_transfor...
  function test_visualize_component_coords_reuse_view (line 44) | def test_visualize_component_coords_reuse_view(benzene_transforms):
  function test_visualize_3D_mapping (line 54) | def test_visualize_3D_mapping(benzene_phenol_mapping):

FILE: src/openfe/utils/atommapping_network_plotting.py
  class AtomMappingEdge (line 17) | class AtomMappingEdge(Edge):
    method __init__ (line 32) | def __init__(self, node_artist1: Node, node_artist2: Node, data: Dict):
    method _draw_mapped_molecule (line 37) | def _draw_mapped_molecule(
    method _get_image_extents (line 76) | def _get_image_extents(self):
    method select (line 90) | def select(self, event, graph):
    method unselect (line 113) | def unselect(self):
  class LigandNode (line 123) | class LigandNode(Node):
    method _make_artist (line 124) | def _make_artist(self, x, y, dx, dy):
    method register_artist (line 128) | def register_artist(self, ax):
    method extent (line 132) | def extent(self):
    method xy (line 139) | def xy(self):
  class AtomMappingNetworkDrawing (line 143) | class AtomMappingNetworkDrawing(GraphDrawing):
  function plot_atommapping_network (line 159) | def plot_atommapping_network(network: LigandNetwork):

FILE: src/openfe/utils/ligand_utils.py
  function get_alchemical_charge_difference (line 6) | def get_alchemical_charge_difference(mapping: LigandAtomMapping) -> int:

FILE: src/openfe/utils/logging_control.py
  class _BaseLogFilter (line 5) | class _BaseLogFilter(ABC):
    method __init__ (line 14) | def __init__(self, strings: str | list[str]) -> None:
    method filter (line 20) | def filter(self, record: logging.LogRecord) -> bool:
  class _MsgIncludesStringFilter (line 36) | class _MsgIncludesStringFilter(_BaseLogFilter):
    method filter (line 48) | def filter(self, record: logging.LogRecord) -> bool:
  class _AppendMsgFilter (line 67) | class _AppendMsgFilter(_BaseLogFilter):
    method __init__ (line 78) | def __init__(self, strings: str | list[str]) -> None:
    method filter (line 83) | def filter(self, record: logging.LogRecord) -> bool:
  function _silence_message (line 103) | def _silence_message(msg: str | list[str], logger_names: str | list[str]...
  function _silence_logger (line 128) | def _silence_logger(logger_names: str | list[str], level: int = logging....
  function _append_logger (line 149) | def _append_logger(suffix: str | list[str], logger_names: str | list[str...

FILE: src/openfe/utils/network_plotting.py
  class Node (line 26) | class Node:
    method __init__ (line 41) | def __init__(self, node, x: float, y: float, dx=0.1, dy=0.1):
    method _make_artist (line 49) | def _make_artist(self, x, y, dx, dy):
    method register_artist (line 52) | def register_artist(self, ax: MPL_Axes):
    method extent (line 57) | def extent(self) -> tuple[float, float, float, float]:
    method xy (line 63) | def xy(self) -> tuple[float, float]:
    method select (line 67) | def select(self, event: MPL_MouseEvent, graph: GraphDrawing):  # -no-cov-
    method unselect (line 71) | def unselect(self):
    method edge_select (line 75) | def edge_select(self, edge: Edge):
    method update_location (line 79) | def update_location(self, x: float, y: float):
    method contains (line 86) | def contains(self, event: MPL_MouseEvent) -> bool:
    method on_mousedown (line 90) | def on_mousedown(self, event: MPL_MouseEvent, graph: GraphDrawing):
    method on_drag (line 107) | def on_drag(self, event: MPL_MouseEvent, graph: GraphDrawing):
    method on_mouseup (line 131) | def on_mouseup(self, event: MPL_MouseEvent, graph: GraphDrawing):
  class Edge (line 139) | class Edge:
    method __init__ (line 156) | def __init__(self, node_artist1: Node, node_artist2: Node, data: dict):
    method _make_artist (line 162) | def _make_artist(self, node_artist1: Node, node_artist2: Node, data: d...
    method register_artist (line 166) | def register_artist(self, ax: MPL_Axes):
    method contains (line 170) | def contains(self, event: MPL_MouseEvent) -> bool:
    method _edge_xs_ys (line 175) | def _edge_xs_ys(node1: Node, node2: Node):
    method on_mousedown (line 186) | def on_mousedown(self, event: MPL_MouseEvent, graph: GraphDrawing):
    method on_drag (line 190) | def on_drag(self, event: MPL_MouseEvent, graph: GraphDrawing):
    method on_mouseup (line 194) | def on_mouseup(self, event: MPL_MouseEvent, graph: GraphDrawing):
    method unselect (line 198) | def unselect(self):
    method select (line 205) | def select(self, event: MPL_MouseEvent, graph: GraphDrawing):
    method update_locations (line 213) | def update_locations(self):
  class EventHandler (line 219) | class EventHandler:
    method __init__ (line 246) | def __init__(self, graph: GraphDrawing):
    method connect (line 253) | def connect(self, canvas: MPL_FigureCanvasBase):
    method disconnect (line 263) | def disconnect(self, canvas: MPL_FigureCanvasBase):
    method _get_event_container (line 269) | def _get_event_container(self, event: MPL_MouseEvent):
    method on_mousedown (line 285) | def on_mousedown(self, event: MPL_MouseEvent):
    method on_drag (line 296) | def on_drag(self, event: MPL_MouseEvent):
    method on_mouseup (line 303) | def on_mouseup(self, event: MPL_MouseEvent):
  class GraphDrawing (line 327) | class GraphDrawing:
    method __init__ (line 347) | def __init__(self, graph: nx.Graph, positions=None, ax=None):
    method _ipython_display_ (line 380) | def _ipython_display_(self):  # -no-cov-
    method edges_for_node (line 383) | def edges_for_node(self, node: Node) -> list[Edge]:
    method _get_nodes_extent (line 388) | def _get_nodes_extent(self):
    method reset_bounds (line 393) | def reset_bounds(self):
    method draw (line 407) | def draw(self):
    method _register_node (line 412) | def _register_node(self, node: Any, position: tuple[float, float]):
    method _register_edge (line 421) | def _register_edge(self, edge: tuple[Node, Node, dict]):

FILE: src/openfe/utils/optional_imports.py
  function requires_package (line 12) | def requires_package(package_name: str) -> Callable:

FILE: src/openfe/utils/remove_oechem.py
  function without_oechem_backend (line 10) | def without_oechem_backend():

FILE: src/openfe/utils/silence_root_logging.py
  function silence_root_logging (line 6) | def silence_root_logging():

FILE: src/openfe/utils/system_probe.py
  function _get_disk_usage (line 13) | def _get_disk_usage(
  function _get_psutil_info (line 127) | def _get_psutil_info() -> dict[str, dict[str, str]]:
  function _get_hostname (line 245) | def _get_hostname() -> str:
  function _get_gpu_info (line 276) | def _get_gpu_info() -> dict[str, dict[str, str]]:
  function _probe_system (line 387) | def _probe_system(paths: Optional[Iterable[pathlib.Path]] = None) -> dict:
  function log_system_probe (line 501) | def log_system_probe(level=logging.DEBUG, paths: Optional[Iterable[os.Pa...

FILE: src/openfe/utils/visualization_3D.py
  function _get_max_dist_in_x (line 22) | def _get_max_dist_in_x(atom_mapping: AtomMapping) -> float:
  function _translate (line 47) | def _translate(mol, shift: Union[Tuple[float, float, float], NDArray[np....
  function _add_spheres (line 72) | def _add_spheres(view: py3Dmol.view, mol1: Chem.Mol, mol2: Chem.Mol, map...
  function view_components_3d (line 112) | def view_components_3d(
  function view_mapping_3d (line 159) | def view_mapping_3d(

FILE: src/openfecli/cli.py
  class OpenFECLI (line 15) | class OpenFECLI(CLI):
    method get_loaders (line 19) | def get_loaders(self):
    method get_installed_plugins (line 24) | def get_installed_plugins(self):
  function main (line 42) | def main(log):

FILE: src/openfecli/clicktypes/hyphenchoice.py
  function _normalize_to_hyphen (line 4) | def _normalize_to_hyphen(string):
  class HyphenAwareChoice (line 8) | class HyphenAwareChoice(click.Choice):
    method __init__ (line 9) | def __init__(self, choices, case_sensitive=True):
    method convert (line 13) | def convert(self, value, param, ctx):

FILE: src/openfecli/commands/atommapping.py
  function allow_two_molecules (line 10) | def allow_two_molecules(ctx, param, value):
  function atommapping (line 26) | def atommapping(mol, mapper, output):
  function generate_mapping (line 44) | def generate_mapping(mapper, molA, molB):
  function atommapping_print_dict_main (line 69) | def atommapping_print_dict_main(mapper, molA, molB):
  function atommapping_visualize_main (line 75) | def atommapping_visualize_main(mapper, molA, molB, file, ext):

FILE: src/openfecli/commands/fetch.py
  class SingleModulePluginLoader (line 17) | class SingleModulePluginLoader(CLIPluginLoader):
    method __init__ (line 20) | def __init__(self, module_name, plugin_class):
    method _find_candidates (line 27) | def _find_candidates(self):
    method _make_nsdict (line 31) | def _make_nsdict(candidate):
  class FetchCLI (line 35) | class FetchCLI(CLI):
    method get_loaders (line 44) | def get_loaders(self):
    method get_installed_plugins (line 47) | def get_installed_plugins(self):
  function fetch (line 53) | def fetch():

FILE: src/openfecli/commands/gather.py
  function _get_column (line 18) | def _get_column(val: float | int) -> int:
  function format_estimate_uncertainty (line 46) | def format_estimate_uncertainty(
  function format_df_with_precision (line 100) | def format_df_with_precision(
  function is_results_json (line 179) | def is_results_json(fpath: os.PathLike | str) -> bool:
  function load_json (line 184) | def load_json(fpath: os.PathLike | str) -> dict:
  function _get_names (line 207) | def _get_names(result: dict) -> tuple[str, str]:
  function _get_type (line 237) | def _get_type(result: dict) -> Literal["vacuum", "solvent", "complex"]:
  function _legacy_get_type (line 266) | def _legacy_get_type(res_fn: os.PathLike | str) -> Literal["vacuum", "so...
  function _get_result_id (line 278) | def _get_result_id(
  function _load_valid_result_json (line 305) | def _load_valid_result_json(fpath: os.PathLike | str) -> tuple[tuple | N...
  function _generate_bad_legs_error_message (line 345) | def _generate_bad_legs_error_message(bad_legs: list[tuple[set[str], tupl...
  function _get_ddgs (line 371) | def _get_ddgs(legs: dict, allow_partial=False) -> pd.DataFrame:
  function _generate_ddg (line 440) | def _generate_ddg(legs: dict, allow_partial: bool) -> pd.DataFrame:
  function _generate_raw (line 469) | def _generate_raw(legs: dict, allow_partial=True) -> pd.DataFrame:
  function _check_legs_have_sufficient_repeats (line 506) | def _check_legs_have_sufficient_repeats(legs):
  function _generate_dg_mle (line 517) | def _generate_dg_mle(legs: dict, allow_partial: bool) -> pd.DataFrame:
  function _collect_result_jsons (line 612) | def _collect_result_jsons(results: List[os.PathLike | str]) -> List[path...
  function _get_legs_from_result_jsons (line 641) | def _get_legs_from_result_jsons(
  function rich_print_to_stdout (line 707) | def rich_print_to_stdout(df: pd.DataFrame) -> None:
  function gather (line 774) | def gather(

FILE: src/openfecli/commands/gather_abfe.py
  function _get_name (line 20) | def _get_name(result: dict) -> str:
  function _load_valid_result_json (line 45) | def _load_valid_result_json(fpath: os.PathLike | str) -> tuple[tuple | N...
  function _get_legs_from_result_jsons (line 88) | def _get_legs_from_result_jsons(
  function _error_std (line 151) | def _error_std(r):
  function _error_mbar (line 158) | def _error_mbar(r):
  function _generate_dg (line 169) | def _generate_dg(results_dict: dict[str, dict[str, list]], allow_partial...
  function _generate_dg_raw (line 211) | def _generate_dg_raw(results_dict: dict[str, dict[str, list]], allow_par...
  function gather_abfe (line 293) | def gather_abfe(

FILE: src/openfecli/commands/gather_septop.py
  function _load_valid_result_json (line 20) | def _load_valid_result_json(fpath: os.PathLike | str) -> tuple[tuple | N...
  function _get_legs_from_result_jsons (line 63) | def _get_legs_from_result_jsons(
  function _get_names (line 137) | def _get_names(result: dict) -> tuple[str, str]:
  function _error_std (line 164) | def _error_std(r):
  function _error_mbar (line 171) | def _error_mbar(r):
  function _get_ddgs (line 182) | def _get_ddgs(
  function _infer_unc_col_name (line 226) | def _infer_unc_col_name(df: pd.DataFrame) -> str:
  function _generate_ddg (line 233) | def _generate_ddg(results_dict, allow_partial: bool = False) -> pd.DataF...
  function _generate_dg_mle (line 241) | def _generate_dg_mle(
  function _generate_raw (line 291) | def _generate_raw(
  function gather_septop (line 379) | def gather_septop(

FILE: src/openfecli/commands/generate_partial_charges.py
  function charge_molecules (line 41) | def charge_molecules(molecules, yaml_settings, output, n_cores, overwrit...

FILE: src/openfecli/commands/plan_rbfe_network.py
  function plan_rbfe_network_main (line 21) | def plan_rbfe_network_main(
  function plan_rbfe_network (line 138) | def plan_rbfe_network(

FILE: src/openfecli/commands/plan_rhfe_network.py
  function plan_rhfe_network_main (line 22) | def plan_rhfe_network_main(
  function plan_rhfe_network (line 110) | def plan_rhfe_network(

FILE: src/openfecli/commands/quickrun.py
  function _format_exception (line 15) | def _format_exception(exception) -> str:
  function _hash_quickrun_inputs (line 20) | def _hash_quickrun_inputs(output, transformation):
  function quickrun (line 48) | def quickrun(transformation, work_dir, output, resume):

FILE: src/openfecli/commands/test.py
  function test (line 19) | def test(long, download_only):

FILE: src/openfecli/commands/view_ligand_network.py
  function view_ligand_network (line 13) | def view_ligand_network(ligand_network: os.PathLike):

FILE: src/openfecli/fetching.py
  class _Fetcher (line 12) | class _Fetcher:
    method __init__ (line 31) | def __init__(
    method resources (line 48) | def resources(self):
    method __call__ (line 51) | def __call__(self, directory: pathlib.Path):
    method plugin (line 55) | def plugin(self):
  class URLFetcher (line 96) | class URLFetcher(_Fetcher):
    method __call__ (line 105) | def __call__(self, dest_dir):
  class PkgResourceFetcher (line 120) | class PkgResourceFetcher(_Fetcher):
    method __call__ (line 129) | def __call__(self, dest_dir):
  class FetchablePlugin (line 154) | class FetchablePlugin(CommandPlugin):
    method __init__ (line 160) | def __init__(self, command, section, requires_ofe, fetcher):
    method filenames (line 170) | def filenames(self):

FILE: src/openfecli/parameters/mapper.py
  function _atommapper_from_openfe_setup (line 9) | def _atommapper_from_openfe_setup(user_input, context):
  function _atommapper_from_qualname (line 13) | def _atommapper_from_qualname(user_input, context):

FILE: src/openfecli/parameters/mol.py
  function _load_molecule_from_smiles (line 7) | def _load_molecule_from_smiles(user_input, context):
  function _load_molecule_from_sdf (line 25) | def _load_molecule_from_sdf(user_input, context):
  function _load_molecule_from_mol2 (line 37) | def _load_molecule_from_mol2(user_input, context):

FILE: src/openfecli/parameters/molecules.py
  function _smcs_from_sdf (line 12) | def _smcs_from_sdf(sdf):
  function _smcs_from_mol2 (line 22) | def _smcs_from_mol2(mol2):
  function load_molecules (line 31) | def load_molecules(file_or_directory):
  function molecule_getter (line 67) | def molecule_getter(user_input, context):

FILE: src/openfecli/parameters/output.py
  function get_file_and_extension (line 7) | def get_file_and_extension(user_input, context):

FILE: src/openfecli/parameters/output_dir.py
  function get_dir (line 8) | def get_dir(user_input, context):

FILE: src/openfecli/parameters/plan_network_options.py
  class MapperSelection (line 26) | class MapperSelection(BaseModel):
  class NetworkSelection (line 33) | class NetworkSelection(BaseModel):
  class PartialChargeSelection (line 40) | class PartialChargeSelection(BaseModel):
  class CliYaml (line 47) | class CliYaml(BaseModel):
  function parse_yaml_planner_options (line 55) | def parse_yaml_planner_options(contents: str) -> CliYaml:
  function load_yaml_planner_options (line 88) | def load_yaml_planner_options(path: Optional[str], context) -> PlanNetwo...

FILE: src/openfecli/parameters/protein.py
  fu
Condensed preview — 465 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (4,881K chars).
[
  {
    "path": ".dockerignore",
    "chars": 64,
    "preview": "# Ignore everything\n*\n\n# Only allow\n!production/environment.yml\n"
  },
  {
    "path": ".git-blame-ignore-revs",
    "chars": 839,
    "preview": "# https://github.com/OpenFreeEnergy/openfe/pull/1604 - ruff formatting part 1\n3a08b6809fc57662e4146db3c7ccedfbc7c7c8df  "
  },
  {
    "path": ".gitattributes",
    "chars": 0,
    "preview": ""
  },
  {
    "path": ".github/CONTRIBUTING.md",
    "chars": 916,
    "preview": "# Contributing to OpenFE\n\nThanks for contributing to the OpenFE software project!\nRead our [code of conduct](../Code_of_"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE/release_template.md",
    "chars": 1432,
    "preview": "<!--\nChecklist for releasing a new version of openfe. \n-->\n\nMake the PR:\n* [ ] Create a new release prep branch correspo"
  },
  {
    "path": ".github/pull_request_template.md",
    "chars": 1574,
    "preview": "<!--\nThank you for pull request.\nBelow are a few things we ask you kindly to self-check before getting a review. Remove "
  },
  {
    "path": ".github/workflows/aws-cpu-long-tests.yaml",
    "chars": 3307,
    "preview": "name: \"manual AWS: CPU long tests\"\non:\n  workflow_dispatch:\n\njobs:\n  start-aws-runner:\n    runs-on: ubuntu-latest\n    pe"
  },
  {
    "path": ".github/workflows/aws-gpu-integration-tests.yaml",
    "chars": 3451,
    "preview": "name: \"manual AWS: GPU integration tests\"\non:\n  workflow_dispatch:\n\njobs:\n  start-aws-runner:\n    runs-on: ubuntu-latest"
  },
  {
    "path": ".github/workflows/ci.yaml",
    "chars": 5420,
    "preview": "name: \"CI\"\non:\n  pull_request:\n   # Skip CI if changed files only affect the following folders\n   # - docs: documentatio"
  },
  {
    "path": ".github/workflows/clean-pr-caches.yaml",
    "chars": 946,
    "preview": "# from https://docs.github.com/en/actions/how-tos/manage-workflow-runs/manage-caches#force-deleting-cache-entries\nname: "
  },
  {
    "path": ".github/workflows/cron-conda.yaml",
    "chars": 1860,
    "preview": "name: \"cron: conda builds daily tests\"\non:\n  workflow_dispatch:\n  schedule:\n    # At 05:00 UTC every day\n    - cron: \"0 "
  },
  {
    "path": ".github/workflows/cron-docker.yaml",
    "chars": 2754,
    "preview": "name: \"cron: docker image daily tests\"\n\non:\n  push:\n    branches:\n      - main\n  schedule:\n    # nightly tests\n    - cro"
  },
  {
    "path": ".github/workflows/cron-feedstock-build-tests.yaml",
    "chars": 2619,
    "preview": "# tests this openfe commit and gufe main to check for \n# conda-feedstock build issues\nname: \"cron: weekly feedstock pack"
  },
  {
    "path": ".github/workflows/cron-package-test.yaml",
    "chars": 1717,
    "preview": "name: \"cron: package install daily tests\"\non:\n  workflow_dispatch:\n  schedule:\n    # At 03:00 UTC daily\n    - cron: \"0 3"
  },
  {
    "path": ".github/workflows/griffe-api-break.yaml",
    "chars": 2442,
    "preview": "name: \"PR: griffe check for API breaks\"\n\non:\n  pull_request_target:\n    branches:\n      - main\n\njobs:\n  check:\n    runs-"
  },
  {
    "path": ".github/workflows/mypy.yaml",
    "chars": 1210,
    "preview": "name: \"PR: mypy static type checking\"\non:\n  pull_request:\n    branches:\n      - main\n  push:\n    branches:\n      - main\n"
  },
  {
    "path": ".github/workflows/release-docker-image.yaml",
    "chars": 6386,
    "preview": "# This workflow uses actions that are not certified by GitHub.\n# They are provided by a third-party and are governed by\n"
  },
  {
    "path": ".github/workflows/release-installers.yaml",
    "chars": 2476,
    "preview": "name: \"release: make single-file installers\"\n\non:\n  workflow_dispatch:\n\ndefaults:\n  run:\n    shell: bash -leo pipefail {"
  },
  {
    "path": ".github/workflows/release-make-condalock.yaml",
    "chars": 2836,
    "preview": "name: \"release: create openfe conda-lock file\"\n\non:\n  workflow_dispatch:\n\ndefaults:\n  run:\n    shell: bash -leo pipefail"
  },
  {
    "path": ".github/workflows/release-prep-examplenotebooks.yaml",
    "chars": 1232,
    "preview": "name: \"release prep: test example notebooks\"\n\non:\n  workflow_dispatch:\n\nconcurrency:\n  group: \"${{ github.workflow }}-${"
  },
  {
    "path": ".github/workflows/release-prep-feedstock.yaml",
    "chars": 3352,
    "preview": "# tests this openfe commit with the latest gufe release\n# meant to be used for release prep to catch feedstock issues be"
  },
  {
    "path": ".gitignore",
    "chars": 3020,
    "preview": "# custom ignores\n.duecredit.p\n.xxrun\n.idea/\n.vscode/\n\n# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$p"
  },
  {
    "path": ".pre-commit-config.yaml",
    "chars": 845,
    "preview": "ci:\n    autoupdate_schedule: quarterly\n    # comment / label \"pre-commit.ci autofix\" to a pull request to manually trigg"
  },
  {
    "path": ".readthedocs.yaml",
    "chars": 302,
    "preview": "version: 2\n\nbuild:\n  os: \"ubuntu-24.04\"\n  tools:\n    python: \"miniconda3-3.12-24.9\"\n\nsphinx:\n   configuration: docs/conf"
  },
  {
    "path": "CITATION.cff",
    "chars": 1483,
    "preview": "cff-version: 1.2.0\nmessage: \"If you use this software, please cite it as below.\"\nauthors:\n- family-names: \"Alibay\"\n  giv"
  },
  {
    "path": "Code_of_Conduct.md",
    "chars": 2715,
    "preview": "## Code of Conduct ##\n\nThis project is dedicated to providing a welcoming and supportive environment for all people, reg"
  },
  {
    "path": "LICENSE",
    "chars": 1071,
    "preview": "MIT License\n\nCopyright (c) 2022 OpenFreeEnergy\n\nPermission is hereby granted, free of charge, to any person obtaining a "
  },
  {
    "path": "MANIFEST.in",
    "chars": 866,
    "preview": "recursive-include src/openfe/tests/data/ *.sdf\nrecursive-include src/openfe/tests/data/ *.bz2\nrecursive-include src/open"
  },
  {
    "path": "README.md",
    "chars": 2202,
    "preview": "[![Logo](https://img.shields.io/badge/OSMF-OpenFreeEnergy-%23002f4a)](https://openfree.energy/)\n[![build](https://github"
  },
  {
    "path": "codecov.yml",
    "chars": 37,
    "preview": "coverage:\n  status:\n    project: off\n"
  },
  {
    "path": "devtools/data/fix_rbfe_results.py",
    "chars": 1571,
    "preview": "\"\"\"A script to fix up rbfe_results.tar.gz\n\nUseful if Settings are ever changed in a backwards-incompatible way\n\nWill exp"
  },
  {
    "path": "devtools/data/gen_serialized_results.py",
    "chars": 12754,
    "preview": "\"\"\"\nDev script to generate some result jsons that are used for testing\n\nGenerates\n- ABFEProtocol_json_results.gz\n  - use"
  },
  {
    "path": "devtools/debug_openmm.sh",
    "chars": 929,
    "preview": "#!/usr/bin/env bash\n\necho \"Run this script with your conda env activated\"\necho \"Invoke the script like this: \"\necho \"./d"
  },
  {
    "path": "devtools/installer/construct.yaml",
    "chars": 535,
    "preview": "name: OpenFEforge\nversion: {{ environ[\"VERSION\"] }}\ncompany: OpenFE\nlicense_file: ../../LICENSE\n\nchannels:\n  - conda-for"
  },
  {
    "path": "docs/CHANGELOG.rst",
    "chars": 27450,
    "preview": "=========\nChangelog\n=========\n\n.. current developments\n\nv1.11.1\n====================\n\n**Fixed:**\n\n* Fixed slow response "
  },
  {
    "path": "docs/Makefile",
    "chars": 653,
    "preview": "# Minimal makefile for Sphinx documentation\n#\n\n# You can set these variables from the command line, and also\n# from the "
  },
  {
    "path": "docs/_ext/sass.py",
    "chars": 2575,
    "preview": "\"\"\"\nsphinxcontrib-sass\nhttps://github.com/attakei-lab/sphinxcontrib-sass\nKayuza Takei\nApache 2.0\n\nModified to:\n- Write d"
  },
  {
    "path": "docs/_sass/deflist-flowchart.scss",
    "chars": 9410,
    "preview": ":root {\n    --arrow-thickness: 4px;\n    --arrow-head-size: 7px;\n    --arrow-length: 2em;\n    --arrow-multiple-gap: 20px;"
  },
  {
    "path": "docs/_templates/autosummary/base.rst",
    "chars": 96,
    "preview": ".. title:: {{ objname }}\n\n.. currentmodule:: {{ module }}\n\n.. auto{{ objtype }}:: {{ objname }}\n"
  },
  {
    "path": "docs/_templates/autosummary/class.rst",
    "chars": 96,
    "preview": ".. title:: {{ objname }}\n\n.. currentmodule:: {{ module }}\n\n.. auto{{ objtype }}:: {{ objname }}\n"
  },
  {
    "path": "docs/conf.py",
    "chars": 7722,
    "preview": "# Configuration file for the Sphinx documentation builder.\n#\n# This file only contains a selection of the most common op"
  },
  {
    "path": "docs/cookbook/bespoke_parameters.nblink",
    "chars": 79,
    "preview": "{\n   \"path\": \"../ExampleNotebooks/cookbook/bespoke_parameters_showcase.ipynb\"\n}"
  },
  {
    "path": "docs/cookbook/choose_protocol.nblink",
    "chars": 68,
    "preview": "{\n   \"path\": \"../ExampleNotebooks/cookbook/choose_protocol.ipynb\"\n}\n"
  },
  {
    "path": "docs/cookbook/create_alchemical_network.nblink",
    "chars": 78,
    "preview": "{\n   \"path\": \"../ExampleNotebooks/cookbook/create_alchemical_network.ipynb\"\n}\n"
  },
  {
    "path": "docs/cookbook/dumping_transformation.rst",
    "chars": 1366,
    "preview": ".. _dumping_transformations:\n\nDumping a ``Transformation`` to JSON\n====================================\n\nIf you're tryin"
  },
  {
    "path": "docs/cookbook/generate_ligand_network.nblink",
    "chars": 76,
    "preview": "{\n   \"path\": \"../ExampleNotebooks/cookbook/generate_ligand_network.ipynb\"\n}\n"
  },
  {
    "path": "docs/cookbook/hand_write_ligand_network.nblink",
    "chars": 78,
    "preview": "{\n   \"path\": \"../ExampleNotebooks/cookbook/hand_write_ligand_network.ipynb\"\n}\n"
  },
  {
    "path": "docs/cookbook/index.rst",
    "chars": 5960,
    "preview": ".. _cookbooks:\n\nCookbook\n========\n\nThis section describes common tasks involving the OpenFE Python API.\n\nThe :any:`OpenF"
  },
  {
    "path": "docs/cookbook/jq_inspection.rst",
    "chars": 2087,
    "preview": ".. _jq_inspection:\n\nUsing ``jq`` to inspect OpenFE JSONs\n==============================================\nSometimes you ma"
  },
  {
    "path": "docs/cookbook/ligandnetwork_vis.nblink",
    "chars": 70,
    "preview": "{\n   \"path\": \"../ExampleNotebooks/cookbook/ligandnetwork_vis.ipynb\"\n}\n"
  },
  {
    "path": "docs/cookbook/loading_molecules.nblink",
    "chars": 70,
    "preview": "{\n   \"path\": \"../ExampleNotebooks/cookbook/loading_molecules.ipynb\"\n}\n"
  },
  {
    "path": "docs/cookbook/network_from_orion_fepp.nblink",
    "chars": 76,
    "preview": "{\n   \"path\": \"../ExampleNotebooks/cookbook/network_from_orion_fepp.ipynb\"\n}\n"
  },
  {
    "path": "docs/cookbook/rfe_alchemical_planners.nblink",
    "chars": 76,
    "preview": "{\n   \"path\": \"../ExampleNotebooks/cookbook/rfe_alchemical_planners.ipynb\"\n}\n"
  },
  {
    "path": "docs/cookbook/user_charges.nblink",
    "chars": 65,
    "preview": "{\n   \"path\": \"../ExampleNotebooks/cookbook/user_charges.ipynb\"\n}\n"
  },
  {
    "path": "docs/environment.yaml",
    "chars": 1144,
    "preview": "name: openfe-docs\nchannels:\n- https://conda.anaconda.org/conda-forge\n\n# explicit pins to speed up build:\ndependencies:\n-"
  },
  {
    "path": "docs/guide/cli/cli_basics.rst",
    "chars": 3124,
    "preview": "CLI basics\n==========\n\nThe ``openfe`` command consists of several subcommands. This is similar to\ntools like ``gmx``, wh"
  },
  {
    "path": "docs/guide/cli/cli_yaml.rst",
    "chars": 4071,
    "preview": ".. _userguide_cli_yaml_interface:\n\nCustomising CLI planning with YAML settings\n========================================="
  },
  {
    "path": "docs/guide/cli/index.rst",
    "chars": 428,
    "preview": ".. _userguide_cli_interface:\n\nCLI Interface\n=============\n\nIn addition to the powerful Python API, OpenFE provides a sim"
  },
  {
    "path": "docs/guide/execution/execution_theory.rst",
    "chars": 1986,
    "preview": ".. _userguide_execution_theory:\n\nProtocols and the Execution Model Theory\n========================================\n\nProt"
  },
  {
    "path": "docs/guide/execution/index.rst",
    "chars": 472,
    "preview": ".. _userguide_execution:\n\nExecution\n=========\n\nWith a :class:`.Transformation` defined, the next step is to execute this"
  },
  {
    "path": "docs/guide/execution/quickrun_execution.rst",
    "chars": 10153,
    "preview": ".. _userguide_quickrun:\n\nExecution with Quickrun\n=======================\n\nThe planning and preparation of a campaign of "
  },
  {
    "path": "docs/guide/index.rst",
    "chars": 270,
    "preview": "User\\ |nbsp|\\ Guide\n===================\n\n.. toctree::\n    :maxdepth: 2\n\n    introduction\n    setup/index\n    execution/i"
  },
  {
    "path": "docs/guide/introduction.rst",
    "chars": 4983,
    "preview": ".. _guide-introduction:\n\nIntroduction \n============\n\nHere we present an overview of the workflow for calculating free en"
  },
  {
    "path": "docs/guide/protocols/absolutebinding.rst",
    "chars": 8020,
    "preview": ".. _userguide_abfe_protocol:\n\nAbsolute Binding Protocol\n=========================\n\nOverview\n--------\n\nThe :class:`Absolu"
  },
  {
    "path": "docs/guide/protocols/absolutesolvation.rst",
    "chars": 6687,
    "preview": "Absolute Solvation Protocol\n===========================\n\nOverview\n--------\n\nThe :class:`AbsoluteSolvationProtocol <.Abso"
  },
  {
    "path": "docs/guide/protocols/index.rst",
    "chars": 264,
    "preview": ".. _userguide_protocols:\n\nDetails of Specific Protocols\n=============================\n\nDetails on the theory and behavio"
  },
  {
    "path": "docs/guide/protocols/plainmd.rst",
    "chars": 3269,
    "preview": "Plain MD Protocol\n=================\n\nOverview\n--------\n\nThe :class:`.PlainMDProtocol` enables the user to run a Molecula"
  },
  {
    "path": "docs/guide/protocols/relativehybridtopology.rst",
    "chars": 13843,
    "preview": ".. _userguide_relative_hybrid_topology_protocol:\n\nRelative Hybrid Topology Protocol\n=================================\n\nO"
  },
  {
    "path": "docs/guide/protocols/septop.rst",
    "chars": 9576,
    "preview": ".. _userguide_septop_protocol:\n\nSeparated Topologies Protocol\n=============================\n\nOverview\n--------\n\nThe :cla"
  },
  {
    "path": "docs/guide/results/index.rst",
    "chars": 249,
    "preview": ".. _userguide_results:\n\nResults Gathering\n=================\n\nWith simulations completed,\nthe results of individual simul"
  },
  {
    "path": "docs/guide/results/working_with_networks.rst",
    "chars": 3280,
    "preview": ".. _userguide_result_networks:\n\nWorking with networks of results\n================================\n\nAfter running a **net"
  },
  {
    "path": "docs/guide/results/working_with_results.rst",
    "chars": 2894,
    "preview": ".. _userguide_individual_results:\n\nWorking with individual results\n===============================\n\nWith :ref:`execution"
  },
  {
    "path": "docs/guide/setup/alchemical_network_model.rst",
    "chars": 3570,
    "preview": ".. _alchemical_network_model:\n\nAlchemical Networks: Planning a Simulation Campaign\n====================================="
  },
  {
    "path": "docs/guide/setup/chemical_systems_and_thermodynamic_cycles.rst",
    "chars": 10790,
    "preview": ".. _userguide_chemicalsystems_and_components:\n\nComponents, Chemical Systems and Thermodynamic Cycles\n==================="
  },
  {
    "path": "docs/guide/setup/creating_atom_mappings_and_scores.rst",
    "chars": 4342,
    "preview": ".. _userguide_mappings:\n.. _Creating Atom Mappings:\n\nCreating Atom Mappings\n======================\n\n``Atom Mapping`` obj"
  },
  {
    "path": "docs/guide/setup/creating_ligand_networks.rst",
    "chars": 2475,
    "preview": ".. _userguide_ligand_network:\n\nDefining the Ligand Network\n===========================\nA :class:`.LigandNetwork` is a ne"
  },
  {
    "path": "docs/guide/setup/defining_protocols.rst",
    "chars": 4579,
    "preview": ".. _defining-protocols:\n\nProtocols in OpenFE\n============================\n\nA :class:`.Protocol` is a computational metho"
  },
  {
    "path": "docs/guide/setup/index.rst",
    "chars": 1450,
    "preview": ".. _userguide_setup:\n\nSimulation Setup\n================\n\nThis section provides details on how to set up a free energy ca"
  },
  {
    "path": "docs/guide/troubleshooting.rst",
    "chars": 3702,
    "preview": "\nTroubleshooting Simulations \n===========================\n\nThis guide covers tips and strategies for troubleshooting sim"
  },
  {
    "path": "docs/guide/under_the_hood.rst",
    "chars": 4788,
    "preview": ".. _under-the-hood:\n\nUnder the Hood\n==============\n\n.. module:: openfe\n    :noindex:\n\nIf you want to implement your own "
  },
  {
    "path": "docs/index.rst",
    "chars": 3613,
    "preview": ".. template taken from SciPy who took it from Pandas (keep the chain going)\n\n.. module:: openfe\n\n======================="
  },
  {
    "path": "docs/installation.rst",
    "chars": 26904,
    "preview": "Installation\n============\n\n**openfe** is currently only compatible with POSIX systems (macOS and UNIX/Linux). \nSee `Supp"
  },
  {
    "path": "docs/make.bat",
    "chars": 765,
    "preview": "@ECHO OFF\n\npushd %~dp0\n\nREM Command file for Sphinx documentation\n\nif \"%SPHINXBUILD%\" == \"\" (\n\tset SPHINXBUILD=sphinx-bu"
  },
  {
    "path": "docs/reference/api/alchemical_network_planning.rst",
    "chars": 1111,
    "preview": ".. _Alchemical Network Planning:\n\nSimulation Campaign Planning\n============================\n\nWhile a :class:`LigandNetwo"
  },
  {
    "path": "docs/reference/api/atom_mappers.rst",
    "chars": 1692,
    "preview": ".. _Atom Mappers:\n\nAtom Mappings\n=============\n\nTools for mapping atoms in one molecule to those in another. Used to gen"
  },
  {
    "path": "docs/reference/api/defining_and_executing_simulations.rst",
    "chars": 770,
    "preview": ".. _reference_execution:\n\nDefining and Executing Simulations\n==================================\n\n.. _executors:\n\nExecuti"
  },
  {
    "path": "docs/reference/api/index.rst",
    "chars": 646,
    "preview": ".. _api:\n\n.. note::\n   We have reproduced API documentation from the `gufe`_ package here for convenience.\n   `gufe`_ se"
  },
  {
    "path": "docs/reference/api/ligand_network.rst",
    "chars": 1094,
    "preview": "Ligand Network Tools\n====================\n\n.. module:: openfe.setup\n    :noindex:\n\nLigand Network\n--------------\n\nA netw"
  },
  {
    "path": "docs/reference/api/openmm_binding_afe.rst",
    "chars": 1533,
    "preview": "OpenMM Absolute Binding Free Energy Protocol\n============================================\n\n.. _afe binding protocol api:"
  },
  {
    "path": "docs/reference/api/openmm_md.rst",
    "chars": 897,
    "preview": "OpenMM Molecular Dynamics (MD) Protocol\n=======================================\n\n.. _md protocol api:\n\nA Protocol for ru"
  },
  {
    "path": "docs/reference/api/openmm_protocol_settings.rst",
    "chars": 4917,
    "preview": "OpenMM Protocol Settings\n========================\n\n.. _openmm protocol settings api:\n\nThis page documents the Settings c"
  },
  {
    "path": "docs/reference/api/openmm_rfe.rst",
    "chars": 2461,
    "preview": "OpenMM Relative Free Energy Protocol\n====================================\n\n.. _rfe protocol api:\n\nThis section provides "
  },
  {
    "path": "docs/reference/api/openmm_septop.rst",
    "chars": 2726,
    "preview": "OpenMM Separated Topologies Protocol\n====================================\n\n.. _septop protocol api:\n\nThis section provid"
  },
  {
    "path": "docs/reference/api/openmm_solvation_afe.rst",
    "chars": 2504,
    "preview": "OpenMM Absolute Solvation Free Energy Protocol\n==============================================\n\n.. _afe solvation protoco"
  },
  {
    "path": "docs/reference/api/systems_and_components.rst",
    "chars": 746,
    "preview": "Chemical Systems and Components\n===============================\n\nWe describe a chemical system as being made up of one o"
  },
  {
    "path": "docs/reference/cli/charge_molecules.rst",
    "chars": 191,
    "preview": ".. _cli_charge_molecules:\n\n``charge-molecules`` command\n============================\n\n.. click:: openfecli.commands.gene"
  },
  {
    "path": "docs/reference/cli/gather.rst",
    "chars": 791,
    "preview": ".. _cli_gather:\n\n``gather`` command\n====================\n\nCurrently, ``openfe gather`` is only able to gather results fr"
  },
  {
    "path": "docs/reference/cli/index.rst",
    "chars": 169,
    "preview": ".. _cli-reference:\n\nCLI Reference\n=============\n\n.. toctree::\n    :maxdepth: 1\n\n    charge_molecules\n    plan_rhfe_netwo"
  },
  {
    "path": "docs/reference/cli/plan_rbfe_network.rst",
    "chars": 190,
    "preview": ".. _cli_plan-rbfe-network:\n\n``plan-rbfe-network`` command\n=============================\n\n.. click:: openfecli.commands.p"
  },
  {
    "path": "docs/reference/cli/plan_rhfe_network.rst",
    "chars": 190,
    "preview": ".. _cli_plan-rhfe-network:\n\n``plan-rhfe-network`` command\n=============================\n\n.. click:: openfecli.commands.p"
  },
  {
    "path": "docs/reference/cli/quickrun.rst",
    "chars": 136,
    "preview": ".. _cli_quickrun:\n\n``quickrun`` command\n====================\n\n.. click:: openfecli.commands.quickrun:quickrun\n   :prog: "
  },
  {
    "path": "docs/reference/index.rst",
    "chars": 478,
    "preview": "Reference\n=========\n\nThis contains details of the Python API as well as a reference to the\ncommand line interface.\n\n.. n"
  },
  {
    "path": "docs/tutorials/.gitignore",
    "chars": 15,
    "preview": "assets/\ninputs/"
  },
  {
    "path": "docs/tutorials/abfe_analysis_tutorial.nblink",
    "chars": 71,
    "preview": "{\n   \"path\": \"../ExampleNotebooks/abfe_tutorial/abfe_analysis.ipynb\"\n}\n"
  },
  {
    "path": "docs/tutorials/abfe_tutorial.nblink",
    "chars": 154,
    "preview": "{\n   \"path\": \"../ExampleNotebooks/abfe_tutorial/abfe_tutorial.ipynb\",\n   \"extra-media\": [\n      \"../ExampleNotebooks/abf"
  },
  {
    "path": "docs/tutorials/ahfe_tutorial.nblink",
    "chars": 154,
    "preview": "{\n   \"path\": \"../ExampleNotebooks/ahfe_tutorial/ahfe_tutorial.ipynb\",\n   \"extra-media\": [\n      \"../ExampleNotebooks/ahf"
  },
  {
    "path": "docs/tutorials/charge_molecules_cli_tutorial.rst",
    "chars": 137,
    "preview": ".. _charge_molecules_cli_tutorial:\n\n.. include:: /ExampleNotebooks/cli_tutorials/cli_charge_molecules.md\n   :parser: mys"
  },
  {
    "path": "docs/tutorials/index.rst",
    "chars": 2825,
    "preview": "Tutorials\n=========\n\n.. todo: make sure we can inline the tutorial, for now we only provide links\n\nBelow is a collection"
  },
  {
    "path": "docs/tutorials/md_tutorial.nblink",
    "chars": 134,
    "preview": "{\n   \"path\": \"../ExampleNotebooks/openmm_md/plain_md.ipynb\",\n   \"extra-media\": [\n      \"../ExampleNotebooks/openmm_md/as"
  },
  {
    "path": "docs/tutorials/plotting_with_cinnabar.nblink",
    "chars": 106,
    "preview": "{\n   \"path\": \"../ExampleNotebooks/plotting_rbfes_with_cinnabar/PlottingFreeEnergiesUsingCinnabar.ipynb\"\n}\n"
  },
  {
    "path": "docs/tutorials/rbfe_cli_tutorial.rst",
    "chars": 117,
    "preview": ".. _rbfe_cli_tutorial:\n\n.. include:: /ExampleNotebooks/rbfe_tutorial/cli_tutorial.md\n   :parser: myst_parser.sphinx_\n"
  },
  {
    "path": "docs/tutorials/rbfe_membrane_protein.nblink",
    "chars": 75,
    "preview": "{\n   \"path\": \"../ExampleNotebooks/membranes/rbfe_membrane_protein.ipynb\"\n}\n"
  },
  {
    "path": "docs/tutorials/rbfe_python_tutorial.nblink",
    "chars": 78,
    "preview": "{\n   \"path\": \"../ExampleNotebooks/rbfe_tutorial/rbfe_python_tutorial.ipynb\"\n}\n"
  },
  {
    "path": "docs/tutorials/septop_analysis_tutorial.nblink",
    "chars": 73,
    "preview": "{\n   \"path\": \"../ExampleNotebooks/openmm_septop/septop_analysis.ipynb\"\n}\n"
  },
  {
    "path": "docs/tutorials/septop_tutorial.nblink",
    "chars": 158,
    "preview": "{\n   \"path\": \"../ExampleNotebooks/openmm_septop/septop_tutorial.ipynb\",\n   \"extra-media\": [\n      \"../ExampleNotebooks/o"
  },
  {
    "path": "docs/tutorials/showcase_notebook.nblink",
    "chars": 248,
    "preview": "{\n   \"path\": \"../ExampleNotebooks/showcase/openfe_showcase.ipynb\",\n   \"extra-media\": [\n      \"../ExampleNotebooks/showca"
  },
  {
    "path": "environment.yml",
    "chars": 2072,
    "preview": "name: openfe_env\nchannels:\n  - conda-forge\ndependencies:\n  - cinnabar ~=0.5.0\n  - click >=8.2.0\n  - coverage\n  - dask>=2"
  },
  {
    "path": "news/TEMPLATE.rst",
    "chars": 173,
    "preview": "**Added:**\n\n* <news item>\n\n**Changed:**\n\n* <news item>\n\n**Deprecated:**\n\n* <news item>\n\n**Removed:**\n\n* <news item>\n\n**F"
  },
  {
    "path": "production/Dockerfile",
    "chars": 1041,
    "preview": "FROM mambaorg/micromamba:1.4.1\n\nLABEL org.opencontainers.image.source=https://github.com/OpenFreeEnergy/openfe\nLABEL org"
  },
  {
    "path": "production/environment.yml",
    "chars": 206,
    "preview": "name: openfe_env\nchannels:\n  - conda-forge\ndependencies:\n  - cudatoolkit==11.8\n  - jupyterlab\n  - notebook\n  - openfe\n  "
  },
  {
    "path": "pyproject.toml",
    "chars": 2252,
    "preview": "[build-system]\nbuild-backend = \"setuptools.build_meta\"\nrequires = [\n  \"setuptools>=77.0.3\",\n  \"setuptools-scm>=8\",\n]\n\n[p"
  },
  {
    "path": "rever.xsh",
    "chars": 176,
    "preview": "$PROJECT = $GITHUB_REPO = 'openfe'\n$GITHUB_ORG = 'OpenFreeEnergy'\n\n$ACTIVITIES = ['changelog']\n\n$CHANGELOG_FILENAME = 'd"
  },
  {
    "path": "src/openfe/__init__.py",
    "chars": 2154,
    "preview": "# Before we do anything else, we want to disable JAX\n# acceleration by default but if a user has set\n# PYMBAR_DISABLE_JA"
  },
  {
    "path": "src/openfe/analysis/__init__.py",
    "chars": 152,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/analysis/plotting.py",
    "chars": 10879,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/data/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "src/openfe/data/_downloader.py",
    "chars": 878,
    "preview": "import pooch\n\nfrom ._registry import zenodo_data_registry\n\n\ndef retrieve_registry_data(zenodo_registry: list[dict], path"
  },
  {
    "path": "src/openfe/data/_registry.py",
    "chars": 1150,
    "preview": "import pooch\n\nPOOCH_CACHE = pooch.os_cache(\"openfe\")\n\nzenodo_rfe_simulation_nc = dict(\n    base_url=\"doi:10.5281/zenodo."
  },
  {
    "path": "src/openfe/due.py",
    "chars": 2024,
    "preview": "# emacs: at the end of the file\n# ex: set sts=4 ts=4 sw=4 et:\n# ## ### ### ### ### ### ### ### ### ### ### ### ### ### #"
  },
  {
    "path": "src/openfe/orchestration/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "src/openfe/protocols/__init__.py",
    "chars": 129,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/protocols/openmm_afe/__init__.py",
    "chars": 1448,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/protocols/openmm_afe/abfe_units.py",
    "chars": 19674,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/protocols/openmm_afe/afe_protocol_results.py",
    "chars": 20717,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/protocols/openmm_afe/ahfe_units.py",
    "chars": 8982,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/protocols/openmm_afe/base_afe_units.py",
    "chars": 62689,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/protocols/openmm_afe/equil_afe_settings.py",
    "chars": 14563,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/protocols/openmm_afe/equil_binding_afe_method.py",
    "chars": 21992,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/protocols/openmm_afe/equil_solvation_afe_method.py",
    "chars": 20710,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/protocols/openmm_md/__init__.py",
    "chars": 498,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/protocols/openmm_md/plain_md_methods.py",
    "chars": 41257,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/protocols/openmm_md/plain_md_settings.py",
    "chars": 1611,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/protocols/openmm_rfe/__init__.py",
    "chars": 515,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/protocols/openmm_rfe/_rfe_utils/__init__.py",
    "chars": 89,
    "preview": "from . import (\n    lambdaprotocol,\n    multistate,\n    relative,\n    topologyhelpers,\n)\n"
  },
  {
    "path": "src/openfe/protocols/openmm_rfe/_rfe_utils/lambdaprotocol.py",
    "chars": 13941,
    "preview": "# Very slightly adapted from perses https://github.com/choderalab/perses\n# License: MIT\n# OpenFE note: eventually we aim"
  },
  {
    "path": "src/openfe/protocols/openmm_rfe/_rfe_utils/multistate.py",
    "chars": 13763,
    "preview": "#############################################################################\n# HYBRID SYSTEM SAMPLERS\n#################"
  },
  {
    "path": "src/openfe/protocols/openmm_rfe/_rfe_utils/relative.py",
    "chars": 125332,
    "preview": "# This code is a slightly modified version of the HybridTopologyFactory code\n# from https://github.com/choderalab/perses"
  },
  {
    "path": "src/openfe/protocols/openmm_rfe/_rfe_utils/topologyhelpers.py",
    "chars": 27465,
    "preview": "# This code is in parts based on TopologyProposal in perses\n# (https://github.com/choderalab/perses)\n# The eventual goal"
  },
  {
    "path": "src/openfe/protocols/openmm_rfe/equil_rfe_methods.py",
    "chars": 1026,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/protocols/openmm_rfe/equil_rfe_settings.py",
    "chars": 5080,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/protocols/openmm_rfe/hybridtop_protocol_results.py",
    "chars": 8609,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/protocols/openmm_rfe/hybridtop_protocols.py",
    "chars": 26377,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/protocols/openmm_rfe/hybridtop_units.py",
    "chars": 63337,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/protocols/openmm_septop/__init__.py",
    "chars": 786,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/protocols/openmm_septop/base_units.py",
    "chars": 59456,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/protocols/openmm_septop/equil_septop_method.py",
    "chars": 23564,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/protocols/openmm_septop/equil_septop_settings.py",
    "chars": 12249,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/protocols/openmm_septop/septop_protocol_results.py",
    "chars": 20120,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/protocols/openmm_septop/septop_units.py",
    "chars": 46144,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/protocols/openmm_septop/utils.py",
    "chars": 3771,
    "preview": "from openmmtools import states\nfrom openmmtools.states import GlobalParameterState\n\n\nclass SepTopParameterState(GlobalPa"
  },
  {
    "path": "src/openfe/protocols/openmm_utils/__init__.py",
    "chars": 1,
    "preview": "\n"
  },
  {
    "path": "src/openfe/protocols/openmm_utils/charge_generation.py",
    "chars": 19621,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/protocols/openmm_utils/mdtraj_utils.py",
    "chars": 1686,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/protocols/openmm_utils/multistate_analysis.py",
    "chars": 17720,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/protocols/openmm_utils/omm_compute.py",
    "chars": 2902,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/protocols/openmm_utils/omm_settings.py",
    "chars": 27051,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/protocols/openmm_utils/serialization.py",
    "chars": 2565,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/protocols/openmm_utils/settings_validation.py",
    "chars": 9004,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/protocols/openmm_utils/system_creation.py",
    "chars": 9784,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/protocols/openmm_utils/system_validation.py",
    "chars": 12562,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/protocols/restraint_utils/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "src/openfe/protocols/restraint_utils/geometry/__init__.py",
    "chars": 213,
    "preview": "from .base import BaseRestraintGeometry, HostGuestRestraintGeometry\nfrom .boresch import BoreschRestraintGeometry\nfrom ."
  },
  {
    "path": "src/openfe/protocols/restraint_utils/geometry/base.py",
    "chars": 1158,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/protocols/restraint_utils/geometry/boresch/__init__.py",
    "chars": 116,
    "preview": "from .geometry import (\n    BoreschRestraintGeometry,\n    find_boresch_restraint,\n    find_guest_atom_candidates,\n)\n"
  },
  {
    "path": "src/openfe/protocols/restraint_utils/geometry/boresch/geometry.py",
    "chars": 11590,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/protocols/restraint_utils/geometry/boresch/guest.py",
    "chars": 7469,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/protocols/restraint_utils/geometry/boresch/host.py",
    "chars": 28711,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/protocols/restraint_utils/geometry/flatbottom.py",
    "chars": 3973,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/protocols/restraint_utils/geometry/harmonic.py",
    "chars": 3279,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/protocols/restraint_utils/geometry/utils.py",
    "chars": 25543,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/protocols/restraint_utils/openmm/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "src/openfe/protocols/restraint_utils/openmm/omm_forces.py",
    "chars": 3681,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/protocols/restraint_utils/openmm/omm_restraints.py",
    "chars": 23736,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/protocols/restraint_utils/settings.py",
    "chars": 7007,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/setup/__init__.py",
    "chars": 544,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/setup/alchemical_network_planner/__init__.py",
    "chars": 251,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/setup/alchemical_network_planner/abstract_alchemical_network_planner.py",
    "chars": 478,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/setup/alchemical_network_planner/relative_alchemical_network_planner.py",
    "chars": 14454,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/setup/atom_mapping/__init__.py",
    "chars": 255,
    "preview": "from gufe import LigandAtomMapping\nfrom kartograf import KartografAtomMapper\n\nfrom . import lomap_scorers, perses_scorer"
  },
  {
    "path": "src/openfe/setup/atom_mapping/ligandatommapper.py",
    "chars": 2125,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/setup/atom_mapping/lomap_mapper.py",
    "chars": 251,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/setup/atom_mapping/lomap_scorers.py",
    "chars": 493,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/setup/atom_mapping/perses_mapper.py",
    "chars": 3909,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/setup/atom_mapping/perses_scorers.py",
    "chars": 4400,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/setup/chemicalsystem_generator/__init__.py",
    "chars": 268,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/setup/chemicalsystem_generator/abstract_chemicalsystem_generator.py",
    "chars": 689,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/setup/chemicalsystem_generator/easy_chemicalsystem_generator.py",
    "chars": 4654,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/setup/ligand_network_planning.py",
    "chars": 15463,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  },
  {
    "path": "src/openfe/storage/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "src/openfe/storage/metadatastore.py",
    "chars": 3545,
    "preview": "# This code is part of OpenFE and is licensed under the MIT license.\n# For details, see https://github.com/OpenFreeEnerg"
  }
]

// ... and 265 more files (download for full content)

About this extraction

This page contains the full source code of the OpenFreeEnergy/openfe GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 465 files (4.5 MB), approximately 1.2M tokens, and a symbol index with 2321 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!