MD Plugin¶
Package: caddforge-md
Public source tag
This page describes caddforge-md 0.13.0, tagged as v0.13.0.
PyPI publication is pending; see the version matrix.
The MD plugin provides molecular dynamics preparation, simulation, and analysis workflows.
Requirements and example status¶
The example library provides downloadable configuration templates for every workflow on this page, plus a shared Python runner. File paths refer to inputs you must prepare; these are not bundled research datasets. Schema validation does not establish scientific validity or engine availability. See the version matrix for release tags and publication status.
fix-structure¶
Conservatively repairs protein hydrogens in a protein-only PDB, protein-ligand complex PDB, or existing POPC/POPE membrane system before OpenMM simulations. The tool preserves explicit protein protonation choices where possible, repairs configured membrane lipid hydrogens with OpenMM force-field templates, leaves ligands/cofactors untouched, and preserves ligand/cofactor CONECT records and PDB formal-charge fields by default.
CLI:
YAML:
backend: local
input_pdb: data/complex.pdb
forcefields:
- amber14-all.xml
- amber14/tip3p.xml
ph: 7.0
protonation_policy: preserve
termini_policy: charged
fix_nonstandard_residues: auto
strict_all: false
membrane_lipid_resnames:
- POPC
- POPE
validate_openmm_system: auto
sanitize_mixed_residues: auto
preserve_conect: true
quiet: false
output_dir_name: output
output_filename: null
validate_openmm_system: auto keeps normal non-membrane behavior unchanged and runs an OpenMM ForceField.createSystem() validation when configured membrane lipid residues are present. sanitize_mixed_residues: auto separates mixed PDB residue ids and reassembles split atom records before OpenMM parsing, for example POPC records interrupted by CL/T3P records that reuse the same residue number. POPC/POPE are detected from both standard and 4-character PDB residue-name layouts. For POPC/POPE Amber-style inputs, include force fields that cover protein, lipids, water, and ions; amber14-all.xml includes lipid templates and amber14/tip3p.xml covers TIP3P water and common ions. Unmatched ligands/cofactors left untouched by this repair tool are reported in repair_metadata.openmm_system_validation.ignored_unmatched_residues and must still be parameterized by the downstream MD workflow.
Python:
from caddforge.core.execution import load_result_for_job, submit_workflow
from caddforge.plugins import get_tool
spec = get_tool("md", "fix-structure")
cfg = spec.config_cls(
input_pdb="data/complex.pdb",
forcefields=["amber14-all.xml", "amber14/tip3p.xml"],
backend="local",
)
workflow = spec.workflow_cls(cfg)
job = submit_workflow(
workflow, cfg, backend_name=cfg.backend,
input_ports=tuple(getattr(spec.plugin, "input_ports", ())),
)
result = load_result_for_job(job)
if result is not None:
print(result.paths["output_pdb"])
Outputs to inspect: results/output/<input_stem>_fixed.pdb,
results/result_record.json. In pipelines, bind input artifact structure and consume output
artifact repaired_structure.
Terminal chemistry¶
termini_policy controls terminal chemistry separately from side-chain protonation:
| Policy | Behavior |
|---|---|
charged (default) |
Prepare free N-termini as positively charged and free C-termini as negatively charged, independently of pH. Repair missing terminal OXT when the backbone permits reconstruction. |
preserve |
Preserve the input terminal state and validate against the supplied force fields. Ambiguous or incomplete terminal groups fail with a residue-specific explanation. |
cap |
Add ACE/NME caps to eligible neutral or incomplete termini. Already charged or capped termini remain unchanged. |
A protonated, uncapped C-terminus (–COOH) is neutral. A deprotonated C-terminus
(–COO⁻) is charged. A neutral NME cap (–CONHCH₃) is chemically different from
an uncapped neutral acid. Capping can affect both N- and C-termini.
Terminal policy takes precedence over protonation_policy only at terminal
groups. Thus the default charged policy can remove HXT even when
protonation_policy: preserve; unrelated residue substitutions remain forbidden.
Missing terminal hydrogens can make preservation ambiguous (for example, only
one H on an ordinary free N-terminus). Prepare the intended terminal state
explicitly rather than relying on pH to select it in preserve mode. Hydrogen
repair still fills unrelated missing hydrogens.
Stock OpenMM amber14-all.xml supports standard charged termini and ACE/NME caps,
but does not include a neutral uncapped –COOH template. To preserve that form,
supply a compatible force field or additional, scientifically validated templates:
input_pdb: data/protein_with_explicit_terminal_states.pdb
forcefields:
- amber14-all.xml
- /absolute/path/to/your_validated_terminal_templates.xml
protonation_policy: preserve
termini_policy: preserve
The extra XML above is user-supplied, not bundled with CADDForge. Force-field
compatibility is checked; preserve mode never silently converts an unsupported
neutral terminus. Setting validate_openmm_system: never does not disable
protein/cap template matching or terminal-state validation. This feature concerns
terminal states of recognized protein residues, not arbitrary modified residues.
For standard charged preparation, set termini_policy: charged. For explicitly
requested caps, set termini_policy: cap; side-chain preservation can stay enabled.
Approximate reconstructed atoms and caps should be minimized before production MD.
Migration: cap_neutral_termini: true is a deprecated alias for
termini_policy: cap. Combining it with an explicit different policy fails.
false does not override the new policy. fix_nonstandard_residues now controls
modified-residue normalization only; setting it to never does not disable
terminal treatment. Users who previously relied on protonation_policy: preserve
to reject terminal conversions must now also set termini_policy: preserve.
The report at paths.normalization records requested/effective policy, original
terminal evidence, normalization edits or added caps, final decisions, and
validation status under normalization.terminal_treatment. It is retained on
failure. Successful results also expose repair_metadata.terminal_treatment.
Pipeline example¶
version: 1
name: prepare-protein
stages:
- id: repair
tool: md/fix-structure
config:
input_pdb: data/protein.pdb
forcefields: [amber14-all.xml, amber14/tip3p.xml]
protonation_policy: preserve
termini_policy: charged
outputs:
protein: output_pdb
- id: cleanup
tool: md/minimize
inputs:
protein: repair.protein
config:
ligand_path: data/ligand.sdf
mode: ligand_only
The input must be a protein-only receptor for this example; ligand chemistry is
supplied separately to minimize. The repair tool's existing input port
structure and output port repaired_structure are unchanged. The explicit
protein: output_pdb output alias above binds the same PDB for the next stage.
center-and-align¶
Centers, wraps, optionally aligns, and writes a processed trajectory.
CLI:
YAML:
backend: local
topology_path: data/system.pdb
trajectory_path: data/traj.xtc
center_selection: protein or resname MOL
keep_selection: protein or resname MOL
align_selection: protein and backbone
unwrap: true
center: true
wrap: true
center_mode: geometry
wrap_compound: residues
align_weights: mass
output_dir_name: output
output_trajectory_filename: traj-centered.xtc
output_structure_filename: traj-centered.pdb
Python:
from caddforge.core.execution import load_result_for_job, submit_workflow
from caddforge.plugins import get_tool
spec = get_tool("md", "center-and-align")
cfg = spec.config_cls(
topology_path="data/system.pdb",
trajectory_path="data/traj.xtc",
center_selection="protein or resname MOL",
keep_selection="protein or resname MOL",
align_selection="protein and backbone",
backend="local",
)
workflow = spec.workflow_cls(cfg)
job = submit_workflow(
workflow, cfg, backend_name=cfg.backend,
input_ports=tuple(getattr(spec.plugin, "input_ports", ())),
)
result = load_result_for_job(job)
if result is not None:
print(result.paths)
Outputs to inspect: results/output/<trajectory>, results/output/<structure>,
results/result_record.json.
extract-frames¶
Extracts one or more frames from a trajectory. For multi-frame selections it writes a sliced trajectory and a first selected-frame structure; for single-frame selections it writes only structure-style outputs.
CLI:
YAML:
backend: local
topology_path: data/traj-centered.pdb
trajectory_path: data/traj-centered.xtc
ligand_template_sdf: data/template.sdf
ligand_resname: UNK
extract_selection: protein or resname UNK
frame_index: null
last_ns: 10.0
frame_slice: null
write_per_frame_pdbs: false
output_dir_name: output
output_trajectory_filename: last-10ns.xtc
output_structure_filename: last-10ns_first_complex.pdb
protein_trajectory_filename: last-10ns_protein.xtc
protein_structure_filename: last-10ns_first_protein.pdb
ligand_sdf_filename: last-10ns_first_ligand.sdf
Use exactly one frame-selection mode. A Python-style slice is written as:
Python:
from caddforge.core.execution import load_result_for_job, submit_workflow
from caddforge.plugins import get_tool
spec = get_tool("md", "extract-frames")
cfg = spec.config_cls(
topology_path="data/traj-centered.pdb",
trajectory_path="data/traj-centered.xtc",
ligand_template_sdf="data/template.sdf",
ligand_resname="UNK",
extract_selection="protein or resname UNK",
last_ns=10.0,
backend="local",
)
workflow = spec.workflow_cls(cfg)
job = submit_workflow(
workflow, cfg, backend_name=cfg.backend,
input_ports=tuple(getattr(spec.plugin, "input_ports", ())),
)
result = load_result_for_job(job)
if result is not None:
print(result.paths)
Outputs to inspect: results/output/<structure>, optional results/output/<trajectory> for
multi-frame selections, optional protein-only outputs, optional corrected ligand SDF,
results/output/frame_manifest.json, results/result_record.json.
cluster-representative¶
Clusters selected trajectory frames and writes the medoid of the largest cluster. By default,
ligand systems cluster on ligand heavy atoms when ligand_resname is provided, while
extract_selection controls the atoms written to the representative complex PDB.
CLI:
YAML:
backend: local
topology_path: data/traj-centered.pdb
trajectory_path: data/traj-centered.xtc
ligand_template_sdf: data/template.sdf
ligand_resname: UNK
cluster_selection: null
extract_selection: protein or resname UNK
last_ns: 10.0
frame_slice: null
distance_threshold_nm: 0.2
output_dir_name: output
output_complex_filename: largest_cluster_representative_complex.pdb
output_protein_filename: largest_cluster_representative_protein.pdb
output_ligand_sdf_filename: largest_cluster_representative_ligand.sdf
clustering_summary_filename: clustering_summary.json
For slice-based clustering, replace last_ns with:
Python:
from caddforge.core.execution import load_result_for_job, submit_workflow
from caddforge.plugins import get_tool
spec = get_tool("md", "cluster-representative")
cfg = spec.config_cls(
topology_path="data/traj-centered.pdb",
trajectory_path="data/traj-centered.xtc",
ligand_template_sdf="data/template.sdf",
ligand_resname="UNK",
extract_selection="protein or resname UNK",
last_ns=10.0,
distance_threshold_nm=0.2,
backend="local",
)
workflow = spec.workflow_cls(cfg)
job = submit_workflow(
workflow, cfg, backend_name=cfg.backend,
input_ports=tuple(getattr(spec.plugin, "input_ports", ())),
)
result = load_result_for_job(job)
if result is not None:
print(result.metrics)
Outputs to inspect: results/output/largest_cluster_representative_complex.pdb, optional
results/output/largest_cluster_representative_protein.pdb, optional corrected ligand SDF,
results/output/clustering_summary.json, results/result_record.json.
plain-md¶
Runs a standard OpenMM simulation, with optional OpenFF/SMIRNOFF parameterization for non-standard
molecules. For already complete systems, system_preparation_mode: prepared skips hydrogen,
solvent, and ion addition and builds a snug periodic box around the provided atoms. Membrane
systems can use OpenMM's membrane-specific barostat with barostat: membrane.
CLI:
caddforge run md plain-md --config plain_md.yaml
caddforge run md plain-md --config plain_md.yaml --backend slurm --slurm-time 24:00:00
YAML:
backend: local
system_pdb_path: data/system.pdb
molecule_sdf_paths:
- data/ligand.sdf
ph: 7.4
system_preparation_mode: solvate
padding: 1.0
ionic_strength: 0.15
prepared_box_margin: 0.0
nonbonded_cutoff: 1.0
barostat: isotropic
pressure: 1.0
membrane_surface_tension: 0.0
membrane_xy_mode: isotropic
membrane_z_mode: free
barostat_frequency: 25
temperature: 300.0
step_size_fs: 4
simulation_time_ns: 1.0
trajectory_filename: traj.xtc
report_interval: 10000
checkpoint_interval: 5000
output_dir_name: output
seed: null
repeats: 1
checkpoint_path: null
max_restarts: 10
Python:
from caddforge.core.execution import load_result_for_job, submit_workflow
from caddforge.plugins import get_tool
spec = get_tool("md", "plain-md")
cfg = spec.config_cls(
system_pdb_path="data/system.pdb",
molecule_sdf_paths=["data/ligand.sdf"],
simulation_time_ns=1.0,
backend="local",
)
workflow = spec.workflow_cls(cfg)
job = submit_workflow(
workflow, cfg, backend_name=cfg.backend,
input_ports=tuple(getattr(spec.plugin, "input_ports", ())),
)
result = load_result_for_job(job)
if result is not None:
print(result.metrics)
Outputs to inspect: results/output/*_prepared.pdb, results/output/*_minimized.pdb,
results/output/traj.xtc, results/output/checkpoint.chk, results/result_record.json.
The standardized result exposes prepared_structure, minimized_structure, trajectory,
checkpoint, and output_dir as pipeline-ready result paths.
Set repeats above one to submit independent simulations with distinct reproducible seeds. The
parent records child IDs and publishes replicas_manifest.json with aggregate completion metrics
when every child reaches a terminal state. Slurm repeat children are submitted before the parent
waits for results.
For an already prepared system, use:
In this mode, padding and ionic_strength are ignored. The workflow derives an
orthorhombic box from the existing coordinates plus prepared_box_margin and fails if the
resulting box is too small for the configured nonbonded cutoff.
For membrane systems oriented in the XY plane with the membrane normal along Z, use:
barostat: membrane
membrane_surface_tension: 0.0
membrane_xy_mode: isotropic
membrane_z_mode: free
barostat_frequency: 25
minimize¶
Minimize one protein–ligand complex with OpenMM L-BFGS. This is local pose cleanup: the reported physical-model energy includes implicit solvation and is not a binding affinity or a score for comparing different chemical systems. The tool emits finite poses with quality flags by default; strict mode can reject flagged jobs while retaining their structures and diagnostics.
Minimize installation and execution¶
Use an environment containing AmberTools (pdb4amber, antechamber, parmchk2,
tleap), OpenMM >=8.2, ParmEd, RDKit, NumPy, SciPy, and this plugin. For example:
conda create -n caddforge-minimize -c conda-forge python=3.13 ambertools openmm=8.2 parmed rdkit numpy scipy
conda activate caddforge-minimize
# Use the reviewed public checkout, which includes the Amber validation fixes.
python -m pip install -e '/path/to/caddforge-md[minimize]'
caddforge show-config md minimize
caddforge run md minimize --config minimize.yaml
Save this configuration as minimize.yaml, replacing the input paths:
complex_pdb_path: data/complex.pdb
ligand_path: data/ligand.sdf
lig_resname: LIG
mode: ligand_only
pocket_radius_angstrom: 5.0
shell_radius_angstrom: 5.0
backbone_restraint_strength: moderate
shell_restraint_strength: very_strong
tolerance_kj_mol_nm: 10.0
max_iterations: 1000
platform: CPU
run_directory: runs/minimize-one-complex
For separate inputs, replace complex_pdb_path with protein_pdb_path; retain
ligand_path in the same coordinate frame. To submit through Slurm, use
caddforge run md minimize --config minimize.yaml --backend slurm with your
site's backend/environment settings. There is no dedicated
minimize GUI in this revision.
AmberTools is installed separately from the Python extra. A prepared Amber input does not run AmberTools. Scientific imports are deferred until execution. CLI and pipeline execution use core input staging, including transparent gzip for declared input files. Checksums in chemistry/mapping records refer to the decompressed bytes. Nested cofactor chemistry paths are ordinary uncompressed configuration inputs and must be available on the execution node.
from cadd_md.minimize import MinimizeConfig, MinimizeWorkflow
config = MinimizeConfig(
complex_pdb_path="complex.pdb",
ligand_path="ligand.sdf",
lig_resname="LIG",
mode="pocket_restrained",
run_directory="runs/cleanup",
registry_path="runs/job_registry.sqlite",
)
workflow = MinimizeWorkflow(config)
result = workflow.run(workflow.prepare_job())
print(result.status, result.metrics)
print(result.paths["minimized_complex"])
Paths follow the existing config convention: relative paths resolve from the
submission working directory. Use the core CLI/backend for Slurm submission;
the scientific workflow contains no scheduler-specific execution logic.
Choose a fresh run_directory for each run; existing minimization outputs are
preserved and cannot be overwritten by a new job.
Minimize inputs and physical model¶
Supply either complex_pdb_path, or protein_pdb_path plus a ligand pose in the
same coordinate frame. ligand_path is always required and supplies authoritative
SDF/MOL/MOL2 chemistry. For a complex PDB, coordinates come from that complex;
template coordinates are not used to align or move the pose. For separate inputs,
the ligand file must contain one explicitly hydrogenated 3D molecule. The target
residue is selected by lig_resname, which must occur exactly once.
Preparation preserves supplied atoms, protonation, ligand stereochemistry, and
disulfides, adds only missing hydrogens, and rejects missing protein heavy atoms.
Repair missing protein heavy atoms upstream before submitting the complex; this tool only adds missing hydrogens. Bare histidines need
unambiguous hydrogen evidence or an explicit residue_states entry. Optional
protein_states_path and protein_links_path must be provided together and match
the input structure checksum. Covalent target ligands are rejected.
Automatic preparation uses ff14SB, GAFF2/AM1-BCC, TIP3P retained waters, and OBC2
with mbondi2 radii, ACE surface area, solute dielectric 1, solvent dielectric
78.5, and zero salt screening. It is nonperiodic with NoCutoff; no solvent box
or neutralizing ions are added. Retained waters are explicit solute particles in
the GB model, a hybrid approximation requiring application-specific validation.
All force-field choices, parameterization commands, checksums, and available
software versions are retained.
Both atom typing and missing-parameter lookup explicitly select GAFF2. Unresolved
parmchk2 parameter warnings fail preparation and require reviewed parameters.
Generated GAFF2/AM1-BCC charges are checked and, when necessary, normalized independently for each organic component. Minimize and OpenBPMD apply a uniform per-atom offset only when the total correction is at most 0.005 e and each atom changes by at most 0.0001 e. These are conservative engineering limits, not universal scientific guarantees. Final component charges must sum to the authoritative formal charge within 1e-8 e, and mapped charges are verified again after Amber topology construction. Invalid chemistry or larger residuals fail preparation; supplied Amber charges are never normalized.
Preparation retains the original *.raw.mol2, the final MOL2, and a
*.charge_validation.json report with raw/final sums, correction, policy limits,
checksums, and the decision. charge_validation in the preparation summary links
the reports, including rejected corrections. Applied corrections also produce a
log warning. A residual within the charge-token rounding bound is distinguished
from a larger generation residual; the latter is not assumed to be rounding.
Changing ligand_net_charge or disabling quality checks is not a remedy for this
error. These rules also apply to pipeline and scatter jobs without new YAML
options.
Automatic preparation also retains Antechamber's ANTECHAMBER_AC.AC bond
evidence. The shared validator checks mapped atoms, coordinates, atom types,
connectivity (including hydrogen attachments), and total formal charge against
the authoritative ligand. A missing or inconsistent AC file fails preparation.
Amber MOL2 bond tokens are not always literal chemical bond orders: for example, carboxylate C–O bonds can both be written as single bonds. Validation accepts supported Amber encodings only in their permitted chemical environments and requires one complete authoritative or RDKit resonance form to explain every bond. Aromatic/Kekulé alternatives are supported; arbitrary bond-order changes, tautomers, and changed protonation are not accepted. At most 1,000 candidate forms are examined. An inconclusive capped search fails explicitly.
The charge report contains a graph_validation record with source checksums,
atom mapping, candidate count, interpreted bonds, and the acceptance or rejection
reason. The validators never rewrite bond records, atom types, coordinates, or
the authoritative ligand. Charge normalization runs only after graph validation.
These rules apply equally to minimize, OpenBPMD, organic cofactors, and pipeline
jobs; no configuration override is needed for supported representations.
Noncovalent organic cofactors require cofactors entries with residue_index
and chemistry_path; optional atom_map_path uses the shared explicit atom-map
schema against the original complex. Residue and atom indices are zero-based
indices in preparation/inputs/input_identity.json, not PDB serial numbers.
Automatic isolated ions are restricted to Na+, K+, and Cl- and require explicit
isolated_ion_residue_indices. These declarations must not be used to stand in
for a coordinated metal-site model.
Coordinated metals, metall cofactors, and other supported supplied components
use all three fields: amber_topology_path, amber_coordinates_path, and
amber_mapping_path, alongside the complete input complex. The topology must
be nonperiodic, contain all bonded/nonbonded parameters and positive GB
radii/screening factors, and have finite charges and positive masses. Unsupported
force forms and virtual sites fail explicitly. The supplied radii and charges
are used without substituting automatic values. Every non-isolated metal needs
a metal_sites entry containing atom_index, oxidation_state,
coordinating_atom_indices, and a descriptive parameter_source.
The prepared Amber mapping is JSON:
{
"schema_version": 1,
"input_sha256": "SHA256 of the complex PDB",
"topology_sha256": "SHA256 of the Amber topology",
"coordinates_sha256": "SHA256 of the Amber coordinates",
"input_atom_indices": [0, 1, 2, null],
"input_residue_indices": [0]
}
The arrays follow Amber topology order and must cover every input atom/residue
exactly once. null is allowed only for an added hydrogen. Array lengths must
match the actual topology; the short arrays above illustrate the schema only.
Parameter sources and oxidation states are declarations, not an automatic
validation of a metal model's physical accuracy.
Minimize regions and modes¶
Pocket residues have any heavy atom within pocket_radius_angstrom: 5.0 of any
ligand heavy atom. The shell contains other protein residues with any heavy atom
within shell_radius_angstrom: 5.0 of any pocket heavy atom. Both selections
expand to whole residues and stay fixed during minimization. An explicit
pocket_residues: ["A:42", "A:43:B"] replaces automatic pocket selection;
_ denotes a blank chain. Unknown, duplicate, ambiguous, or empty selections fail.
mode |
Pocket side chains | Pocket backbone | Shell |
|---|---|---|---|
ligand_only (default) |
Fixed | Fixed | Fixed |
sidechains_free_backbone_fixed |
Free | Fixed | Fixed |
sidechains_free_backbone_restrained |
Free | Restrained | Fixed |
pocket_restrained |
Free | Restrained | Strongly restrained |
pocket_free_shell_restrained |
Free | Free | Strongly restrained |
The target ligand is free and distant protein is fixed in every mode. Backbone
atoms are N/CA/C/O/OXT; terminal caps count as backbone. Hydrogens inherit their
bonded parent's region. Waters, ions, metals, and cofactors are fixed by default;
component_mobility permits explicit whole-residue fixed, free, or
restrained overrides keyed by input residue index.
Positional restraints use E = ½ k |r − r₀|², with k in kJ mol⁻¹ nm⁻².
Named strengths are weak: 100, moderate: 1000, and very_strong: 10000;
positive numeric strengths are accepted. backbone_restraint_strength defaults
to moderate, and shell_restraint_strength to very_strong. They are starting
regimes, not universally validated settings. Only heavy atoms are restrained
unless restrain_hydrogens: true. Fixed regions include their hydrogens.
Fixed particles have zero mass in a private minimization-system copy. The
exported physical system retains original masses. Bonds and water geometry are
flexible by default. constraints: mobile_hbonds constrains bonds involving
hydrogen only when both endpoints are mobile, using their force-field equilibrium
lengths and retaining the physical bonded terms. Constraints involving fixed
atoms are rejected. Disulfide connectivity is preserved across region boundaries.
Minimize convergence, diagnostics, and quality¶
Defaults are tolerance_kj_mol_nm: 10.0, max_iterations: 1000,
constraint_tolerance: 0.00001, platform: CPU, and cpu_threads: 1.
GPU platforms accept precision: single|mixed|double where supported. Explicitly
requested unavailable platforms or precision settings fail instead of falling back.
The iteration budget spans OpenMM constraint restarts. The trace distinguishes
the physical-plus-positional objective from OpenMM's temporary bond-constraint
penalty. Final convergence is independently checked using RMS force components
over mobile atoms, projected onto the allowed constraint tangent space when
needed, plus maximum relative constraint error. A normal OpenMM return alone
does not establish convergence. Status is verified_converged, iteration_limit,
not_verified, or failed; the record also reports a reached budget even if the
last iterate independently satisfies the convergence tests.
The implementation targets the OpenMM 8.2 minimizer and reporter behavior.
Diagnostics include initial/final molecular-mechanics, GB, physical, positional, and total objective energies; physical/total RMS and maximum forces for all, mobile, and fixed atoms; iteration counts; constraint errors; heavy-atom ligand, pocket, backbone, shell, receptor, and component RMSDs without alignment; and maximum displacements with atom indices. Preparation displacement is separate. Ordinary nonbonded clashes, 1–4 overlaps, and declared metal coordination are reported separately. Bonded and angle-connected contacts are excluded.
Default review triggers under quality_thresholds are:
| Field | Default |
|---|---|
ligand_rmsd_angstrom |
1.0 |
ligand_max_displacement_angstrom |
2.0 |
pocket_backbone_rmsd_angstrom |
0.5 |
receptor_max_displacement_angstrom |
2.0 |
absolute_energy_per_atom_kj_mol |
10000 |
maximum_force_kj_mol_nm |
100000 |
allowed_vdw_overlap_angstrom |
0.6 |
coordination_distance_change_angstrom |
0.3 |
objective_increase_absolute_kj_mol |
0.01 |
objective_increase_relative |
1e-8 |
These are review triggers, not universal validity bounds; geometric overlaps,
especially 1–4 contacts, need chemical interpretation. Nonfinite states, invalid
chemistry, or any fixed-atom movement always fail. Other findings produce
machine-readable quality_flags. Set strict_quality: true to fail on every
flag, or combine strict_quality: true with strict_quality_flags to fail only on selected codes such as
convergence_unverified, large_ligand_displacement, or
residual_nonbonded_clashes. Finite strict-failure poses remain available.
Minimize artifacts and pipelines¶
Canonical results are saved in results/result_record.json. Scientific artifacts
are under results/minimize/: structures/ contains minimized PDB/SDF files and
fresh handoff records; preparation/ contains the physical system, initial
coordinates, atom mapping, and provenance; minimization/ contains diagnostics,
full-precision final coordinates, and the iteration trace. Exact region membership
is in results/minimize/regions.json.
Pipeline input names differ from config fields:
| Input port | Config field |
|---|---|
complex |
complex_pdb_path |
protein |
protein_pdb_path |
ligand |
ligand_path |
ligand_atom_map |
ligand_atom_map_path |
protein_states, protein_links |
protein_states_path, protein_links_path |
amber_topology, amber_coordinates, amber_mapping |
amber_topology_path, amber_coordinates_path, amber_mapping_path |
Supply exactly one structure input and a ligand chemistry file. The paired protein-state/link inputs and the three prepared-Amber inputs must be complete when provided.
| Output port | Result.paths key |
|---|---|
complex |
minimized_complex |
protein |
minimized_protein |
ligand |
minimized_ligand |
result_record |
result_record |
diagnostics |
diagnostics |
All other output ports below use the same name as their result-path key. Output ports expose
complex, protein, ligand, diagnostics, regions, atom_mapping,
output_mapping, preparation_summary, physical_system, prepared_positions,
minimized_positions, amber_topology, amber_coordinates, structure_manifest,
protein_states, protein_links, receptor_protein_states,
receptor_protein_links, optional iteration_trace, result_record, and output_dir.
The complex handoff records match the complex checksum; receptor_* records
match the protein output. A covalently connected environment component is retained
in the receptor export to avoid cutting chemical bonds, and this is recorded.
The ligand SDF preserves chemistry, title, and user properties. PDB coordinates are rounded to 0.001 Å and SDF coordinates to 0.0001 Å; NumPy coordinate artifacts retain full precision in nanometers, in physical-system particle order. The Amber coordinate artifact is the prepared starting configuration. Use the separate minimized coordinate artifact for the final pose. The exported physical system contains no zero-mass freezes or positional restraints and still needs an appropriate MD protocol before dynamics.
Download example configurations for ligand only, free side chains/fixed backbone, free side chains/restrained backbone, restrained pocket, free pocket/restrained shell, component overrides, and supplied metal parameters. See the minimize pipeline examples for complete YAML and artifact flow. The pipeline uses existing scatter/collect/filter stages and preserves per-pose provenance. Filtering quality flags does not establish pose accuracy; do not rank unrelated ligands by total minimization energy.
Validation in the caddforge-md checkout: python -m pytest tests/test_minimize_*.py. Scientific fixtures use
generated peptides/ligands and are marked external; optional GPU comparison
tests skip with an explicit reason when no usable GPU platform exists.
openbpmd¶
Runs OpenBPMD preprocessing, simulations, and averaged multi-replica analysis.
CLI:
YAML:
backend: local
complex_pdb_path: data/complex.pdb
lig_resname: MOL
ligand_net_charge: 0
nreps: 10
hill_height: 0.05
deposit_interval_ps: 1.0
log_sync_interval_seconds: 30
output_dir_name: output
analysis_dir_name: analysis
Python:
from caddforge.core.execution import load_result_for_job, submit_workflow
from caddforge.plugins import get_tool
spec = get_tool("md", "openbpmd")
cfg = spec.config_cls(
complex_pdb_path="data/complex.pdb",
lig_resname="MOL",
nreps=10,
hill_height=0.05,
deposit_interval_ps=1.0,
backend="local",
)
workflow = spec.workflow_cls(cfg)
job = submit_workflow(
workflow, cfg, backend_name=cfg.backend,
input_ports=tuple(getattr(spec.plugin, "input_ports", ())),
)
result = load_result_for_job(job)
if result is not None:
print(result.paths)
Outputs to inspect: prep/, results/output/, results/analysis/results.csv,
results/analysis/multi_rep_timeseries.csv, results/progress.json,
results/result_record.json.
OpenBPMD streams subprocess output to scratch-first logs and publishes best-effort replica progress
from the per-replica simulation logs. log_sync_interval_seconds controls publication frequency;
temporary shared-filesystem failures are retried without changing the scientific outcome.
grand¶
Runs Grand Canonical Monte Carlo (GCMC) water sampling in a ligand-centred pocket region and then selects a single representative protein-ligand-water structure from the resulting trajectory, all within one job. The tool combines two stages so you obtain a usable representative hydration state without a separate manual step.
GCMC sampling stage. The first stage prepares the input protein-ligand complex (adds hydrogens
at the configured pH, optionally solvates with padding and ionic strength, restrains solute heavy
atoms, and adds ghost waters) and then performs GCMC insertion and deletion sampling inside a
ligand-centred spherical region defined by sphere_radius_angstrom. It uses the configured
initialization passes and maximum production passes with a running water-count convergence check.
The stage writes the final-state checkpoint before any trajectory readback, then produces a
trajectory, a per-frame ghost-water state file (one row per frame listing the inactive ghost-water
residue indices for that frame), and a hydrated-complex structure. Post-processing is non-strict: if
it fails, the error is recorded in the stage summary while the completed checkpoint and trajectory
are preserved so the run can continue.
Representative-selection stage. The second stage runs on the produced trajectory and uses the
per-frame ghost-water state file to exclude, for each analysed frame, that frame's ghost waters from
all downstream selection and output. It deterministically selects exactly one representative frame
from within the configured frame analysis interval (repeated runs on the same trajectory and
configuration select the same frame) and writes a representative complex structure containing the
ligand residue, the protein atoms, and the active non-ghost water residues present at the selected
frame. When a ligand template SDF and ligand_resname are configured, it also writes a
template-corrected ligand SDF whose bond orders and chemistry come from the template and whose
coordinates come from the selected frame, plus an optional protein-only structure. Template ligand
export is non-strict: on failure the error is recorded in the selection summary and the
representative complex is still written.
CLI:
caddforge run md grand --config grand.yaml
caddforge run md grand --config grand.yaml --backend slurm --slurm-time 24:00:00
YAML:
backend: local
complex_pdb_path: data/complex.pdb
ligand_template_sdf: data/ligand_template.sdf
ligand_resname: LIG
sphere_radius_angstrom: 10.0
n_ghost_waters: 150
nonbonded_cutoff_nm: 0.9
temperature_kelvin: 300.0
initialization_passes: 7500
max_production_passes: 100000
ph: 7.4
padding_nm: 1.2
ionic_strength_molar: 0.15
ligand_selection: null
align_selection: null
frame_analysis_interval: 1
log_sync_interval_seconds: 30
output_dir_name: output
trajectory_filename: gcmc_trajectory.dcd
ghost_water_filename: ghost_water_state.txt
checkpoint_filename: gcmc_checkpoint.chk
hydrated_complex_filename: hydrated_complex.pdb
gcmc_summary_filename: gcmc_summary.json
representative_complex_filename: representative_complex.pdb
representative_protein_filename: representative_protein.pdb
representative_ligand_filename: representative_ligand.sdf
selection_summary_filename: selection_summary.json
Python:
from caddforge.core.execution import load_result_for_job, submit_workflow
from caddforge.plugins import get_tool
spec = get_tool("md", "grand")
cfg = spec.config_cls(
complex_pdb_path="data/complex.pdb",
ligand_template_sdf="data/ligand_template.sdf",
ligand_resname="LIG",
sphere_radius_angstrom=10.0,
frame_analysis_interval=1,
backend="local",
)
workflow = spec.workflow_cls(cfg)
job = submit_workflow(
workflow, cfg, backend_name=cfg.backend,
input_ports=tuple(getattr(spec.plugin, "input_ports", ())),
)
result = load_result_for_job(job)
if result is not None:
print(result.metrics)
Configuration options¶
GCMC sampling:
complex_pdb_path: input protein-ligand complex PDB (required, must exist,.pdb).ligand_template_sdf: ligand template SDF providing bond orders and chemistry for ligand export (required, must exist,.sdf/.sd).ligand_resname: residue name identifying the ligand in the complex (required, 1-4 characters, no whitespace or path separators). Used by both stages.sphere_radius_angstrom: radius of the ligand-centred sampling region (default10.0,> 0).n_ghost_waters: number of ghost waters added for insertion/deletion (default150,>= 1).nonbonded_cutoff_nm: nonbonded interaction cutoff in nanometres (default0.9,> 0).temperature_kelvin: simulation temperature in kelvin (default300.0,> 0).initialization_passes: equilibration passes before production sampling (default7500,>= 1).max_production_passes: maximum production passes (default100000,>= 1).ph: pH used when adding hydrogens during preparation (default7.4).padding_nm: solvent box padding in nanometres (default1.2,> 0).ionic_strength_molar: ionic strength in molar used when solvating (default0.15,>= 0).
Representative selection:
ligand_selection: atom selection for the ligand. Derived fromligand_resname(resname <ligand_resname>) when left unset.align_selection: optional atom selection used to align frames; blank is normalized to none.frame_analysis_interval: analyse every Nth trajectory frame (default1, positive integer).
Log syncing and output:
log_sync_interval_seconds: interval between periodic log-sync copies (default30,1-3600inclusive).output_dir_name: directory underresults/that holds stage outputs (defaultoutput, no path separators).- Output filename fields (
trajectory_filename,ghost_water_filename,checkpoint_filename,hydrated_complex_filename,gcmc_summary_filename,representative_complex_filename,representative_protein_filename,representative_ligand_filename,selection_summary_filename): plain filenames only, each with a documented default.
All input file paths are validated and normalized to absolute paths at configuration time, so invalid inputs are reported before any compute job is submitted and inputs resolve correctly when the run executes from a scratch directory.
Slurm scratch execution¶
On the Slurm backend under an allocated runtime, the workflow executes both stages inside a
node-local scratch working directory (rooted at SLURM_TMPDIR) rather than on shared storage, so
intermediate I/O does not stress the shared filesystem. When both stages finish successfully, the
complete produced output directory is copied back from scratch to the job directory results output
directory, preserving its relative structure, and a completeness check verifies every produced file
was reproduced. If a stage fails after producing some outputs, those partial outputs are copied back
on a best-effort basis and the result records whether the partial copy succeeded. The scratch
working directory is removed after the copy-back attempt. When not running under an allocated
runtime (for example on the local backend), both stages execute directly in the job directory
results output directory and no copy-back is needed.
Log syncing¶
During long runs under an allocated runtime, the stdout and stderr logs are written in the scratch
working directory and periodically copied back to the job directory once per
log_sync_interval_seconds, so you can monitor progress while the job is still running. Log-sync
copying is resilient: a temporary copy failure is logged as a warning and never crashes the job or
changes its status, and the next copy is attempted at the following interval. When a stage finishes,
whether it succeeds or fails, a final copy is performed so the job directory logs reflect the final
state of the run.
Outputs¶
Stage outputs are written under results/<output_dir_name>/:
gcmc_trajectory.dcd: the GCMC sampling trajectory.ghost_water_state.txt: per-frame ghost-water state file (one row per trajectory frame).gcmc_checkpoint.chk: final-state checkpoint written before trajectory readback.hydrated_complex.pdb: the hydrated-complex structure.gcmc_summary.json: GCMC stage summary (produced paths, frame count, success/failure).representative_complex.pdb: the selected representative protein-ligand-water complex.representative_protein.pdb: optional protein-only representative structure.representative_ligand.sdf: optional template-corrected representative ligand.selection_summary.json: selection stage summary (selected frame index, success/failure).results/result_record.jsonandresults/results.json: the standardized result and a human-readable summary.results/progress.json: best-effort stage, pass-count, acceptance-rate, water-count, and ETA snapshot.
The standardized result exposes representative_complex, representative_protein,
representative_ligand, trajectory, checkpoint, and output_dir as pipeline-ready result
paths, and its metrics include n_frames_sampled and representative_frame_index.
Compressed inputs¶
Declared MD structure, molecule, topology, trajectory, checkpoint, and restart inputs may use a
trailing .gz. CADDForge expands transparent wrappers at runtime while preserving the original
paths. Plain inputs retain their existing behavior.
inducia¶
Inducia is a composite induced-fit docking and pose-stability workflow built from PheSA, GNINA, GRAND, and OpenBPMD. One job accepts one three-dimensional query ligand; use pipeline scatter for a multi-record query library.
The modes are:
template: run PheSA at the fixed pharmacophore-point weights 0.00 through 1.00 in steps of 0.05, cluster the resulting poses by direct heavy-atom RMSD in the binding coordinate frame, identify clashing protein residues, and minimize each representative with those residues flexible in GNINA.independent: run full flexible GNINA docking in an explicit box or autobox. The flexible-residue radius and maximum residue count are configurable.
Every surviving GNINA pose is hydrated by GRAND. Hydrated poses with finite GNINA CNNscores are
ordered from highest to lowest, and openbpmd.max_input_complexes limits how many continue to
OpenBPMD. Final structures are ranked by ascending OpenBPMD CompScore.
CLI:
Template-mode configuration shape:
mode: template
backend: local
receptor_pdb_path: data/receptor.pdb
query_ligand_sdf_path: data/query.sdf
template_ligand_sdf_path: data/known_binder.sdf
ligand_resname: LIG
max_local_workers: 1
phesa:
max_confs: 200
clustering:
rmsd_cutoff_angstrom: 1.0
clash_detection:
allowed_vdw_overlap_angstrom: 0.4
gnina:
cnn_scoring: rescore
pose_sort_order: CNNscore
grand:
sphere_radius_angstrom: 10.0
n_ghost_waters: 150
initialization_passes: 7500
max_production_passes: 100000
openbpmd:
max_input_complexes: null
nreps: 10
hill_height: 0.05
deposit_interval_ps: 1.0
progress:
interval_seconds: 30
Independent mode replaces template_ligand_sdf_path with either autobox_ligand_path or both
box_center and box_size. Its GNINA settings include
independent_flex_radius_angstrom (default 3.5) and
independent_max_flexible_residues (default 12).
Child phases use at most max_local_workers locally. With Slurm, all siblings in a phase are
submitted before monitoring begins. Parent stdout records phase lifecycle, child IDs and statuses,
heartbeats, concise child progress, completion, and failure information. Follow it with
caddforge logs <job-id> --follow; detailed command logs remain in the child directories.
Structured events are written to results/inducia_progress.jsonl. Temporary registry or shared
filesystem failures are treated as monitoring outages and retried. Allocated-runtime publication
copies file content without filesystem metadata for CIFS compatibility and verifies it by SHA-256.
Primary outputs are best_complex, best_protein, best_ligand, ranking_table,
pose_manifest, ranked complex/protein/ligand path collections, and output_dir. The complete pose
manifest retains PheSA scores, cluster membership, flexible residues, all child jobs and statuses,
CNNscore shortlist rank, GRAND/OpenBPMD artifacts, CompScore, and failure provenance.
Inducia requires caddforge-core>=0.21.0, caddforge-descriptors>=0.8.0, and
caddforge-docking>=0.3.0, plus the external scientific runtimes required by PheSA, GNINA, GRAND,
OpenMM, AmberTools, and OpenBPMD.