Skip to content

Conversation

@jaclark5
Copy link
Collaborator

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.

@jaclark5 jaclark5 requested a review from Copilot February 10, 2026 19:37
@jaclark5 jaclark5 linked an issue Feb 10, 2026 that may be closed by this pull request
Copy link

Copilot AI left a 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.py script to generate an ASFE AlchemicalNetwork (and a pontibus inputs manifest) from BenchmarkData.
  • 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
Copy link

Copilot AI Feb 10, 2026

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”.

Suggested change
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

Copilot uses AI. Check for mistakes.
Comment on lines +13 to +19

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
Copy link

Copilot AI Feb 10, 2026

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.

Suggested change
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)

Copilot uses AI. Check for mistakes.
cofactors = None
if benchmark_sys.cofactors is not None:
cofactors = ofebu.process_sdf(
benchmark_sys.ligands[PARTIAL_CHARGE], return_dict=False
Copy link

Copilot AI Feb 10, 2026

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.

Suggested change
benchmark_sys.ligands[PARTIAL_CHARGE], return_dict=False
benchmark_sys.cofactors[PARTIAL_CHARGE], return_dict=False

Copilot uses AI. Check for mistakes.
"""
# The settings here are effectively the "fast" settings
# shown in the validation.
settings: ASFEProtocol = ASFEProtocol.default_settings()
Copy link

Copilot AI Feb 10, 2026

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).

Suggested change
settings: ASFEProtocol = ASFEProtocol.default_settings()
settings: ASFESettings = ASFEProtocol.default_settings()

Copilot uses AI. Check for mistakes.
Comment on lines 240 to 241
cofactors : dict[str, SmallMoleculeComponent] | None
Dictionary of SmallMoleculeComponents to act as co-solute
Copy link

Copilot AI Feb 10, 2026

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).

Suggested change
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

Copilot uses AI. Check for mistakes.
Comment on lines +436 to +438
# 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(
Copy link

Copilot AI Feb 10, 2026

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.

Suggested change
# 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")

Copilot uses AI. Check for mistakes.
Comment on lines +310 to +312
# # Can be run HPC3
# for transformation in alchem_network.edges:
# transformation.to_json(os.path.join(OUTPUT_DIR, f"{transformation.name}.json"))
Copy link

Copilot AI Feb 10, 2026

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.

Suggested change
# # 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.

Copilot uses AI. Check for mistakes.
* 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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add SFE: scripts to "scripts" dir

1 participant