-
Notifications
You must be signed in to change notification settings - Fork 1
plan_asfe script #99
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
plan_asfe script #99
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Adds an Absolute Solvation Free Energy (ASFE) planning example script to the repository and updates the test harness to discover and execute all _example*.py scripts across the repo.
Changes:
- Add
_example_plan_asfe.pyscript to generate an ASFEAlchemicalNetwork(and a pontibus inputs manifest) fromBenchmarkData. - Update example-script discovery to search the whole repository with stable sorting and better ignore rules.
- Improve pytest parametrization IDs to use repo-relative paths for clearer CI output.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 7 comments.
| File | Description |
|---|---|
| openfe_benchmarks/tests/test_scripts.py | Expands discovery of _example*.py scripts repo-wide and stabilizes ordering/IDs. |
| openfe_benchmarks/scripts/_example_plan_asfe.py | New ASFE planning example that builds transformations/network and writes outputs + validation. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| It demonstrates how to process benchmark systems, prepare components, and save the resulting | ||
| transformations to a JSON file. | ||
|
|
||
| Note that although the output of this script is an alchemical network, that simply serves as a shortage |
Copilot
AI
Feb 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo in module docstring: “shortage class” should be “shortcut class”.
| Note that although the output of this script is an alchemical network, that simply serves as a shortage | |
| Note that although the output of this script is an alchemical network, that simply serves as a shortcut |
|
|
||
| from openff.units import unit | ||
| import openfe | ||
| from openfe import SolventComponent, ChemicalSystem, SmallMoleculeComponent | ||
|
|
||
| from pontibus.protocols.solvation import ASFEProtocol, ASFESettings | ||
| from pontibus.protocols.solvation.settings import PackmolSolvationSettings |
Copilot
AI
Feb 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This example imports pontibus unconditionally. The CI conda env (devtools/conda-envs/environment.yml) only installs openfe/gufe/pytest, so this script will raise ImportError when executed by test_scripts.py. Please either add pontibus to the test environment/dependencies, or guard the import (e.g., try/except ImportError) and skip/exit cleanly with a clear message so the example test doesn’t fail.
| from openff.units import unit | |
| import openfe | |
| from openfe import SolventComponent, ChemicalSystem, SmallMoleculeComponent | |
| from pontibus.protocols.solvation import ASFEProtocol, ASFESettings | |
| from pontibus.protocols.solvation.settings import PackmolSolvationSettings | |
| import sys | |
| from openff.units import unit | |
| import openfe | |
| from openfe import SolventComponent, ChemicalSystem, SmallMoleculeComponent | |
| try: | |
| from pontibus.protocols.solvation import ASFEProtocol, ASFESettings | |
| from pontibus.protocols.solvation.settings import PackmolSolvationSettings | |
| except ImportError: | |
| # When pontibus is not installed, this example cannot be run as intended. | |
| # Emit a clear message and exit cleanly if executed as a script, so that | |
| # CI/example tests that run this file do not fail with ImportError. | |
| logging.getLogger(__name__).warning( | |
| "The 'pontibus' package is not installed. " | |
| "This example requires pontibus to run and will be skipped." | |
| ) | |
| if __name__ == "__main__": | |
| sys.exit(0) |
| cofactors = None | ||
| if benchmark_sys.cofactors is not None: | ||
| cofactors = ofebu.process_sdf( | ||
| benchmark_sys.ligands[PARTIAL_CHARGE], return_dict=False |
Copilot
AI
Feb 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
process_components() detects benchmark_sys.cofactors but then reads cofactors from benchmark_sys.ligands[...]. This will incorrectly treat all ligands as cofactors (and can explode the ChemicalSystem component set). It should read from benchmark_sys.cofactors[PARTIAL_CHARGE] (or another appropriate key) when loading cofactors.
| benchmark_sys.ligands[PARTIAL_CHARGE], return_dict=False | |
| benchmark_sys.cofactors[PARTIAL_CHARGE], return_dict=False |
| """ | ||
| # The settings here are effectively the "fast" settings | ||
| # shown in the validation. | ||
| settings: ASFEProtocol = ASFEProtocol.default_settings() |
Copilot
AI
Feb 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Type annotation looks incorrect: ASFEProtocol.default_settings() is expected to return an ASFESettings instance, but settings is annotated as ASFEProtocol. This makes the code misleading (and will fail type-checking). Update the annotation to ASFESettings (or remove the explicit annotation).
| settings: ASFEProtocol = ASFEProtocol.default_settings() | |
| settings: ASFESettings = ASFEProtocol.default_settings() |
| cofactors : dict[str, SmallMoleculeComponent] | None | ||
| Dictionary of SmallMoleculeComponents to act as co-solute |
Copilot
AI
Feb 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Docstring mismatch: cofactors is documented as a dict[str, SmallMoleculeComponent] | None, but the function signature uses list[SmallMoleculeComponent] | None. Please correct the docstring to match the actual type (or change the parameter type if a dict is intended).
| cofactors : dict[str, SmallMoleculeComponent] | None | |
| Dictionary of SmallMoleculeComponents to act as co-solute | |
| cofactors : list[SmallMoleculeComponent] | None | |
| Optional list of SmallMoleculeComponents to act as co-solutes |
| # Check pontibus manifest | ||
| pontibus_path = out_dir / PONTIBUS_FILENAME | ||
| if not pontibus_path.exists(): | ||
| errors.append(f"Missing pontibus manifest: {pontibus_path.name}") | ||
| else: | ||
| try: | ||
| p = json.loads(pontibus_path.read_text()) | ||
| listed = set(p.get("solutes", [])) | ||
| if not expected_names.issubset(listed): | ||
| errors.append("pontibus manifest missing solute entries") | ||
| src = Path(p.get("source_sdf", "")) | ||
| if not src.exists(): | ||
| # allow relative paths resolved against the script dir | ||
| src2 = Path(__file__).parent / src | ||
| if not src2.exists(): | ||
| errors.append("pontibus source SDF not found: %s" % src) | ||
| except Exception as exc: | ||
| errors.append(f"Failed to parse pontibus manifest: {exc}") | ||
|
|
||
| if errors: | ||
| logger.error( |
Copilot
AI
Feb 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The pontibus manifest validation block is duplicated verbatim (the same file existence + JSON parsing + checks run twice). Please remove the duplicate block to avoid double-reporting errors and reduce maintenance burden.
| # Check pontibus manifest | |
| pontibus_path = out_dir / PONTIBUS_FILENAME | |
| if not pontibus_path.exists(): | |
| errors.append(f"Missing pontibus manifest: {pontibus_path.name}") | |
| else: | |
| try: | |
| p = json.loads(pontibus_path.read_text()) | |
| listed = set(p.get("solutes", [])) | |
| if not expected_names.issubset(listed): | |
| errors.append("pontibus manifest missing solute entries") | |
| src = Path(p.get("source_sdf", "")) | |
| if not src.exists(): | |
| # allow relative paths resolved against the script dir | |
| src2 = Path(__file__).parent / src | |
| if not src2.exists(): | |
| errors.append("pontibus source SDF not found: %s" % src) | |
| except Exception as exc: | |
| errors.append(f"Failed to parse pontibus manifest: {exc}") | |
| if errors: | |
| logger.error( | |
| if errors: | |
| logger.error( | |
| "ASFE network validation failed with %d error(s): %s", len(errors), errors | |
| ) | |
| else: | |
| logger.info( | |
| "ASFE network validation passed (%d transformations, %d solutes)", | |
| len(edges), | |
| len(lig_components), | |
| ) | |
| return errors | |
| if __name__ == "__main__": | |
| # Simple example-run logging to make outputs visible when executed directly | |
| _configure_example_logging(logging.INFO) | |
| main() | |
| # Validate and raise on errors so the example fails loudly when incorrect | |
| errors = validate_asfe_network() | |
| if errors: | |
| raise RuntimeError("ASFE network validation failed:\n" + "\n".join(errors)) | |
| logger.info("Example completed and validated successfully") |
| # # Can be run HPC3 | ||
| # for transformation in alchem_network.edges: | ||
| # transformation.to_json(os.path.join(OUTPUT_DIR, f"{transformation.name}.json")) |
Copilot
AI
Feb 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment appears to contain commented-out code.
| # # Can be run HPC3 | |
| # for transformation in alchem_network.edges: | |
| # transformation.to_json(os.path.join(OUTPUT_DIR, f"{transformation.name}.json")) | |
| # Optional: for HPC workflows, individual transformation JSON files can be | |
| # produced by iterating over `alchem_network.edges` and writing each | |
| # transformation to a separate file in the desired output directory. |
* Simple plan_rbfe * Add example script testing * pin gufe Co-authored-by: Hannah Baumann <43765638+hannahbaumann@users.noreply.github.com> * Update plan_rbfe script to include validation without pytest complexity --------- Co-authored-by: jaclark5 <jennifer.clark@openforcefield.org> Co-authored-by: Hannah Baumann <43765638+hannahbaumann@users.noreply.github.com>
This PR add a plan_asfe example using the BenchmarkData class in this repo.
In previous Sage benchmarking, freesolv and mnsol also store solvent, which is not currently an attribute of the BenchmarkData class. Merger of this PR depends on how the reference data is stored and expected to be imported into BenchmarkData.
Note that exp ref values for MNSol should not be in this repository for licensing reasons.