CADDForge FEP Plugin
Install this plugin with python -m pip install caddforge-fep. It depends directly
on caddforge-core>=0.21.0; Python imports and CLI commands still use caddforge.
For the complete public toolbox, including user-facing extras, install
python -m pip install caddforge. When migrating an existing environment, uninstall
the former caddforge core distribution before installing renamed core and updated plugins.
caddforge-fep provides free energy perturbation workflows as plugins for the CADDForge framework.
Optional GUI dependencies
The core FEP workflows can be installed without GUI dependencies.
If you want to use the FEP GUI extension, install the optional GUI extra:
pip install "caddforge-fep[gui]"
Installation notes
- It is recommended to install
openfeand the rest of the scientific runtime stack withconda. - To avoid environment conflicts, install all required
condapackages first, and only then install CADDForge packages withpip.
Typical order:
conda create -n caddforge-fep python=3.10
conda activate caddforge-fep
conda install -c conda-forge openfe openff-toolkit rdkit pdbfixer
pip install caddforge-core caddforge-fep
Notes:
- The current GUI extension covers both
openfe-abfeandopenfe-rbfe-septop. - The
openfe-rbfe-septopGUI includes an interactive ligand-network canvas with draggable nodes, manual edge creation/removal, edge rescoring, and network save/load support.
Implemented Tools
Tool: openfe-abfe
What it does
The openfe-abfe tool prepares, and optionally runs, OpenFE absolute binding free energy calculations through the standard CADDForge workflow/job interface. It supports:
- ligand input from a multi-record SDF with ligand selection by index
- protein input from PDB
- optional protein cleanup with
pdbfixer - OpenFE settings overrides through dot-path config entries
- local execution or Slurm execution with scratch work under
SLURM_TMPDIR - standardized CADDForge outputs (
result_record.json, transformation JSON, OpenFE logs)
External dependencies
These tools and libraries are required at runtime and are not installed automatically by this Python package:
openfeopenffrdkitpdbfixer
Installation note:
openfeis best installed withconda.- Install the
condapackages first, then installcaddforge-core/caddforge-fepwithpip.
CLI usage
caddforge run fep openfe-abfe --config abfe.yaml
Python API usage
from caddforge.core.execution import load_result_for_job, submit_workflow
from caddforge.plugins import get_tool
spec = get_tool("fep", "openfe-abfe")
cfg = spec.config_cls(
ligand_sdf="ligand_neutral.sdf",
protein_pdb="protein_fixed.pdb",
ligand_index=0,
backend="local", # or "slurm"
run_quickrun=True,
)
workflow = spec.workflow_cls(cfg)
job = submit_workflow(workflow, cfg, backend_name=cfg.backend)
result = load_result_for_job(job)
if result is not None:
print(result.status.value, result.metrics)
Example config
ligand_sdf: ligand_neutral.sdf
protein_pdb: protein_fixed.pdb
ligand_index: 0
fix_protein_pdb: false
partial_charge_method: am1bcc
off_toolkit_backend: ambertools
host_min_distance_nm: 0.5
host_max_distance_nm: 1.5
protocol_repeats: 1
settings_overrides:
complex_simulation_settings.real_time_analysis_interval: 10 picosecond
complex_equil_simulation_settings.equilibration_length: 0.2 nanosecond
complex_equil_simulation_settings.production_length: 0.5 nanosecond
complex_equil_simulation_settings.equilibration_length_nvt: 0.10 nanosecond
complex_simulation_settings.equilibration_length: 0.2 nanosecond
complex_simulation_settings.production_length: 2.0 nanosecond
complex_simulation_settings.n_replicas: 68
solvent_equil_simulation_settings.equilibration_length: 0.1 nanosecond
solvent_equil_simulation_settings.production_length: 0.5 nanosecond
solvent_equil_simulation_settings.equilibration_length_nvt: 0.05 nanosecond
solvent_simulation_settings.equilibration_length: 0.1 nanosecond
solvent_simulation_settings.production_length: 1 nanosecond
solvent_simulation_settings.real_time_analysis_interval: 10 picosecond
solvent_simulation_settings.n_replicas: 60
engine_settings.compute_platform: CUDA
complex_lambda_settings.lambda_elec:
[0, 0.05, 0.1, 0.14, 0.19, 0.24, 0.29, 0.33, 0.38, 0.43, 0.48, 0.52, 0.57, 0.62, 0.67, 0.71,
0.76, 0.81, 0.86, 0.9, 0.95, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
complex_lambda_settings.lambda_vdw:
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.06, 0.12, 0.18, 0.24,
0.28, 0.32, 0.36, 0.4, 0.44, 0.48, 0.52, 0.56, 0.6, 0.62, 0.64, 0.66, 0.68, 0.69, 0.7, 0.71,
0.72, 0.73, 0.74, 0.75, 0.76, 0.77, 0.78, 0.79, 0.8, 0.81, 0.82, 0.83, 0.83, 0.84, 0.85,
0.86, 0.87, 0.88, 0.89, 0.9, 0.91, 0.92, 0.94, 0.96, 0.98, 1]
complex_lambda_settings.lambda_restraints:
[0, 0, 0, 0.01, 0.01, 0.02, 0.03, 0.04, 0.06, 0.07, 0.09, 0.11, 0.13, 0.16, 0.18, 0.21, 0.24,
0.27, 0.3, 0.33, 0.37, 0.4, 0.44, 0.49, 0.53, 0.57, 0.62, 0.67, 0.72, 0.77, 0.83, 0.88,
0.94, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1]
solvent_lambda_settings.lambda_elec:
[0, 0.01, 0.02, 0.03, 0.04, 0.07, 0.09, 0.11, 0.13, 0.15, 0.17, 0.22, 0.26, 0.3, 0.35, 0.39,
0.43, 0.48, 0.52, 0.57, 0.61, 0.65, 0.7, 0.74, 0.78, 0.83, 0.87, 0.91, 0.96, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
solvent_lambda_settings.lambda_vdw:
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0.1, 0.18, 0.26, 0.32, 0.38, 0.42, 0.46, 0.5, 0.53, 0.56, 0.59, 0.62, 0.65, 0.68, 0.71,
0.74, 0.76, 0.78, 0.8, 0.81, 0.83, 0.84, 0.86, 0.88, 0.9, 0.92, 0.94, 0.96, 0.98, 1]
solvent_lambda_settings.lambda_restraints:
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
run_quickrun: true
quickrun_extra_args: []
Tool-specific config options
Required / primary inputs
ligand_sdf(Path): SDF containing the ligand set.protein_pdb(Path): Protein receptor PDB.ligand_index(int, default0): SDF record index to use for the ABFE calculation.
Optional preparation options
fix_protein_pdb(bool, defaultfalse): Iftrue, runpdbfixerbefore OpenFE setup.partial_charge_method(str, default"am1bcc"): Partial charge method for OpenFF charge generation.off_toolkit_backend(str, default"ambertools"): OpenFF backend used for charge assignment.charge_n_conformers(int, default1): Number of conformers for charge generation.charge_nagl_model(str | None): Optional NAGL model name.charge_processors(int, default1): Number of processors for charge assignment.overwrite_charges(bool, defaultfalse): Overwrite existing molecule charges.require_neutral_ligand(bool, defaulttrue): Enforce the OpenFE ABFE neutral-ligand limitation.
Protocol options
host_min_distance_nm(float, default0.5): Minimum Boresch restraint host distance in nm.host_max_distance_nm(float, default1.5): Maximum Boresch restraint host distance in nm.protocol_repeats(int | None, default1): Number of independent protocol repeats per leg. Explicitnullretains OpenFE's repeat default.settings_overrides(dict[str, Any]): The ABFE defaults below, merged with explicit dot-path overrides. Omitted, empty, or partial dictionaries retain all unspecified defaults.
| Settings group | NVT equilibration | Equilibration | Production | Lambda replicas | Analysis interval |
|---|---|---|---|---|---|
| Complex pre-equilibration | 0.10 ns | 0.2 ns | 0.5 ns | — | — |
| Complex alchemical simulation | — | 0.2 ns | 2.0 ns | 68 | 10 ps |
| Solvent pre-equilibration | 0.05 ns | 0.1 ns | 0.5 ns | — | — |
| Solvent alchemical simulation | — | 0.1 ns | 1 ns | 60 | 10 ps |
The default compute platform is CUDA. Each complex lambda array has 68 entries;
each solvent lambda array has 60 entries, with solvent restraints zero throughout.
n_replicas counts lambda replicas within a leg; it is distinct from independent
protocol_repeats. Keep each leg's three lambda-array lengths consistent with its
replica count when overriding the schedules.
The CLI, Python API, and GUI use the same defaults. Explicit
settings_overrides.protocol_repeats takes precedence over the top-level
protocol_repeats. Generated examples specify repeats only at the top level.
settings_overrides accepts plain values or strings of the form <number> <unit>, for example:
5 nanosecond0.15 molar
Execution / output options
transformation_dir_name(str, default"abfe_json"): Directory name used for OpenFE transformation JSON files.run_quickrun(bool, defaulttrue): Iftrue, executeopenfe quickrun.quickrun_workdir(Path | None): Optional OpenFE working directory.quickrun_output(Path | None): Optional output JSON path foropenfe quickrun.quickrun_extra_args(list[str], default[]): Extra raw arguments appended toopenfe quickrun.
Outputs
For each run, artifacts are written under runs/<date>/<job-id>/:
<transformation_dir_name>/: OpenFE transformation JSONresults/openfe_results.json: OpenFE quickrun output JSONresults/openfe_stdout.log: OpenFE stdoutresults/openfe_stderr.log: OpenFE stderrresults/result_record.json: standardized CADDForgeResultopenfe_run/: OpenFE working directorystdout.log/stderr.log: CADDForge workflow logs
Tool: openfe-rbfe-septop
What it does
The openfe-rbfe-septop tool plans, prepares, and optionally runs separate-topology RBFE calculations with OpenFE SepTop. It supports:
- input as one shared protein plus many ligand poses, or as many aligned protein-plus-ligand input pairs
- explicit project-compound selection via config
- automatic ligand-network generation with:
- charge bucketing
- compatibility grouping based on similarity and protein-clash filtering
- project-centered radial maps
- local orphan-cluster hubs
- optional diagnostic cycle edges
- extension of an existing persisted ligand network without removing existing edges
- portable self-contained network bundles with bundled proteins plus reusable completed edge records
- bundle refresh mode to rebuild a fresh portable bundle from an existing finished campaign
- persisted ligand-network JSON plus GraphML export
- local sequential execution or Slurm fan-out submission with one child job per transformation edge
- an interactive GUI workflow with network visualization and manual editing
External dependencies
These tools and libraries are required at runtime and are not installed automatically by this Python package:
openfeopenffrdkit
Notes:
- This workflow assumes ligand poses are already meaningful and aligned in the protein frame.
- For Slurm, the campaign job plans/prepares the network once and then submits one child job per transformation so edges can run independently in parallel.
openfeis best installed withconda, and allcondadependencies should be installed before adding the CADDForge packages withpip.
CLI usage
caddforge run fep openfe-rbfe-septop --config rbfe_septop.yaml
Refresh a portable bundle from an existing finished campaign:
caddforge run fep openfe-rbfe-septop --config rbfe_refresh_bundle.yaml
Python API usage
from caddforge.core.execution import load_result_for_job, submit_workflow
from caddforge.plugins import get_tool
spec = get_tool("fep", "openfe-rbfe-septop")
cfg = spec.config_cls(
ligand_sdf="ligands.sdf",
ligand_sdfs=[],
protein_pdb="protein.pdb",
project_compound_ids=["proj_anchor_01", "proj_anchor_02"],
operation_mode="run",
backend="local", # or "slurm"
protocol_repeats=3,
)
workflow = spec.workflow_cls(cfg)
job = submit_workflow(workflow, cfg, backend_name=cfg.backend)
result = load_result_for_job(job)
if result is not None:
print(result.status.value, result.metrics)
Example config
operation_mode: run
ligand_sdf: ligands.sdf
ligand_sdfs: []
protein_pdb: protein.pdb
complexes: []
project_compound_ids:
- proj_anchor_01
- proj_anchor_02
bundle_refresh_source_path: null
existing_network_path: null
existing_network_bundle_path: null
existing_transformations_manifest_path: null
export_network_bundle: true
reuse_completed_edges: true
partial_charge_method: am1bcc
off_toolkit_backend: ambertools
protocol_repeats: 3
settings_overrides:
engine_settings.compute_platform: CUDA
compatibility_threshold: 0.45
project_assignment_threshold: 0.35
local_cluster_threshold: 0.55
max_clash_score: 0.35
diagnostic_cycle_edges_per_component: 1
run_quickrun: true
quickrun_extra_args: []
Tool-specific config options
Required / primary inputs
Use either:
ligand_sdfand/orligand_sdfs, together withprotein_pdb
Or:
complexes
Primary fields:
operation_mode("plan" | "extend" | "prepare" | "run" | "refresh_bundle", default"run"): Network-only planning, network extension, transformation preparation, full execution, or rebuilding a portable bundle from an existing finished campaign/source.ligand_sdf(Path | None): Optional SDF containing ligands already placed in the protein frame.ligand_sdfs(list[Path], default[]): Optional list of SDF files used with the shared protein. Each file may contain one or many ligands.protein_pdb(Path | None): Shared aligned protein PDB used withligand_sdfand/orligand_sdfs.complexes(list[SeptopComplexInput], default[]): Alternative aligned per-ligand input pairs, each containing aprotein_pdband aligand_sdf.project_compound_ids(list[str], default[]): Explicit ligand identifiers used as project anchors.bundle_refresh_source_path(Path | None): Required foroperation_mode: refresh_bundle. Accepts a campaign directory, aresults/directory, aligand_network.json, or an existing bundle zip.existing_network_path(Path | None): Persisted network JSON used forextend,prepare, orrunfrom an existing network.existing_network_bundle_path(Path | None): Portable self-contained RBFE bundle zip used forextend,prepare, orrun.existing_transformations_manifest_path(Path | None): Existingtransformations_manifest.jsonused to reuse already prepared transformation JSON files without regenerating them.
SeptopComplexInput fields:
ligand_sdf(Path): Ligand SDF file.protein_pdb(Path): Protein PDB file for that ligand pose.ligand_index(int, default0): Ligand record to use from the SDF.ligand_id(str | None): Reserved identifier field; current implementation derives IDs from ligand names/properties.
Current limitation:
- A
complexesentry is not a single combined protein-ligand complex PDB file. The current implementation expectsprotein_pdbplusligand_sdffor each entry.
Example complexes config:
complexes:
- protein_pdb: poses/lig1_protein.pdb
ligand_sdf: poses/lig1_ligand.sdf
ligand_index: 0
- protein_pdb: poses/lig2_protein.pdb
ligand_sdf: poses/lig2_ligand.sdf
ligand_index: 0
project_compound_ids:
- lig1_ref
Network planning options
compatibility_threshold(float, default0.45): Minimum pair similarity for direct compatibility.project_assignment_threshold(float, default0.35): Minimum similarity required to attach a ligand directly to a project hub.local_cluster_threshold(float, default0.55): Minimum similarity used to identify local orphan clusters.max_clash_score(float, default0.35): Maximum tolerated per-ligand protein-clash score for compatibility.diagnostic_cycle_edges_per_component(int, default1): Number of optional extra diagnostic edges to add per connected component.
Planning behavior:
- Split ligands by formal charge.
- Build compatibility components using similarity and protein-clash filtering.
- Use explicit
project_compound_idsas preferred centers when available. - Build local subclusters for ligands that are similar to each other but not close to a project hub.
- Preserve existing edges when
operation_mode: extend.
Reuse behavior:
- Use
existing_network_pathwhen you want to regenerate transformations from a saved ligand network. - Use
existing_network_bundle_pathwhen you want to load a self-contained prior network including bundled proteins and reusable completed edge records. - Use
existing_transformations_manifest_pathwhen you already have prepared transformation JSON files and want to reuse them directly. - In
runmode,existing_transformations_manifest_pathavoids regenerating the transformation JSON files. reuse_completed_edges(bool, defaulttrue) reuses previously completed edges when compatible edge records are found next toexisting_network_pathor insideexisting_network_bundle_path.refresh_bundleis a lightweight local-only maintenance mode. It does not plan, prepare, or run simulations; it just rebuilds a freshligand_network_bundle.zipfrom an existing campaign/source plus its currentedge_records.- Use
bundle_refresh_source_pathwithoperation_mode: refresh_bundlewhen you want to rebuild a portable bundle after a finished Slurm campaign.
Charge / protocol options
partial_charge_method(str, default"am1bcc"): Partial charge method for OpenFF charge generation.off_toolkit_backend(str, default"ambertools"): OpenFF backend used for charge assignment.charge_n_conformers(int, default1): Number of conformers for charge generation.charge_nagl_model(str | None): Optional NAGL model name.charge_processors(int, default1): Number of processors for charge assignment.overwrite_charges(bool, defaultfalse): Overwrite existing molecule charges.protocol_repeats(int | None): Optional override for SepTop protocol repeats.settings_overrides(dict[str, Any], default{}): Dot-path overrides applied toSepTopProtocol.default_settings().
Execution / output options
transformation_dir_name(str, default"transformations"): Directory name used for transformation JSON files.network_dir_name(str, default"network"): Directory name used for persisted network artifacts.run_quickrun(bool, defaulttrue): Iftrue, executeopenfe quickruninrunmode.quickrun_extra_args(list[str], default[]): Extra raw arguments appended toopenfe quickrun.export_network_bundle(bool, defaulttrue): Write a portableligand_network_bundle.zipintoresults/.
Backend behavior:
- Local backend: edges are executed sequentially.
- Slurm backend: the campaign prepares the network and transformation JSONs once, then submits one Slurm child job per edge.
refresh_bundlemust use the local backend.
Outputs
For each campaign run, artifacts are written under runs/<date>/<job-id>/:
<network_dir_name>/ligand_network.json: canonical persisted RBFE network<network_dir_name>/ligand_network.graphml: GraphML view of the networkresults/ligand_network.json: exportable copy of the persisted ligand network JSONresults/ligand_network.graphml: exportable copy of the GraphML network viewresults/ligand_network_bundle.zip: portable self-contained network bundle with bundled proteins and reusable edge recordsresults/ligand_network_summary.txt: human-readable network summaryresults/transformations_manifest.json: manifest of prepared transformations and edge run directoriesresults/edge_records/: canonical per-edge reusable result cache used for extension / rerun reusetransformations/: one OpenFE transformation JSON per planned edgeedges/<edge-id>/: per-edge run directories
For local run mode, each edge directory contains:
results/openfe_results.jsonresults/openfe_stdout.logresults/openfe_stderr.logopenfe_run/
Campaign-level artifacts:
results/result_record.json: standardized CADDForgeResultstdout.log/stderr.log: campaign workflow logs
Notes:
- In Slurm mode, edge execution status is tracked per child job.
- The parent campaign job records the planned network and submitted child jobs; child jobs own the actual per-edge simulation logs and status.
- Reuse of previously completed edges is driven by the edge cache under
results/edge_records/. A portable bundle is sufficient for later extension if you want to move or archive a network as a single file.
Compressed inputs
ABFE and RBFE ligand SDF and protein PDB inputs, including paths nested in RBFE complexes, may be
provided with a trailing .gz. CADDForge expands each wrapper in runtime scratch while preserving
the original compressed path in saved configuration and provenance. Gzipped existing-network JSON
and transformation manifests are read directly so relative references remain anchored to the
manifest directory; gzip-wrapped self-contained network bundles are staged as plain bundle files.
Continuous integration and releases
.github/workflows/release.yml builds a wheel and source archive, validates
metadata, and tests the installed wheel on Linux with Python 3.10 and 3.12.
The lightweight test selection is recorded in scripts/ci_tests.json;
external scientific engines are validated separately. CI installs the same
wheel with its [gui] extra so GUI regression tests use the declared GUI
dependencies, including caddforge-gui.
Pushing a matching v* release tag publishes the tested artifacts to PyPI.
Branch pushes, pull requests and manual runs never publish. Configure a
PyPI Trusted Publisher for the idorsia repository, release.yml workflow
and pypi environment before pushing release tags. Runtime CADDForge
dependencies must already be available publicly; publish core and GUI before
FEP/docking, and publish docking/descriptors before MD.
Metadata
Release files for caddforge-fep 0.7.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| caddforge_fep-0.7.0.tar.gz | 88.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| caddforge_fep-0.7.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 168.6 kB
Release files / caddforge_fep-0.7.0.tar.gz
| Download URL | caddforge_fep-0.7.0.tar.gz |
|---|---|
| Size | 88.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a05590fd50d51b234c03f3e3495be3209cac03c586994752016585612de29c81
|
|
BLAKE2b-256 checksum How to use checksums |
bc05bd36ccaba0207b23403299a81287320d5734248713e6efe6b62b7fa60be2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 13, 2026.
Transparency logRelease files / caddforge_fep-0.7.0-py3-none-any.whl
| Download URL | caddforge_fep-0.7.0-py3-none-any.whl |
|---|---|
| Size | 80.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c65ec727fdef07ab29e88b655923362b3afb54b8f794dfbe208a047dcff50a25
|
|
BLAKE2b-256 checksum How to use checksums |
263a46e173b23138a7ec0e5956e16439883e0bdfd12910582634dbc8cb896bfe
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 13, 2026.
Transparency log