caddforge-docs 0.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- caddforge_docs/__init__.py +1 -0
- caddforge_docs/cli.py +20 -0
- caddforge_docs/server.py +124 -0
- caddforge_docs/site/404.html +2118 -0
- caddforge_docs/site/assets/_mkdocstrings.css +237 -0
- caddforge_docs/site/assets/images/favicon.png +0 -0
- caddforge_docs/site/assets/javascripts/bundle.d7400e89.min.js +16 -0
- caddforge_docs/site/assets/javascripts/bundle.d7400e89.min.js.map +7 -0
- caddforge_docs/site/assets/javascripts/workers/search.2c215733.min.js +42 -0
- caddforge_docs/site/assets/javascripts/workers/search.2c215733.min.js.map +7 -0
- caddforge_docs/site/assets/licenses/W3C-Software-and-Document-2015.txt +15 -0
- caddforge_docs/site/assets/licenses/autogrid-COPYING-GPL-2.txt +339 -0
- caddforge_docs/site/assets/licenses/autogrid-GCC-RUNTIME-COPYING-GPL-3.txt +674 -0
- caddforge_docs/site/assets/licenses/autogrid-GCC-RUNTIME-EXCEPTION.txt +73 -0
- caddforge_docs/site/assets/licenses/autogrid-GCC-RUNTIME-NOTICE.txt +24 -0
- caddforge_docs/site/assets/licenses/caddforge-opencosmors-COPYING.txt +674 -0
- caddforge_docs/site/assets/licenses/caddforge-opencosmors-LICENSE.txt +165 -0
- caddforge_docs/site/assets/licenses/caddforge-unidock-tools-LICENSE.txt +202 -0
- caddforge_docs/site/assets/licenses/clipboard-LICENSE.txt +19 -0
- caddforge_docs/site/assets/licenses/escape-html-LICENSE.txt +2 -0
- caddforge_docs/site/assets/licenses/focus-visible-LICENSE.txt +4 -0
- caddforge_docs/site/assets/licenses/fontawesome-LICENSE.txt +165 -0
- caddforge_docs/site/assets/licenses/fuzzaldrin-plus-LICENSE.txt +21 -0
- caddforge_docs/site/assets/licenses/lunr-LICENSE.txt +17 -0
- caddforge_docs/site/assets/licenses/material-LICENSE.txt +20 -0
- caddforge_docs/site/assets/licenses/material-design-color-LICENSE.txt +31 -0
- caddforge_docs/site/assets/licenses/material-shadows-LICENSE.txt +31 -0
- caddforge_docs/site/assets/licenses/mkdocs-material-LICENSE.txt +19 -0
- caddforge_docs/site/assets/licenses/mkdocs-material-extensions-LICENSE.txt +21 -0
- caddforge_docs/site/assets/licenses/mkdocs-section-index-LICENSE.txt +21 -0
- caddforge_docs/site/assets/licenses/mkdocstrings-LICENSE.txt +15 -0
- caddforge_docs/site/assets/licenses/mkdocstrings-python-LICENSE.txt +15 -0
- caddforge_docs/site/assets/licenses/octicons-LICENSE.txt +21 -0
- caddforge_docs/site/assets/licenses/preact-LICENSE.txt +19 -0
- caddforge_docs/site/assets/licenses/properdocs-LICENSE.txt +26 -0
- caddforge_docs/site/assets/licenses/pymdown-extensions-LICENSE.txt +113 -0
- caddforge_docs/site/assets/licenses/rxjs-LICENSE.txt +180 -0
- caddforge_docs/site/assets/licenses/simple-LICENSE.txt +30 -0
- caddforge_docs/site/assets/licenses/tiny-emitter-LICENSE.txt +19 -0
- caddforge_docs/site/assets/licenses/tslib-LICENSE.txt +12 -0
- caddforge_docs/site/assets/licenses/zeno-rocha-components-LICENSE.txt +22 -0
- caddforge_docs/site/assets/stylesheets/main.ec1eaa64.min.css +1 -0
- caddforge_docs/site/assets/stylesheets/main.ec1eaa64.min.css.map +1 -0
- caddforge_docs/site/assets/stylesheets/palette.ab4e12ef.min.css +1 -0
- caddforge_docs/site/assets/stylesheets/palette.ab4e12ef.min.css.map +1 -0
- caddforge_docs/site/core/cli/index.html +2446 -0
- caddforge_docs/site/core/concepts/index.html +2340 -0
- caddforge_docs/site/core/configuration/index.html +2300 -0
- caddforge_docs/site/core/index.html +2170 -0
- caddforge_docs/site/core/jobs-results/index.html +2315 -0
- caddforge_docs/site/core/plugins/index.html +2200 -0
- caddforge_docs/site/core/slurm/index.html +2562 -0
- caddforge_docs/site/development/ci-publishing/index.html +2345 -0
- caddforge_docs/site/development/contributing/index.html +2311 -0
- caddforge_docs/site/development/documentation/index.html +2380 -0
- caddforge_docs/site/development/index.html +2201 -0
- caddforge_docs/site/development/release-notes/index.html +2504 -0
- caddforge_docs/site/development/release-readiness/index.html +2333 -0
- caddforge_docs/site/development/security/index.html +2181 -0
- caddforge_docs/site/development/tool-authoring/index.html +2435 -0
- caddforge_docs/site/examples/index.html +2465 -0
- caddforge_docs/site/examples/manifest.json +158 -0
- caddforge_docs/site/examples/minimize/component_overrides.yaml +14 -0
- caddforge_docs/site/examples/minimize/ligand_only.yaml +13 -0
- caddforge_docs/site/examples/minimize/pipeline_docking.yaml +84 -0
- caddforge_docs/site/examples/minimize/pipeline_scatter.yaml +49 -0
- caddforge_docs/site/examples/minimize/pocket_free_shell_restrained.yaml +13 -0
- caddforge_docs/site/examples/minimize/pocket_restrained.yaml +13 -0
- caddforge_docs/site/examples/minimize/prepared_metal.yaml +14 -0
- caddforge_docs/site/examples/minimize/sidechains_free_backbone_fixed.yaml +13 -0
- caddforge_docs/site/examples/minimize/sidechains_free_backbone_restrained.yaml +13 -0
- caddforge_docs/site/examples/pipelines/descriptor-model-and-candidate-filter.yaml +44 -0
- caddforge_docs/site/examples/pipelines/dock-extract-and-minimize-poses.yaml +85 -0
- caddforge_docs/site/examples/pipelines/docking-screen-and-rescore.yaml +55 -0
- caddforge_docs/site/examples/pipelines/flexible-gnina-to-openbpmd-to-md-to-abfe.yaml +189 -0
- caddforge_docs/site/examples/pipelines/gnina-flexible-complex-artifacts.yaml +52 -0
- caddforge_docs/site/examples/pipelines/minimize-a-multi-pose-sdf.yaml +46 -0
- caddforge_docs/site/examples/pipelines/pose-level-md-selection.yaml +95 -0
- caddforge_docs/site/examples/pipelines/result-collection-and-diagnostics.yaml +55 -0
- caddforge_docs/site/examples/quickstart/diagnostics.yaml +8 -0
- caddforge_docs/site/examples/quickstart/index.html +2172 -0
- caddforge_docs/site/examples/quickstart/pipeline.yaml +23 -0
- caddforge_docs/site/examples/quickstart/run.py +18 -0
- caddforge_docs/site/examples/quickstart/run_pipeline.py +6 -0
- caddforge_docs/site/examples/quickstart/scores.csv +7 -0
- caddforge_docs/site/examples/quickstart/values.csv +31 -0
- caddforge_docs/site/examples/run_tool.py +40 -0
- caddforge_docs/site/examples/tools/datascience-csv-scatter.yaml +17 -0
- caddforge_docs/site/examples/tools/datascience-distribution-diagnostics.yaml +10 -0
- caddforge_docs/site/examples/tools/descriptors-ani-conformers.yaml +9 -0
- caddforge_docs/site/examples/tools/descriptors-ani-psa.yaml +9 -0
- caddforge_docs/site/examples/tools/descriptors-cluster-molecules.yaml +11 -0
- caddforge_docs/site/examples/tools/descriptors-phesa.yaml +7 -0
- caddforge_docs/site/examples/tools/docking-gnina.yaml +12 -0
- caddforge_docs/site/examples/tools/docking-idockya.yaml +8 -0
- caddforge_docs/site/examples/tools/docking-unidock.yaml +12 -0
- caddforge_docs/site/examples/tools/fep-openfe-abfe.yaml +53 -0
- caddforge_docs/site/examples/tools/fep-openfe-rbfe-septop.yaml +23 -0
- caddforge_docs/site/examples/tools/md-center-and-align.yaml +16 -0
- caddforge_docs/site/examples/tools/md-cluster-representative.yaml +16 -0
- caddforge_docs/site/examples/tools/md-extract-frames.yaml +17 -0
- caddforge_docs/site/examples/tools/md-fix-structure.yaml +20 -0
- caddforge_docs/site/examples/tools/md-grand.yaml +28 -0
- caddforge_docs/site/examples/tools/md-inducia.yaml +29 -0
- caddforge_docs/site/examples/tools/md-minimize.yaml +13 -0
- caddforge_docs/site/examples/tools/md-openbpmd.yaml +11 -0
- caddforge_docs/site/examples/tools/md-plain-md.yaml +28 -0
- caddforge_docs/site/examples/tools/ml-explore-classifier.yaml +5 -0
- caddforge_docs/site/examples/tools/ml-model-factory.yaml +32 -0
- caddforge_docs/site/examples/tools/ml-model-zoo.yaml +16 -0
- caddforge_docs/site/examples/tools/ml-pca.yaml +10 -0
- caddforge_docs/site/examples/tools/ml-simple-gp.yaml +17 -0
- caddforge_docs/site/examples/tools/qm-transfer-free-energy.yaml +11 -0
- caddforge_docs/site/explanation/references/index.html +2218 -0
- caddforge_docs/site/explanation/reproducibility/index.html +2306 -0
- caddforge_docs/site/explanation/third-party-notices/index.html +2599 -0
- caddforge_docs/site/getting-started/first-pipeline/index.html +2195 -0
- caddforge_docs/site/getting-started/first-workflow/index.html +2329 -0
- caddforge_docs/site/getting-started/index.html +2200 -0
- caddforge_docs/site/getting-started/installation/index.html +2541 -0
- caddforge_docs/site/gui/index.html +2267 -0
- caddforge_docs/site/gui/plugin-authoring/index.html +2280 -0
- caddforge_docs/site/gui/walkthrough/index.html +2181 -0
- caddforge_docs/site/how-to/jobs/index.html +2195 -0
- caddforge_docs/site/how-to/scatter/index.html +2193 -0
- caddforge_docs/site/how-to/troubleshooting/index.html +2429 -0
- caddforge_docs/site/index.html +2359 -0
- caddforge_docs/site/objects.inv +0 -0
- caddforge_docs/site/pipelines/artifacts/index.html +2314 -0
- caddforge_docs/site/pipelines/built-ins/index.html +2670 -0
- caddforge_docs/site/pipelines/examples/index.html +3186 -0
- caddforge_docs/site/pipelines/index.html +2164 -0
- caddforge_docs/site/pipelines/specification/index.html +2278 -0
- caddforge_docs/site/plugins/datascience/index.html +2405 -0
- caddforge_docs/site/plugins/descriptors/index.html +2609 -0
- caddforge_docs/site/plugins/docking/index.html +2480 -0
- caddforge_docs/site/plugins/fep/index.html +2508 -0
- caddforge_docs/site/plugins/index.html +2216 -0
- caddforge_docs/site/plugins/md/index.html +3795 -0
- caddforge_docs/site/plugins/ml/index.html +2815 -0
- caddforge_docs/site/plugins/qm/index.html +2309 -0
- caddforge_docs/site/plugins/version-matrix/index.html +2449 -0
- caddforge_docs/site/reference/api/index.html +4414 -0
- caddforge_docs/site/reference/index.html +2166 -0
- caddforge_docs/site/search/search_index.json +1 -0
- caddforge_docs/site/sitemap.xml +191 -0
- caddforge_docs/site/sitemap.xml.gz +0 -0
- caddforge_docs-0.3.0.dist-info/METADATA +158 -0
- caddforge_docs-0.3.0.dist-info/RECORD +152 -0
- caddforge_docs-0.3.0.dist-info/WHEEL +4 -0
- caddforge_docs-0.3.0.dist-info/entry_points.txt +2 -0
- caddforge_docs-0.3.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"],"fields":{"title":{"boost":1000.0},"text":{"boost":1.0},"tags":{"boost":1000000.0}}},"docs":[{"location":"","title":"CADDForge","text":"<p>CADDForge is the central CADD toolbox framework for running computational chemistry and modelling workflows through a consistent Python API, CLI, plugin system, job model, and pipeline layer.</p> <p>The project is split into a lightweight core package and tool-specific plugin packages:</p> Package Purpose <code>caddforge</code> Metadata-only installer for the complete public toolbox. <code>caddforge-core</code> Core abstractions, CLI, job execution, backends, pipeline engine, plugin registry. <code>caddforge-docking</code> Docking and rescoring workflows. <code>caddforge-descriptors</code> Molecular descriptor and property workflows. <code>caddforge-md</code> Molecular dynamics setup and execution workflows. <code>caddforge-fep</code> Absolute and relative free-energy workflows. <code>caddforge-qm</code> COSMO-RS transfer-free-energy workflow. <code>caddforge-ml</code> Machine-learning workflows and model utilities. <code>caddforge-datascience</code> Generic data-science utilities exposed as CADDForge tools. <code>caddforge-gui</code> Web GUI and plugin GUI extension runtime. <code>caddforge-docs</code> Installed documentation and optional browser command."},{"location":"#design-goals","title":"Design goals","text":"<ul> <li>Standardize CADD workflow execution without putting tool-specific science into core.</li> <li>Keep workflows backend-agnostic through local and HPC backends.</li> <li>Store configs, logs, metadata, and results in predictable run directories.</li> <li>Let plugins declare their inputs, outputs, and pipeline contracts.</li> <li>Support large-scale file and path-collection scatter through the central pipeline executor.</li> </ul>"},{"location":"#choose-a-starting-point","title":"Choose a starting point","text":"<ul> <li>New users: install selected components, then run the local workflow tutorial.</li> <li>Pipeline users: follow the small pipeline tutorial before adapting scientific templates.</li> <li>Python users: consult the public API and example library.</li> <li>Contributors: read development guidance.</li> </ul> <p>The planned <code>caddforge</code> distribution installs the complete public bundle, including <code>caddforge-docs</code> and its offline viewer. Its updated public release is still pending; install published components individually in the meantime. <code>caddforge-core</code> owns Python imports and the CLI, and docs remain optional for selective installations. These pages describe the tagged public component set; check the version matrix and release prerequisites for availability.</p>"},{"location":"#quick-links","title":"Quick links","text":"<ul> <li>Installation</li> <li>First workflow</li> <li>CLI reference</li> <li>Pipeline documentation</li> <li>Plugin catalog</li> <li>Tool authoring</li> </ul>"},{"location":"core/","title":"Core","text":"<p>Public source tag</p> <p>This page describes <code>caddforge-core</code> <code>0.22.0</code>, tagged as <code>v0.22.0</code>. Published on PyPI; see the version matrix.</p> <p><code>caddforge-core</code> provides the shared runtime that all tool plugins build on:</p> <ul> <li>base abstractions for data, workflows, jobs, backends, and results</li> <li>Pydantic-based configuration loading and validation</li> <li>CLI entry point</li> <li>plugin discovery</li> <li>local and Slurm execution backends</li> <li>standardized run directories, logs, metadata, and result loading</li> <li>pipeline specification and execution</li> </ul> <p>The core package must stay independent of specific scientific tools. Tool-specific behavior belongs in plugin packages.</p> <p>For HPC usage, see the Slurm backend guide.</p>"},{"location":"core/cli/","title":"CLI","text":"<p>The <code>caddforge</code> CLI is the main user interface for running workflows and inspecting jobs.</p>"},{"location":"core/cli/#open-documentation","title":"Open documentation","text":"<p>Install <code>caddforge-docs</code> to enable the optional documentation command. Version <code>0.3.0</code> is tagged and awaiting package-index publication; install the source checkout with <code>python -m pip install ../caddforge-docs</code> for now. Once that release is available:</p> <pre><code>python -m pip install caddforge-docs\ncaddforge docs\n</code></pre> <p>The planned public bundle includes this package automatically. Core-only and selective plugin installations add it separately. See the version matrix for current availability.</p> <p>The command serves the documentation bundled with the installed docs release, opens the default browser, and runs until Ctrl+C. No source checkout, documentation build tools, or internet connection are needed. It defaults to <code>127.0.0.1</code> with an automatically assigned port:</p> <pre><code>caddforge docs --host 0.0.0.0 --port 8001\ncaddforge docs --host localhost --no-browser\ncaddforge docs --host ::1\n</code></pre> <p><code>--host</code> accepts hostnames, IPv4, and IPv6 addresses. Wildcard bindings open a loopback browser URL; remote clients should use the server's reachable address and printed port. <code>--no-browser</code> suppresses automatic opening. If no browser can be launched, the server stays available at the printed URL. The command is absent when the docs package is not installed.</p>"},{"location":"core/cli/#discover-tools","title":"Discover tools","text":"<pre><code>caddforge list-tools\n</code></pre>"},{"location":"core/cli/#show-a-config-template","title":"Show a config template","text":"<pre><code>caddforge show-config <section> <tool>\n</code></pre> <p>Example:</p> <pre><code>caddforge show-config docking unidock\n</code></pre>"},{"location":"core/cli/#run-a-workflow","title":"Run a workflow","text":"<pre><code>caddforge run <section> <tool> --config config.yaml\n</code></pre> <p>Examples:</p> <pre><code>caddforge run docking unidock --config unidock.yaml\ncaddforge run md openbpmd --config openbpmd.yaml\ncaddforge run fep openfe-abfe --config abfe.yaml\n</code></pre>"},{"location":"core/cli/#pack-a-slurm-environment","title":"Pack a Slurm environment","text":"<p>Create a relocatable Conda environment archive and its managed SHA-256 sidecar:</p> <pre><code># Pack the active environment.\ncaddforge pack-env --output /shared/envs/caddforge-dev.tar.gz\n\n# Pack a named environment.\ncaddforge pack-env --name caddforge --output /shared/envs/caddforge.tar.gz\n\n# Pack an explicit environment prefix.\ncaddforge pack-env --prefix /scratch/$USER/envs/caddforge \\\n --output /shared/envs/caddforge.tar.gz\n</code></pre> <p><code>--name</code> and <code>--prefix</code> are mutually exclusive. The output must end in <code>.tar.gz</code>. Existing archive or sidecar files are protected unless <code>--force</code> is supplied:</p> <pre><code>caddforge pack-env --output /shared/envs/caddforge-dev.tar.gz --force\n</code></pre> <p>For an intentionally modified Conda environment whose package metadata references missing files, <code>--ignore-missing-files</code> forwards the corresponding <code>conda-pack</code> option. The resulting archive may be incomplete, so use this only when those omissions are understood:</p> <pre><code>caddforge pack-env --output /shared/envs/caddforge-dev.tar.gz --ignore-missing-files\n</code></pre> <p>The command keeps the <code>conda-pack</code> percentage bar on one updating terminal line, streams normal messages, prints periodic elapsed-time and archive-size heartbeats during quiet periods, and reports hashing progress. On success it prints the archive path, sidecar path, digest, size, elapsed time, and the <code>slurm_env_pack_path</code> config entry.</p> <p>The generated sidecar is named <code><archive>.sha256</code> and uses standard <code>sha256sum</code> format. See the Slurm backend guide for cache and compatibility behavior.</p>"},{"location":"core/cli/#jobs","title":"Jobs","text":"<pre><code>caddforge status <job-id>\ncaddforge logs <job-id>\ncaddforge logs <job-id> --follow\ncaddforge export <job-id>\n</code></pre> <p><code>logs --follow</code> prints newly synchronized stdout until the job reaches a terminal state. Use <code>--poll-interval <seconds></code> to change the default one-second check interval. Follow mode tolerates temporary log-file and registry outages, handles scratch-synchronized snapshot replacement, and suppresses duplicate lines carrying the same <code>event=<sequence></code> identifier. Press Ctrl+C to stop following without cancelling the job.</p> <p>Workflow results are stored in the job run directory by default. Use <code>caddforge export <job-id></code> to copy <code>results/</code> back to the submission directory, or set <code>export_results: true</code> in the workflow config to do that automatically when the job finishes.</p>"},{"location":"core/cli/#pipelines","title":"Pipelines","text":"<pre><code>caddforge pipeline run --config pipeline.yaml\ncaddforge pipeline status <pipeline-id>\ncaddforge pipeline show-artifacts <pipeline-id>\n</code></pre>"},{"location":"core/concepts/","title":"Concepts","text":""},{"location":"core/concepts/#dataset-and-moleculeset","title":"Dataset and MoleculeSet","text":"<p><code>Dataset</code> represents a reproducible collection of data such as molecules and labels. <code>MoleculeSet</code> represents a collection of molecules used by workflows and models. Both are frozen dataclasses, but contained lists and molecular objects are not deeply immutable. <code>Dataset.from_molecules()</code> is implemented; <code>Dataset.from_file()</code> currently raises <code>NotImplementedError</code>. File input is handled by tool-specific configuration/workflows.</p> <p>See the public API reference for constructors and fields.</p> <p>Future data sources may include databases, but tool APIs should continue to accept stable core data objects rather than tool-specific containers.</p>"},{"location":"core/concepts/#workflow","title":"Workflow","text":"<p>A <code>Workflow</code> orchestrates one scientific protocol, such as docking, MD, FEP, QM, or property prediction. Workflows are backend-agnostic and delegate execution to backends through the core job infrastructure.</p>"},{"location":"core/concepts/#job","title":"Job","text":"<p>A <code>Job</code> represents one execution of a workflow. Jobs carry a unique ID, status, timestamps, backend metadata, and paths to the standardized run directory and logs.</p>"},{"location":"core/concepts/#backend","title":"Backend","text":"<p>A <code>Backend</code> submits, tracks, and cancels jobs. Core backends include local execution and Slurm. Workflows should not know Slurm-specific or local-process details.</p>"},{"location":"core/concepts/#result","title":"Result","text":"<p>A <code>Result</code> is the canonical outcome of a job. Results include the job ID, tool name, status, metrics, and paths to important output files. Tools may store richer tool-specific data under their result directory, but the canonical result should remain loadable through the core helpers.</p>"},{"location":"core/configuration/","title":"Configuration","text":"<p>CADDForge separates static configuration from runtime execution.</p> <p>Tool configs are validated Python objects, usually Pydantic models. They should be:</p> <ul> <li>constructible from Python code</li> <li>loadable from YAML or JSON</li> <li>explicit about required inputs and defaults</li> <li>responsible for validation, not execution</li> </ul> <p>Example config:</p> <pre><code>backend: local\nexport_results: false\nreceptor_path: protein.pdbqt\nligand_path: ligand.sdf\n</code></pre> <p><code>export_results</code> is a shared core option. It defaults to <code>false</code>, which keeps outputs under the standard run directory. Set it to <code>true</code> when a workflow should automatically copy its <code>results/</code> directory back to the submission working directory after completion.</p> <p>Use the CLI to inspect templates exposed by installed plugins:</p> <pre><code>caddforge show-config docking unidock\n</code></pre> <p>Workflows receive validated config objects and use them to prepare and run jobs.</p>"},{"location":"core/configuration/#transparent-compressed-inputs","title":"Transparent compressed inputs","text":"<p>At declared plugin boundaries, <code>.csv.gz</code>, <code>.tsv.gz</code>, and <code>.sdf.gz</code> inputs remain compressed on shared storage and are expanded only when the tool starts. Local jobs use job-scoped temporary storage, single-node Slurm jobs use node-local scratch, and multi-node jobs use a private shared staging directory. Saved configuration and results retain the original compressed source path, and runtime materializations are removed after the job.</p> <p>Only paths declared by a plugin input contract are staged. Formats that natively consume gzip, such as selected large-space inputs or tool-specific compressed formats, remain compressed when the plugin marks them as native. Plain inputs keep their existing behavior.</p> <p>Pipeline splitting can stream <code>.csv.gz</code>, <code>.tsv.gz</code>, and <code>.sdf.gz</code> sources into compressed shards; each shard job expands only its own input. A compressed output is finalized atomically from a plain tool output when the output port declares its configuration field.</p>"},{"location":"core/configuration/#shared-slurm-environment-options","title":"Shared Slurm environment options","text":"<p>Packed environments use the following shared fields:</p> <pre><code>backend: slurm\nslurm_env_pack_path: /shared/envs/caddforge.tar.gz\nslurm_env_cache_enabled: true\n# Optional absolute path on compute nodes:\n# slurm_env_cache_dir: /scratch/caddforge_environments/my-user\n</code></pre> <p><code>slurm_env_cache_enabled</code> defaults to <code>true</code>. With caching enabled, CADDForge reuses unpacked environments in per-user node-local storage. Set it to <code>false</code> to use the original behavior where every job unpacks its own environment under job-private scratch.</p> <p><code>slurm_env_cache_dir</code> overrides the automatically selected node-local cache root and must be an absolute compute-node path. Cache retention and lock timeouts are core policies and are not configured per job.</p> <p><code>slurm_env_pack_path</code> is mutually exclusive with <code>slurm_wheelhouse_path</code> and <code>slurm_requirements_path</code>. See the Slurm backend guide for complete environment setup.</p>"},{"location":"core/jobs-results/","title":"Jobs and Results","text":"<p>CADDForge writes each workflow execution into a standardized run directory.</p> <p>Example layout:</p> <pre><code>runs/\n 2026-05-05/\n <job-id>/\n config.json\n config.yaml # optional tool-specific copy\n metadata.json\n stdout.log\n stderr.log\n results/\n results.json\n result_record.json\n tool_specific/\n</code></pre>"},{"location":"core/jobs-results/#metadata","title":"Metadata","text":"<p>Core saves <code>config.json</code> as the canonical restart configuration. A tool may also write a YAML copy; preserve the original submitted YAML alongside the run for provenance.</p> <p><code>metadata.json</code> should capture job identity, tool name, user, timestamps, backend, versions, and key runtime parameters when available.</p>"},{"location":"core/jobs-results/#registry","title":"Registry","text":"<p>The job registry maps job IDs to status and paths. CLI commands such as <code>status</code> and <code>logs</code> should use the shared registry helpers rather than plugin-specific lookup code.</p> <p>Registry access is serialized with a filesystem lock so controller-side updates remain consistent under multiprocess fan-out and shared filesystems. Slurm worker payloads do not write to the shared registry; status refresh reconciles scheduler state with the job-local metadata and canonical result records. A completed scientific result is therefore not overwritten by a conflicting terminal scheduler state.</p> <p>Composite and repeat workflows may record parent-child relationships. Repeat parents are refreshed through their children and publish <code>replicas_manifest.json</code> plus aggregate completion metrics once all children become terminal.</p>"},{"location":"core/jobs-results/#loading-results","title":"Loading results","text":"<p>Use the core execution helpers to load canonical results:</p> <pre><code>from caddforge.core.execution import load_result_for_job\n\nresult = load_result_for_job(job)\n</code></pre> <p>Tool-specific summaries may exist. <code>result_record.json</code> takes precedence when present; the loader also accepts a canonical <code>results.json</code> from older or simpler workflows. Use <code>load_result_for_job</code> to select the record instead of loading a tool-specific summary as though it were a CADDForge result.</p>"},{"location":"core/plugins/","title":"Plugins","text":"<p>CADDForge tools are discovered as plugins. The core package defines the interfaces; tool packages register implementations.</p> <p>Plugin packages typically provide:</p> <ul> <li>a validated config class</li> <li>a workflow class</li> <li>a <code>Tool</code> implementation</li> <li>Python entry points under <code>caddforge.tools</code></li> <li>optional GUI extensions</li> <li>optional pipeline input and output ports</li> </ul> <p>Example entry point group:</p> <pre><code>[project.entry-points.\"caddforge.tools\"]\ndocking-unidock = \"cadd_docking.tool:UniDockTool\"\n</code></pre> <p>The core CLI uses these entry points to resolve commands such as:</p> <pre><code>caddforge run docking unidock --config unidock.yaml\n</code></pre> <p>Python automation should use the same plugin contract instead of importing tool-internal config and workflow classes directly:</p> <pre><code>from caddforge.core.execution import load_result_for_job, submit_workflow\nfrom caddforge.plugins import get_tool\n\nspec = get_tool(\"docking\", \"unidock\")\ncfg = spec.config_cls.from_yaml(\"unidock.yaml\")\nworkflow = spec.workflow_cls(cfg)\n\njob = submit_workflow(\n workflow, cfg, backend_name=cfg.backend,\n input_ports=tuple(getattr(spec.plugin, \"input_ports\", ())),\n)\nresult = load_result_for_job(job)\n</code></pre> <p>Forward <code>spec.plugin.input_ports</code> to submission so core can stage declared compressed inputs before the scientific workflow runs. The CLI and pipeline executor already forward these contracts. Keep original compressed paths in the submitted configuration; core manages temporary expanded files.</p> <p>Core code must not hardcode specific tool names.</p>"},{"location":"core/slurm/","title":"Slurm Backend","text":"<p>CADDForge can submit workflows to Slurm through the core <code>SlurmBackend</code>. The backend writes a payload, submits an <code>sbatch</code> script, and uses CADDForge's Slurm runner on the compute node to execute the workflow.</p> <p>Use Slurm for CADDForge-native workflows that can run in a normal Python environment on a compute node. Check each public tool's runtime requirements before selecting a backend.</p>"},{"location":"core/slurm/#basic-usage","title":"Basic Usage","text":"<p>Replace the partition and account placeholders with values from your cluster administrator.</p> <p>Set <code>backend: slurm</code> in the tool config or pass <code>--backend slurm</code> from the CLI:</p> <pre><code>caddforge run md plain-md --config plain_md.yaml --backend slurm --slurm-time 24:00:00\n</code></pre> <p>Common Slurm config fields:</p> <pre><code>backend: slurm\nslurm_partition: your-partition\nslurm_time: \"24:00:00\"\nslurm_account: your-account\nslurm_nodes: 1\nslurm_ntasks: 1\nslurm_cpus_per_task: 8\nsbatch_options:\n gres: gpu:1\n</code></pre> <p>Environment-related shared fields include <code>slurm_env_pack_path</code>, <code>slurm_env_cache_enabled</code>, <code>slurm_env_cache_dir</code>, <code>slurm_wheelhouse_path</code>, <code>slurm_requirements_path</code>, and <code>slurm_setup_commands</code>.</p>"},{"location":"core/slurm/#environment-requirement","title":"Environment Requirement","text":"<p>Slurm jobs run on compute nodes, not in the interactive shell where <code>caddforge</code> was launched. The compute node must have access to a compatible Python environment containing:</p> <ul> <li><code>caddforge</code></li> <li>the plugin package being run</li> <li>all Python dependencies</li> <li>all scientific runtime dependencies needed by the workflow</li> </ul> <p>For heavy scientific stacks, install conda-managed packages first, then install CADDForge packages with pip.</p>"},{"location":"core/slurm/#option-a-packed-condamicromamba-environment","title":"Option A: Packed Conda/Micromamba Environment","text":"<p>Use this when the workflow depends on conda-managed scientific packages such as OpenMM, RDKit, OpenFE, or OpenFF. Dependency resolution happens once before submission, and compute nodes reuse a checksum-addressed unpacked environment cache by default.</p> <p>Create the environment, install CADDForge and the required plugins, then use <code>caddforge pack-env</code>. The core package already depends on <code>conda-pack</code>:</p> <pre><code>micromamba create -n caddforge -c conda-forge python=3.10\nmicromamba activate caddforge\nmicromamba install -c conda-forge openmm rdkit\n# Requires the selected MD version to be published; see the version matrix.\npython -m pip install caddforge-core caddforge-md\n\n# Pack the active environment.\ncaddforge pack-env --output /shared/envs/caddforge.tar.gz\n\n# Alternatively, select a named environment or explicit prefix.\ncaddforge pack-env --name caddforge --output /shared/envs/caddforge.tar.gz\ncaddforge pack-env --prefix /path/to/caddforge \\\n --output /shared/envs/caddforge.tar.gz\n</code></pre> <p>The command creates:</p> <pre><code>/shared/envs/caddforge.tar.gz\n/shared/envs/caddforge.tar.gz.sha256\n</code></pre> <p>The sidecar is written in standard <code>sha256sum</code> format. During a long build, <code>pack-env</code> keeps the <code>conda-pack</code> percentage bar on one updating terminal line, streams normal messages, reports elapsed time and the growing archive size during quiet periods, and displays hashing progress.</p> <p>Existing output is protected by default. Use <code>--force</code> for a deliberate fixed-name replacement:</p> <pre><code>caddforge pack-env --output /shared/envs/caddforge-dev.tar.gz --force\n</code></pre> <p>Use the packed environment in the workflow config:</p> <pre><code>backend: slurm\nslurm_env_pack_path: /shared/envs/caddforge.tar.gz\n</code></pre> <p>No sidecar path is configured. CADDForge discovers <code><slurm_env_pack_path>.sha256</code> automatically.</p>"},{"location":"core/slurm/#sidecar-and-fallback-behavior","title":"Sidecar and fallback behavior","text":"<p>With caching enabled, submission uses the following compatibility order:</p> <ol> <li>A valid sidecar supplies the trusted SHA-256 immediately, so the large archive is not hashed by the submission command.</li> <li>If the sidecar is missing, malformed, or names another archive, CADDForge warns and falls back to computing the full archive SHA-256. Manually created packs therefore remain supported.</li> <li>If <code>slurm_env_cache_enabled: false</code>, CADDForge skips sidecar lookup and submission-time hashing, then unpacks the environment separately in each job's private scratch directory. This preserves the behavior used before environment caching was introduced.</li> </ol> <p>The first job requiring a digest on a compute node copies the archive to job-private scratch, verifies it against the selected SHA-256, and installs it into the node-local cache. Later jobs on that node reuse the completed cache entry without copying or unpacking the archive again.</p>"},{"location":"core/slurm/#node-local-cache","title":"Node-local cache","text":"<p>The default per-user cache root is:</p> <pre><code>$SCRATCH_BASE/caddforge_environments/$USER\n</code></pre> <p>It contains separate environment, lock, and staging directories. Cache locks are node-local and never lock or modify the source archive. Jobs retain a shared usage lock while running, preventing cleanup from deleting an active environment. Entries idle for more than 14 days are removed opportunistically; the lock timeout is 30 minutes. These are core policies, not per-job options.</p> <p>Sites can override the root with an absolute compute-node path:</p> <pre><code>slurm_env_cache_dir: /scratch/caddforge-environments/my-user\n</code></pre> <p>Node-local caches may disappear during node cleanup or replacement. CADDForge rebuilds missing entries from the shared archive.</p>"},{"location":"core/slurm/#replacing-a-managed-pack","title":"Replacing a managed pack","text":"<p><code>pack-env --force</code> creates and hashes the replacement before briefly acquiring a hidden advisory publication lock next to the archive. CADDForge submissions coordinate with this lock when it is present. The source archive itself is never locked.</p> <p>An archive and sidecar are separate files, so direct external replacement bypasses this coordination and is unsupported for managed packs. If sidecar publication fails, CADDForge removes the old sidecar when possible, causing submissions to use the safe full-hash fallback.</p> <p>A job records the digest selected at submission. If a fixed-name archive is replaced before an uncached queued job starts, that job fails checksum verification rather than running a different environment. A job whose matching digest is already cached on its compute node can still use that cache entry.</p>"},{"location":"core/slurm/#option-b-wheelhouse-and-per-job-virtualenv","title":"Option B: Wheelhouse and Per-Job Virtualenv","text":"<p>Use this when all required packages are available as wheels from a package index or a prepared wheelhouse.</p> <p>Build a wheelhouse:</p> <pre><code>python -m pip freeze > requirements.txt\npython -m pip download -r requirements.txt -d /shared/wheelhouse\n</code></pre> <p>Use it in the workflow config:</p> <pre><code>backend: slurm\nslurm_wheelhouse_path: /shared/wheelhouse\nslurm_requirements_path: /shared/requirements.txt\n</code></pre> <p>The Slurm job creates a temporary virtualenv, installs from the wheelhouse, and runs the workflow.</p>"},{"location":"core/slurm/#option-c-site-specific-setup-commands","title":"Option C: Site-Specific Setup Commands","text":"<p>Use <code>slurm_setup_commands</code> only when your cluster already provides a supported module or activation workflow:</p> <pre><code>backend: slurm\nslurm_setup_commands:\n - module purge\n - module load caddforge/0.2\n - source /shared/envs/caddforge/bin/activate\n</code></pre> <p>If you use setup commands, do not also set <code>slurm_env_pack_path</code>, <code>slurm_wheelhouse_path</code>, or <code>slurm_requirements_path</code>. The setup commands are then fully responsible for making the correct Python executable available.</p>"},{"location":"core/slurm/#practical-rules","title":"Practical Rules","text":"<ul> <li>Ensure all referenced environment paths are readable from compute nodes.</li> <li>Use exactly one environment strategy per job.</li> <li>Keep packed environments on shared storage, not in a login-node-only path.</li> <li>Prefer <code>caddforge pack-env</code> so submission can use its managed SHA-256 sidecar.</li> <li>Use <code>--force</code> only when replacing a fixed-name archive is intentional, and avoid replacement while uncached jobs using that archive are queued.</li> <li>Rebuild the environment when plugin versions or scientific dependencies change.</li> <li>Prefer packed conda environments for workflows with compiled scientific dependencies.</li> <li>Prefer wheelhouses for pure Python or locally packaged stacks.</li> </ul>"},{"location":"development/","title":"Development","text":"<p>CADDForge development should preserve the split between core runtime behavior and tool-specific science.</p>"},{"location":"development/#rules-of-thumb","title":"Rules of thumb","text":"<ul> <li>Keep core independent of specific scientific tools.</li> <li>Register tools as plugins instead of hardcoding them in core.</li> <li>Use typed, validated config models.</li> <li>Use shared job, registry, metadata, and result helpers.</li> <li>Keep pipeline stages generic unless the behavior belongs in a plugin.</li> <li>Add focused tests for config validation, artifact contracts, scatter behavior, and result loading.</li> </ul>"},{"location":"development/#contribute-and-maintain","title":"Contribute and maintain","text":"<ul> <li>Release notes: changes in each documentation package release.</li> <li>Contributing: setup, review, support, and community expectations.</li> <li>Documentation: authoring, explicit API generation, examples, and validation.</li> <li>Security reporting: confidential reports and deployment boundaries.</li> <li>Release checklist: licensing, public content, history, and package availability.</li> </ul>"},{"location":"development/ci-publishing/","title":"CI and Publishing","text":"<p>Documentation validation is separate from deployment and Python package upload.</p>"},{"location":"development/ci-publishing/#validate-a-change","title":"Validate a change","text":"<pre><code>python -m pip install -r requirements.txt -r requirements-test.txt\nproperdocs build --strict --site-dir site\npython scripts/check_links.py site\npython -m pytest -q\n</code></pre> <p>The tests build wheels and rebuild from a source distribution, check packaged assets, and test the installed-docs CLI and server. Tutorial integration tests require compatible core and data-science installations; they report skips when those packages are absent. Run them explicitly in a compatible public-component environment before release.</p> <pre><code>python -m pytest tests/test_examples.py -q --require-examples\n</code></pre> <p>GitHub branch and tag pushes and pull requests run documentation checks without requiring deployment. Publication jobs depend on successful validation. Optional scientific template schema checks use an explicit list of public configuration classes; they do not discover arbitrary installed plugins.</p>"},{"location":"development/ci-publishing/#host-the-generated-site","title":"Host the generated site","text":"<p>GitHub Pages uses <code>.github/workflows/pages.yml</code>. Enable Pages with the GitHub Actions source, then opt in with repository variable <code>DOCS_PUBLISH_ENABLED=true</code>. Builds produce static artifacts on ordinary changes, but deployments run only for an enabled default branch, including a manual workflow on that branch. Release tags run validation without deploying Pages.</p> <p>Configure the public site and repository URLs in <code>properdocs.yml</code> after hosting is established. The source is platform-neutral. The installed docs package ships its own site; hosting is not required to use <code>caddforge docs</code>.</p>"},{"location":"development/ci-publishing/#python-distribution-releases","title":"Python distribution releases","text":"<p><code>caddforge-core</code> owns Python modules and the CLI. The metadata-only bundle publishes the <code>caddforge</code> distribution and pins the reviewed component set. See release prerequisites before any release.</p> <p>Every public component uses <code>.github/workflows/release.yml</code> for branch/PR validation and publication on pushed <code>v*</code> tags, matching PyOCL and the maintained third-party tools. A single Python 3.12 build produces the wheel and source archive; tests install that exact wheel on Linux with Python 3.10 and 3.12. Publication depends on all required checks. Manual runs and branch pushes do not publish. No <code>PYPI_PUBLISH_ENABLED</code> variable is used.</p> <p>Metadata checks run on ordinary builds as well as tags. They reject private dependencies and direct Git requirements, compare wheel/source metadata, and require a release tag to match the built version. Release tags must have public versions without local or <code>.dev</code> suffixes; a <code>0.x</code> version still represents the project's initial development series. Core and plugin versions come from Git tags. Docs and bundle versions are explicit in <code>pyproject.toml</code> and must be updated before tagging.</p> <p>The tests run from temporary fixture copies without checkout <code>src</code> directories. Core, data-science, GUI, ML, QM and docking run their regression suites. Descriptors run plugin, clustering and PheSA tests without downloading ANI weights. MD runs configuration, trajectory and workflow contract tests; external engines and long simulations remain separate. FEP tests orchestration and settings without requiring an OpenFE runtime. FEP and docking install the candidate wheel with its <code>[gui]</code> extra so GUI regression tests exercise the declared dependencies, including the published <code>caddforge-gui</code> package. Each selection is recorded in <code>scripts/ci_tests.json</code>. These profiles do not claim GPU or complete scientific-engine validation.</p> <p>Docs additionally validates source examples, assets, links and distribution rebuilding, tests the installed viewer, and requires successful execution of the CLI/Python tutorials using published core and data science. The docs wheel is installed with its <code>[test]</code> extra, including Rich for terminal-output assertions. The bundle runs the ownership migration with an explicit core source checkout, and requires a clean full installation from public PyPI before publishing. Its release must follow its pinned components.</p> <p>For each PyPI project, configure a Trusted Publisher with GitHub owner <code>idorsia</code>, the corresponding repository, workflow filename <code>release.yml</code>, and environment <code>pypi</code>. For the bundle the PyPI project is <code>caddforge</code>, while the repository is <code>caddforge-bundle</code>. GitHub environments may require reviewer approval if desired. Repository workflows cannot configure these PyPI account settings for you. Publish core first, then GUI before FEP and docking. Publish docking and descriptors before MD, and data-science before docs. Publish the bundle last.</p> <p>Pushing a release tag is the publication action once the Trusted Publisher is configured. <code>.github/workflows/pages.yml</code> handles site deployment independently and retains the explicit <code>DOCS_PUBLISH_ENABLED=true</code> opt-in.</p>"},{"location":"development/contributing/","title":"Contributing","text":"<p>Contributions to documentation, examples, tests, infrastructure, and scientific integrations are welcome. For a substantial change, discuss the intended behavior in the relevant package repository before implementation.</p>"},{"location":"development/contributing/#set-up-a-focused-development-environment","title":"Set up a focused development environment","text":"<p>Clone core and the public package you intend to change. Use compatible revisions from the version matrix. Install scientific conda dependencies before pip packages when the plugin requires them.</p> <pre><code>python -m venv .venv\n# POSIX shells; on Windows use .venv\\Scripts\\activate.\nsource .venv/bin/activate\npython -m pip install -e ../caddforge-core\npython -m pip install -e ../caddforge-datascience\npython -m pip install pytest\npython -m pytest ../caddforge-datascience/tests -q\n</code></pre> <p>For docs-only work, install <code>requirements.txt</code> and <code>requirements-test.txt</code> in this repository; core and scientific packages are not needed to render the site. See documentation maintenance for generation and validation.</p>"},{"location":"development/contributing/#prepare-a-change-for-review","title":"Prepare a change for review","text":"<p>Keep core independent of scientific tools, use validated configurations and shared execution/result helpers, and register integrations through public entry points. Add focused tests for observable behavior, document new interfaces and outputs, and update version callouts when behavior changes. Use semantic commit messages, for example <code>docs: add a runnable pipeline tutorial</code> or <code>fix(md): preserve input chemistry</code>.</p> <p>In the pull/merge request, describe the problem, resulting behavior, validation, scientific assumptions, and compatibility impact. Label heavyweight tests and record prerequisites. Do not include credentials or research data in tests or reports.</p>"},{"location":"development/contributing/#support-and-community-expectations","title":"Support and community expectations","text":"<p>Use the issue tracker of the relevant public package for reproducible bugs and documentation questions. Include a minimal synthetic example, versions, backend, expected behavior, and error output. Discuss scientific validation separately from software failures. The public hosting URL is configured when the project is published; these docs deliberately do not link to unpublished repositories or invent contacts.</p> <p>Be respectful, give constructive technical feedback, and avoid personal or discriminatory remarks. Project maintainers review contributions and resolve disputes; a formal code of conduct and a monitored reporting route are publication prerequisites. Do not post security-sensitive details in ordinary issues; see security reporting.</p>"},{"location":"development/documentation/","title":"Documentation","text":"<p>This repository is the central documentation site for CADDForge.</p>"},{"location":"development/documentation/#installed-documentation","title":"Installed documentation","text":"<p><code>caddforge-docs</code> ships a built static site and registers the optional <code>caddforge docs</code> CLI command. It depends on core's generic command extension interface, introduced in core 0.21.0. Serving the installed site uses Python's standard library; ProperDocs and its plugins are needed only for builds or authoring.</p> <p>Run <code>python -m build</code> to create wheel and source distributions. The wheel hook runs a strict ProperDocs build into the ignored <code>src/caddforge_docs/site/</code> directory. The source distribution includes the site sources, configuration, and build hook, and excludes generated site files. Editable installations also build the installed view; reinstall to refresh it after edits. Use <code>properdocs serve --open</code> when live reloading is needed. Build failures abort packaging.</p>"},{"location":"development/documentation/#source-of-truth","title":"Source of truth","text":"<p>For now, plugin-specific <code>README.md</code> files remain unchanged. Central docs should still be updated when user-facing behavior, config fields, outputs, or pipeline contracts change.</p> <p>Later, plugin READMEs can be shortened to package summaries that link here.</p>"},{"location":"development/documentation/#writing-guidelines","title":"Writing guidelines","text":"<ul> <li>Document user workflows before internal implementation details.</li> <li>Prefer runnable CLI and Python examples.</li> <li>Keep config examples short and valid.</li> <li>Mention external software dependencies clearly.</li> <li>Document canonical output paths and result keys used by pipelines.</li> <li>Keep pipeline examples explicit about artifact names and stage IDs.</li> <li>When plugin behavior, config fields, outputs, or pipeline contracts change, update the plugin page's documented version callout and the plugin version matrix in the same change.</li> </ul>"},{"location":"development/documentation/#local-validation","title":"Local validation","text":"<pre><code>python -m pip install -r requirements.txt\nproperdocs build --strict\n</code></pre>"},{"location":"development/documentation/#public-documentation-maintenance","title":"Public documentation maintenance","text":"<p>Use explicit public package and symbol allowlists. Do not generate the site by importing or discovering every installed plugin. <code>scripts/update_reference.py</code> reads only selected core files using Python's AST; the generated reference is checked in so an isolated docs build needs no scientific packages or adjacent checkout.</p> <pre><code>python scripts/update_reference.py ../caddforge-core/src/caddforge\npython scripts/update_reference.py ../caddforge-core/src/caddforge --check\n</code></pre> <p>Generate against the reviewed core version and review signatures/docstrings for accuracy and public content. Update the generator's version description when the reference baseline changes. Scientific templates and <code>docs/examples/manifest.json</code> are explicit public examples, not an export of all installed tools.</p> <pre><code>python -m pip install -r requirements.txt -r requirements-test.txt\npython scripts/validate_examples.py\n# In a compatible public scientific environment, validate JSON schemas:\npython scripts/validate_examples.py --runtime\nproperdocs build --strict --site-dir site\npython scripts/check_links.py site\npython -m pytest -q\npython -m pytest tests/test_examples.py -q --require-examples\n</code></pre> <p>JSON schema validation checks required fields, types, enums and bounds, but deliberately cannot verify placeholder files or all custom model validators. Runtime input validation and scientific checks remain necessary. Docs-only CI checks syntax and manifest coverage; release validation adds public schemas and actual lightweight tutorial execution.</p> <p>Keep tutorials complete, with input provenance and expected outputs. Label prepared-input and heavyweight examples as templates. Keep configuration downloads synchronized with page snippets, and link shared explanations for units, reproducibility, and limitations. The writing structure follows Di\u00e1taxis.</p> <p>Review scientific data provenance, screenshots, datasets, and repository history before public disclosure. Never copy previous run outputs into public examples.</p> <p>The documentation theme is pinned to the asset version covered by the third-party notices. Review generated dependencies and update the pins, asset hook, notices, and licence texts together when upgrading it.</p>"},{"location":"development/release-notes/","title":"Documentation Release Notes","text":""},{"location":"development/release-notes/#030","title":"0.3.0","text":"<p>Documentation source tag: <code>v0.3.0</code>. Package-index publication is pending.</p>"},{"location":"development/release-notes/#public-component-source-tags","title":"Public component source tags","text":"<ul> <li> <p>Core <code>v0.22.0</code>, QM <code>v0.3.0</code>, ML <code>v0.4.0</code>, GUI <code>v0.6.0</code>, data science <code>v0.5.0</code>, descriptors <code>v0.11.0</code>, FEP <code>v0.7.0</code>, docking <code>v0.7.0</code>, and MD <code>v0.13.0</code> identify the reviewed public source commits recorded in the version matrix. Publication was checked on 2026-10-15: all listed component versions except MD <code>0.13.0</code> are published on PyPI. MD is tagged and awaiting publication. Documentation <code>0.3.0</code> is tagged and awaiting publication; the updated bundle release remains pending.</p> </li> <li> <p>Core CI installs the <code>[test]</code> extra, including RDKit for hydrogenation coverage, and its help assertions handle plain and styled terminal output. QM uses the published <code>caddforge-opencosmors==0.0.1</code> dependency and <code>pyocl-idor>=0.8.3</code> with Java 21 or newer.</p> </li> <li> <p>MD tests support Python 3.10 through the conditional <code>tomli</code> backport. Its installed-wheel runner preserves declared pytest markers while excluding checkout runtime sources.</p> </li> </ul>"},{"location":"development/release-notes/#documentation-and-packaging","title":"Documentation and packaging","text":"<ul> <li>Record verified package-index availability, update current installation and migration examples, and generate API provenance from the version matrix.</li> <li> <p>Normalize ANSI formatting in CLI error assertions and cover plain and styled output. The wheel CI job installs the declared <code>[test]</code> extra.</p> </li> <li> <p>Standardize package CI on tested wheels, Python 3.10/3.12, release metadata checks, and tag-triggered PyPI Trusted Publishing. Add docs package releases alongside opt-in Pages, run bundle migration tests with core sources, and derive packaging expectations from current metadata instead of inherited pins.</p> </li> <li> <p>Install PyOCL using the public <code>pyocl-idor>=0.8.3</code> distribution; Python imports remain <code>pyocl</code>. Provision Java 21 in affected CI workflows and document the Java 21 or newer runtime requirement.</p> </li> <li> <p>Start new public repositories without earlier development history. New public component tags and verified publication status are recorded above. Documentation continues its version sequence at <code>0.3.0</code>; the bundle version awaits assignment.</p> </li> <li>Declare MIT licensing and ship project and third-party licence texts.</li> <li>Remove ESOL and Glide / Glide WS integrations from the public component sources.</li> <li>Pin the documentation theme to the reviewed assets and reject unreviewed theme versions or non-English search assets.</li> </ul> <p>The notes below describe inherited development milestones, not tags or releases in the new public repositories. See the version matrix.</p>"},{"location":"development/release-notes/#021","title":"0.2.1","text":"<ul> <li>Document bundle <code>0.22.0</code>, which includes <code>caddforge-docs==0.2.1</code> and the <code>caddforge docs</code> command by default.</li> <li>Keep documentation authoring and test extras outside the runtime bundle.</li> <li>Update installation examples, CLI guidance, the version matrix, and release ordering.</li> <li>Retain separate docs installation instructions for bundle <code>0.21.0</code>, core-only, and selective plugin environments.</li> </ul> <p>This inherited milestone does not establish a new public release or publication.</p>"},{"location":"development/release-notes/#020","title":"0.2.0","text":"<p>This inherited milestone introduced the packaged documentation. New public package releases and public site deployment remain separate actions.</p>"},{"location":"development/release-notes/#installed-documentation","title":"Installed documentation","text":"<ul> <li>Added the optional <code>caddforge docs</code> command, registered only when this package is installed.</li> <li>Ship a complete static site with search, assets, and downloadable examples for offline viewing.</li> <li>Support <code>--host</code>, <code>--port</code>, and <code>--no-browser</code>, including IPv4, IPv6, and hostnames.</li> <li>Build the site into wheels and editable installations; source distributions contain everything needed to rebuild it. Reinstall after source edits to refresh the installed view.</li> </ul>"},{"location":"development/release-notes/#guides-and-reference","title":"Guides and reference","text":"<ul> <li>Added runnable CLI and Python tutorials using synthetic table data and a two-stage core pipeline.</li> <li>Added configurations for 26 documented scientific workflows and eight advanced pipeline templates.</li> <li>Added job, scatter, troubleshooting, GUI, reproducibility, contributor, and security guidance.</li> <li>Generate the public core API reference from an explicit source allowlist without importing plugins.</li> <li>Document core <code>0.21.0</code>, bundle <code>0.21.0</code>, and the latest tagged public plugins, including MD terminal policies and encoded PheSA references.</li> <li>Forward declared runtime inputs in Python submission examples so compressed inputs use core staging.</li> </ul>"},{"location":"development/release-notes/#validation-and-publication","title":"Validation and publication","text":"<ul> <li>Check local links and anchors, example syntax and schemas, and packaged artifacts.</li> <li>Validate GitHub branch/tag pushes; Pages deployment remains opt-in and limited to the default branch after validation.</li> <li>Keep scientific engine validation distinct from template schema checks and lightweight tutorials.</li> <li>Record outstanding licensing, public-index dependency, hosting, and reporting-route prerequisites in the release checklist.</li> </ul>"},{"location":"development/release-readiness/","title":"Public Release Checklist","text":"<p>These docs describe the reviewed public component set. They do not assert that tagged packages have already been published or that the full bundle currently resolves from a public package index.</p>"},{"location":"development/release-readiness/#documentation-and-package-checks","title":"Documentation and package checks","text":"<ul> <li>Run strict builds and local-link/anchor checks. Verify rendered pages, search indexes, and assets in wheels, and rebuild from source distributions.</li> <li>Run lightweight tutorials and validate the supplied scientific templates against the intended public plugin versions. Record external-engine validation separately.</li> <li>Review examples, screenshots, scientific data provenance, and repository history before exposing an existing repository. A clean current tree does not clean history.</li> <li>Set public repository/site URLs and monitored support and security-reporting routes.</li> <li>Add reviewed contributor/community policies to public package repositories.</li> </ul>"},{"location":"development/release-readiness/#deferred-gui-security-work","title":"Deferred GUI security work","text":"<p>The focused GUI security fix remains a follow-up: apply authentication consistently to plugin extension routes, support authenticated browser requests, and ensure uploaded filenames cannot escape their staging directory. Validate these together before exposing the GUI to untrusted clients.</p>"},{"location":"development/release-readiness/#licensing-and-distribution-prerequisites","title":"Licensing and distribution prerequisites","text":"<p>Package metadata declares MIT, and each package includes the project license text. The GUI ships React's full notice and MIT text. The docs ship the third-party notices and full licence texts with the site; unused language-search libraries are excluded from the English site. MD retains the vendored OpenBPMD notice and licence. Preserve these notices and review them when dependencies or generated assets change. Model weights and scientific data remain subject to their own terms; the project licence does not replace those terms.</p> <p>The edited QM and docking sources use the published maintained distributions <code>caddforge-opencosmors==0.0.1</code> and <code>caddforge-unidock-tools==1.1.3</code>, respectively, replacing the previous direct Git dependencies. PyOCL is published as <code>pyocl-idor</code>; the edited component sources require <code>pyocl-idor>=0.8.3</code> and Java 21 or newer. Python imports remain <code>pyocl</code>. The inherited CADDForge releases do not contain these dependency changes. Release updated components and test the resulting bundle pins against public indexes. Verify the full installation before enabling package publication. Create the public release repositories, record reviewed commits, and create release tags once the packages are clean. Update the version matrix with those actual versions, commits, tags, and publication status; a Git tag is not evidence of a published package.</p> <p>The current matrix records all nine new public component tags and separately verified package-index availability. MD publication remains pending as of 2026-10-15. Documentation <code>0.3.0</code> has source tag <code>v0.3.0</code> and awaits publication. The bundle still needs a new unused version, release commit, and tag; do not publish its edited sources under the inherited version. Update and test the bundle pins, including <code>caddforge-docs==0.3.0</code>, once all required component distributions are available.</p> <p>The bundle remains in its <code>0.x</code> initial development series. Reserve <code>1.0.0</code> for a future stable API commitment; the bundle and components have independent version sequences.</p> <p>Release core, then GUI before FEP and docking, then descriptors and docking before MD. Publish data science before docs, and publish the bundle last with exact tested pins. The bundle includes docs as a default dependency; the docs wheel must be available on the chosen package index before that bundle is installed. Component versions remain independent. Update documented versions and migration instructions with each release. Before pushing a release tag, configure the project's PyPI Trusted Publisher for <code>release.yml</code> and the <code>pypi</code> environment. Pushing a matching <code>v*</code> tag publishes after validation; ordinary documentation changes and manual runs do not publish. Site deployment remains separately opt-in.</p>"},{"location":"development/security/","title":"Security Reporting and Deployment","text":"<p>Before public publication, maintainers must configure a monitored confidential vulnerability-reporting route and document supported release versions in each public repository's security policy. These docs do not yet advertise a contact address or response-time commitment.</p> <p>When the public repository enables private vulnerability reporting, use that channel for suspected vulnerabilities. If no confidential route is available, ask maintainers to provide one without posting exploit details, credentials, or sensitive data. Ordinary issues are appropriate for non-sensitive bug reports.</p> <p>Configurations, scripts, plugins, and environment setup commands are executable inputs. Use trusted sources and inspect examples before running them. Browser access to docs is read-only static serving, while the GUI can submit jobs. Binding either server to a reachable interface requires appropriate network access control; do not assume authentication or transport encryption is provided.</p> <p>For a local preview:</p> <pre><code>caddforge docs --host 127.0.0.1 --no-browser\ncaddforge gui --host 127.0.0.1 --port 8000\n</code></pre> <p>Remove sensitive identifiers from logs, saved configurations, registry files, screenshots, and example inputs before sharing a report.</p>"},{"location":"development/tool-authoring/","title":"Tool Authoring","text":"<p>A tool plugin should provide a narrow integration layer around scientific workflow logic.</p>"},{"location":"development/tool-authoring/#distribution-dependencies","title":"Distribution dependencies","text":"<p>Declare <code>caddforge-core>=0.21.0</code> in plugin <code>project.dependencies</code>; do not depend on <code>caddforge</code>, which is the complete toolbox bundle. Keep Python imports and entry-point groups under <code>caddforge</code>. Adding a public plugin to the bundle requires an explicit, exact dependency pin and a newly tested bundle release. Only reviewed public components belong in the public bundle.</p>"},{"location":"development/tool-authoring/#optional-cli-commands","title":"Optional CLI commands","text":"<p>An installed package can add a top-level CLI command through the <code>caddforge.cli_commands</code> entry-point group. Core 0.21.0 introduces this interface. The entry-point name becomes the command name; its target is a callable with a Typer-compatible signature and help docstring:</p> <pre><code>[project.entry-points.\"caddforge.cli_commands\"]\ndocs = \"caddforge_docs.cli:docs\"\n</code></pre> <p>Core registers these commands after its built-in commands and groups. Conflicts, duplicate command names, and extensions that fail to load or have unsupported signatures are logged and skipped, leaving built-in commands available. Keep extension imports lightweight and load expensive runtime dependencies only when the command executes. Removing the providing package removes its command from subsequent CLI invocations. Plugins that only expose scientific tools should continue to use the <code>caddforge.tools</code> group.</p>"},{"location":"development/tool-authoring/#required-pieces","title":"Required pieces","text":"<ul> <li>Config model with validation and YAML/JSON loading support.</li> <li>Workflow class that prepares and runs jobs.</li> <li>Tool metadata class registered through <code>caddforge.tools</code> entry points.</li> <li>Canonical <code>Result</code> output with metrics and paths.</li> <li>Tests for config validation, workflow setup, and result generation.</li> </ul>"},{"location":"development/tool-authoring/#pipeline-contracts","title":"Pipeline contracts","text":"<p>Expose pipeline ports when a tool should participate in pipelines:</p> <pre><code>InputPort(name=\"ligands\", kind=ArtifactKind.MOLECULE_COLLECTION, config_field=\"ligand_paths\")\nOutputPort(name=\"scores\", kind=ArtifactKind.TABULAR_SCORES, result_path_key=\"best_scores_csv\")\nOutputPort(\n name=\"pose_files\",\n kind=ArtifactKind.POSE_COLLECTION,\n result_path_key=\"pose_manifest_csv\",\n required=False,\n path_collection_column=\"pose_file\",\n)\n</code></pre> <p>Use declared ports or explicit <code>outputs</code> mappings. Do not force downstream stages to infer paths from tool-specific output directories.</p>"},{"location":"development/tool-authoring/#runtime-input-and-compression-contracts","title":"Runtime input and compression contracts","text":"<p>Set <code>InputPort.config_field</code> to the configuration path that receives a pipeline artifact. Core also uses declared inputs to materialize transparent gzip wrappers immediately before execution. For a nested collection, set <code>runtime_selector</code>, for example <code>input_files[].path</code>. Mark a selector with <code>InputCompressionPolicy.NATIVE</code> when the scientific tool consumes the compressed stream itself.</p> <p>Outputs requested with a <code>.gz</code> suffix require an <code>OutputPort.config_field</code> so core can distinguish the requested destination from an existing compressed input and finalize it atomically.</p> <p>Structured collection fields can declare a <code>SplitContract</code> that identifies the path member to split, preserves sibling metadata in every shard, and selects a deterministic reducer for shard outputs.</p>"},{"location":"development/tool-authoring/#scratch-publication-helpers","title":"Scratch publication helpers","text":"<p>Use <code>LogSync</code> for mutable stdout, stderr, or progress snapshots written in scratch. Set <code>atomic=True</code> for structured snapshots and inspect its <code>LogSyncReport</code> when the final publication state belongs in the result. Synchronization failures are retried and must not change scientific job status.</p> <p>Use <code>ArtifactSyncQueue</code> for completed immutable artifacts. It publishes by atomic replacement, retries transient failures, and reports unresolved files after the final pass. Do not place tool-specific behavior in these core helpers.</p>"},{"location":"development/tool-authoring/#backends","title":"Backends","text":"<p>Workflow code should not contain Slurm-specific or local-process orchestration. Submit and track jobs through the core backend interfaces.</p>"},{"location":"examples/","title":"Example Library","text":"<p>Start with the local workflow and small pipeline. Their synthetic inputs and expected outputs are included and exercised by integration tests. See data provenance.</p>"},{"location":"examples/#scientific-configuration-templates","title":"Scientific configuration templates","text":"<p>These templates accompany the public plugin pages. They need prepared inputs and the scientific runtimes listed below. They are schema-checked against the tagged components in the version matrix; engine execution is not part of the docs-only CI. Replace placeholders, check molecular preparation, and scale compute settings before running a scientific job. They do not download experimental data.</p> Workflow Configuration Runtime / preparation <code>docking/unidock</code> YAML Uni-Dock executable, supported GPU; prepared PDBQT receptor and ligand SDF; replace box center/size <code>docking/idockya</code> YAML pyocl-idor/OpenChemLib (Java 21+); receptor and native ligand SDF in the same frame <code>docking/gnina</code> YAML GNINA executable; prepared receptor and ligands; review CNN/GPU settings <code>descriptors/ani-psa</code> YAML RDKit, TorchANI and model weights; check allowed elements and conformer settings <code>descriptors/phesa</code> YAML pyocl-idor/OpenChemLib (Java 21+); validated reference chemistry and coordinate-frame handling <code>descriptors/ani-conformers</code> YAML RDKit, TorchANI and model weights; SMILES and supported elements <code>descriptors/cluster-molecules</code> YAML RDKit and declared clustering dependencies; CSV/SMILES/SDF inputs; select a sensible cluster count <code>md/fix-structure</code> YAML OpenMM/PDBFixer and matching force fields; check protonation, termini, and untouched cofactors <code>md/center-and-align</code> YAML MDAnalysis; matching topology and trajectory, atom selections <code>md/extract-frames</code> YAML MDAnalysis and applicable trajectory readers; frame/time selections <code>md/cluster-representative</code> YAML MDAnalysis and clustering stack; matching topology/trajectory and representative criteria <code>md/plain-md</code> YAML OpenMM and preparation/parameterization stack; authoritative protein/ligand chemistry <code>md/minimize</code> YAML OpenMM, ParmEd and AmberTools for automatic preparation; or validated prepared topology and mapping <code>md/openbpmd</code> YAML OpenMM, OpenBPMD preparation stack and AmberTools; complete complex and ligand chemistry <code>md/grand</code> YAML grand, OpenMM, OpenMMTools/force fields and OpenFF; ligand-centered pocket and parameterized complex <code>md/inducia</code> YAML Public descriptor/docking dependencies, GNINA, grand, OpenMM, AmberTools and OpenBPMD; prepared inputs <code>fep/openfe-abfe</code> YAML OpenFE, OpenFF and parameterization stack; prepared complex, convergence assessment <code>fep/openfe-rbfe-septop</code> YAML OpenFE/SepTop runtime, ligand mapping and parameterization; scientifically suitable paired systems <code>qm/transfer-free-energy</code> YAML PySCF/openCOSMO-RS stack; conformers, solvent structures, and standard state <code>ml/simple-gp</code> YAML Declared GP/ML stack; numeric CSV features and training/prediction split <code>ml/pca</code> YAML Declared ML stack; numeric CSV and appropriate preprocessing <code>ml/model-zoo</code> YAML Dependencies for selected registry models; leakage-free training/validation data <code>ml/model-factory</code> YAML Dependencies for selected registry models; explicit target/task and split <code>ml/explore-classifier</code> YAML Interactive UI dependencies and prepared classification data <code>datascience/csv-scatter</code> YAML pandas/Bokeh; CSV columns and optional RDKit for molecule hover <code>datascience/distribution-diagnostics</code> YAML pandas/SciPy/Matplotlib; numeric CSV column; runnable synthetic tutorial also supplied"},{"location":"examples/#run-a-downloaded-template","title":"Run a downloaded template","text":"<p>Save run_tool.py beside a chosen configuration. Supply all input files, then run from that directory so relative workflow paths resolve consistently:</p> <pre><code>caddforge show-config datascience distribution-diagnostics\ncaddforge run datascience distribution-diagnostics --config diagnostics.yaml\npython run_tool.py datascience distribution-diagnostics diagnostics.yaml\n</code></pre> <p>Use your template's section, tool, and filename in these commands. The Python runner uses <code>get_tool</code> and <code>submit_workflow</code>, passes declared input ports for runtime staging, and checks the canonical result. For Slurm, it prints the submitted job ID; inspect status before reading completed outputs. Per-plugin pages explain expected files and result keys. No generic CLI command validates the scientific suitability of inputs.</p>"},{"location":"examples/#units-and-preparation","title":"Units and preparation","text":"Family Quantities to record Docking / alignment Coordinate frame and distance units; score definition, not an inferred affinity unit MD / minimization Length/time/temperature units in field names; engine energy units, force field, and restraint definitions FEP / QM Energy units reported by the selected engine, temperature, standard state, uncertainty/convergence Descriptors Descriptor/model definition; PheSA frame behavior; dimensionless similarities where appropriate ML / data science Feature/target units, transformations, split and seed; synthetic examples are dimensionless <p>See scientific interpretation and method references. Advanced pipeline templates and minimization configurations require prepared input data; their schema checks are separate from real scientific execution.</p>"},{"location":"examples/quickstart/","title":"Example Data Provenance","text":"<p><code>values.csv</code> and <code>scores.csv</code> are invented, dimensionless teaching data authored for these tutorials. They contain no experimental measurements, receptor coordinates, compound identifiers from a research project, or external datasets. Their numeric values must not be interpreted as predictions or binding energies.</p> <p>Configurations and scripts demonstrate the public interfaces documented here. Package metadata declares MIT and includes the project license text. No third-party dataset is redistributed.</p> <p>Follow the workflow tutorial and the pipeline tutorial. Save their files together in a writable directory. Generated runs are not example inputs.</p>"},{"location":"explanation/references/","title":"Scientific References and Attribution","text":"<p>The following public upstream projects describe the scientific methods and their citation guidance. Select the references for the actual engine/model version used; this list does not establish redistribution rights for models or datasets.</p> Workflow family Public upstream reference Molecular representations and clustering RDKit documentation Shape alignment and iDockYa OpenChemLib source and documentation, PyOCL Python distribution ANI models TorchANI documentation Uni-Dock Uni-Dock source and citation guidance CNN docking and rescoring GNINA source and citation guidance Simulation and minimization OpenMM documentation Water sampling grand source and references Free-energy calculations OpenFE documentation Electronic structure PySCF documentation ML models and validation scikit-learn documentation <p>CADDForge has no project DOI recorded in these docs. Until one exists, identify the public repository, release tag or commit, component versions, and access date in your software citation. Do not invent a DOI or publication.</p>"},{"location":"explanation/reproducibility/","title":"Reproducibility and Scientific Interpretation","text":"<p>CADDForge standardizes execution and provenance; it does not establish that a scientific protocol is suitable for a particular target or dataset.</p>"},{"location":"explanation/reproducibility/#keep-a-reproducible-record","title":"Keep a reproducible record","text":"<p>Preserve input files and checksums, submitted configuration, canonical results, job metadata, logs, pipeline/scatter manifests, component and engine versions, force fields, model identifiers, seeds, platform, and relevant compute settings. Core and bundle versions are different: the bundle pins a tested component set; core implements runtime behavior. Use the version matrix.</p> <p>Set available random seeds explicitly. A fixed seed does not promise identical results across GPU models, floating-point precision, dependency versions, or parallel schedules. Compare scientific tolerances rather than byte equality for stochastic results. The synthetic introductory examples are deliberately small.</p>"},{"location":"explanation/reproducibility/#prepare-and-interpret-molecular-inputs","title":"Prepare and interpret molecular inputs","text":"<p>Verify protonation, stereochemistry, bond orders, explicit hydrogens, residue naming, missing atoms, coordinate frame, and force-field compatibility before docking, minimization, MD, or FEP. A successful preparation step is not a guarantee of the intended chemistry. Do not apply experimental interpretations to toy inputs.</p> <p>Docking scores rank poses within a protocol; they are not measured affinities. Minimization convergence describes an optimization, not equilibration or pose validity. MD and FEP need sampling/convergence assessment and uncertainty appropriate to the method. QM outputs depend on geometry, conformers, electronic structure, solvent model, temperature, and standard state. ML evaluation should avoid leakage between related compounds, fit preprocessing on training data, and report split, metric, and applicability domain.</p>"},{"location":"explanation/reproducibility/#units-and-references","title":"Units and references","text":"<p>Use explicitly named units such as <code>_angstrom</code>, <code>_ps</code>, and <code>_K</code> where available. Do not infer an energy unit from a generic <code>score</code> or <code>value</code> column. Per-tool reference tables distinguish quantities; inspect the engine's documented units and record conversions. pIC50 is a logarithmic quantity, not a concentration column.</p> <p>Cite CADDForge's exact component versions and the scientific engines, models, force fields, and methods used. Public method references point to authoritative upstream documentation. A CADDForge citation does not replace the underlying method citations.</p>"},{"location":"explanation/third-party-notices/","title":"Third-party Notices","text":"<p>CADDForge's own code is MIT licensed. Components included in the rendered documentation retain their upstream copyright and licence terms. The complete texts below accompany the site, its wheel, and its source distribution.</p>"},{"location":"explanation/third-party-notices/#theme-and-rendered-documentation","title":"Theme and rendered documentation","text":"Component Upstream Licence text Material for MkDocs squidfunk/mkdocs-material MIT ProperDocs properdocs/properdocs BSD mkdocstrings mkdocstrings/mkdocstrings ISC mkdocstrings-python mkdocstrings/python ISC Section Index oprypin/mkdocs-section-index MIT PyMdown Extensions facelessuser/pymdown-extensions MIT and upstream notices Material Extensions facelessuser/mkdocs-material-extensions MIT"},{"location":"explanation/third-party-notices/#javascript-and-styles","title":"JavaScript and styles","text":"<p>The reviewed theme assets come from Material for MkDocs 9.7.7. Its upstream dependency lockfile records the component versions. Dependencies used only to compile the theme are not themselves shipped as executable build tools.</p> <p>Documentation build dependencies pin this theme version, and the asset hook rejects a different installed version. When upgrading, review the actual bundled components and update the dependency pins, <code>scripts/site_assets.py</code>, this inventory, and the full licence texts together. These build dependencies are not required to view the installed documentation.</p> Component Version Upstream Licence text clipboard.js 2.0.11 zenorocha/clipboard.js MIT good-listener / delegate / select 1.2.2 / 3.2.0 / 1.1.2 Zeno Rocha MIT tiny-emitter 2.1.0 scottcorgan/tiny-emitter MIT escape-html 1.0.3 component/escape-html MIT focus-visible 5.2.1 WICG/focus-visible Upstream notice, W3C terms fuzzaldrin-plus 0.6.0 jeancroy/fuzzaldrin-plus MIT Lunr 2.3.9 olivernn/lunr.js MIT RxJS 7.8.2 ReactiveX/rxjs Apache 2.0 tslib helpers 2.7.0 microsoft/tslib 0BSD Preact 10.28.2 preactjs/preact MIT material-design-color 2.3.2 mrmlnc/material-color MIT material-shadows 3.0.1 mrmlnc/material-shadows MIT <p>Copyrights include Martin Donath (theme), Zeno Rocha (clipboard and listener utilities), Scott Corgan (emitter), TJ Holowaychuk, Andreas Lubbe and Tiancheng Gu (escaping), WICG contributors (focus), Jean Christophe Roy and GitHub Inc. (fuzzy matching), Oliver Nightingale (search), Google, Netflix, Microsoft and contributors (RxJS), Jason Miller (Preact), and Denis Malinochkin (colours and shadows). The licence files preserve the individual notices.</p> <p>The fuzzaldrin-plus notice was checked against the redistributed original in IAH's licence inventory and Siemens' software notices. The material-shadows copyright was checked against UNIVERGE BLUE's software notices and its upstream MIT declaration.</p> <p>The site provides English search. The build excludes unused language-search assets, including <code>lunr-languages</code>, TinySegmenter and Wordcut. English search uses Lunr inside the retained search worker. Adding another search language requires reviewing and shipping its licences and any corresponding source obligations before enabling it.</p>"},{"location":"explanation/third-party-notices/#icons","title":"Icons","text":"<p>Material embeds icons from these upstream collections; their original licences are preserved even when only a subset of icons is rendered.</p> Collection Upstream Licence text Font Awesome Free FortAwesome/Font-Awesome CC BY 4.0 icons, OFL fonts, MIT code Material Design Icons Templarian/MaterialDesign Upstream terms Octicons primer/octicons MIT Simple Icons simple-icons/simple-icons CC0 and upstream notices"},{"location":"explanation/third-party-notices/#scientific-runtime-dependencies","title":"Scientific runtime dependencies","text":"<p>PyOCL is installed separately as <code>pyocl-idor</code> and imported as <code>pyocl</code>. Its distribution supplies the Java helper classes and OpenChemLib runtime; these files are not copied into CADDForge wheels or documentation assets. The dependency retains its own licence and upstream notices. Refer to its published distribution and OpenChemLib upstream for attribution and redistribution terms.</p> <p>The <code>third-party/</code> workspace maintains two independent distributions published on PyPI. They are not vendored into CADDForge runtime wheels. Their upstream Python code and parameter data are unchanged; Uni-Dock Tools ships a rebuilt AutoGrid executable with its complete corresponding source.</p> Distribution Pinned upstream Terms and licence texts <code>caddforge-opencosmors</code> <code>0.0.1</code> openCOSMO-RS_py commit 64e42bb LGPLv3, incorporating GPLv3 <code>caddforge-unidock-tools</code> <code>1.1.3</code> Uni-Dock tag 1.1.3, commit caf3161 Python code: Apache-2.0; AutoGrid executable and parameter data: GPLv2 or later <p>The import names remain <code>opencosmorspy</code> and <code>unidock_tools</code>; the Uni-Dock Tools command remains <code>unidocktools</code>. The openCOSMO-RS licence follows the newer upstream <code>LICENSE</code> and confirmed licence-change commit; its historical README and setup metadata have an outdated GPLv2 statement. Its wheel must be supplied with its matching complete source archive and LGPL/GPL texts.</p> <p>The maintained Uni-Dock Tools distribution replaces its original prebuilt AutoGrid executable with a validated Linux x86-64 build reported as <code>4.2.9+caddforge.1</code>. Its source is AutoGrid v4.2.9 with a documented compatibility patch, including the GPL-licensed directional hydrogen-bond vector loop from commit c3d5eafe. The source, original GPL text, patch, offline build helper, and provenance are included in both its wheel and source archive. Wheel installation places these under <code>share/caddforge-unidock-tools/autogrid/</code> in the Python environment. The matching GCC 11.4.0 OpenMP runtime is statically linked under GPLv3-or-later with the GCC Runtime Library Exception 3.1. Its GPL text, exception, and original notice are included. The exception permits this GCC-compiled combination without changing the licences of the independent AutoGrid and Uni-Dock Tools code. The <code>manylinux_2_28_x86_64</code> wheel needs Linux x86-64 with glibc 2.28 or newer; users need no separate <code>libgomp</code> installation. The old ADFR Suite executable was identified exactly, but its complete source could not be verified; that binary is excluded. Optional BH-tree source files with conflicting MSMS-specific notices are neither distributed nor compiled.</p> <p>The original <code>AD4.1_bound.dat</code> retains the copyright of Garrett M. Morris, David S. Goodsell, Ruth Huey, and Arthur J. Olson. AutoGrid source retains The Scripps Research Institute notices. These components retain GPLv2-or-later terms; the complete distribution is not solely Apache-2.0. All 87 AD4 maps in the three public regression boxes match the original executable exactly. The direct atom scan is slower for AD4 grid generation; current default CADDForge receptor preparation does not generate those grids. GPU docking is not covered by those checks; see the release's validation report and source README for practical limits.</p> <p>Each distribution preserves upstream authors, original notices and licence files, and records the exact revision and runtime hashes in <code>UPSTREAM.json</code>. CADDForge itself remains MIT licensed; renaming these dependencies does not relicense them. Both published distributions were installed together from public PyPI in a fresh Python 3.12 Linux x86-64 environment. Imports, command help, the bundled AutoGrid executable and source hashes, and a water/ethanol COSMO-RS numerical regression passed. The edited QM and docking sources now require <code>caddforge-opencosmors==0.0.1</code> and <code>caddforge-unidock-tools==1.1.3</code>, respectively. Release updated components and test new bundle pins before claiming a complete public CADDForge installation.</p>"},{"location":"explanation/third-party-notices/#other-caddforge-packages","title":"Other CADDForge packages","text":"<p>The GUI ships React's full MIT text and copyright notice in <code>caddforge_gui/static/THIRD_PARTY_LICENSES</code>. Its Mol* helper loads the upstream viewer at runtime. The MD package preserves the vendored OpenBPMD notice and MIT licence in its source and wheel. Scientific method citations are listed separately in Method References.</p> <p>When changing the theme, frontend dependencies, or embedded assets, review and update these notices and full texts against the actual generated distribution. Preserve embedded upstream comments when rebuilding JavaScript and CSS.</p>"},{"location":"getting-started/","title":"Getting Started","text":"<p>Start here if you want to install CADDForge, discover installed tools, and run a first workflow.</p> <p>Most users interact with CADDForge through the <code>caddforge</code> CLI. Method developers and pipeline authors typically use both the CLI and the Python API.</p>"},{"location":"getting-started/#basic-workflow","title":"Basic workflow","text":"<ol> <li>Install the <code>caddforge</code> bundle, or core and selected plugins in the same Python environment.</li> <li>Use <code>caddforge list-tools</code> to check what the environment can run.</li> <li>Use <code>caddforge show-config <section> <tool></code> to generate or inspect a config template.</li> <li>Run the workflow with <code>caddforge run <section> <tool> --config config.yaml</code>.</li> <li>Inspect status, logs, and results through the CLI or Python API.</li> </ol>"},{"location":"getting-started/#tutorials-and-next-steps","title":"Tutorials and next steps","text":"<ul> <li>First local workflow: analyze supplied synthetic data through CLI and Python.</li> <li>First pipeline: select rows using two generic stages.</li> <li>GUI walkthrough: inspect the same recorded job in a browser.</li> <li>Troubleshooting: diagnose environment, configuration, and execution errors.</li> </ul>"},{"location":"getting-started/first-pipeline/","title":"First Pipeline: Select Rows in Two Stages","text":"<p>This tutorial requires only <code>caddforge-core</code>. It uses six invented rows and generic core stages; no scientific plugin, GPU, or external engine is required.</p> <p>Save scores.csv, pipeline.yaml, and run_pipeline.py in one writable directory. Run commands there so the registry and generated run directories stay together.</p> <pre><code>caddforge pipeline run --config pipeline.yaml\n</code></pre> <p>The first stage sorts scores in ascending order and retains one row per ligand. The second receives <code>best_per_ligand.filtered_table</code>, then keeps the best two rows globally. Input CSV paths for built-in stages resolve relative to the pipeline YAML.</p> <p>Use the printed pipeline ID:</p> <pre><code>caddforge pipeline status <pipeline-id>\ncaddforge pipeline show-artifacts <pipeline-id>\n</code></pre> <p>Inspect the printed artifact paths. <code>best_per_ligand.csv</code> contains <code>a2</code>, <code>b1</code>, and <code>c1</code>; <code>shortlist.csv</code> contains <code>c1</code> followed by <code>a2</code>. The toy scores have no physical units and no scientific meaning.</p> <p>The equivalent Python execution is:</p> <pre><code>from caddforge.pipeline.executor import run_pipeline\n\nresult = run_pipeline(\"pipeline.yaml\", emit=print)\nprint(result.pipeline_job.id)\nprint(result.artifacts[\"shortlist.filtered_table\"].primary_path())\n</code></pre> <pre><code>python run_pipeline.py\n</code></pre> <p>Both commands create new runs. Stable stage IDs and artifact names connect the stages without guessing output directories. Group-wise selection preserves ligand coverage; selecting two rows globally in the first stage would answer a different question. Continue with artifact contracts, scatter and reduction, and advanced templates.</p>"},{"location":"getting-started/first-workflow/","title":"First Workflow: Analyze a Small Table","text":"<p>Run a local job, inspect its outputs, and repeat the same execution from Python. This example uses 30 invented numeric values. It needs no GPU, external engine, downloaded model, or molecular structures.</p>"},{"location":"getting-started/first-workflow/#prepare-the-environment-and-files","title":"Prepare the environment and files","text":"<p>See installation for release availability and source installation. In a compatible environment, install <code>caddforge-datascience</code>; it installs core automatically:</p> <pre><code>python -m pip install caddforge-datascience\ncaddforge list-tools\ncaddforge show-config datascience distribution-diagnostics\n</code></pre> <p>Download values.csv, diagnostics.yaml, and run.py into one new directory. Run commands from that directory. Keep CSV and YAML filenames unchanged. Downloaded <code>.py</code> and <code>.yaml</code> links may open as text; save them using the browser's save command.</p> <p>The configuration selects <code>backend: local</code>, a fixed seed, and ten histogram bins. Relative workflow input paths resolve from the submission working directory.</p> <pre><code>caddforge run datascience distribution-diagnostics --config diagnostics.yaml\n</code></pre> <p>The terminal prints a job ID. Substitute that ID in:</p> <pre><code>caddforge status <job-id>\ncaddforge logs <job-id>\ncaddforge list-jobs\n</code></pre> <p>Keep the same working directory for these commands so they use the same default registry. Job status should be <code>COMPLETED</code>. Under the job's <code>runs/<date>/<job-id>/results/</code>, expect:</p> File Meaning <code>distribution_report.json</code> Distribution report; <code>counts.n_total</code> is <code>30</code> <code>distribution_diagnostics.png</code> Histogram and Q\u2013Q plot <code>result_record.json</code> Canonical job result and named output paths <p>The configuration gives the scientific report its own filename, separate from canonical result records. Load reports through their named result paths. Normality p-values and recommendations describe this toy sample; they do not validate an assay or establish a scientific distribution. The values are dimensionless.</p>"},{"location":"getting-started/first-workflow/#run-from-python","title":"Run from Python","text":"<p>The supplied script uses the same configuration and core submission helpers:</p> <pre><code>python run.py\n</code></pre> <pre><code>from caddforge.core.execution import load_result_for_job, submit_workflow\nfrom caddforge.core.job import JobStatus\nfrom caddforge.plugins import get_tool\n\nspec = get_tool(\"datascience\", \"distribution-diagnostics\")\ncfg = spec.config_cls.from_yaml(\"diagnostics.yaml\")\nworkflow = spec.workflow_cls(cfg)\njob = submit_workflow(\n workflow, cfg, backend_name=cfg.backend,\n input_ports=tuple(getattr(spec.plugin, \"input_ports\", ())),\n)\nresult = load_result_for_job(job)\nif result is None or result.status != JobStatus.COMPLETED:\n raise RuntimeError(f\"Job {job.id} failed; inspect its logs\")\nprint(job.id)\nprint(result.paths[\"report_json\"])\nprint(result.metrics[\"n_total\"])\n</code></pre> <p>This creates a second job. Local submission waits for the workflow by default; asynchronous or Slurm submission requires checking completion before loading results. Continue with a small pipeline, or consult troubleshooting if a step fails.</p>"},{"location":"getting-started/installation/","title":"Installation","text":"<p>CADDForge packages require Python 3.10 or newer.</p>"},{"location":"getting-started/installation/#choose-the-smallest-environment-for-your-task","title":"Choose the smallest environment for your task","text":"<p>For the introductory table tutorial, core plus <code>caddforge-datascience</code> is sufficient; no molecular simulation environment is needed. For the generic pipeline tutorial, core alone is sufficient. Scientific plugin extras do not install every external engine, GPU driver, or prepared input dataset.</p> Component Declared Python support Validation / runtime scope Core and docs Python 3.10+ Package CI targets Linux/Python 3.10 and 3.12; local documentation checks also use 3.13 Full bundle Check each pinned component Release CI targets Linux/Python 3.10 and 3.12; validation of the updated public pins remains pending Scientific tools Check plugin and engine requirements External binaries, platform/GPU support, and conda stacks vary; this is not a claim of universal support"},{"location":"getting-started/installation/#install-conda-managed-scientific-stacks-first","title":"Install conda-managed scientific stacks first","text":"<p>If a plugin recommends installing scientific dependencies with conda, install those conda packages before installing any pip packages, including <code>caddforge</code>.</p> <p>Recommended order:</p> <pre><code>conda create -n caddforge python=3.10\nconda activate caddforge\n\n# Install heavy scientific/runtime packages first.\nconda install -c conda-forge openfe openff-toolkit rdkit pdbfixer openmm\n\n# Install published CADDForge components after the conda environment is complete.\npython -m pip install caddforge-core==0.22.0 caddforge-fep==0.7.0\n</code></pre> <p>Installing conda packages after pip packages have already populated the environment can replace or downgrade shared dependencies and leave the environment inconsistent. When in doubt, create a fresh environment and install the conda-managed stack first.</p> <p>For Slurm jobs, the compute node also needs access to this environment. See the Slurm backend guide for packed-environment and wheelhouse options.</p>"},{"location":"getting-started/installation/#install-caddforge-packages","title":"Install CADDForge packages","text":"<p>The updated public bundle is still awaiting its docs release, final component pins, and full installation validation. Install published components individually for now; the version matrix records availability. After that bundle release, this command installs the complete toolbox, including GUI integrations, MD minimization, ML UI dependencies, and installed documentation:</p> <pre><code>python -m pip install caddforge\n</code></pre> <p>The metadata-only <code>caddforge</code> bundle installs no modules or commands itself. Its copied metadata retains baseline <code>0.22.1</code> and pins core <code>0.21.0</code>, data science <code>0.4.0</code>, descriptors <code>0.10.1</code>, docking <code>0.6.1</code>, FEP <code>0.6.0</code>, GUI <code>0.5.0</code>, MD <code>0.12.1</code>, ML <code>0.3.0</code>, QM <code>0.2.1</code>, and docs <code>0.2.1</code> for local validation. These inherited pins do not identify releases of the edited public sources. New public component versions are tagged; the bundle pins must be updated and tested with the docs <code>0.3.0</code> before bundle publication. Documentation authoring and test extras are excluded from the runtime bundle.</p> <p>For core alone or selected plugins:</p> <pre><code>python -m pip install caddforge-core==0.22.0\npython -m pip install caddforge-qm==0.3.0\n</code></pre> <p>Plugins depend directly on <code>caddforge-core</code> and install it automatically. Python imports remain <code>caddforge</code> and CLI commands remain <code>caddforge ...</code>.</p> <p>The versions in the selective-install examples above are published on PyPI. MD <code>0.13.0</code> is tagged and awaiting publication as of 2026-10-15; use its tagged source checkout until it is available on the index. Full-bundle release validation targets Linux on Python 3.10 and 3.12. Installing Python plugins does not install external scientific engines.</p> <p>The bundle's <code>0.x</code> series remains initial development: its public API and supported component set may change. Version <code>1.0.0</code> is reserved for a future stable API commitment. The version matrix records tags and publication status.</p> <p>The core package includes <code>conda-pack</code> as a dependency. This provides <code>caddforge pack-env</code>, which creates managed Slurm environment archives and checksum sidecars without a separate <code>conda-pack</code> installation step.</p> <p>For editable installs from checkouts containing the public tags, select the versions in the version matrix and install without version overrides:</p> <pre><code>git -C ../caddforge-core switch --detach v0.22.0\npython -m pip install -e ../caddforge-core\ngit -C ../caddforge-docking switch --detach v0.7.0\npython -m pip install -e \"../caddforge-docking[gui]\"\n</code></pre> <p>For MD before its index publication, use a checkout containing <code>v0.13.0</code>:</p> <pre><code>git -C ../caddforge-md switch --detach v0.13.0\npython -m pip install -e ../caddforge-md\n</code></pre> <p>Published artifacts must use their actual public release versions. Temporary VCS version overrides are only for local validation, never for publishing.</p> <p>After installation, verify plugin discovery:</p> <pre><code>caddforge list-tools\n</code></pre>"},{"location":"getting-started/installation/#external-software","title":"External software","text":"<p>Some plugins call external scientific software that is not installed by the Python package. Examples include docking engines, QM programs, and MD engines. Check the plugin pages for tool-specific runtime requirements.</p>"},{"location":"getting-started/installation/#pyocl-and-java","title":"PyOCL and Java","text":"<p>QM, descriptors, and docking install the public <code>pyocl-idor>=0.8.3</code> distribution. MD can use it through those plugins, and the complete bundle includes it indirectly. The Python import package remains <code>pyocl</code>.</p> <p>Install Java 21 or newer and point <code>JAVA_HOME</code> to that installation. The affected GitHub workflows already provision Java 21. To check the Python package and JVM in the same environment:</p> <pre><code>python -m pip show pyocl-idor\njava -version\npython -c \"import pyocl; print(pyocl.__version__)\"\n</code></pre> <p>A JVM lookup or class-version error indicates a missing, misconfigured, or older Java runtime; see troubleshooting.</p>"},{"location":"getting-started/installation/#documentation-package","title":"Documentation package","text":"<p>The planned bundle includes docs automatically. Documentation <code>0.3.0</code> is tagged; its package-index publication is pending. Until then, install the source checkout with <code>python -m pip install ../caddforge-docs</code>. Once its public release is available, core-only and selective plugin installations can install it separately:</p> <pre><code>python -m pip install caddforge-docs\ncaddforge docs\n</code></pre> <p>After installing a bundle that includes docs, run <code>caddforge docs</code> directly.</p> <p>Installing this package enables the optional <code>docs</code> command on core 0.21.0 or later. It opens the documentation shipped with the installed docs release and serves until Ctrl+C. The built site works without internet access or documentation build tools. Use <code>--host</code> and <code>--port</code> to choose the listening address, or <code>--no-browser</code> to open the printed URL manually. See the CLI reference.</p>"},{"location":"getting-started/installation/#gui-extras","title":"GUI extras","text":"<p>The planned complete bundle includes GUI extras. For selective installations, install the plugin's GUI extra. Docking and FEP are published; the MD command requires its tagged release to be available on the index:</p> <pre><code>python -m pip install \"caddforge-docking[gui]\"\npython -m pip install \"caddforge-md[gui]\"\npython -m pip install \"caddforge-fep[gui]\"\n</code></pre>"},{"location":"getting-started/installation/#migrating-an-existing-installation","title":"Migrating an existing installation","text":"<p>Prefer a fresh environment. To migrate the former core distribution in place, install the renamed core and upgrade every retained plugin to its public release. The example below retains docking and QM; adapt that list to your environment:</p> <pre><code>python -m pip uninstall caddforge\npython -m pip install --upgrade caddforge-core caddforge-docking caddforge-qm\npython -m pip check\ncaddforge list-tools\n</code></pre> <p>For a minimal installation, install <code>caddforge-core</code> instead and upgrade every retained plugin to its migrated release. Keep older plugin releases in their original environments: they depend on the legacy <code>caddforge</code> core distribution. Do not install both old and renamed core distributions, because they own the same Python files. If both were installed, remove the old <code>caddforge</code> distribution and force-reinstall <code>caddforge-core</code> afterwards.</p> <p><code>importlib.metadata.version(\"caddforge-core\")</code> reports the runtime version; <code>version(\"caddforge\")</code> reports the optional bundle version. MD provenance distinguishes both. Historical configuration, registry, result, and provenance records remain readable. Uninstalling the new bundle does not uninstall its dependencies.</p>"},{"location":"gui/","title":"GUI","text":"<p>Package: <code>caddforge-gui</code></p> <p>Public source tag</p> <p>This page describes <code>caddforge-gui</code> <code>0.6.0</code>, tagged as <code>v0.6.0</code>. Published on PyPI; see the version matrix.</p> <p>The GUI package provides a FastAPI server, REST/SSE API, static frontend assets, and a plugin GUI extension runtime.</p>"},{"location":"gui/#launch-the-gui","title":"Launch the GUI","text":"<p>CLI:</p> <pre><code>caddforge gui --host 0.0.0.0 --port 8000\n</code></pre> <p>Python package install:</p> <pre><code>python -m pip install caddforge-gui\n</code></pre> <p>Open the server URL in a browser, for example:</p> <pre><code>http://localhost:8000\n</code></pre>"},{"location":"gui/#jobs-dashboard","title":"Jobs dashboard","text":"<p>The jobs dashboard uses server-side pagination and filtering for large registries. Search matches job ID, job name, tool name, section, and backend; page-size and navigation controls request only the visible result window. The underlying <code>/api/v1/jobs</code> endpoint accepts <code>limit</code> and <code>offset</code> and returns total-count metadata.</p>"},{"location":"gui/#install-plugin-gui-extensions","title":"Install plugin GUI extensions","text":"<p>Some plugins expose optional GUI extras:</p> <pre><code>python -m pip install \"caddforge-docking[gui]\"\npython -m pip install \"caddforge-md[gui]\"\npython -m pip install \"caddforge-fep[gui]\"\n</code></pre> <p>Install the GUI package, core package, and plugin extras in the same environment so extension entry points can be discovered. Check the version matrix first; MD <code>0.13.0</code> is tagged and awaiting PyPI publication as of 2026-10-15. Its tagged source installation is available while index publication is pending.</p> <p>GUI submissions forward each plugin's declared runtime input contracts to core. This allows the same transparent gzip staging and nested input behavior as CLI and pipeline submission.</p>"},{"location":"gui/#frontend-development","title":"Frontend development","text":"<pre><code>cd caddforge-gui/frontend\nnpm install\nnpm run build\n</code></pre> <p>The build writes static assets into <code>caddforge-gui/src/caddforge_gui/static</code>.</p>"},{"location":"gui/#plugin-ui-kit","title":"Plugin UI kit","text":"<p>Plugin GUIs can reuse shared frontend helpers from:</p> <pre><code>caddforge_gui/static/assets/plugin_uikit.js\n</code></pre> <p>The runtime URL for extensions is:</p> <pre><code>/static/assets/plugin_uikit.js\n</code></pre> <p>Shared backend helpers live in:</p> <pre><code>caddforge_gui.plugin_runtime\n</code></pre>"},{"location":"gui/plugin-authoring/","title":"Plugin GUI Authoring","text":"<p>Plugin GUI extensions should use the shared GUI runtime instead of duplicating common upload, staging, path-token, and job-listing behavior.</p>"},{"location":"gui/plugin-authoring/#frontend-helpers","title":"Frontend helpers","text":"<p>Import shared primitives from the plugin UI kit:</p> <pre><code>import { field, row, section, createFilePicker } from \"../../assets/plugin_uikit.js\";\n</code></pre> <p>Common helpers include:</p> <ul> <li><code>stepSection</code></li> <li><code>modeSwitchSection</code></li> <li><code>backendSection</code></li> <li><code>submitBar</code></li> <li><code>createPathOrFileInput</code></li> </ul> <p>Docking and pose-analysis pages can reuse the Mol* viewer helper:</p> <pre><code>import { createDockingMolstarViewer } from \"../../assets/molstar_viewer.js\";\n</code></pre>"},{"location":"gui/plugin-authoring/#backend-helpers","title":"Backend helpers","text":"<p>Extension routers can use:</p> <ul> <li><code>encode_path_token</code></li> <li><code>decode_path_token</code></li> <li><code>list_tool_jobs</code></li> <li><code>make_staging_dir</code></li> <li><code>store_upload</code></li> <li><code>move_to_directory</code></li> <li><code>ensure_job_within_run_dir</code></li> </ul> <p>For runtime concerns such as backend construction and job submission, use <code>caddforge.core.execution</code>.</p>"},{"location":"gui/walkthrough/","title":"Inspect a Job in the GUI","text":"<p>Complete the local workflow tutorial first. The GUI can display the same recorded jobs; a scientific GUI extension is not needed just to inspect an existing job.</p> <p>Install <code>caddforge-gui</code> into that environment and start from the tutorial directory:</p> <pre><code>python -m pip install caddforge-gui\ncaddforge gui --host 127.0.0.1 --port 8000 --registry-path runs/job_registry.sqlite\n</code></pre> <p>Open the printed URL. In the jobs dashboard, search for the tutorial's printed job ID or tool name, select the matching job, and inspect its recorded status and details. For a large registry, use filters and page navigation. Compare the displayed ID and status with <code>caddforge status <job-id></code> in another terminal.</p> <p>A missing job usually means the server is reading a different registry. Keep the registry path explicit and use the original tutorial directory. Output links and available tool panels depend on the installed packages and their GUI extensions; do not assume every scientific tool has a custom panel.</p> <p>Stop the server with Ctrl+C when finished. For remote use, configure an appropriate host and protected network route; see security guidance. The docs server and GUI are separate processes. Continue with GUI extension authoring if you are adding a tool interface.</p>"},{"location":"how-to/jobs/","title":"Manage Jobs and Results","text":"<p>Run these commands from the submission directory so they share the default registry:</p> <pre><code>caddforge list-jobs\ncaddforge status <job-id>\ncaddforge logs <job-id>\ncaddforge logs <job-id> --follow\n</code></pre> <p>Status refresh reconciles scheduler information with recorded results. Log following is useful during long runs; Ctrl+C stops following without cancelling the job. <code>logs</code> prints stdout; inspect <code>stderr.log</code> directly in the recorded run directory when needed. Core currently has no general CLI <code>cancel</code> command. Use your scheduler's cancellation mechanism when appropriate, then refresh status.</p> <p>After completion, use named result paths rather than assuming output directory names:</p> <pre><code>from caddforge.core.execution import load_result_for_job\nfrom caddforge.core.job_registry import get_job\n\njob = get_job(\"replace-with-your-job-id\")\nif job is None:\n raise LookupError(\"Job not found in this registry\")\nresult = load_result_for_job(job)\nif result is None:\n raise RuntimeError(\"No canonical result yet; check status and logs\")\nprint(result.status.value)\nprint(result.paths)\n</code></pre> <p><code>caddforge export <job-id></code> uses recorded submission metadata to copy completed outputs when an export is applicable. Do not remove scratch or original runs until outputs have been verified. <code>caddforge restart <job-id></code> creates a new recorded execution from the saved tool configuration; it does not guarantee engine-specific checkpoint continuation. Check <code>restart --help</code> and the tool's checkpoint inputs.</p> <p>For an isolated registry in Python, pass <code>registry_path</code> to submission and lookup helpers. CLI <code>status</code> and <code>logs</code> use the default registry; not every CLI command accepts an override. See the CLI reference and canonical result layout.</p>"},{"location":"how-to/scatter/","title":"Scatter Work and Reduce Results","text":"<p>Use scatter when independent input shards can run as separate jobs. Start with a small pipeline, then adapt a scientific pipeline template.</p> <pre><code># Template: replace tool, config, field, and result key with declared public ports.\nversion: 1\nname: batch-analysis\ndefaults:\n backend: local\nstages:\n - id: batch\n tool: section/tool\n config: tool.yaml\n scatter:\n by: input_path\n strategy: csv\n chunk_size: 100\n outputs:\n table: output_csv\n</code></pre> <p>This is a contract template, not a runnable tool configuration. Supply exactly one of <code>chunk_size</code> and <code>chunks</code>. Check the installed plugin's input field and reducer before applying scatter. Independent shards are submitted before waiting on Slurm.</p> <p>File scatter supports SDF, CSV, TSV, lines, and automatic detection. For path collections, a <code>Path</code> input receives one path per shard; a <code>list[Path]</code> input can receive chunks. Keep <code>scatter_manifest.csv</code> and reduced artifact metadata to join results back to inputs. Do not align independently sorted collections by row number.</p> <p>Molecular workflows often require pose-level execution followed by ligand-level selection. Use <code>poses.extract</code>, scatter the pose collection, collect metrics with <code>results.collect</code>, then apply <code>group_by: ligand</code> and <code>top_n_per_group</code>. Inspect manifests and failures before treating a reduced table as complete. See artifacts and built-ins.</p>"},{"location":"how-to/troubleshooting/","title":"Troubleshooting","text":"<p>Start with the smallest local example before troubleshooting a scientific engine or cluster.</p>"},{"location":"how-to/troubleshooting/#installation-or-tool-discovery-fails","title":"Installation or tool discovery fails","text":"<pre><code>python -m pip check\npython -m pip show caddforge-core\ncaddforge list-tools\n</code></pre> <p>Use the same environment for installation and execution. Plugins install core automatically but may need external executables or conda-managed dependencies. <code>pip check</code> checks Python dependency metadata; it cannot verify an engine or GPU. See installation and the selected plugin page. Candidate versions are not necessarily published; use compatible source tags until release.</p> <p>If discovery raises an import error, inspect its first failing module and install the plugin's declared requirements. Ordinary tool discovery loads installed entry points; a broken scientific plugin can prevent discovery. Do not assume it is ignored.</p>"},{"location":"how-to/troubleshooting/#pyocl-or-java-fails","title":"PyOCL or Java fails","text":"<p>Install the distribution <code>pyocl-idor>=0.8.3</code>; Python code imports it as <code>pyocl</code>. For a missing Python dependency:</p> <pre><code>python -m pip install \"pyocl-idor>=0.8.3\"\n</code></pre> <p>PyOCL requires Java 21 or newer. Set <code>JAVA_HOME</code> to the intended Java installation. If JPype cannot find <code>libjvm</code>, verify that this runtime is installed and discoverable. An <code>UnsupportedClassVersionError</code> can indicate an older Java runtime loading the bundled Java 21 helper classes. Check the runtime JPype finds:</p> <pre><code>java -version\npython -c \"import jpype; print(jpype.getDefaultJVMPath())\"\n</code></pre> <p>If the old <code>pyocl</code> distribution is installed, remove it before installing <code>pyocl-idor</code>: both distributions own the same Python import namespace. Restart Python or the notebook kernel after changing a running JVM.</p>"},{"location":"how-to/troubleshooting/#configuration-or-input-paths-are-rejected","title":"Configuration or input paths are rejected","text":"<pre><code>caddforge show-config datascience distribution-diagnostics\ncaddforge run datascience distribution-diagnostics --help\n</code></pre> <p>Compare fields and types with the installed tool's template. Supply required paths, including authoritative ligand chemistry for molecular workflows. CLI workflow inputs are resolved from the submission directory. Built-in pipeline <code>input_path</code> values resolve relative to the pipeline YAML. Use absolute paths when diagnosing ambiguity. A schema-valid file can still refer to missing or scientifically unsuitable input.</p>"},{"location":"how-to/troubleshooting/#job-is-missing-or-failed","title":"Job is missing or failed","text":"<pre><code>caddforge list-jobs\ncaddforge status <job-id>\ncaddforge logs <job-id>\n</code></pre> <p>Use the original working directory and registry. The logs command shows stdout; inspect <code>stderr.log</code> directly in the recorded run directory when needed. Inspect saved configuration, <code>metadata.json</code>, and the canonical result, then the first error in the logs. An absent result may mean the job is still running, not that it succeeded. Preserve the original run; use a new run directory for a corrected submission.</p>"},{"location":"how-to/troubleshooting/#slurm-submission-or-execution-fails","title":"Slurm submission or execution fails","text":"<p>Run <code>caddforge backend-check --help</code> and test the intended environment with that command. Check scheduler state, partition/account/time limits, environment accessibility, node filesystem permissions, and engine availability on the compute node. A successful <code>sbatch</code> submission does not establish scientific completion. See Slurm setup for packed environments and wheelhouses.</p>"},{"location":"how-to/troubleshooting/#browser-based-interfaces-do-not-open","title":"Browser-based interfaces do not open","text":"<p>For installed docs, verify <code>caddforge-docs</code> is installed in the CLI environment:</p> <pre><code>caddforge docs --host localhost --no-browser\n</code></pre> <p>Open the printed URL manually. Choose a different <code>--port</code> if the requested port is occupied. For a remote server, select a reachable <code>--host</code> and an appropriate network or SSH tunnel; loopback addresses are local to the machine running the server. GUI configuration and docs serving are separate commands.</p>"},{"location":"how-to/troubleshooting/#prepare-a-useful-bug-report","title":"Prepare a useful bug report","text":"<p>Include package versions, platform, backend, a minimal synthetic reproducer, expected behavior, actual behavior, and the relevant error. Remove credentials, personal paths, unpublished compounds, receptor structures, and research data before sharing configurations, logs, screenshots, or registry files. See contributing.</p>"},{"location":"pipelines/","title":"Pipelines","text":"<p>CADDForge pipelines chain installed tool plugins and generic built-in stages through file-based artifacts. The pipeline layer lives in <code>caddforge-core</code>; scientific workflow logic stays in tool plugins.</p> <p>Run a pipeline:</p> <pre><code>caddforge pipeline run --config pipeline.yaml\n</code></pre> <p>Inspect it:</p> <pre><code>caddforge pipeline status <pipeline-id>\ncaddforge pipeline show-artifacts <pipeline-id>\n</code></pre> <p>Pipelines are designed for local and HPC execution, including scatter over large input files or collections of pose/molecule paths.</p>"},{"location":"pipelines/artifacts/","title":"Artifacts","text":"<p>Pipeline stages exchange <code>ArtifactRef</code> objects. Downstream stages should not guess tool-specific directory layouts.</p> <p>Important fields:</p> <ul> <li><code>path</code>: primary path or manifest path</li> <li><code>paths</code>: multi-path collection, such as one SDF file per retained pose</li> <li><code>metadata</code>: provenance hints, result path keys, scatter manifests, and path-collection markers</li> </ul>"},{"location":"pipelines/artifacts/#input-and-output-ports","title":"Input and output ports","text":"<p>Tool plugins can expose pipeline contracts:</p> <ul> <li><code>InputPort(name, kind, config_field)</code> maps a pipeline input to a config field.</li> <li><code>OutputPort(name, kind, result_path_key)</code> maps a pipeline output to a key in <code>Result.paths</code>.</li> <li><code>OutputPort(..., required=False)</code> marks outputs that are produced only for some configurations. Missing optional result path keys are skipped instead of failing the stage.</li> <li><code>OutputPort(..., path_collection_column=\"pose_file\")</code> treats the result path as a CSV manifest and exposes the named column as <code>ArtifactRef.paths</code>. The artifact <code>path</code> remains the manifest, so downstream stages can keep both provenance and exact file paths.</li> </ul>"},{"location":"pipelines/artifacts/#scatter-provenance","title":"Scatter provenance","text":"<p>Scatter over file inputs supports SDF, CSV, line-based, and auto splitting. Scatter over path collections supports one job per path or chunked path lists depending on the downstream config field type.</p> <p>When a scattered tool stage receives multiple path-collection inputs and non-scatter inputs have the same number of paths as the scatter input, CADDForge binds those paths by row order for each shard. Use this for paired artifacts selected from the same manifest row, such as an ABFE ligand SDF plus its matching protein PDB. If the paired config field is typed as <code>list[Path]</code>, each shard receives a one-item list containing the matching path. Use this for tools such as plain MD where the complex PDB is a single <code>Path</code> but the matching ligand SDF is passed through a list field. The scatter field is still the only field listed under <code>scatter.by</code>.</p> <p>For path-collection scatter, the executor writes <code>scatter_manifest.csv</code> so downstream stages can join results back to ligand, pose, molecule, or source path metadata.</p> <p>When scattered CSV, TSV, SDF, JSONL, or manifest outputs are reduced to one canonical file, downstream stages bind that merged file and split it again according to their own scatter contract. The reduced artifact can still retain individual shard paths for provenance. Genuine path collections continue to bind their member paths directly.</p>"},{"location":"pipelines/artifacts/#compressed-boundaries","title":"Compressed boundaries","text":"<p>Pipeline tools can consume and produce <code>.csv.gz</code>, <code>.tsv.gz</code>, and <code>.sdf.gz</code> through explicit port contracts. Inputs stay compressed between stages and are materialized only at tool runtime. Core atomically compresses declared outputs after the tool succeeds and persists the finalized path in the canonical result. A scattered stage must use a unique compressed output destination per shard.</p>"},{"location":"pipelines/built-ins/","title":"Built-in Stages","text":"<p>Built-in pipeline stages are generic, reusable stages implemented in core. They use <code>type:</code> instead of <code>tool:</code> and have fixed output artifact names.</p>"},{"location":"pipelines/built-ins/#output-names","title":"Output Names","text":"Stage type Output artifacts <code>filter.tabular</code> <code>filtered_table</code> <code>filter.molecules</code> <code>molecules</code> <code>poses.extract</code> <code>manifest</code>, <code>poses</code> <code>molecules.hydrogenate</code> <code>manifest</code>, <code>molecules</code> <code>results.collect</code> <code>table</code> <p>Use these names in downstream references, for example <code>rank_top.filtered_table</code> or <code>collect_results.table</code>.</p>"},{"location":"pipelines/built-ins/#filtertabular","title":"<code>filter.tabular</code>","text":"<p>Filters, derives columns, sorts, keeps top rows, and supports group-wise selection for CSV-like tables.</p> <p>Input source\u2014configure exactly one:</p> <ul> <li><code>table</code>: upstream tabular artifact, usually a CSV/TSV score table or collected metrics table.</li> <li><code>config.input_path</code>: CSV/TSV source file, useful when <code>filter.tabular</code> is the first pipeline stage. Relative paths resolve against the pipeline specification directory.</li> </ul> <p>Optional inputs:</p> <ul> <li><code>ids</code>: CSV artifact containing identifiers to retain before filtering and ranking. By default, <code>filter.tabular</code> matches the <code>ligand</code> column in both tables; override this with <code>id_column</code> for the main table and <code>ids_column</code> for the identifier table.</li> </ul> <p>Output:</p> <ul> <li><code>filtered_table</code>: CSV table written to <code>output_name</code>.</li> </ul> <p>Common config keys:</p> <ul> <li><code>output_name</code>: output CSV filename, default <code>filtered_scores.csv</code>.</li> <li><code>input_delimiter</code>, <code>output_delimiter</code>: optional explicit delimiters; otherwise CSV/TSV selection follows the file suffix.</li> <li><code>derive</code>: mapping of new column names to restricted expressions.</li> <li><code>filters</code>: list of structured filters with <code>column</code>, <code>op</code>, and <code>value</code>.</li> <li><code>where</code>: restricted boolean expression evaluated per row.</li> <li><code>sort_by</code>: column used for sorting.</li> <li><code>ascending</code>: sort direction, default <code>true</code>.</li> <li><code>group_by</code>: column used for group-wise selection.</li> <li><code>top_n_per_group</code>: keep this many rows per group after sorting.</li> <li><code>top_n</code>: keep this many rows globally after filtering/sorting/grouping.</li> <li><code>columns</code>: optional output column order/subset.</li> <li><code>id_column</code>: column in <code>table</code> used with the optional <code>ids</code> input; default <code>ligand</code>.</li> <li><code>ids_column</code>: column in <code>ids</code> used with the optional <code>ids</code> input; defaults to <code>id_column</code>.</li> </ul> <p>Structured filter example:</p> <pre><code>- id: favorable_scores\n type: filter.tabular\n inputs:\n table: docking.scores\n config:\n filters:\n - column: best_score\n op: \"<=\"\n value: -8.0\n - column: ligand\n op: \"!=\"\n value: control\n sort_by: best_score\n ascending: true\n top_n: 500\n</code></pre> <p>Derived-column and expression example:</p> <pre><code>- id: efficient_ligands\n type: filter.tabular\n inputs:\n table: docking.scores\n config:\n derive:\n ligand_efficiency: best_score / heavy_atom_count\n where: ligand_efficiency <= -0.3 and heavy_atom_count > 12\n sort_by: ligand_efficiency\n ascending: true\n</code></pre> <p>Group-wise pose selection example:</p> <pre><code>- id: best_pose_per_ligand\n type: filter.tabular\n inputs:\n table: collected_pose_metrics.table\n config:\n group_by: ligand\n sort_by: comp_score\n ascending: true\n top_n_per_group: 1\n</code></pre> <p>Use <code>group_by</code> for ligand-level selection from pose-level tables. Global <code>top_n</code> alone selects the best rows across all ligands, not one pose per ligand.</p> <p>Identifier-retention example:</p> <pre><code>- id: retained_poses\n type: filter.tabular\n inputs:\n table: docking.flexible_complexes_manifest\n ids: retained_ligands.filtered_table\n config:\n id_column: ligand\n ids_column: ligand\n group_by: ligand\n sort_by: CNNscore\n ascending: false\n top_n_per_group: 4\n</code></pre> <p>Source-stage example:</p> <pre><code>- id: initial_filter\n type: filter.tabular\n config:\n input_path: data/measurements.tsv\n filters:\n - column: status\n op: \"==\"\n value: accepted\n output_name: accepted.tsv\n</code></pre>"},{"location":"pipelines/built-ins/#filtermolecules","title":"<code>filter.molecules</code>","text":"<p>Retains molecule or pose files based on a filtered table.</p> <p>Required inputs:</p> <ul> <li><code>molecules</code>: SDF file, directory, or path collection.</li> <li><code>ids</code>: table containing IDs or exact paths to retain.</li> </ul> <p>Output:</p> <ul> <li><code>molecules</code>: retained SDF or retained path collection.</li> </ul> <p>Common config keys:</p> <ul> <li><code>id_column</code>: identifier column, default <code>ligand_id</code>.</li> <li><code>path_column</code>: exact path column to retain, if available.</li> <li><code>output_name</code>: output SDF filename, default <code>filtered_molecules.sdf</code>.</li> </ul> <p>Preferred exact-path usage:</p> <pre><code>- id: retain_best_poses\n type: filter.molecules\n inputs:\n molecules: hydrogenated_poses.molecules\n ids: best_pose_per_ligand.filtered_table\n config:\n path_column: output_file\n</code></pre> <p>ID-based usage:</p> <pre><code>- id: retain_ligands\n type: filter.molecules\n inputs:\n molecules: docking.poses\n ids: top_scores.filtered_table\n config:\n id_column: ligand\n</code></pre> <p>Path columns are preferred because they avoid filename guessing. Auto-detected path columns include <code>pose_file</code>, <code>molecule_file</code>, <code>ligand_file</code>, <code>path</code>, and <code>file</code>.</p>"},{"location":"pipelines/built-ins/#posesextract","title":"<code>poses.extract</code>","text":"<p>Splits SDF/SDFGZ pose artifacts into one normalized SDF file per pose and writes a manifest.</p> <p>Required inputs:</p> <ul> <li><code>poses</code>: SDF/SDFGZ file, directory, or path collection.</li> </ul> <p>Optional inputs:</p> <ul> <li><code>table</code>: score/provenance table to join into the pose manifest.</li> </ul> <p>Outputs:</p> <ul> <li><code>manifest</code>: CSV with pose provenance.</li> <li><code>poses</code>: path collection of one-pose SDF files.</li> </ul> <p>Common config keys:</p> <ul> <li><code>output_name</code>: manifest filename, default <code>pose_manifest.csv</code>.</li> <li><code>ligand_column</code>: ligand ID column in the optional table, default <code>ligand</code>.</li> <li><code>path_column</code>: path column in the optional table; auto-detected when omitted.</li> <li><code>score_column</code>: optional SDF property to copy into the manifest.</li> <li><code>sort_by</code>, <code>ascending</code>: optional manifest sorting.</li> <li><code>top_n_per_ligand</code>: optional pose thinning after sorting.</li> </ul> <p>Example:</p> <pre><code>- id: extract_poses\n type: poses.extract\n inputs:\n poses: gnina.poses\n table: gnina.scores\n config:\n output_name: pose_manifest.csv\n ligand_column: ligand\n path_column: pose_file\n</code></pre> <p>The manifest includes stable columns such as <code>ligand</code>, <code>pose_id</code>, <code>pose_rank</code>, <code>pose_file</code>, <code>source_pose_file</code>, and <code>source_record_index</code>.</p>"},{"location":"pipelines/built-ins/#moleculeshydrogenate","title":"<code>molecules.hydrogenate</code>","text":"<p>Repairs missing explicit hydrogens after docking and writes one repaired SDF per input molecule or pose.</p> <p>Required inputs:</p> <ul> <li><code>molecules</code>: SDF file, directory, or path collection.</li> </ul> <p>Optional inputs:</p> <ul> <li><code>table</code>: metadata table to preserve ligand and pose IDs.</li> <li><code>templates</code>: SDF file, directory, or path collection containing reference molecules for template-based bond-order repair.</li> </ul> <p>Outputs:</p> <ul> <li><code>manifest</code>: CSV with repair provenance.</li> <li><code>molecules</code>: path collection of repaired SDF files.</li> </ul> <p>Common config keys:</p> <ul> <li><code>ligand_column</code>: ligand ID column, default <code>ligand</code>.</li> <li><code>pose_id_column</code>: pose ID column, default <code>pose_id</code>.</li> <li><code>template_path_column</code>: explicit template path column, <code>auto</code>, or empty. Default <code>auto</code>.</li> <li><code>on_template_failure</code>: <code>warn</code>, <code>fail</code>, or <code>ignore</code>. Default <code>warn</code>.</li> <li><code>on_failure</code>: <code>fail</code> or <code>warn</code>. Default <code>fail</code>.</li> <li><code>add_coords</code>: add coordinates for hydrogens, default <code>true</code>.</li> <li><code>output_name</code>: manifest filename, default <code>hydrogenated_manifest.csv</code>.</li> </ul> <p>Direct hydrogen addition:</p> <pre><code>- id: hydrogenate_poses\n type: molecules.hydrogenate\n inputs:\n molecules: extract_poses.poses\n table: extract_poses.manifest\n config:\n ligand_column: ligand\n pose_id_column: pose_id\n on_failure: fail\n</code></pre> <p>Template-based repair:</p> <pre><code>- id: hydrogenate_poses\n type: molecules.hydrogenate\n inputs:\n molecules: extract_poses.poses\n table: extract_poses.manifest\n templates: prepared_ligands.molecules\n config:\n template_path_column: auto\n on_template_failure: warn\n</code></pre> <p>Template-based repair is preferred when available because it can preserve protonation state and bond orders from prepared ligand inputs. The manifest records <code>template_file</code>, <code>template_status</code>, <code>hydrogens_before</code>, <code>hydrogens_after</code>, <code>status</code>, and <code>message</code>.</p>"},{"location":"pipelines/built-ins/#resultscollect","title":"<code>results.collect</code>","text":"<p>Collects JSON result files from scattered jobs, extracts configured JSON-path metrics, and joins scatter or table provenance.</p> <p>Required inputs:</p> <ul> <li><code>results</code>: result file, result path collection, or reduced scattered result artifact.</li> </ul> <p>Optional inputs:</p> <ul> <li><code>table</code>: provenance table. If omitted, <code>results.collect</code> uses scatter provenance when present.</li> </ul> <p>Output:</p> <ul> <li><code>table</code>: CSV table written to <code>output_name</code>.</li> </ul> <p>Common config keys:</p> <ul> <li><code>output_name</code>: output CSV filename, default <code>collected_results.csv</code>.</li> <li><code>metrics</code>: mapping of output column names to JSON paths.</li> </ul> <p>Example:</p> <pre><code>- id: collect_openbpmd\n type: results.collect\n inputs:\n results: openbpmd_per_pose.result_record\n config:\n metrics:\n comp_score: metrics.comp_score\n pose_score: metrics.pose_score\n output_name: openbpmd_pose_metrics.csv\n</code></pre> <p>If <code>metrics</code> is omitted, the stage collects <code>status</code>. JSON paths use dot notation through nested objects and lists, for example <code>metrics.delta_g_kcal_mol</code> or <code>extra.molecule_results.0.name</code>.</p>"},{"location":"pipelines/built-ins/#pose-level-pattern","title":"Pose-Level Pattern","text":"<p>For pose-level pipelines:</p> <ol> <li>Filter ligands or poses from docking tables.</li> <li>Use <code>poses.extract</code> to create one file per pose.</li> <li>Use <code>molecules.hydrogenate</code> before MD or free-energy stages that need explicit hydrogens.</li> <li>Scatter pose-level tools over the repaired path collection.</li> <li>Use <code>results.collect</code> to collect per-pose metrics.</li> <li>Use <code>filter.tabular</code> with <code>group_by: ligand</code> and <code>top_n_per_group</code> to select poses per ligand.</li> <li>Use <code>filter.molecules</code> with an exact path column to retain the selected files.</li> </ol>"},{"location":"pipelines/examples/","title":"Pipeline Examples","text":"<p>These examples show complete pipeline YAML files and the artifact flow between stages. Replace input paths and config filenames with project-specific files.</p> <p>Run any example with:</p> <pre><code>caddforge pipeline run --config pipeline.yaml\n</code></pre> <p>These are advanced configuration templates. They require prepared molecular/data inputs and the runtimes listed in the example library. Start with the small runnable pipeline. The supplied configs are schema-checked; full external-engine execution is a separate validation step. Replace all scientific paths and box coordinates before running.</p>"},{"location":"pipelines/examples/#download-configurations","title":"Download configurations","text":"<ul> <li>Docking Screen and Rescore</li> <li>Gnina Flexible Complex Artifacts</li> <li>Flexible Gnina to OpenBPMD to MD to ABFE</li> <li>Minimize a Multi-Pose SDF</li> <li>Dock, Extract, and Minimize Poses</li> <li>Pose-Level MD Selection</li> <li>Descriptor, Model, and Candidate Filter</li> <li>Result Collection and Diagnostics</li> </ul>"},{"location":"pipelines/examples/#docking-screen-and-rescore","title":"Docking Screen and Rescore","text":"<p>This pipeline screens a multi-record SDF with UniDock, filters by score, retains the selected poses, and rescores them with Gnina.</p> <pre><code>version: 1\nname: docking-screen-rescore\n\ndefaults:\n backend: local\n registry_path: runs/job_registry.sqlite\n job_name: docking-screen-rescore\n\nstages:\n - id: unidock_screen\n tool: docking/unidock\n config: configs/unidock.yaml\n scatter:\n # Split the ligand_multisdf_path field from configs/unidock.yaml into independent shards.\n by: ligand_multisdf_path\n chunk_size: 50000\n outputs:\n # Expose Result.paths[\"best_scores_csv\"] as unidock_screen.scores.\n scores: best_scores_csv\n # Expose Result.paths[\"poses_dir\"] as unidock_screen.poses.\n poses: poses_dir\n\n - id: top_unidock_scores\n type: filter.tabular\n inputs:\n # Read the score table produced by the previous stage.\n table: unidock_screen.scores\n config:\n sort_by: best_score\n ascending: true\n top_n: 1000\n output_name: top_unidock_scores.csv\n\n - id: retain_unidock_poses\n type: filter.molecules\n inputs:\n # Filter the pose directory/path collection from UniDock.\n molecules: unidock_screen.poses\n # Use the filtered table from filter.tabular; this built-in always emits filtered_table.\n ids: top_unidock_scores.filtered_table\n config:\n # The values in this column are matched against pose metadata or filenames.\n id_column: ligand\n\n - id: gnina_rescore\n tool: docking/gnina\n config: configs/gnina_rescore.yaml\n inputs:\n # Bind the retained pose path collection to GninaConfig.ligand_paths.\n ligand_paths: retain_unidock_poses.molecules\n outputs:\n # Expose Gnina result paths for downstream stages.\n scores: best_scores_csv\n poses: poses_dir\n</code></pre> <p>Artifact flow:</p> <ul> <li><code>unidock_screen.scores</code> points to the reduced UniDock <code>best_scores.csv</code>.</li> <li><code>unidock_screen.poses</code> points to the UniDock pose directory or path collection.</li> <li><code>top_unidock_scores.filtered_table</code> is the retained top-N table.</li> <li><code>retain_unidock_poses.molecules</code> is a retained pose path collection.</li> <li><code>gnina_rescore.scores</code> is the rescoring table for downstream ranking.</li> </ul>"},{"location":"pipelines/examples/#gnina-flexible-complex-artifacts","title":"Gnina Flexible Complex Artifacts","text":"<p>This pipeline runs flexible Gnina docking and uses the default Gnina output ports. The flexible artifact ports are optional: they exist only when flexible docking, <code>write_flex_receptor_output</code>, and <code>write_flexible_complexes</code> are enabled in <code>configs/gnina_flexible.yaml</code>.</p> <pre><code>version: 1\nname: gnina-flexible-artifacts\n\ndefaults:\n backend: local\n registry_path: runs/job_registry.sqlite\n job_name: gnina-flexible-artifacts\n\nstages:\n - id: gnina_flexible\n tool: docking/gnina\n config: configs/gnina_flexible.yaml\n\n - id: first_flexible_pose_per_ligand\n type: filter.tabular\n inputs:\n # This is the flexible_complexes_manifest.csv emitted by Gnina.\n table: gnina_flexible.flexible_complexes_manifest\n config:\n group_by: ligand\n sort_by: pose_rank\n ascending: true\n top_n_per_group: 1\n output_name: first_flexible_pose_per_ligand.csv\n\n - id: retain_ligand_pose_sdfs\n type: filter.molecules\n inputs:\n # Manifest-backed path collection from the ligand_pose_file column.\n molecules: gnina_flexible.flexible_ligand_poses\n ids: first_flexible_pose_per_ligand.filtered_table\n config:\n path_column: ligand_pose_file\n\n - id: retain_protein_pdbs\n type: filter.molecules\n inputs:\n # Manifest-backed path collection from the protein_file column.\n molecules: gnina_flexible.flexible_proteins\n ids: first_flexible_pose_per_ligand.filtered_table\n config:\n path_column: protein_file\n\n - id: retain_complex_pdbs\n type: filter.molecules\n inputs:\n # Manifest-backed path collection from the complex_file column.\n molecules: gnina_flexible.flexible_complexes\n ids: first_flexible_pose_per_ligand.filtered_table\n config:\n path_column: complex_file\n</code></pre> <p>Artifact flow:</p> <ul> <li><code>gnina_flexible.scores</code> is the normal <code>best_scores.csv</code>.</li> <li><code>gnina_flexible.flexible_complexes_manifest</code> is the per-pose flexible artifact manifest.</li> <li><code>gnina_flexible.flexible_ligand_poses</code>, <code>gnina_flexible.flexible_proteins</code>, and <code>gnina_flexible.flexible_complexes</code> are manifest-backed path collections.</li> <li>The retained artifacts contain matched ligand-only SDFs, protein-only PDBs, and complex PDBs for the selected flexible pose per ligand.</li> </ul>"},{"location":"pipelines/examples/#flexible-gnina-to-openbpmd-to-md-to-abfe","title":"Flexible Gnina to OpenBPMD to MD to ABFE","text":"<p>This pipeline keeps ligands whose best Gnina score is <code><= -12</code>, selects the top 4 flexible poses per retained ligand by <code>CNNscore</code> (larger is better), runs OpenBPMD on those protein-ligand complexes, keeps the pose with the best OpenBPMD <code>comp_score</code>, runs 10 ns plain MD for that complex, centers and aligns the MD trajectory, extracts a whole-frame cluster representative, and runs OpenFE ABFE on the representative protein-only PDB plus representative ligand-only SDF.</p> <pre><code>version: 1\nname: flexible-gnina-openbpmd-md-abfe\n\ndefaults:\n backend: slurm\n registry_path: runs/job_registry.sqlite\n job_name: flexible-gnina-openbpmd-md-abfe\n\nstages:\n - id: gnina_flexible\n tool: docking/gnina\n config: configs/gnina_flexible.yaml\n\n - id: retained_ligands\n type: filter.tabular\n inputs:\n # One row per ligand; best_score is lower-is-better affinity.\n table: gnina_flexible.scores\n config:\n filters:\n - column: best_score\n op: \"<=\"\n value: -12.0\n columns: [ligand, best_score]\n output_name: retained_ligands.csv\n\n - id: top4_flexible_poses\n type: filter.tabular\n inputs:\n # One row per generated flexible pose, with complex_file/protein_file/ligand_pose_file.\n table: gnina_flexible.flexible_complexes_manifest\n # Retain all poses belonging to ligands that passed the best-score cutoff.\n ids: retained_ligands.filtered_table\n config:\n id_column: ligand\n ids_column: ligand\n group_by: ligand\n sort_by: CNNscore\n ascending: false\n top_n_per_group: 4\n columns:\n - ligand\n - pose_rank\n - minimizedAffinity\n - CNNscore\n - complex_file\n - protein_file\n - ligand_pose_file\n output_name: top4_flexible_poses.csv\n\n - id: retain_top4_complexes\n type: filter.molecules\n inputs:\n # OpenBPMD should receive the full protein-ligand complex PDB.\n molecules: gnina_flexible.flexible_complexes\n ids: top4_flexible_poses.filtered_table\n config:\n path_column: complex_file\n\n - id: openbpmd_top4\n tool: md/openbpmd\n config: configs/openbpmd_pose.yaml\n inputs:\n # Bind each selected complex PDB to OpenBPMDConfig.complex_pdb_path.\n complex_pdb_path: retain_top4_complexes.molecules\n scatter:\n by: complex_pdb_path\n chunk_size: 1\n outputs:\n # results.collect resolves the sibling result_record.json from this path.\n result_record: results_json\n\n - id: collect_openbpmd\n type: results.collect\n inputs:\n results: openbpmd_top4.result_record\n # Preserve ligand, pose_rank, and the exact flexible artifact paths.\n table: top4_flexible_poses.filtered_table\n config:\n metrics:\n comp_score: metrics.comp_score\n pose_score: metrics.pose_score\n output_name: openbpmd_top4_metrics.csv\n\n - id: best_openbpmd_pose\n type: filter.tabular\n inputs:\n table: collect_openbpmd.table\n config:\n group_by: ligand\n sort_by: comp_score\n ascending: true\n top_n_per_group: 1\n columns:\n - ligand\n - pose_rank\n - comp_score\n - pose_score\n - complex_file\n - protein_file\n - ligand_pose_file\n - result_file\n output_name: best_openbpmd_pose_per_ligand.csv\n\n - id: retain_best_complexes\n type: filter.molecules\n inputs:\n # Plain MD receives the full protein-ligand complex PDB for the selected pose.\n molecules: gnina_flexible.flexible_complexes\n ids: best_openbpmd_pose.filtered_table\n config:\n path_column: complex_file\n\n - id: retain_best_ligands\n type: filter.molecules\n inputs:\n # Plain MD needs the ligand-only SDF for non-standard ligand parameterization.\n molecules: gnina_flexible.flexible_ligand_poses\n ids: best_openbpmd_pose.filtered_table\n config:\n path_column: ligand_pose_file\n\n - id: plain_md_10ns\n tool: md/plain-md\n config:\n simulation_time_ns: 10.0\n trajectory_filename: traj.xtc\n output_dir_name: output\n inputs:\n system_pdb_path: retain_best_complexes.molecules\n molecule_sdf_paths: retain_best_ligands.molecules\n scatter:\n # Scatter over complex PDBs. The matching ligand SDF is bound as a one-item\n # molecule_sdf_paths list for the same shard.\n by: system_pdb_path\n chunk_size: 1\n\n - id: center_align_md\n tool: md/center-and-align\n config:\n center_selection: protein or resname LIG\n keep_selection: protein or resname LIG\n align_selection: protein and backbone\n output_trajectory_filename: traj-centered.xtc\n output_structure_filename: traj-centered.pdb\n inputs:\n # The plain-md trajectory uses the prepared solvated system topology.\n topology_path: plain_md_10ns.prepared_structure\n trajectory_path: plain_md_10ns.trajectory\n scatter:\n by: trajectory_path\n chunk_size: 1\n outputs:\n centered_structure: output_structure\n centered_trajectory: output_trajectory\n\n - id: cluster_representative\n tool: md/cluster-representative\n config:\n ligand_resname: LIG\n cluster_selection: all\n extract_selection: all\n last_ns: 10.0\n output_complex_filename: representative_complex.pdb\n output_protein_filename: representative_protein.pdb\n output_ligand_sdf_filename: representative_ligand.sdf\n inputs:\n topology: center_align_md.centered_structure\n trajectory: center_align_md.centered_trajectory\n # The ligand template preserves bond orders, hydrogens, and formal charges in the exported SDF.\n ligand_template: retain_best_ligands.molecules\n scatter:\n by: trajectory_path\n chunk_size: 1\n\n - id: openfe_abfe\n tool: fep/openfe-abfe\n config: configs/openfe_abfe.yaml\n inputs:\n # ABFE receives separate files from the representative frame, not the original docked pose.\n ligand_sdf: cluster_representative.ligand\n protein_pdb: cluster_representative.protein\n scatter:\n # Scatter over ligand_sdf; the matching representative protein_pdb is bound by row order.\n by: ligand_sdf\n chunk_size: 1\n outputs:\n results: results_json\n</code></pre> <p>Input choices:</p> <ul> <li>Flexible Gnina must be configured with a PDB receptor, <code>output_format: sdf</code>, <code>write_flex_receptor_output: true</code>, and <code>write_flexible_complexes: true</code>.</li> <li>OpenBPMD receives <code>complex_pdb_path</code> from <code>gnina_flexible.flexible_complexes</code> because it operates on a full protein-ligand complex.</li> <li>Plain MD receives the selected complex PDB as <code>system_pdb_path</code> and the matching ligand-only SDF as a one-item <code>molecule_sdf_paths</code> list for ligand parameterization.</li> <li>The <code>LIG</code> residue name in <code>center_align_md</code> and <code>cluster_representative</code> must match <code>flexible_complex_ligand_resname</code> from the Gnina config.</li> <li><code>center_align_md</code> uses <code>plain_md_10ns.prepared_structure</code> as topology and <code>plain_md_10ns.trajectory</code> as trajectory.</li> <li><code>cluster_representative</code> uses the centered structure/trajectory and the matching ligand template SDF. <code>cluster_selection: all</code> makes the representative frame come from the whole retained centered system rather than ligand-only clustering.</li> <li>ABFE receives <code>ligand_sdf</code> and <code>protein_pdb</code> from the cluster representative. It does not receive the original docked ligand SDF, the original flexible protein PDB, or the representative complex PDB.</li> </ul>"},{"location":"pipelines/examples/#minimize-a-multi-pose-sdf","title":"Minimize a Multi-Pose SDF","text":"<p>Use <code>md/minimize</code> to clean up already prepared poses against one receptor snapshot. Install a plugin revision containing <code>minimize</code> (see the version matrix). Each SDF record must contain one explicitly hydrogenated 3D molecule with authoritative chemistry, already positioned in the receptor coordinate frame. The receptor PDB must have complete protein heavy atoms and no target ligand. A separate ligand file is not aligned automatically.</p> <p>Download the SDF scatter pipeline. Save it as <code>pipeline_scatter.yaml</code>, update the paths, and run:</p> <pre><code>caddforge pipeline run --config pipeline_scatter.yaml\n</code></pre> <pre><code># Poses must share receptor coordinates and contain explicit hydrogens.\n# Paths resolve relative to the submission working directory.\nversion: 1\nname: pose-cleanup\n\ndefaults:\n backend: local\n run_directory: runs/pose-cleanup\n registry_path: runs/job_registry.sqlite\n export_results: false\n\nstages:\n - id: minimize\n tool: md/minimize\n config:\n protein_pdb_path: receptor.pdb\n ligand_path: poses.sdf\n mode: ligand_only\n scatter:\n by: ligand_path\n chunk_size: 1\n outputs:\n results: result_record\n ligands: minimized_ligand\n\n - id: collect\n type: results.collect\n inputs:\n results: minimize.results\n config:\n metrics:\n quality_passed: metrics.quality_passed\n convergence: metrics.convergence_status\n ligand_rmsd_angstrom: metrics.ligand_heavy_atom_rmsd_angstrom\n minimized_ligand_file: paths.minimized_ligand\n\n - id: accepted\n type: filter.tabular\n inputs:\n table: collect.table\n config:\n filters:\n - column: quality_passed\n op: '=='\n value: 1\n</code></pre> <p><code>minimize.results</code> is the collection of canonical result records; <code>minimize.ligands</code> is the merged minimized SDF. <code>collect.table</code> has one row per job, while <code>accepted.filtered_table</code> retains only rows without quality flags. Its <code>minimized_ligand_file</code> column points to each final ligand SDF. No accepted rows is a valid result: inspect the rejected diagnostics before changing review thresholds. The table filter does not itself filter the merged SDF.</p> <p>SDF titles and properties survive minimization. For explicit <code>ligand</code> and <code>pose_id</code> columns joined by core scatter provenance, use the extracted path collection in the next example. Total minimization energy is local cleanup information, not a binding score or a basis for ranking unrelated ligands.</p>"},{"location":"pipelines/examples/#dock-extract-and-minimize-poses","title":"Dock, Extract, and Minimize Poses","text":"<p>This example runs rigid-receptor GNINA docking, extracts one SDF per pose, restores explicit hydrogens, and scatters minimization over that path collection. It uses the same receptor PDB for docking and cleanup. Supply a GNINA executable and the GNINA requirements, plus the minimize runtime.</p> <p>Download the docking/minimization pipeline. Run it with <code>caddforge pipeline run --config pipeline_docking.yaml</code> after replacing the <code>data/</code> paths.</p> <pre><code># Rigid-receptor GNINA docking followed by local OpenMM pose cleanup.\n# Supply GNINA on PATH and authoritative ligand chemistry in the input SDF.\nversion: 1\nname: dock-extract-minimize\n\ndefaults:\n backend: local\n run_directory: runs/dock-extract-minimize\n registry_path: runs/job_registry.sqlite\n export_results: false\n\nstages:\n - id: dock\n tool: docking/gnina\n config:\n receptor_path: data/receptor.pdb\n ligand_path: data/ligands.sdf\n autobox_ligand_path: data/reference_ligand.sdf\n output_format: sdf\n outputs:\n poses: poses_dir\n scores: best_scores_csv\n\n - id: extract\n type: poses.extract\n inputs:\n poses: dock.poses\n table: dock.scores\n config:\n ligand_column: ligand\n path_column: pose_file\n output_name: pose_manifest.csv\n\n - id: hydrogenate\n type: molecules.hydrogenate\n inputs:\n molecules: extract.poses\n table: extract.manifest\n config:\n on_template_failure: fail\n on_failure: fail\n add_coords: true\n\n - id: minimize\n tool: md/minimize\n config:\n protein_pdb_path: data/receptor.pdb\n mode: sidechains_free_backbone_restrained\n backbone_restraint_strength: moderate\n strict_quality: false\n inputs:\n ligand: hydrogenate.molecules\n scatter:\n by: ligand_path\n chunk_size: 1\n outputs:\n results: result_record\n ligands: minimized_ligand\n\n - id: collect\n type: results.collect\n inputs:\n results: minimize.results\n config:\n metrics:\n quality_passed: metrics.quality_passed\n quality_flag_count: metrics.quality_flag_count\n convergence: metrics.convergence_status\n ligand_rmsd_angstrom: metrics.ligand_heavy_atom_rmsd_angstrom\n minimized_complex_file: paths.minimized_complex\n minimized_ligand_file: paths.minimized_ligand\n diagnostics_file: paths.diagnostics\n output_name: minimized_pose_metrics.csv\n\n - id: accepted\n type: filter.tabular\n inputs:\n table: collect.table\n config:\n filters:\n - column: quality_passed\n op: '=='\n value: 1\n output_name: accepted_minimized_poses.csv\n</code></pre> <ul> <li><code>extract.poses</code> contains one SDF per pose; <code>extract.manifest</code> records ligand and pose IDs. Hydrogenation preserves those IDs in its output manifest.</li> <li>The minimize input port is <code>ligand</code>; the scatter config field is <code>ligand_path</code>. Each shard receives exactly one pose.</li> <li><code>results.collect</code> automatically joins the path-collection scatter manifest, preserving <code>ligand</code> and <code>pose_id</code> alongside minimization metrics.</li> <li><code>accepted.filtered_table</code> points to the minimized ligand and complex files. Original provenance columns such as <code>pose_file</code> or <code>output_file</code> can still identify the input poses; consume the explicit <code>minimized_*_file</code> columns for final structures, including any relaxed receptor coordinates.</li> </ul> <p>Direct hydrogen addition relies on the docking SDF's chemistry and cannot recover missing bond orders or an intended protonation state. Confirm those upstream. When an upstream stage provides authoritative templates, bind its artifact as <code>templates</code> on <code>hydrogenate</code> (see template-based repair). <code>on_template_failure: fail</code> prevents fallback if a configured template repair fails; it does not require a template to be present.</p> <p>For flexible-receptor docking, carry the matching per-pose receptor/complex through paired scatter; do not combine all ligand poses with the original rigid receptor. Keep <code>strict_quality: false</code> while collecting finite flagged poses for review. Strict mode deliberately fails rejected jobs and may stop the pipeline before collection. Hard chemistry, parameterization, and nonfinite-state errors always fail. A quality pass alone does not establish pose accuracy.</p> <p>For Slurm, change <code>defaults.backend</code> to <code>slurm</code> and supply the environment/resources from the Slurm guide. Choose resources per tool: the minimize stage defaults to CPU even when the upstream docking stage uses a GPU.</p>"},{"location":"pipelines/examples/#pose-level-md-selection","title":"Pose-Level MD Selection","text":"<p>This pipeline extracts one file per docking pose, repairs explicit hydrogens after docking, scatters OpenBPMD over each repaired pose, collects per-pose metrics, selects the best pose per ligand, and retains exact pose files.</p> <pre><code>version: 1\nname: pose-level-openbpmd-selection\n\ndefaults:\n backend: slurm\n registry_path: runs/job_registry.sqlite\n job_name: pose-level-openbpmd-selection\n\nstages:\n - id: gnina_docking\n tool: docking/gnina\n config: configs/gnina_docking.yaml\n outputs:\n # Expose the score CSV and pose directory from Result.paths.\n scores: best_scores_csv\n poses: poses_dir\n\n - id: extract_poses\n type: poses.extract\n inputs:\n # Split every SDF/SDFGZ pose from Gnina into a normalized one-pose SDF file.\n poses: gnina_docking.poses\n # Optional but useful: carries score-table metadata into the pose manifest.\n table: gnina_docking.scores\n config:\n output_name: pose_manifest.csv\n ligand_column: ligand\n path_column: pose_file\n\n - id: repair_pose_hydrogens\n type: molecules.hydrogenate\n inputs:\n # Hydrogenate the extracted pose path collection before handing poses to OpenBPMD.\n molecules: extract_poses.poses\n # Reuse the pose manifest so ligand, pose_id, and pose_file provenance are preserved.\n table: extract_poses.manifest\n config:\n ligand_column: ligand\n pose_id_column: pose_id\n # Without a templates input, hydrogens are added directly to each pose.\n # See the built-in stage docs for template-based bond-order repair.\n on_template_failure: warn\n on_failure: fail\n add_coords: true\n output_name: hydrogenated_pose_manifest.csv\n\n - id: openbpmd_per_pose\n tool: md/openbpmd\n config: configs/openbpmd_pose.yaml\n inputs:\n # Bind each hydrogenated pose file to OpenBPMDConfig.ligand_path.\n ligand_path: repair_pose_hydrogens.molecules\n scatter:\n # Path-collection scatter submits each ligand_path shard independently.\n by: ligand_path\n chunk_size: 1\n outputs:\n # Expose canonical per-shard result records for collection.\n result_record: result_record_json\n\n - id: collect_pose_metrics\n type: results.collect\n inputs:\n # Collect metrics from every scattered OpenBPMD result record.\n results: openbpmd_per_pose.result_record\n # Join explicit hydrogenation metadata to the collected metrics.\n table: repair_pose_hydrogens.manifest\n config:\n metrics:\n comp_score: metrics.comp_score\n pose_score: metrics.pose_score\n output_name: openbpmd_pose_metrics.csv\n\n - id: best_pose_per_ligand\n type: filter.tabular\n inputs:\n # results.collect always emits collect_pose_metrics.table.\n table: collect_pose_metrics.table\n config:\n group_by: ligand\n sort_by: comp_score\n ascending: true\n top_n_per_group: 1\n output_name: best_pose_per_ligand.csv\n\n - id: retain_best_poses\n type: filter.molecules\n inputs:\n # Filter the hydrogenated pose files, not the pre-repair Gnina poses.\n molecules: repair_pose_hydrogens.molecules\n ids: best_pose_per_ligand.filtered_table\n config:\n # Prefer exact file paths from the filtered table over filename matching.\n path_column: output_file\n</code></pre> <p>Artifact flow:</p> <ul> <li><code>extract_poses.poses</code> is a path collection with one SDF per pose.</li> <li><code>repair_pose_hydrogens.molecules</code> is the repaired path collection used by OpenBPMD.</li> <li>Path-collection scatter creates one OpenBPMD shard per repaired pose.</li> <li><code>results.collect</code> joins per-pose metrics to pose/hydrogenation provenance.</li> <li><code>filter.tabular</code> uses <code>group_by: ligand</code>; this selects the best pose per ligand rather than the globally best poses.</li> <li><code>retain_best_poses.molecules</code> contains exact selected repaired pose files.</li> </ul>"},{"location":"pipelines/examples/#descriptor-model-and-candidate-filter","title":"Descriptor, Model, and Candidate Filter","text":"<p>This pipeline computes descriptors, trains an ML model, and filters candidates by predicted value. The example assumes the descriptor and ML configs agree on the generated table columns.</p> <pre><code>version: 1\nname: descriptor-ml-filter\n\ndefaults:\n backend: local\n registry_path: runs/job_registry.sqlite\n\nstages:\n - id: psa_descriptors\n tool: descriptors/ani-psa\n config: configs/ani_psa_batch.yaml\n outputs:\n # Expose Result.paths[\"results_csv\"] as psa_descriptors.table.\n table: results_csv\n\n - id: train_gp\n tool: ml/simple-gp\n config:\n train_path: data/training_descriptors.csv\n target_column: measured_property\n # Use the table artifact from the descriptor stage as the GP test/prediction set.\n test_path: psa_descriptors.table\n feature_columns: [ani_psa, heavy_atom_count]\n kernel: rbf\n mode: train\n model_path: artifacts/simple_gp_model.joblib\n outputs:\n # Expose Result.paths[\"predictions_csv\"] as train_gp.predictions.\n predictions: predictions_csv\n\n - id: promising_candidates\n type: filter.tabular\n inputs:\n table: train_gp.predictions\n config:\n filters:\n - column: y_pred\n op: \">=\"\n value: 7.0\n sort_by: y_pred\n ascending: false\n top_n: 250\n output_name: promising_candidates.csv\n</code></pre> <p>Artifact flow:</p> <ul> <li><code>psa_descriptors.table</code> is the descriptor table.</li> <li><code>train_gp.predictions</code> is the model prediction table.</li> <li><code>promising_candidates.filtered_table</code> is the ranked filtered candidate list.</li> </ul>"},{"location":"pipelines/examples/#result-collection-and-diagnostics","title":"Result Collection and Diagnostics","text":"<p>This pipeline collects scattered JSON results, filters the collected metrics, and produces a distribution diagnostic report.</p> <pre><code>version: 1\nname: collect-and-diagnose\n\ndefaults:\n backend: local\n registry_path: runs/job_registry.sqlite\n\nstages:\n - id: scattered_transfer\n tool: qm/transfer-free-energy\n config: configs/transfer_free_energy.yaml\n scatter:\n # Split the ligand SDF into one-molecule transfer calculations.\n by: ligand_sdf\n chunk_size: 1\n outputs:\n # Expose per-shard canonical result records.\n result_record: result_record_json\n\n - id: collect_transfer\n type: results.collect\n inputs:\n results: scattered_transfer.result_record\n config:\n metrics:\n # Copy payload[\"metrics\"][\"delta_g_kcal_mol\"] into a table column.\n delta_g_kcal_mol: metrics.delta_g_kcal_mol\n output_name: transfer_metrics.csv\n\n - id: filter_transfer\n type: filter.tabular\n inputs:\n # results.collect emits collect_solvation.table.\n table: collect_transfer.table\n config:\n filters:\n - column: delta_g_kcal_mol\n op: \"<=\"\n value: -3.0\n sort_by: delta_g_kcal_mol\n ascending: true\n output_name: favorable_transfer.csv\n\n - id: distribution_report\n tool: datascience/distribution-diagnostics\n config:\n # Built-in stage outputs can be bound into inline tool config fields.\n csv_path: filter_transfer.filtered_table\n column: delta_g_kcal_mol\n bins: 50\n include_log: false\n outputs:\n report: report_json\n plot: plot_png\n</code></pre> <p>Artifact flow:</p> <ul> <li><code>scattered_transfer.result_record</code> is a reduced collection of result records.</li> <li><code>collect_transfer.table</code> joins JSON metrics to scatter provenance.</li> <li><code>filter_transfer.filtered_table</code> is a subset table.</li> <li><code>distribution_report.report</code> and <code>distribution_report.plot</code> summarize the filtered distribution.</li> </ul>"},{"location":"pipelines/specification/","title":"Pipeline Specification","text":"<p>Pipeline YAML uses stages. A stage is either a tool plugin stage or a generic built-in stage.</p> <pre><code>version: 1\nname: docking-screen\n\ndefaults:\n backend: local\n registry_path: runs/job_registry.sqlite\n\nstages:\n - id: unidock_screen\n tool: docking/unidock\n config: unidock.yaml\n scatter:\n by: ligand_multisdf_path\n chunk_size: 50000\n outputs:\n scores: best_scores_csv\n poses: poses_dir\n\n - id: top_score_filter\n type: filter.tabular\n inputs:\n table: unidock_screen.scores\n config:\n sort_by: best_score\n ascending: true\n top_n: 1000\n</code></pre>"},{"location":"pipelines/specification/#rules","title":"Rules","text":"<ul> <li><code>tool</code> stages resolve installed plugins through the registry.</li> <li><code>type</code> stages call generic built-ins from core.</li> <li><code>inputs</code> reference upstream artifacts as <code>stage_id.artifact_name</code>.</li> <li><code>outputs</code> map pipeline artifact names to workflow result path keys. If omitted, CADDForge uses the tool plugin's declared output ports.</li> <li>Optional plugin output ports are skipped when a workflow configuration does not produce the corresponding result path. Manifest-backed output ports can expose a CSV path column as a multi-path artifact for downstream scatter or filtering.</li> <li>If a scattered stage receives additional path-collection inputs with the same length as the scatter input, those paths are bound by row order for each shard. Use this only for paired artifacts selected from the same provenance table, such as ligand SDF and protein PDB files for the same pose. For paired <code>list[Path]</code> config fields, each shard receives a one-item list with its matching path.</li> <li>Scatter strategies include <code>sdf</code>, <code>csv</code>, <code>tsv</code>, <code>lines</code>, and <code>auto</code>. Plugin split contracts can apply these strategies to path members inside structured collection fields and declare deterministic output reducers.</li> <li>Stage IDs are part of the artifact API and should remain stable in examples.</li> <li>Defaults are applied centrally by the executor.</li> </ul>"},{"location":"plugins/","title":"Plugin Catalog","text":"<p>Plugins provide scientific workflows while reusing the shared CADDForge runtime.</p> <p>The central plugin pages summarize the available tools and their main inputs, outputs, and external requirements. The existing plugin <code>README.md</code> files remain in place for now and can continue to hold package-local details until the central docs fully replace them.</p> <p>See the plugin version matrix for the exact plugin revisions described by these pages.</p>"},{"location":"plugins/#available-plugin-packages","title":"Available plugin packages","text":"Plugin Tools Docking <code>unidock</code>, <code>idockya</code>, <code>gnina</code> Descriptors <code>ani-psa</code>, <code>phesa</code>, <code>ani-conformers</code>, <code>cluster-molecules</code> MD <code>fix-structure</code>, <code>center-and-align</code>, <code>extract-frames</code>, <code>cluster-representative</code>, <code>plain-md</code>, <code>minimize</code>, <code>openbpmd</code>, <code>grand</code>, <code>inducia</code> FEP <code>openfe-abfe</code>, <code>openfe-rbfe-septop</code> QM <code>transfer-free-energy</code> ML <code>simple-gp</code>, <code>pca</code>, <code>model-zoo</code>, <code>model-factory</code>, <code>explore-classifier</code> Data Science CSV scatter, distribution diagnostics"},{"location":"plugins/datascience/","title":"Data Science Plugin","text":"<p>Package: <code>caddforge-datascience</code></p> <p>Public source tag</p> <p>This page describes <code>caddforge-datascience</code> <code>0.5.0</code>, tagged as <code>v0.5.0</code>. Published on PyPI; see the version matrix.</p> <p>The data-science plugin provides generic data utilities as CADDForge tools.</p>"},{"location":"plugins/datascience/#requirements-and-example-status","title":"Requirements and example status","text":"<p>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.</p>"},{"location":"plugins/datascience/#csv-scatter","title":"<code>csv-scatter</code>","text":"<p>Generates an interactive Bokeh scatter plot from a CSV/TSV file with optional color mapping, molecule hover images, and threshold sliders.</p> <p>CLI:</p> <pre><code>caddforge show-config datascience csv-scatter\ncaddforge run datascience csv-scatter --config csv_scatter.yaml\n</code></pre> <p>YAML:</p> <pre><code>csv_path: data/metrics.csv\nx_column: feature_x\ny_column: feature_y\ncolor_column: feature_group\nsmiles_column: smiles\nid_column: compound_id\nhover_columns: [compute_time]\nx_scale: linear\ny_scale: log\npalette: viridis\nthresholds_enabled: true\nx_positive_direction: low\ny_positive_direction: high\npoint_size: 10\nalpha: 0.8\ntitle: Synthetic metrics\n</code></pre> <p>Python:</p> <pre><code>from caddforge.core.execution import load_result_for_job, submit_workflow\nfrom caddforge.plugins import get_tool\n\nspec = get_tool(\"datascience\", \"csv-scatter\")\ncfg = spec.config_cls(\n csv_path=\"data/metrics.csv\",\n x_column=\"feature_x\",\n y_column=\"feature_y\",\n color_column=\"feature_group\",\n backend=\"local\",\n)\nworkflow = spec.workflow_cls(cfg)\njob = submit_workflow(\n workflow, cfg, backend_name=cfg.backend,\n input_ports=tuple(getattr(spec.plugin, \"input_ports\", ())),\n)\nresult = load_result_for_job(job)\nif result is not None:\n print(result.paths)\n</code></pre> <p>Outputs to inspect:</p> <ul> <li><code>results/<plot html></code>: interactive plot</li> <li><code>results/result_record.json</code>: canonical result</li> </ul>"},{"location":"plugins/datascience/#distribution-diagnostics","title":"<code>distribution-diagnostics</code>","text":"<p>Diagnoses whether a numeric column looks approximately normal or log-normal using summary statistics, normality tests, histograms, and Q-Q plots.</p> <p>CLI:</p> <pre><code>caddforge show-config datascience distribution-diagnostics\ncaddforge run datascience distribution-diagnostics --config distribution_diagnostics.yaml\n</code></pre> <p>YAML:</p> <pre><code>csv_path: data/assay_values.csv\ncolumn: pIC50\nbins: 50\nclip_std: null\nsample_size: 5000\nrandom_seed: 0\ninclude_log: true\nplot_filename: distribution_diagnostics.png\nresults_filename: distribution_report.json\n</code></pre> <p>Python:</p> <pre><code>from caddforge.core.execution import load_result_for_job, submit_workflow\nfrom caddforge.plugins import get_tool\n\nspec = get_tool(\"datascience\", \"distribution-diagnostics\")\ncfg = spec.config_cls(\n csv_path=\"data/assay_values.csv\", column=\"pIC50\",\n results_filename=\"distribution_report.json\",\n)\nworkflow = spec.workflow_cls(cfg)\njob = submit_workflow(\n workflow, cfg, backend_name=cfg.backend,\n input_ports=tuple(getattr(spec.plugin, \"input_ports\", ())),\n)\nresult = load_result_for_job(job)\nif result is not None:\n print(result.paths[\"report_json\"])\n</code></pre> <p>Outputs to inspect:</p> <ul> <li><code>results/distribution_report.json</code>: statistics and diagnostics</li> <li><code>results/distribution_diagnostics.png</code>: diagnostic plot</li> <li><code>results/result_record.json</code>: canonical result</li> </ul>"},{"location":"plugins/datascience/#compressed-inputs","title":"Compressed inputs","text":"<p><code>csv-scatter</code> accepts <code>.csv.gz</code> and <code>.tsv.gz</code>; <code>distribution-diagnostics</code> accepts <code>.csv.gz</code>. CADDForge expands these inputs only at workflow runtime and preserves the configured compressed path in provenance.</p>"},{"location":"plugins/descriptors/","title":"Descriptors Plugin","text":"<p>Package: <code>caddforge-descriptors</code></p> <p>Public source tag</p> <p>This page describes <code>caddforge-descriptors</code> <code>0.11.0</code>, tagged as <code>v0.11.0</code>. Published on PyPI; see the version matrix.</p> <p>The descriptors plugin provides molecular descriptor, conformer, shape/pharmacophore similarity, and clustering workflows.</p>"},{"location":"plugins/descriptors/#requirements-and-example-status","title":"Requirements and example status","text":"<p>PheSA imports <code>pyocl</code>, supplied by <code>pyocl-idor>=0.8.3</code>. Its OpenChemLib runtime requires Java 21 or newer and a configured <code>JAVA_HOME</code>.</p> <p>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.</p>"},{"location":"plugins/descriptors/#ani-psa","title":"<code>ani-psa</code>","text":"<p>Computes ANI-weighted 3D polar surface area values from one molecule or a batch.</p> <p>CLI:</p> <pre><code>caddforge run descriptors ani-psa --config ani_psa.yaml\n</code></pre> <p>YAML:</p> <pre><code>smiles: \"CC(=O)Oc1ccccc1C(=O)O\"\nnum_confs: 100\ntop_n: 10\nrmsd_cluster_thresh: 0.5\nmax_mmff_iters: 200\ntemperature: 298.15\nmodel_name: ANI2x\ndevice: cpu\n</code></pre> <p>Python:</p> <pre><code>from caddforge.core.execution import load_result_for_job, submit_workflow\nfrom caddforge.plugins import get_tool\n\nspec = get_tool(\"descriptors\", \"ani-psa\")\ncfg = spec.config_cls(smiles=\"CCO\", top_n=10, backend=\"local\")\nworkflow = spec.workflow_cls(cfg)\njob = submit_workflow(\n workflow, cfg, backend_name=cfg.backend,\n input_ports=tuple(getattr(spec.plugin, \"input_ports\", ())),\n)\nresult = load_result_for_job(job)\nif result is not None:\n print(result.metrics)\n</code></pre> <p>Outputs to inspect: <code>results/results.json</code>, <code>results/results.csv</code>, <code>results/result_record.json</code>.</p>"},{"location":"plugins/descriptors/#phesa","title":"<code>phesa</code>","text":"<p>Computes OpenChemLib PheSA shape/pharmacophore similarities between one reference molecule and one or more candidate molecules. The reference can be provided as SMILES, SDF, or an encoded PheSA descriptor file; candidates can be provided as SMILES strings or single-/multi-compound SDF files.</p> <p>Reference mode</p> <p>When <code>reference_smiles</code> is used, the reference descriptor is generated with <code>DescriptorHandlerShape</code> and the configured <code>max_confs</code> and <code>pp_weight</code>. When <code>reference_sdf</code> is used, the reference descriptor is generated with OpenChemLib's one-conformation handler. Candidate descriptors always use <code>DescriptorHandlerShape</code> with the configured <code>max_confs</code> and <code>pp_weight</code>.</p> <p>Only SDF-reference mode restores aligned coordinates into the original SDF coordinate frame. SMILES and encoded-reference modes retain the generated PheSA alignment frame. Do not combine those coordinates with a receptor's coordinates without a validated transformation.</p> <p>Choose exactly one of <code>reference_smiles</code>, <code>reference_sdf</code>, and <code>reference_phesa_file</code>:</p> <pre><code># Template: supply one encoded descriptor generated by a compatible public runtime.\nreference_phesa_file: data/reference_phesa.txt\ncandidate_smiles: [\"CCCO\", \"CCCCO\"]\nmax_confs: 200\npp_weight: 0.5\n</code></pre> <p>The whole UTF-8 file contains one encoded descriptor; surrounding whitespace is stripped before decoding. This mode reuses its stored reference representation. <code>max_confs</code> and <code>pp_weight</code> configure generated candidate descriptors. The aligned reference and candidate SDFs, including any merged candidate SDF, remain in the PheSA frame because an original SDF-coordinate transformation is unavailable.</p> <p>CLI:</p> <pre><code>caddforge run descriptors phesa --config phesa.yaml\n</code></pre> <p>YAML:</p> <pre><code>reference_smiles: \"CCO\"\ncandidate_smiles:\n - \"CCCO\"\n - \"CCCCO\"\nmax_confs: 200\npp_weight: 0.5\n</code></pre> <p>SDF reference and candidate files:</p> <pre><code>reference_sdf: data/reference.sdf\ncandidate_sdf_files:\n - data/candidates.sdf\n - data/extra_candidate.sdf\nmax_confs: 200\npp_weight: 0.5\nwrite_merged_aligned_candidates_sdf: true\n</code></pre> <p>Each valid molecule record in <code>candidate_sdf_files</code> is scored as one candidate. PHESA uses SDF record names as <code>candidate_id</code> values when present, falling back to the source file stem plus the record index.</p> <p>Set <code>write_merged_aligned_candidates_sdf: true</code> to additionally write a merged SDF containing all successfully aligned candidate molecules. The default filename is <code>results/aligned/aligned_candidates.sdf</code> and can be changed with <code>merged_aligned_candidates_sdf_name</code>.</p> <p>Python:</p> <pre><code>from caddforge.core.execution import load_result_for_job, submit_workflow\nfrom caddforge.plugins import get_tool\n\nspec = get_tool(\"descriptors\", \"phesa\")\ncfg = spec.config_cls(\n reference_smiles=\"CCO\",\n candidate_smiles=[\"CCCO\", \"CCCCO\"],\n max_confs=200,\n pp_weight=0.5,\n backend=\"local\",\n)\nworkflow = spec.workflow_cls(cfg)\njob = submit_workflow(\n workflow, cfg, backend_name=cfg.backend,\n input_ports=tuple(getattr(spec.plugin, \"input_ports\", ())),\n)\nresult = load_result_for_job(job)\nif result is not None:\n print(result.metrics)\n</code></pre> <p>Pipeline stage:</p> <pre><code>stages:\n - id: phesa_similarity\n tool: descriptors/phesa\n config:\n reference_sdf: data/reference.sdf\n max_confs: 200\n pp_weight: 0.5\n inputs:\n candidates: upstream_stage.poses\n</code></pre> <p>Pipeline ports:</p> Direction Name Kind Config/result key Input <code>reference_sdf</code> file <code>reference_sdf</code> Input <code>reference_phesa_file</code> file <code>reference_phesa_file</code> Input <code>candidates</code> molecule collection <code>candidate_sdf_files</code> Output <code>scores</code> tabular scores <code>scores_csv</code> Output <code>aligned_reference</code> file <code>aligned_reference_sdf</code> Output <code>aligned_candidates</code> molecule collection <code>aligned_manifest_csv</code>, column <code>aligned_candidate_sdf</code> Output <code>merged_aligned_candidates</code> molecule collection <code>merged_aligned_candidates_sdf</code> <p>Outputs to inspect: <code>results/results.json</code>, <code>results/scores.csv</code>, <code>results/aligned/reference.sdf</code>, <code>results/aligned/candidates/</code>, <code>results/aligned/manifest.csv</code>, optional <code>results/aligned/aligned_candidates.sdf</code>, <code>results/result_record.json</code>.</p>"},{"location":"plugins/descriptors/#ani-conformers","title":"<code>ani-conformers</code>","text":"<p>Generates RDKit conformers, re-ranks representatives with ANI, and writes selected conformers to SDF.</p> <p>CLI:</p> <pre><code>caddforge run descriptors ani-conformers --config ani_conformers.yaml\n</code></pre> <p>YAML:</p> <pre><code>smiles: \"CCO\"\nnum_confs: 100\ntop_n: 10\nrmsd_cluster_thresh: 0.5\nmax_mmff_iters: 200\nmodel_name: ANI2x\ndevice: cpu\noutput_sdf_name: ranked_conformers.sdf\n</code></pre> <p>Python:</p> <pre><code>from caddforge.core.execution import load_result_for_job, submit_workflow\nfrom caddforge.plugins import get_tool\n\nspec = get_tool(\"descriptors\", \"ani-conformers\")\ncfg = spec.config_cls(smiles=\"CCO\", top_n=10, num_confs=50, backend=\"local\")\nworkflow = spec.workflow_cls(cfg)\njob = submit_workflow(\n workflow, cfg, backend_name=cfg.backend,\n input_ports=tuple(getattr(spec.plugin, \"input_ports\", ())),\n)\nresult = load_result_for_job(job)\nif result is not None:\n print(result.paths[\"ranked_conformers_sdf\"])\n</code></pre> <p>Outputs to inspect: <code>results/results.json</code>, <code>results/ranked_conformers.sdf</code>, <code>results/result_record.json</code>.</p>"},{"location":"plugins/descriptors/#cluster-molecules","title":"<code>cluster-molecules</code>","text":"<p>Clusters molecules from SMILES/SDF/CSV/TSV input using fingerprint or scaffold algorithms.</p> <p>CLI:</p> <pre><code>caddforge run descriptors cluster-molecules --config cluster_molecules.yaml\n</code></pre> <p>YAML:</p> <pre><code>input_path: data/molecules.csv\nsmiles_column: smiles\nid_column: compound_id\nalgorithm: kmedoids\nfingerprint_radius: 2\nfingerprint_bits: 2048\nkmedoids_n_clusters: 100\nkmedoids_mode: auto\nkmedoids_max_exact_n: 5000\nkmedoids_max_exact_pairs: 25000000\n</code></pre> <p>Python:</p> <pre><code>from caddforge.core.execution import load_result_for_job, submit_workflow\nfrom caddforge.plugins import get_tool\n\nspec = get_tool(\"descriptors\", \"cluster-molecules\")\ncfg = spec.config_cls(\n input_path=\"data/molecules.csv\",\n smiles_column=\"smiles\",\n algorithm=\"kmedoids\",\n kmedoids_n_clusters=100,\n backend=\"local\",\n)\nworkflow = spec.workflow_cls(cfg)\njob = submit_workflow(\n workflow, cfg, backend_name=cfg.backend,\n input_ports=tuple(getattr(spec.plugin, \"input_ports\", ())),\n)\nresult = load_result_for_job(job)\nif result is not None:\n print(result.metrics)\n</code></pre> <p>Outputs to inspect: <code>results/assignments.csv</code>, <code>results/centroids.csv</code>, <code>results/summary.json</code>, <code>results/result_record.json</code>.</p>"},{"location":"plugins/descriptors/#compressed-inputs","title":"Compressed inputs","text":"<p>File-based SMILES, SDF, CSV, and TSV inputs may use a trailing <code>.gz</code>. This includes nested cluster-molecule inputs. CADDForge expands wrappers in runtime scratch; plain inputs and native tool formats remain unchanged.</p>"},{"location":"plugins/docking/","title":"Docking Plugin","text":"<p>Package: <code>caddforge-docking</code></p> <p>Public source tag</p> <p>This page describes <code>caddforge-docking</code> <code>0.7.0</code>, tagged as <code>v0.7.0</code>. Published on PyPI; see the version matrix.</p> <p>The docking plugin provides docking and rescoring workflows through the standard CADDForge workflow/job interface.</p> <p>Install optional GUI support with:</p> <pre><code>python -m pip install \"caddforge-docking[gui]\"\n</code></pre>"},{"location":"plugins/docking/#requirements-and-example-status","title":"Requirements and example status","text":"<p>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.</p>"},{"location":"plugins/docking/#unidock","title":"<code>unidock</code>","text":"<p>Runs GPU-accelerated docking with Uni-Dock.</p> <p>CLI:</p> <pre><code>caddforge run docking unidock --config unidock.yaml\n</code></pre> <p>YAML:</p> <pre><code>receptor_path: data/protein.pdbqt\nligand_multisdf_path: data/ligands.sdf\ncenter_x: 0.0\ncenter_y: 0.0\ncenter_z: 0.0\nsize_x: 20\nsize_y: 20\nsize_z: 20\nsearch_mode: fast\nexhaustiveness: 384\nbatch_size: 50000\n</code></pre> <p>Python:</p> <pre><code>from caddforge.core.execution import load_result_for_job, submit_workflow\nfrom caddforge.plugins import get_tool\n\nspec = get_tool(\"docking\", \"unidock\")\ncfg = spec.config_cls(\n receptor_path=\"data/protein.pdbqt\",\n ligand_multisdf_path=\"data/ligands.sdf\",\n center_x=0.0,\n center_y=0.0,\n center_z=0.0,\n size_x=20.0,\n size_y=20.0,\n size_z=20.0,\n backend=\"local\",\n)\nworkflow = spec.workflow_cls(cfg)\njob = submit_workflow(\n workflow, cfg, backend_name=cfg.backend,\n input_ports=tuple(getattr(spec.plugin, \"input_ports\", ())),\n)\nresult = load_result_for_job(job)\nif result is not None:\n print(result.paths[\"best_scores_csv\"])\n</code></pre> <p>Outputs to inspect: <code>results/docked_out/</code>, <code>results/best_scores.csv</code>, <code>results/result_record.json</code>.</p> <p><code>exhaustiveness</code> defaults to <code>384</code> in this release. Set it explicitly when reproducing runs made with an earlier default.</p>"},{"location":"plugins/docking/#idockya","title":"<code>idockya</code>","text":"<p>Runs docking with OpenChemLib iDockYa through <code>pyocl</code>, provided by <code>pyocl-idor>=0.8.3</code>. Java 21 or newer is required; configure <code>JAVA_HOME</code> accordingly.</p> <p>CLI:</p> <pre><code>caddforge run docking idockya --config idockya.yaml\n</code></pre> <p>YAML:</p> <pre><code>receptor_path: data/receptor.sdf\nnative_ligand_path: data/native_ligand.sdf\nligand_path: data/ligands.sdf\noutput_format: sdf\noutput_dir_name: docked_out\nnum_workers: 0\nmax_inflight_tasks: 0\n</code></pre> <p>Python:</p> <pre><code>from caddforge.core.execution import load_result_for_job, submit_workflow\nfrom caddforge.plugins import get_tool\n\nspec = get_tool(\"docking\", \"idockya\")\ncfg = spec.config_cls(\n receptor_path=\"data/receptor.sdf\",\n native_ligand_path=\"data/native_ligand.sdf\",\n ligand_path=\"data/ligands.sdf\",\n output_format=\"sdf\",\n backend=\"local\",\n)\nworkflow = spec.workflow_cls(cfg)\njob = submit_workflow(\n workflow, cfg, backend_name=cfg.backend,\n input_ports=tuple(getattr(spec.plugin, \"input_ports\", ())),\n)\nresult = load_result_for_job(job)\nif result is not None:\n print(result.metrics)\n</code></pre> <p>Outputs to inspect: <code>results/docked_out/</code>, <code>results/best_scores.csv</code>, <code>results/result_record.json</code>.</p>"},{"location":"plugins/docking/#gnina","title":"<code>gnina</code>","text":"<p>Runs docking or rescoring with the <code>gnina</code> executable.</p> <p>CLI:</p> <pre><code>caddforge run docking gnina --config gnina.yaml\n</code></pre> <p>YAML:</p> <pre><code>receptor_path: data/protein.pdb\nligand_path: data/ligands.sdf\nautobox_ligand_path: data/reference_ligand.sdf\nflexres:\n - A:194\n - A:244\n - A:252\nscoring: dkoes_fast\ncnn_scoring: rescore\nno_gpu: true\noutput_format: sdf\n</code></pre> <p>Python:</p> <pre><code>from caddforge.core.execution import load_result_for_job, submit_workflow\nfrom caddforge.plugins import get_tool\n\nspec = get_tool(\"docking\", \"gnina\")\ncfg = spec.config_cls(\n receptor_path=\"data/protein.pdb\",\n ligand_path=\"data/ligands.sdf\",\n autobox_ligand_path=\"data/reference_ligand.sdf\",\n flexres=[\"A:194\", \"A:244\", \"A:252\"],\n no_gpu=True,\n backend=\"local\",\n)\nworkflow = spec.workflow_cls(cfg)\njob = submit_workflow(\n workflow, cfg, backend_name=cfg.backend,\n input_ports=tuple(getattr(spec.plugin, \"input_ports\", ())),\n)\nresult = load_result_for_job(job)\nif result is not None:\n print(result.paths[\"best_scores_csv\"])\n</code></pre> <p>Outputs to inspect: <code>results/docked_out/</code>, <code>results/gnina_logs/</code>, <code>results/best_scores.csv</code>, <code>results/result_record.json</code>.</p> <p>Flexible docking also writes raw gnina flex receptor outputs when <code>write_flex_receptor_output</code> is enabled. By default, gnina additionally writes one full protein-ligand complex PDB, one protein-only PDB, and one ligand-only SDF per generated pose under <code>results/flexible_complexes/</code>. These artifacts preserve protein and ligand hydrogen presence from the input receptor and ligand records. Ligand bond orders and formal charges are copied from the input ligand; PDB complexes emit ligand bond orders as <code>CONECT</code> records, but downstream support for interpreting those bond orders is reader-dependent. Use a PDB receptor for this mode, or set <code>write_flexible_complexes: false</code> to keep only the raw gnina outputs.</p> <p>Pipeline output aliases include <code>scores</code>, <code>poses</code>, <code>flexible_complexes_manifest</code>, <code>flexible_complexes</code>, <code>flexible_proteins</code>, and <code>flexible_ligand_poses</code>. The flexible artifact aliases are optional and are emitted only when flexible complex generation produces them.</p> <p>On Slurm, GNINA stages static inputs once in node-local scratch, synchronizes live text logs, and publishes completed ligand artifacts asynchronously. <code>log_sync_interval_seconds</code> and <code>result_sync_interval_seconds</code> control those retry intervals. A temporary shared-filesystem outage does not interrupt docking, but the workflow does not report success if required artifacts remain unpublished after the final retry.</p>"},{"location":"plugins/docking/#compressed-inputs","title":"Compressed inputs","text":"<p>Declared receptor, ligand, and reference inputs may use a trailing <code>.gz</code>; CADDForge expands them in runtime scratch. UniDock indexes can reference <code>.sdf.gz</code> ligands.</p>"},{"location":"plugins/fep/","title":"FEP Plugin","text":"<p>Package: <code>caddforge-fep</code></p> <p>Public source tag</p> <p>This page describes <code>caddforge-fep</code> <code>0.7.0</code>, tagged as <code>v0.7.0</code>. Published on PyPI; see the version matrix.</p> <p>The FEP plugin provides OpenFE-based absolute and relative free-energy workflows.</p> <p>Install optional GUI support with:</p> <pre><code>python -m pip install \"caddforge-fep[gui]\"\n</code></pre>"},{"location":"plugins/fep/#requirements-and-example-status","title":"Requirements and example status","text":"<p>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.</p>"},{"location":"plugins/fep/#openfe-abfe","title":"<code>openfe-abfe</code>","text":"<p>Prepares and optionally runs OpenFE absolute binding free energy calculations.</p> <p>The default is one independent protocol repeat per leg using <code>CUDA</code>. Protocol repeats are distinct from the lambda replicas within each leg.</p> Settings group NVT equilibration Equilibration Production Lambda replicas Analysis interval Complex pre-equilibration 0.10 ns 0.2 ns 0.5 ns \u2014 \u2014 Complex alchemical simulation \u2014 0.2 ns 2.0 ns 68 10 ps Solvent pre-equilibration 0.05 ns 0.1 ns 0.5 ns \u2014 \u2014 Solvent alchemical simulation \u2014 0.1 ns 1 ns 60 10 ps <p>The complex and solvent lambda schedules contain 68 and 60 entries respectively; solvent restraints remain zero throughout. The full schedules are shown below and included in the downloadable configuration.</p> <p>Defaults apply consistently through the CLI, Python API, and GUI. Omitted, empty, or partial <code>settings_overrides</code> dictionaries retain unspecified defaults; explicit entries replace individual values. An explicit <code>settings_overrides.protocol_repeats</code> entry takes precedence over top-level <code>protocol_repeats</code>. Explicit top-level <code>protocol_repeats: null</code> keeps OpenFE's repeat default. When changing lambda schedules, update all three arrays and <code>n_replicas</code> together for the affected leg.</p> <p>CLI:</p> <pre><code>caddforge run fep openfe-abfe --config abfe.yaml\n</code></pre> <p>YAML:</p> <pre><code>ligand_sdf: data/ligand_neutral.sdf\nprotein_pdb: data/protein_fixed.pdb\nligand_index: 0\nfix_protein_pdb: false\npartial_charge_method: am1bcc\noff_toolkit_backend: ambertools\nhost_min_distance_nm: 0.5\nhost_max_distance_nm: 1.5\nprotocol_repeats: 1\nsettings_overrides:\n complex_simulation_settings.real_time_analysis_interval: 10 picosecond\n complex_equil_simulation_settings.equilibration_length: 0.2 nanosecond\n complex_equil_simulation_settings.production_length: 0.5 nanosecond\n complex_equil_simulation_settings.equilibration_length_nvt: 0.10 nanosecond\n complex_simulation_settings.equilibration_length: 0.2 nanosecond\n complex_simulation_settings.production_length: 2.0 nanosecond\n complex_simulation_settings.n_replicas: 68\n solvent_equil_simulation_settings.equilibration_length: 0.1 nanosecond\n solvent_equil_simulation_settings.production_length: 0.5 nanosecond\n solvent_equil_simulation_settings.equilibration_length_nvt: 0.05 nanosecond\n solvent_simulation_settings.equilibration_length: 0.1 nanosecond\n solvent_simulation_settings.production_length: 1 nanosecond\n solvent_simulation_settings.real_time_analysis_interval: 10 picosecond\n solvent_simulation_settings.n_replicas: 60\n engine_settings.compute_platform: CUDA\n complex_lambda_settings.lambda_elec:\n [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,\n 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,\n 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]\n complex_lambda_settings.lambda_vdw:\n [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,\n 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,\n 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,\n 0.86, 0.87, 0.88, 0.89, 0.9, 0.91, 0.92, 0.94, 0.96, 0.98, 1]\n complex_lambda_settings.lambda_restraints:\n [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,\n 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,\n 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,\n 1, 1, 1, 1, 1, 1, 1]\n solvent_lambda_settings.lambda_elec:\n [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,\n 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,\n 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]\n solvent_lambda_settings.lambda_vdw:\n [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,\n 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,\n 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]\n solvent_lambda_settings.lambda_restraints:\n [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,\n 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]\nrun_quickrun: true\nquickrun_extra_args: []\n</code></pre> <p>Python:</p> <pre><code>from caddforge.core.execution import load_result_for_job, submit_workflow\nfrom caddforge.plugins import get_tool\n\nspec = get_tool(\"fep\", \"openfe-abfe\")\ncfg = spec.config_cls(\n ligand_sdf=\"data/ligand_neutral.sdf\",\n protein_pdb=\"data/protein_fixed.pdb\",\n ligand_index=0,\n run_quickrun=True,\n backend=\"local\",\n)\nworkflow = spec.workflow_cls(cfg)\njob = submit_workflow(\n workflow, cfg, backend_name=cfg.backend,\n input_ports=tuple(getattr(spec.plugin, \"input_ports\", ())),\n)\nresult = load_result_for_job(job)\nif result is not None:\n print(result.metrics)\n</code></pre> <p>Outputs to inspect: <code>abfe_json/</code>, <code>results/openfe_results.json</code>, <code>results/openfe_stdout.log</code>, <code>results/result_record.json</code>.</p>"},{"location":"plugins/fep/#openfe-rbfe-septop","title":"<code>openfe-rbfe-septop</code>","text":"<p>Plans, prepares, and optionally runs OpenFE SepTop RBFE campaigns.</p> <p>CLI:</p> <pre><code>caddforge run fep openfe-rbfe-septop --config rbfe_septop.yaml\n</code></pre> <p>YAML:</p> <pre><code>operation_mode: run\nligand_sdf: data/ligands.sdf\nligand_sdfs: []\nprotein_pdb: data/protein.pdb\ncomplexes: []\nproject_compound_ids:\n - proj_anchor_01\n - proj_anchor_02\nexport_network_bundle: true\nreuse_completed_edges: true\npartial_charge_method: am1bcc\noff_toolkit_backend: ambertools\nprotocol_repeats: 3\nsettings_overrides:\n engine_settings.compute_platform: CUDA\ncompatibility_threshold: 0.45\nproject_assignment_threshold: 0.35\nlocal_cluster_threshold: 0.55\nmax_clash_score: 0.35\ndiagnostic_cycle_edges_per_component: 1\nrun_quickrun: true\nquickrun_extra_args: []\n</code></pre> <p>Python:</p> <pre><code>from caddforge.core.execution import load_result_for_job, submit_workflow\nfrom caddforge.plugins import get_tool\n\nspec = get_tool(\"fep\", \"openfe-rbfe-septop\")\ncfg = spec.config_cls(\n ligand_sdf=\"data/ligands.sdf\",\n protein_pdb=\"data/protein.pdb\",\n project_compound_ids=[\"proj_anchor_01\", \"proj_anchor_02\"],\n operation_mode=\"run\",\n protocol_repeats=3,\n backend=\"local\",\n)\nworkflow = spec.workflow_cls(cfg)\njob = submit_workflow(\n workflow, cfg, backend_name=cfg.backend,\n input_ports=tuple(getattr(spec.plugin, \"input_ports\", ())),\n)\nresult = load_result_for_job(job)\nif result is not None:\n print(result.metrics)\n</code></pre> <p>Outputs to inspect: <code>network/ligand_network.json</code>, <code>results/ligand_network_bundle.zip</code>, <code>results/transformations_manifest.json</code>, <code>results/edge_records/</code>, <code>results/result_record.json</code>.</p>"},{"location":"plugins/fep/#compressed-inputs","title":"Compressed inputs","text":"<p>ABFE and RBFE ligand, protein, network, manifest, and bundle inputs accept transparent gzip where declared by the workflow, including paths nested in RBFE complex collections. Existing-network JSON and transformation manifests are read natively when required to preserve relative reference resolution; other wrappers are staged as plain runtime inputs.</p>"},{"location":"plugins/md/","title":"MD Plugin","text":"<p>Package: <code>caddforge-md</code></p> <p>Public source tag</p> <p>This page describes <code>caddforge-md</code> <code>0.13.0</code>, tagged as <code>v0.13.0</code>. PyPI publication is pending; see the version matrix.</p> <p>The MD plugin provides molecular dynamics preparation, simulation, and analysis workflows.</p>"},{"location":"plugins/md/#requirements-and-example-status","title":"Requirements and example status","text":"<p>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.</p>"},{"location":"plugins/md/#fix-structure","title":"<code>fix-structure</code>","text":"<p>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 <code>CONECT</code> records and PDB formal-charge fields by default.</p> <p>CLI:</p> <pre><code>caddforge run md fix-structure --config fix_structure.yaml\n</code></pre> <p>YAML:</p> <pre><code>backend: local\ninput_pdb: data/complex.pdb\nforcefields:\n - amber14-all.xml\n - amber14/tip3p.xml\nph: 7.0\nprotonation_policy: preserve\ntermini_policy: charged\nfix_nonstandard_residues: auto\nstrict_all: false\nmembrane_lipid_resnames:\n - POPC\n - POPE\nvalidate_openmm_system: auto\nsanitize_mixed_residues: auto\npreserve_conect: true\nquiet: false\noutput_dir_name: output\noutput_filename: null\n</code></pre> <p><code>validate_openmm_system: auto</code> keeps normal non-membrane behavior unchanged and runs an OpenMM <code>ForceField.createSystem()</code> validation when configured membrane lipid residues are present. <code>sanitize_mixed_residues: auto</code> 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; <code>amber14-all.xml</code> includes lipid templates and <code>amber14/tip3p.xml</code> covers TIP3P water and common ions. Unmatched ligands/cofactors left untouched by this repair tool are reported in <code>repair_metadata.openmm_system_validation.ignored_unmatched_residues</code> and must still be parameterized by the downstream MD workflow.</p> <p>Python:</p> <pre><code>from caddforge.core.execution import load_result_for_job, submit_workflow\nfrom caddforge.plugins import get_tool\n\nspec = get_tool(\"md\", \"fix-structure\")\ncfg = spec.config_cls(\n input_pdb=\"data/complex.pdb\",\n forcefields=[\"amber14-all.xml\", \"amber14/tip3p.xml\"],\n backend=\"local\",\n)\nworkflow = spec.workflow_cls(cfg)\njob = submit_workflow(\n workflow, cfg, backend_name=cfg.backend,\n input_ports=tuple(getattr(spec.plugin, \"input_ports\", ())),\n)\nresult = load_result_for_job(job)\nif result is not None:\n print(result.paths[\"output_pdb\"])\n</code></pre> <p>Outputs to inspect: <code>results/output/<input_stem>_fixed.pdb</code>, <code>results/result_record.json</code>. In pipelines, bind input artifact <code>structure</code> and consume output artifact <code>repaired_structure</code>.</p>"},{"location":"plugins/md/#terminal-chemistry","title":"Terminal chemistry","text":"<p><code>termini_policy</code> controls terminal chemistry separately from side-chain protonation:</p> Policy Behavior <code>charged</code> (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. <code>preserve</code> Preserve the input terminal state and validate against the supplied force fields. Ambiguous or incomplete terminal groups fail with a residue-specific explanation. <code>cap</code> Add ACE/NME caps to eligible neutral or incomplete termini. Already charged or capped termini remain unchanged. <p>A protonated, uncapped C-terminus (<code>\u2013COOH</code>) is neutral. A deprotonated C-terminus (<code>\u2013COO\u207b</code>) is charged. A neutral NME cap (<code>\u2013CONHCH\u2083</code>) is chemically different from an uncapped neutral acid. Capping can affect both N- and C-termini.</p> <p>Terminal policy takes precedence over <code>protonation_policy</code> only at terminal groups. Thus the default <code>charged</code> policy can remove HXT even when <code>protonation_policy: preserve</code>; 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.</p> <p>Stock OpenMM <code>amber14-all.xml</code> supports standard charged termini and ACE/NME caps, but does not include a neutral uncapped <code>\u2013COOH</code> template. To preserve that form, supply a compatible force field or additional, scientifically validated templates:</p> <pre><code>input_pdb: data/protein_with_explicit_terminal_states.pdb\nforcefields:\n - amber14-all.xml\n - /absolute/path/to/your_validated_terminal_templates.xml\nprotonation_policy: preserve\ntermini_policy: preserve\n</code></pre> <p>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 <code>validate_openmm_system: never</code> does not disable protein/cap template matching or terminal-state validation. This feature concerns terminal states of recognized protein residues, not arbitrary modified residues.</p> <p>For standard charged preparation, set <code>termini_policy: charged</code>. For explicitly requested caps, set <code>termini_policy: cap</code>; side-chain preservation can stay enabled. Approximate reconstructed atoms and caps should be minimized before production MD.</p> <p>Migration: <code>cap_neutral_termini: true</code> is a deprecated alias for <code>termini_policy: cap</code>. Combining it with an explicit different policy fails. <code>false</code> does not override the new policy. <code>fix_nonstandard_residues</code> now controls modified-residue normalization only; setting it to <code>never</code> does not disable terminal treatment. Users who previously relied on <code>protonation_policy: preserve</code> to reject terminal conversions must now also set <code>termini_policy: preserve</code>.</p> <p>The report at <code>paths.normalization</code> records requested/effective policy, original terminal evidence, normalization edits or added caps, final decisions, and validation status under <code>normalization.terminal_treatment</code>. It is retained on failure. Successful results also expose <code>repair_metadata.terminal_treatment</code>.</p>"},{"location":"plugins/md/#pipeline-example","title":"Pipeline example","text":"<pre><code>version: 1\nname: prepare-protein\nstages:\n - id: repair\n tool: md/fix-structure\n config:\n input_pdb: data/protein.pdb\n forcefields: [amber14-all.xml, amber14/tip3p.xml]\n protonation_policy: preserve\n termini_policy: charged\n outputs:\n protein: output_pdb\n - id: cleanup\n tool: md/minimize\n inputs:\n protein: repair.protein\n config:\n ligand_path: data/ligand.sdf\n mode: ligand_only\n</code></pre> <p>The input must be a protein-only receptor for this example; ligand chemistry is supplied separately to <code>minimize</code>. The repair tool's existing input port <code>structure</code> and output port <code>repaired_structure</code> are unchanged. The explicit <code>protein: output_pdb</code> output alias above binds the same PDB for the next stage.</p>"},{"location":"plugins/md/#center-and-align","title":"<code>center-and-align</code>","text":"<p>Centers, wraps, optionally aligns, and writes a processed trajectory.</p> <p>CLI:</p> <pre><code>caddforge run md center-and-align --config center_and_align.yaml\n</code></pre> <p>YAML:</p> <pre><code>backend: local\ntopology_path: data/system.pdb\ntrajectory_path: data/traj.xtc\ncenter_selection: protein or resname MOL\nkeep_selection: protein or resname MOL\nalign_selection: protein and backbone\nunwrap: true\ncenter: true\nwrap: true\ncenter_mode: geometry\nwrap_compound: residues\nalign_weights: mass\noutput_dir_name: output\noutput_trajectory_filename: traj-centered.xtc\noutput_structure_filename: traj-centered.pdb\n</code></pre> <p>Python:</p> <pre><code>from caddforge.core.execution import load_result_for_job, submit_workflow\nfrom caddforge.plugins import get_tool\n\nspec = get_tool(\"md\", \"center-and-align\")\ncfg = spec.config_cls(\n topology_path=\"data/system.pdb\",\n trajectory_path=\"data/traj.xtc\",\n center_selection=\"protein or resname MOL\",\n keep_selection=\"protein or resname MOL\",\n align_selection=\"protein and backbone\",\n backend=\"local\",\n)\nworkflow = spec.workflow_cls(cfg)\njob = submit_workflow(\n workflow, cfg, backend_name=cfg.backend,\n input_ports=tuple(getattr(spec.plugin, \"input_ports\", ())),\n)\nresult = load_result_for_job(job)\nif result is not None:\n print(result.paths)\n</code></pre> <p>Outputs to inspect: <code>results/output/<trajectory></code>, <code>results/output/<structure></code>, <code>results/result_record.json</code>.</p>"},{"location":"plugins/md/#extract-frames","title":"<code>extract-frames</code>","text":"<p>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.</p> <p>CLI:</p> <pre><code>caddforge run md extract-frames --config extract_frames.yaml\n</code></pre> <p>YAML:</p> <pre><code>backend: local\ntopology_path: data/traj-centered.pdb\ntrajectory_path: data/traj-centered.xtc\nligand_template_sdf: data/template.sdf\nligand_resname: UNK\nextract_selection: protein or resname UNK\nframe_index: null\nlast_ns: 10.0\nframe_slice: null\nwrite_per_frame_pdbs: false\noutput_dir_name: output\noutput_trajectory_filename: last-10ns.xtc\noutput_structure_filename: last-10ns_first_complex.pdb\nprotein_trajectory_filename: last-10ns_protein.xtc\nprotein_structure_filename: last-10ns_first_protein.pdb\nligand_sdf_filename: last-10ns_first_ligand.sdf\n</code></pre> <p>Use exactly one frame-selection mode. A Python-style slice is written as:</p> <pre><code>frame_index: null\nlast_ns: null\nframe_slice:\n start: 100\n stop: 1000\n step: 10\n</code></pre> <p>Python:</p> <pre><code>from caddforge.core.execution import load_result_for_job, submit_workflow\nfrom caddforge.plugins import get_tool\n\nspec = get_tool(\"md\", \"extract-frames\")\ncfg = spec.config_cls(\n topology_path=\"data/traj-centered.pdb\",\n trajectory_path=\"data/traj-centered.xtc\",\n ligand_template_sdf=\"data/template.sdf\",\n ligand_resname=\"UNK\",\n extract_selection=\"protein or resname UNK\",\n last_ns=10.0,\n backend=\"local\",\n)\nworkflow = spec.workflow_cls(cfg)\njob = submit_workflow(\n workflow, cfg, backend_name=cfg.backend,\n input_ports=tuple(getattr(spec.plugin, \"input_ports\", ())),\n)\nresult = load_result_for_job(job)\nif result is not None:\n print(result.paths)\n</code></pre> <p>Outputs to inspect: <code>results/output/<structure></code>, optional <code>results/output/<trajectory></code> for multi-frame selections, optional protein-only outputs, optional corrected ligand SDF, <code>results/output/frame_manifest.json</code>, <code>results/result_record.json</code>.</p>"},{"location":"plugins/md/#cluster-representative","title":"<code>cluster-representative</code>","text":"<p>Clusters selected trajectory frames and writes the medoid of the largest cluster. By default, ligand systems cluster on ligand heavy atoms when <code>ligand_resname</code> is provided, while <code>extract_selection</code> controls the atoms written to the representative complex PDB.</p> <p>CLI:</p> <pre><code>caddforge run md cluster-representative --config cluster_representative.yaml\n</code></pre> <p>YAML:</p> <pre><code>backend: local\ntopology_path: data/traj-centered.pdb\ntrajectory_path: data/traj-centered.xtc\nligand_template_sdf: data/template.sdf\nligand_resname: UNK\ncluster_selection: null\nextract_selection: protein or resname UNK\nlast_ns: 10.0\nframe_slice: null\ndistance_threshold_nm: 0.2\noutput_dir_name: output\noutput_complex_filename: largest_cluster_representative_complex.pdb\noutput_protein_filename: largest_cluster_representative_protein.pdb\noutput_ligand_sdf_filename: largest_cluster_representative_ligand.sdf\nclustering_summary_filename: clustering_summary.json\n</code></pre> <p>For slice-based clustering, replace <code>last_ns</code> with:</p> <pre><code>last_ns: null\nframe_slice:\n start: 1000\n stop: null\n step: 5\n</code></pre> <p>Python:</p> <pre><code>from caddforge.core.execution import load_result_for_job, submit_workflow\nfrom caddforge.plugins import get_tool\n\nspec = get_tool(\"md\", \"cluster-representative\")\ncfg = spec.config_cls(\n topology_path=\"data/traj-centered.pdb\",\n trajectory_path=\"data/traj-centered.xtc\",\n ligand_template_sdf=\"data/template.sdf\",\n ligand_resname=\"UNK\",\n extract_selection=\"protein or resname UNK\",\n last_ns=10.0,\n distance_threshold_nm=0.2,\n backend=\"local\",\n)\nworkflow = spec.workflow_cls(cfg)\njob = submit_workflow(\n workflow, cfg, backend_name=cfg.backend,\n input_ports=tuple(getattr(spec.plugin, \"input_ports\", ())),\n)\nresult = load_result_for_job(job)\nif result is not None:\n print(result.metrics)\n</code></pre> <p>Outputs to inspect: <code>results/output/largest_cluster_representative_complex.pdb</code>, optional <code>results/output/largest_cluster_representative_protein.pdb</code>, optional corrected ligand SDF, <code>results/output/clustering_summary.json</code>, <code>results/result_record.json</code>.</p>"},{"location":"plugins/md/#plain-md","title":"<code>plain-md</code>","text":"<p>Runs a standard OpenMM simulation, with optional OpenFF/SMIRNOFF parameterization for non-standard molecules. For already complete systems, <code>system_preparation_mode: prepared</code> 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 <code>barostat: membrane</code>.</p> <p>CLI:</p> <pre><code>caddforge run md plain-md --config plain_md.yaml\ncaddforge run md plain-md --config plain_md.yaml --backend slurm --slurm-time 24:00:00\n</code></pre> <p>YAML:</p> <pre><code>backend: local\nsystem_pdb_path: data/system.pdb\nmolecule_sdf_paths:\n - data/ligand.sdf\nph: 7.4\nsystem_preparation_mode: solvate\npadding: 1.0\nionic_strength: 0.15\nprepared_box_margin: 0.0\nnonbonded_cutoff: 1.0\nbarostat: isotropic\npressure: 1.0\nmembrane_surface_tension: 0.0\nmembrane_xy_mode: isotropic\nmembrane_z_mode: free\nbarostat_frequency: 25\ntemperature: 300.0\nstep_size_fs: 4\nsimulation_time_ns: 1.0\ntrajectory_filename: traj.xtc\nreport_interval: 10000\ncheckpoint_interval: 5000\noutput_dir_name: output\nseed: null\nrepeats: 1\ncheckpoint_path: null\nmax_restarts: 10\n</code></pre> <p>Python:</p> <pre><code>from caddforge.core.execution import load_result_for_job, submit_workflow\nfrom caddforge.plugins import get_tool\n\nspec = get_tool(\"md\", \"plain-md\")\ncfg = spec.config_cls(\n system_pdb_path=\"data/system.pdb\",\n molecule_sdf_paths=[\"data/ligand.sdf\"],\n simulation_time_ns=1.0,\n backend=\"local\",\n)\nworkflow = spec.workflow_cls(cfg)\njob = submit_workflow(\n workflow, cfg, backend_name=cfg.backend,\n input_ports=tuple(getattr(spec.plugin, \"input_ports\", ())),\n)\nresult = load_result_for_job(job)\nif result is not None:\n print(result.metrics)\n</code></pre> <p>Outputs to inspect: <code>results/output/*_prepared.pdb</code>, <code>results/output/*_minimized.pdb</code>, <code>results/output/traj.xtc</code>, <code>results/output/checkpoint.chk</code>, <code>results/result_record.json</code>. The standardized result exposes <code>prepared_structure</code>, <code>minimized_structure</code>, <code>trajectory</code>, <code>checkpoint</code>, and <code>output_dir</code> as pipeline-ready result paths.</p> <p>Set <code>repeats</code> above one to submit independent simulations with distinct reproducible seeds. The parent records child IDs and publishes <code>replicas_manifest.json</code> with aggregate completion metrics when every child reaches a terminal state. Slurm repeat children are submitted before the parent waits for results.</p> <p>For an already prepared system, use:</p> <pre><code>system_preparation_mode: prepared\nprepared_box_margin: 0.0\n</code></pre> <p>In this mode, <code>padding</code> and <code>ionic_strength</code> are ignored. The workflow derives an orthorhombic box from the existing coordinates plus <code>prepared_box_margin</code> and fails if the resulting box is too small for the configured nonbonded cutoff.</p> <p>For membrane systems oriented in the XY plane with the membrane normal along Z, use:</p> <pre><code>barostat: membrane\nmembrane_surface_tension: 0.0\nmembrane_xy_mode: isotropic\nmembrane_z_mode: free\nbarostat_frequency: 25\n</code></pre>"},{"location":"plugins/md/#minimize","title":"<code>minimize</code>","text":"<p>Minimize one protein\u2013ligand 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.</p>"},{"location":"plugins/md/#minimize-installation-and-execution","title":"Minimize installation and execution","text":"<p>Use an environment containing AmberTools (<code>pdb4amber</code>, <code>antechamber</code>, <code>parmchk2</code>, <code>tleap</code>), OpenMM >=8.2, ParmEd, RDKit, NumPy, SciPy, and this plugin. For example:</p> <pre><code>conda create -n caddforge-minimize -c conda-forge python=3.13 ambertools openmm=8.2 parmed rdkit numpy scipy\nconda activate caddforge-minimize\n# Use the reviewed public checkout, which includes the Amber validation fixes.\npython -m pip install -e '/path/to/caddforge-md[minimize]'\ncaddforge show-config md minimize\ncaddforge run md minimize --config minimize.yaml\n</code></pre> <p>Save this configuration as <code>minimize.yaml</code>, replacing the input paths:</p> <pre><code>complex_pdb_path: data/complex.pdb\nligand_path: data/ligand.sdf\nlig_resname: LIG\nmode: ligand_only\npocket_radius_angstrom: 5.0\nshell_radius_angstrom: 5.0\nbackbone_restraint_strength: moderate\nshell_restraint_strength: very_strong\ntolerance_kj_mol_nm: 10.0\nmax_iterations: 1000\nplatform: CPU\nrun_directory: runs/minimize-one-complex\n</code></pre> <p>For separate inputs, replace <code>complex_pdb_path</code> with <code>protein_pdb_path</code>; retain <code>ligand_path</code> in the same coordinate frame. To submit through Slurm, use <code>caddforge run md minimize --config minimize.yaml --backend slurm</code> with your site's backend/environment settings. There is no dedicated minimize GUI in this revision.</p> <p>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.</p> <pre><code>from cadd_md.minimize import MinimizeConfig, MinimizeWorkflow\n\nconfig = MinimizeConfig(\n complex_pdb_path=\"complex.pdb\",\n ligand_path=\"ligand.sdf\",\n lig_resname=\"LIG\",\n mode=\"pocket_restrained\",\n run_directory=\"runs/cleanup\",\n registry_path=\"runs/job_registry.sqlite\",\n)\nworkflow = MinimizeWorkflow(config)\nresult = workflow.run(workflow.prepare_job())\nprint(result.status, result.metrics)\nprint(result.paths[\"minimized_complex\"])\n</code></pre> <p>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 <code>run_directory</code> for each run; existing minimization outputs are preserved and cannot be overwritten by a new job.</p>"},{"location":"plugins/md/#minimize-inputs-and-physical-model","title":"Minimize inputs and physical model","text":"<p>Supply either <code>complex_pdb_path</code>, or <code>protein_pdb_path</code> plus a ligand pose in the same coordinate frame. <code>ligand_path</code> 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 <code>lig_resname</code>, which must occur exactly once.</p> <p>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 <code>residue_states</code> entry. Optional <code>protein_states_path</code> and <code>protein_links_path</code> must be provided together and match the input structure checksum. Covalent target ligands are rejected.</p> <p>Automatic preparation uses ff14SB, GAFF2/AM1-BCC, TIP3P retained waters, and OBC2 with <code>mbondi2</code> radii, ACE surface area, solute dielectric 1, solvent dielectric 78.5, and zero salt screening. It is nonperiodic with <code>NoCutoff</code>; 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 <code>parmchk2</code> parameter warnings fail preparation and require reviewed parameters.</p> <p>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.</p> <p>Preparation retains the original <code>*.raw.mol2</code>, the final MOL2, and a <code>*.charge_validation.json</code> report with raw/final sums, correction, policy limits, checksums, and the decision. <code>charge_validation</code> 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 <code>ligand_net_charge</code> or disabling quality checks is not a remedy for this error. These rules also apply to pipeline and scatter jobs without new YAML options.</p> <p>Automatic preparation also retains Antechamber's <code>ANTECHAMBER_AC.AC</code> 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.</p> <p>Amber MOL2 bond tokens are not always literal chemical bond orders: for example, carboxylate C\u2013O 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\u00e9 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.</p> <p>The charge report contains a <code>graph_validation</code> 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.</p> <p>Noncovalent organic cofactors require <code>cofactors</code> entries with <code>residue_index</code> and <code>chemistry_path</code>; optional <code>atom_map_path</code> uses the shared explicit atom-map schema against the original complex. Residue and atom indices are zero-based indices in <code>preparation/inputs/input_identity.json</code>, not PDB serial numbers. Automatic isolated ions are restricted to Na+, K+, and Cl- and require explicit <code>isolated_ion_residue_indices</code>. These declarations must not be used to stand in for a coordinated metal-site model.</p> <p>Coordinated metals, metall cofactors, and other supported supplied components use all three fields: <code>amber_topology_path</code>, <code>amber_coordinates_path</code>, and <code>amber_mapping_path</code>, 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 <code>metal_sites</code> entry containing <code>atom_index</code>, <code>oxidation_state</code>, <code>coordinating_atom_indices</code>, and a descriptive <code>parameter_source</code>.</p> <p>The prepared Amber mapping is JSON:</p> <pre><code>{\n \"schema_version\": 1,\n \"input_sha256\": \"SHA256 of the complex PDB\",\n \"topology_sha256\": \"SHA256 of the Amber topology\",\n \"coordinates_sha256\": \"SHA256 of the Amber coordinates\",\n \"input_atom_indices\": [0, 1, 2, null],\n \"input_residue_indices\": [0]\n}\n</code></pre> <p>The arrays follow Amber topology order and must cover every input atom/residue exactly once. <code>null</code> 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.</p>"},{"location":"plugins/md/#minimize-regions-and-modes","title":"Minimize regions and modes","text":"<p>Pocket residues have any heavy atom within <code>pocket_radius_angstrom: 5.0</code> of any ligand heavy atom. The shell contains other protein residues with any heavy atom within <code>shell_radius_angstrom: 5.0</code> of any pocket heavy atom. Both selections expand to whole residues and stay fixed during minimization. An explicit <code>pocket_residues: [\"A:42\", \"A:43:B\"]</code> replaces automatic pocket selection; <code>_</code> denotes a blank chain. Unknown, duplicate, ambiguous, or empty selections fail.</p> <code>mode</code> Pocket side chains Pocket backbone Shell <code>ligand_only</code> (default) Fixed Fixed Fixed <code>sidechains_free_backbone_fixed</code> Free Fixed Fixed <code>sidechains_free_backbone_restrained</code> Free Restrained Fixed <code>pocket_restrained</code> Free Restrained Strongly restrained <code>pocket_free_shell_restrained</code> Free Free Strongly restrained <p>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; <code>component_mobility</code> permits explicit whole-residue <code>fixed</code>, <code>free</code>, or <code>restrained</code> overrides keyed by input residue index.</p> <p>Positional restraints use E = \u00bd k |r \u2212 r\u2080|\u00b2, with k in kJ mol\u207b\u00b9 nm\u207b\u00b2. Named strengths are <code>weak: 100</code>, <code>moderate: 1000</code>, and <code>very_strong: 10000</code>; positive numeric strengths are accepted. <code>backbone_restraint_strength</code> defaults to <code>moderate</code>, and <code>shell_restraint_strength</code> to <code>very_strong</code>. They are starting regimes, not universally validated settings. Only heavy atoms are restrained unless <code>restrain_hydrogens: true</code>. Fixed regions include their hydrogens.</p> <p>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. <code>constraints: mobile_hbonds</code> 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.</p>"},{"location":"plugins/md/#minimize-convergence-diagnostics-and-quality","title":"Minimize convergence, diagnostics, and quality","text":"<p>Defaults are <code>tolerance_kj_mol_nm: 10.0</code>, <code>max_iterations: 1000</code>, <code>constraint_tolerance: 0.00001</code>, <code>platform: CPU</code>, and <code>cpu_threads: 1</code>. GPU platforms accept <code>precision: single|mixed|double</code> where supported. Explicitly requested unavailable platforms or precision settings fail instead of falling back.</p> <p>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 <code>verified_converged</code>, <code>iteration_limit</code>, <code>not_verified</code>, or <code>failed</code>; 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.</p> <p>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\u20134 overlaps, and declared metal coordination are reported separately. Bonded and angle-connected contacts are excluded.</p> <p>Default review triggers under <code>quality_thresholds</code> are:</p> Field Default <code>ligand_rmsd_angstrom</code> 1.0 <code>ligand_max_displacement_angstrom</code> 2.0 <code>pocket_backbone_rmsd_angstrom</code> 0.5 <code>receptor_max_displacement_angstrom</code> 2.0 <code>absolute_energy_per_atom_kj_mol</code> 10000 <code>maximum_force_kj_mol_nm</code> 100000 <code>allowed_vdw_overlap_angstrom</code> 0.6 <code>coordination_distance_change_angstrom</code> 0.3 <code>objective_increase_absolute_kj_mol</code> 0.01 <code>objective_increase_relative</code> 1e-8 <p>These are review triggers, not universal validity bounds; geometric overlaps, especially 1\u20134 contacts, need chemical interpretation. Nonfinite states, invalid chemistry, or any fixed-atom movement always fail. Other findings produce machine-readable <code>quality_flags</code>. Set <code>strict_quality: true</code> to fail on every flag, or combine <code>strict_quality: true</code> with <code>strict_quality_flags</code> to fail only on selected codes such as <code>convergence_unverified</code>, <code>large_ligand_displacement</code>, or <code>residual_nonbonded_clashes</code>. Finite strict-failure poses remain available.</p>"},{"location":"plugins/md/#minimize-artifacts-and-pipelines","title":"Minimize artifacts and pipelines","text":"<p>Canonical results are saved in <code>results/result_record.json</code>. Scientific artifacts are under <code>results/minimize/</code>: <code>structures/</code> contains minimized PDB/SDF files and fresh handoff records; <code>preparation/</code> contains the physical system, initial coordinates, atom mapping, and provenance; <code>minimization/</code> contains diagnostics, full-precision final coordinates, and the iteration trace. Exact region membership is in <code>results/minimize/regions.json</code>.</p> <p>Pipeline input names differ from config fields:</p> Input port Config field <code>complex</code> <code>complex_pdb_path</code> <code>protein</code> <code>protein_pdb_path</code> <code>ligand</code> <code>ligand_path</code> <code>ligand_atom_map</code> <code>ligand_atom_map_path</code> <code>protein_states</code>, <code>protein_links</code> <code>protein_states_path</code>, <code>protein_links_path</code> <code>amber_topology</code>, <code>amber_coordinates</code>, <code>amber_mapping</code> <code>amber_topology_path</code>, <code>amber_coordinates_path</code>, <code>amber_mapping_path</code> <p>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.</p> Output port <code>Result.paths</code> key <code>complex</code> <code>minimized_complex</code> <code>protein</code> <code>minimized_protein</code> <code>ligand</code> <code>minimized_ligand</code> <code>result_record</code> <code>result_record</code> <code>diagnostics</code> <code>diagnostics</code> <p>All other output ports below use the same name as their result-path key. Output ports expose <code>complex</code>, <code>protein</code>, <code>ligand</code>, <code>diagnostics</code>, <code>regions</code>, <code>atom_mapping</code>, <code>output_mapping</code>, <code>preparation_summary</code>, <code>physical_system</code>, <code>prepared_positions</code>, <code>minimized_positions</code>, <code>amber_topology</code>, <code>amber_coordinates</code>, <code>structure_manifest</code>, <code>protein_states</code>, <code>protein_links</code>, <code>receptor_protein_states</code>, <code>receptor_protein_links</code>, optional <code>iteration_trace</code>, <code>result_record</code>, and <code>output_dir</code>. The complex handoff records match the complex checksum; <code>receptor_*</code> 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.</p> <p>The ligand SDF preserves chemistry, title, and user properties. PDB coordinates are rounded to 0.001 \u00c5 and SDF coordinates to 0.0001 \u00c5; 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.</p> <p>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.</p> <p>Validation in the <code>caddforge-md</code> checkout: <code>python -m pytest tests/test_minimize_*.py</code>. Scientific fixtures use generated peptides/ligands and are marked <code>external</code>; optional GPU comparison tests skip with an explicit reason when no usable GPU platform exists.</p>"},{"location":"plugins/md/#openbpmd","title":"<code>openbpmd</code>","text":"<p>Runs OpenBPMD preprocessing, simulations, and averaged multi-replica analysis.</p> <p>CLI:</p> <pre><code>caddforge run md openbpmd --config openbpmd.yaml\n</code></pre> <p>YAML:</p> <pre><code>backend: local\ncomplex_pdb_path: data/complex.pdb\nlig_resname: MOL\nligand_net_charge: 0\nnreps: 10\nhill_height: 0.05\ndeposit_interval_ps: 1.0\nlog_sync_interval_seconds: 30\noutput_dir_name: output\nanalysis_dir_name: analysis\n</code></pre> <p>Python:</p> <pre><code>from caddforge.core.execution import load_result_for_job, submit_workflow\nfrom caddforge.plugins import get_tool\n\nspec = get_tool(\"md\", \"openbpmd\")\ncfg = spec.config_cls(\n complex_pdb_path=\"data/complex.pdb\",\n lig_resname=\"MOL\",\n nreps=10,\n hill_height=0.05,\n deposit_interval_ps=1.0,\n backend=\"local\",\n)\nworkflow = spec.workflow_cls(cfg)\njob = submit_workflow(\n workflow, cfg, backend_name=cfg.backend,\n input_ports=tuple(getattr(spec.plugin, \"input_ports\", ())),\n)\nresult = load_result_for_job(job)\nif result is not None:\n print(result.paths)\n</code></pre> <p>Outputs to inspect: <code>prep/</code>, <code>results/output/</code>, <code>results/analysis/results.csv</code>, <code>results/analysis/multi_rep_timeseries.csv</code>, <code>results/progress.json</code>, <code>results/result_record.json</code>.</p> <p>OpenBPMD streams subprocess output to scratch-first logs and publishes best-effort replica progress from the per-replica simulation logs. <code>log_sync_interval_seconds</code> controls publication frequency; temporary shared-filesystem failures are retried without changing the scientific outcome.</p>"},{"location":"plugins/md/#grand","title":"<code>grand</code>","text":"<p>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.</p> <p>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 <code>sphere_radius_angstrom</code>. 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.</p> <p>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 <code>ligand_resname</code> 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.</p> <p>CLI:</p> <pre><code>caddforge run md grand --config grand.yaml\ncaddforge run md grand --config grand.yaml --backend slurm --slurm-time 24:00:00\n</code></pre> <p>YAML:</p> <pre><code>backend: local\ncomplex_pdb_path: data/complex.pdb\nligand_template_sdf: data/ligand_template.sdf\nligand_resname: LIG\nsphere_radius_angstrom: 10.0\nn_ghost_waters: 150\nnonbonded_cutoff_nm: 0.9\ntemperature_kelvin: 300.0\ninitialization_passes: 7500\nmax_production_passes: 100000\nph: 7.4\npadding_nm: 1.2\nionic_strength_molar: 0.15\nligand_selection: null\nalign_selection: null\nframe_analysis_interval: 1\nlog_sync_interval_seconds: 30\noutput_dir_name: output\ntrajectory_filename: gcmc_trajectory.dcd\nghost_water_filename: ghost_water_state.txt\ncheckpoint_filename: gcmc_checkpoint.chk\nhydrated_complex_filename: hydrated_complex.pdb\ngcmc_summary_filename: gcmc_summary.json\nrepresentative_complex_filename: representative_complex.pdb\nrepresentative_protein_filename: representative_protein.pdb\nrepresentative_ligand_filename: representative_ligand.sdf\nselection_summary_filename: selection_summary.json\n</code></pre> <p>Python:</p> <pre><code>from caddforge.core.execution import load_result_for_job, submit_workflow\nfrom caddforge.plugins import get_tool\n\nspec = get_tool(\"md\", \"grand\")\ncfg = spec.config_cls(\n complex_pdb_path=\"data/complex.pdb\",\n ligand_template_sdf=\"data/ligand_template.sdf\",\n ligand_resname=\"LIG\",\n sphere_radius_angstrom=10.0,\n frame_analysis_interval=1,\n backend=\"local\",\n)\nworkflow = spec.workflow_cls(cfg)\njob = submit_workflow(\n workflow, cfg, backend_name=cfg.backend,\n input_ports=tuple(getattr(spec.plugin, \"input_ports\", ())),\n)\nresult = load_result_for_job(job)\nif result is not None:\n print(result.metrics)\n</code></pre>"},{"location":"plugins/md/#configuration-options","title":"Configuration options","text":"<p>GCMC sampling:</p> <ul> <li><code>complex_pdb_path</code>: input protein-ligand complex PDB (required, must exist, <code>.pdb</code>).</li> <li><code>ligand_template_sdf</code>: ligand template SDF providing bond orders and chemistry for ligand export (required, must exist, <code>.sdf</code>/<code>.sd</code>).</li> <li><code>ligand_resname</code>: residue name identifying the ligand in the complex (required, 1-4 characters, no whitespace or path separators). Used by both stages.</li> <li><code>sphere_radius_angstrom</code>: radius of the ligand-centred sampling region (default <code>10.0</code>, <code>> 0</code>).</li> <li><code>n_ghost_waters</code>: number of ghost waters added for insertion/deletion (default <code>150</code>, <code>>= 1</code>).</li> <li><code>nonbonded_cutoff_nm</code>: nonbonded interaction cutoff in nanometres (default <code>0.9</code>, <code>> 0</code>).</li> <li><code>temperature_kelvin</code>: simulation temperature in kelvin (default <code>300.0</code>, <code>> 0</code>).</li> <li><code>initialization_passes</code>: equilibration passes before production sampling (default <code>7500</code>, <code>>= 1</code>).</li> <li><code>max_production_passes</code>: maximum production passes (default <code>100000</code>, <code>>= 1</code>).</li> <li><code>ph</code>: pH used when adding hydrogens during preparation (default <code>7.4</code>).</li> <li><code>padding_nm</code>: solvent box padding in nanometres (default <code>1.2</code>, <code>> 0</code>).</li> <li><code>ionic_strength_molar</code>: ionic strength in molar used when solvating (default <code>0.15</code>, <code>>= 0</code>).</li> </ul> <p>Representative selection:</p> <ul> <li><code>ligand_selection</code>: atom selection for the ligand. Derived from <code>ligand_resname</code> (<code>resname <ligand_resname></code>) when left unset.</li> <li><code>align_selection</code>: optional atom selection used to align frames; blank is normalized to none.</li> <li><code>frame_analysis_interval</code>: analyse every Nth trajectory frame (default <code>1</code>, positive integer).</li> </ul> <p>Log syncing and output:</p> <ul> <li><code>log_sync_interval_seconds</code>: interval between periodic log-sync copies (default <code>30</code>, <code>1</code>-<code>3600</code> inclusive).</li> <li><code>output_dir_name</code>: directory under <code>results/</code> that holds stage outputs (default <code>output</code>, no path separators).</li> <li>Output filename fields (<code>trajectory_filename</code>, <code>ghost_water_filename</code>, <code>checkpoint_filename</code>, <code>hydrated_complex_filename</code>, <code>gcmc_summary_filename</code>, <code>representative_complex_filename</code>, <code>representative_protein_filename</code>, <code>representative_ligand_filename</code>, <code>selection_summary_filename</code>): plain filenames only, each with a documented default.</li> </ul> <p>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.</p>"},{"location":"plugins/md/#slurm-scratch-execution","title":"Slurm scratch execution","text":"<p>On the Slurm backend under an allocated runtime, the workflow executes both stages inside a node-local scratch working directory (rooted at <code>SLURM_TMPDIR</code>) 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.</p>"},{"location":"plugins/md/#log-syncing","title":"Log syncing","text":"<p>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 <code>log_sync_interval_seconds</code>, 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.</p>"},{"location":"plugins/md/#outputs","title":"Outputs","text":"<p>Stage outputs are written under <code>results/<output_dir_name>/</code>:</p> <ul> <li><code>gcmc_trajectory.dcd</code>: the GCMC sampling trajectory.</li> <li><code>ghost_water_state.txt</code>: per-frame ghost-water state file (one row per trajectory frame).</li> <li><code>gcmc_checkpoint.chk</code>: final-state checkpoint written before trajectory readback.</li> <li><code>hydrated_complex.pdb</code>: the hydrated-complex structure.</li> <li><code>gcmc_summary.json</code>: GCMC stage summary (produced paths, frame count, success/failure).</li> <li><code>representative_complex.pdb</code>: the selected representative protein-ligand-water complex.</li> <li><code>representative_protein.pdb</code>: optional protein-only representative structure.</li> <li><code>representative_ligand.sdf</code>: optional template-corrected representative ligand.</li> <li><code>selection_summary.json</code>: selection stage summary (selected frame index, success/failure).</li> <li><code>results/result_record.json</code> and <code>results/results.json</code>: the standardized result and a human-readable summary.</li> <li><code>results/progress.json</code>: best-effort stage, pass-count, acceptance-rate, water-count, and ETA snapshot.</li> </ul> <p>The standardized result exposes <code>representative_complex</code>, <code>representative_protein</code>, <code>representative_ligand</code>, <code>trajectory</code>, <code>checkpoint</code>, and <code>output_dir</code> as pipeline-ready result paths, and its <code>metrics</code> include <code>n_frames_sampled</code> and <code>representative_frame_index</code>.</p>"},{"location":"plugins/md/#compressed-inputs","title":"Compressed inputs","text":"<p>Declared MD structure, molecule, topology, trajectory, checkpoint, and restart inputs may use a trailing <code>.gz</code>. CADDForge expands transparent wrappers at runtime while preserving the original paths. Plain inputs retain their existing behavior.</p>"},{"location":"plugins/md/#inducia","title":"<code>inducia</code>","text":"<p>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.</p> <p>The modes are:</p> <ul> <li><code>template</code>: 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.</li> <li><code>independent</code>: run full flexible GNINA docking in an explicit box or autobox. The flexible-residue radius and maximum residue count are configurable.</li> </ul> <p>Every surviving GNINA pose is hydrated by GRAND. Hydrated poses with finite GNINA CNNscores are ordered from highest to lowest, and <code>openbpmd.max_input_complexes</code> limits how many continue to OpenBPMD. Final structures are ranked by ascending OpenBPMD CompScore.</p> <p>CLI:</p> <pre><code>caddforge show-config md inducia\ncaddforge run md inducia --config inducia.yaml\n</code></pre> <p>Template-mode configuration shape:</p> <pre><code>mode: template\nbackend: local\nreceptor_pdb_path: data/receptor.pdb\nquery_ligand_sdf_path: data/query.sdf\ntemplate_ligand_sdf_path: data/known_binder.sdf\nligand_resname: LIG\nmax_local_workers: 1\nphesa:\n max_confs: 200\nclustering:\n rmsd_cutoff_angstrom: 1.0\nclash_detection:\n allowed_vdw_overlap_angstrom: 0.4\ngnina:\n cnn_scoring: rescore\n pose_sort_order: CNNscore\ngrand:\n sphere_radius_angstrom: 10.0\n n_ghost_waters: 150\n initialization_passes: 7500\n max_production_passes: 100000\nopenbpmd:\n max_input_complexes: null\n nreps: 10\n hill_height: 0.05\n deposit_interval_ps: 1.0\nprogress:\n interval_seconds: 30\n</code></pre> <p>Independent mode replaces <code>template_ligand_sdf_path</code> with either <code>autobox_ligand_path</code> or both <code>box_center</code> and <code>box_size</code>. Its GNINA settings include <code>independent_flex_radius_angstrom</code> (default <code>3.5</code>) and <code>independent_max_flexible_residues</code> (default <code>12</code>).</p> <p>Child phases use at most <code>max_local_workers</code> 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 <code>caddforge logs <job-id> --follow</code>; detailed command logs remain in the child directories.</p> <p>Structured events are written to <code>results/inducia_progress.jsonl</code>. 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.</p> <p>Primary outputs are <code>best_complex</code>, <code>best_protein</code>, <code>best_ligand</code>, <code>ranking_table</code>, <code>pose_manifest</code>, ranked complex/protein/ligand path collections, and <code>output_dir</code>. 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.</p> <p>Inducia requires <code>caddforge-core>=0.21.0</code>, <code>caddforge-descriptors>=0.8.0</code>, and <code>caddforge-docking>=0.3.0</code>, plus the external scientific runtimes required by PheSA, GNINA, GRAND, OpenMM, AmberTools, and OpenBPMD.</p>"},{"location":"plugins/ml/","title":"ML Plugin","text":"<p>Package: <code>caddforge-ml</code></p> <p>Public source tag</p> <p>This page describes <code>caddforge-ml</code> <code>0.4.0</code>, tagged as <code>v0.4.0</code>. Published on PyPI; see the version matrix.</p> <p>The ML plugin exposes model training, prediction, dimensionality reduction, model sweeps, and classifier threshold exploration workflows.</p>"},{"location":"plugins/ml/#requirements-and-example-status","title":"Requirements and example status","text":"<p>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.</p>"},{"location":"plugins/ml/#simple-gp","title":"<code>simple-gp</code>","text":"<p>Trains and predicts with a simple Gaussian process regression workflow.</p> <p>CLI:</p> <pre><code>caddforge run ml simple-gp --config simple_gp.yaml\n</code></pre> <p>YAML:</p> <pre><code>train_path: data/train.csv\ntarget_column: y\nfeature_columns: [f1, f2, f3]\nsmiles_column: smiles\nignore_columns: [id, comment]\nfingerprint_types: [morgan, atompair, featmorgan]\nkernel: tanimoto\nnoise: 0.05\ntest_path: data/test.csv\nsave_report: true\nlog_targets: false\ngenerate_scatter_plot: true\nscatter_filename: gp_scatter.html\nmode: train\nmodel_path: simple_gp_model.joblib\nid_column: id\n</code></pre> <p>Python:</p> <pre><code>from caddforge.core.execution import load_result_for_job, submit_workflow\nfrom caddforge.plugins import get_tool\n\nspec = get_tool(\"ml\", \"simple-gp\")\ncfg = spec.config_cls(train_path=\"data/train.csv\", target_column=\"y\", test_path=\"data/test.csv\")\nworkflow = spec.workflow_cls(cfg)\njob = submit_workflow(\n workflow, cfg, backend_name=cfg.backend,\n input_ports=tuple(getattr(spec.plugin, \"input_ports\", ())),\n)\nresult = load_result_for_job(job)\nif result is not None:\n print(result.metrics)\n</code></pre> <p>Outputs to inspect: <code>results/predictions.csv</code>, <code>results/results.json</code>, <code>results/result_record.json</code>.</p>"},{"location":"plugins/ml/#pca","title":"<code>pca</code>","text":"<p>Runs principal component analysis over configured feature columns.</p> <p>CLI:</p> <pre><code>caddforge run ml pca --config pca.yaml\n</code></pre> <p>YAML:</p> <pre><code>data_path: data/features.csv\nfeature_columns: [f1, f2, f3]\nignore_columns: [id]\nn_components: 5\nstandardize: true\ndrop_missing_rows: true\nscores_filename: pca_scores.csv\ncomponents_filename: pca_components.csv\nresults_filename: results.json\n</code></pre> <p>Python:</p> <pre><code>from caddforge.core.execution import load_result_for_job, submit_workflow\nfrom caddforge.plugins import get_tool\n\nspec = get_tool(\"ml\", \"pca\")\ncfg = spec.config_cls(data_path=\"data/features.csv\", n_components=5, backend=\"local\")\nworkflow = spec.workflow_cls(cfg)\njob = submit_workflow(\n workflow, cfg, backend_name=cfg.backend,\n input_ports=tuple(getattr(spec.plugin, \"input_ports\", ())),\n)\nresult = load_result_for_job(job)\nif result is not None:\n print(result.metrics)\n</code></pre> <p>Outputs to inspect: <code>results/pca_scores.csv</code>, <code>results/pca_components.csv</code>, <code>results/results.json</code>.</p>"},{"location":"plugins/ml/#model-zoo","title":"<code>model-zoo</code>","text":"<p>Runs a scikit-learn model from the model registry for regression or classification.</p> <p>CLI:</p> <pre><code>caddforge run ml model-zoo --config model_zoo.yaml\n</code></pre> <p>YAML:</p> <pre><code>train_path: data/train.csv\ntarget_column: y\nfeature_columns: [f1, f2]\nmodel_name: random_forest\ntask: regression\ntest_size: 0.2\nrandom_state: 123\nstandardize_targets: false\ntarget_transform: none\npositive_class: 1\npredictions_filename: predictions.csv\nresults_filename: results.json\nmetrics_filename: metrics.json\nmode: train\nmodel_path: artifacts/model.joblib\n</code></pre> <p>Python:</p> <pre><code>from caddforge.core.execution import load_result_for_job, submit_workflow\nfrom caddforge.plugins import get_tool\n\nspec = get_tool(\"ml\", \"model-zoo\")\ncfg = spec.config_cls(\n train_path=\"data/train.csv\",\n target_column=\"y\",\n model_name=\"random_forest\",\n task=\"regression\",\n backend=\"local\",\n)\nworkflow = spec.workflow_cls(cfg)\njob = submit_workflow(\n workflow, cfg, backend_name=cfg.backend,\n input_ports=tuple(getattr(spec.plugin, \"input_ports\", ())),\n)\nresult = load_result_for_job(job)\nif result is not None:\n print(result.metrics)\n</code></pre> <p>Outputs to inspect: <code>results/predictions.csv</code>, <code>results/metrics.json</code>, <code>results/results.json</code>, <code>artifacts/model.joblib</code>.</p>"},{"location":"plugins/ml/#model-factory","title":"<code>model-factory</code>","text":"<p>Runs multiple model-zoo training jobs against the same data and aggregates metrics, uncertainty diagnostics, confusion summaries, and model artifacts. Predict-only execution is provided by <code>model-zoo</code>, not by the factory.</p> <p>CLI:</p> <pre><code>caddforge run ml model-factory --config model_factory.yaml\n</code></pre> <p>YAML:</p> <pre><code>train_path: data/train.csv\ntarget_column: y\ntask: regression\nfeature_columns: [f1, f2]\nignore_columns: [compound_id, measured_at]\ntest_path: null\nsmiles_column: smiles\nfingerprint_types: [morgan, atompair, featmorgan]\nfp_bits: 2048\nfp_radius: 2\nfp_use_chirality: true\nmodel_names: [ridge, random_forest, xgboost]\nmodel_params:\n random_forest:\n n_estimators: 500\n random_state: 123\ncalibrate: false\ncalibrate_method: sigmoid\ntest_size: 0.2\nrandom_state: 123\npredictions_filename: predictions.csv\nresults_filename: results.json\nmetrics_filename: metrics.json\nid_column: compound_id\nclassification_thresholds: null\ntime_column: measured_at\nstandardize_targets: false\npositive_class: null\ntarget_transform: none\ndrop_non_positive_targets: true\n# log_targets: false # deprecated alias for target_transform: log\n</code></pre> <p>Set <code>task</code> explicitly when possible. With an explicit task and no <code>model_names</code>, the factory runs all compatible registry models. If both values are omitted, the factory attempts every registered model and each subrun infers its own task, so incompatible model/task combinations can be included.</p>"},{"location":"plugins/ml/#model-factory-configuration","title":"Model-factory configuration","text":"<p>These are all options defined by <code>ModelFactoryConfig</code>. Shared options inherited from the core, including <code>backend</code>, <code>run_directory</code>, <code>registry_path</code>, <code>job_name</code>, result export, and Slurm settings, are described in Configuration.</p> Option Type and default Description <code>train_path</code> path, required Training CSV containing the target and every configured training column. <code>target_column</code> string, required Column to predict. Numeric values are required for regression and threshold-binned classification. <code>task</code> <code>regression</code>, <code>classification</code>, or <code>null</code>; default <code>null</code> Task passed to every model-zoo subrun. If omitted, a subrun infers classification for thresholded or non-numeric targets and regression otherwise. An explicit value also filters automatic model selection. <code>feature_columns</code> list of strings or <code>null</code>; default <code>null</code> Numeric features. When omitted, all columns except the target, SMILES column, and ignored columns are selected. They are concatenated with fingerprints when SMILES are also configured. <code>ignore_columns</code> list of strings; default <code>[]</code> Excludes columns from automatic feature selection only. It does not alter explicit <code>feature_columns</code>. <code>test_path</code> path or <code>null</code>; default <code>null</code> External evaluation CSV. When present, all training rows are fitted; otherwise the training CSV is split into fit and holdout sets. <code>smiles_column</code> string or <code>null</code>; default <code>null</code> SMILES column from which RDKit fingerprints are generated. Fingerprints can supplement or replace tabular features. <code>fingerprint_types</code> list of strings; default <code>[morgan, atompair, featmorgan]</code> Fingerprints concatenated per molecule. Supported values: <code>morgan</code>, <code>atompair</code>, and <code>featmorgan</code>. Used only when <code>smiles_column</code> is set. <code>fp_bits</code> integer; default <code>2048</code> Number of bits per fingerprint type. Total fingerprint width is <code>fp_bits</code> multiplied by the number of types. <code>fp_radius</code> integer; default <code>2</code> Radius for Morgan and feature-Morgan fingerprints. <code>fp_use_chirality</code> boolean; default <code>true</code> Include chirality in fingerprint generation. <code>model_names</code> list of strings or <code>null</code>; default <code>null</code> Registry models to run. Valid values are <code>ridge</code>, <code>lasso</code>, <code>elasticnet</code>, <code>bayesian_ridge</code>, <code>logistic_regression</code>, <code>random_forest</code>, <code>extra_trees</code>, <code>svm</code>, <code>knn</code>, <code>gaussian_process</code>, and <code>xgboost</code>. With an explicit task, <code>null</code> selects every compatible model. <code>model_params</code> mapping; default <code>{}</code> Estimator keyword arguments keyed by model name. Each nested mapping is passed directly to the corresponding scikit-learn or XGBoost constructor. <code>calibrate</code> boolean; default <code>false</code> For classification, calibrate probabilities with three-fold <code>CalibratedClassifierCV</code>. Ignored for regression. <code>calibrate_method</code> <code>sigmoid</code> or <code>isotonic</code>; default <code>sigmoid</code> Probability calibration method used when calibration is enabled. <code>test_size</code> float from <code>0.0</code> to <code>0.9</code>; default <code>0.2</code> Holdout fraction when <code>test_path</code> is absent; use a positive value when a holdout is required. Splitting is chronological with <code>time_column</code>; otherwise it is random and classification is stratified when possible. <code>random_state</code> integer; default <code>123</code> Seed for the random holdout split. Estimator seeds must be set separately in <code>model_params</code>. <code>predictions_filename</code> string; default <code>predictions.csv</code> Predictions filename in each model subrun's <code>results/</code> directory. <code>results_filename</code> string; default <code>results.json</code> Model-zoo summary filename in each model subrun's <code>results/</code> directory. <code>metrics_filename</code> string; default <code>metrics.json</code> Metrics filename in each model subrun's <code>results/</code> directory. <code>id_column</code> string or <code>null</code>; default <code>null</code> Column copied to per-model predictions as <code>id</code>; row indices are used when omitted. <code>classification_thresholds</code> list of floats or <code>null</code>; default <code>null</code> Sorted boundaries used with classification to bin a continuous target using <code>numpy.digitize</code>. N thresholds produce N+1 integer classes. Factory confusion summaries require a binary outcome. <code>time_column</code> string or <code>null</code>; default <code>null</code> Datetime-like column for chronological splitting when no external test CSV is supplied. The newest holdout fraction is evaluated. <code>standardize_targets</code> boolean; default <code>false</code> Regression only. Standardizes the target after any transform, then reverses standardization and transformation for predictions and metrics. <code>positive_class</code> integer or <code>null</code>; default <code>null</code> Positive label for binary probability and confusion diagnostics. Subruns prefer class <code>1</code>, then the highest class, when omitted; factory confusion summaries default to <code>1</code>. <code>log_targets</code> boolean; default <code>false</code> Deprecated alias for <code>target_transform: log</code>. It cannot be combined with a non-<code>none</code> target transform. <code>target_transform</code> <code>none</code>, <code>log</code>, or <code>log1p</code>; default <code>none</code> Regression-only transform applied before optional standardization and inverted for outputs. <code>log</code> requires targets greater than zero; <code>log1p</code> requires targets greater than -1. <code>drop_non_positive_targets</code> boolean; default <code>true</code> Drops targets invalid for the selected log transform. If false, invalid targets cause the run to fail. <p>Model compatibility:</p> Task Models Regression only <code>ridge</code>, <code>lasso</code>, <code>elasticnet</code>, <code>bayesian_ridge</code>, <code>gaussian_process</code> Classification only <code>logistic_regression</code> Regression and classification <code>random_forest</code>, <code>extra_trees</code>, <code>svm</code>, <code>knn</code>, <code>xgboost</code> <p>Python:</p> <pre><code>from caddforge.core.execution import load_result_for_job, submit_workflow\nfrom caddforge.plugins import get_tool\n\nspec = get_tool(\"ml\", \"model-factory\")\ncfg = spec.config_cls(\n train_path=\"data/train.csv\",\n target_column=\"y\",\n task=\"regression\",\n backend=\"local\",\n)\nworkflow = spec.workflow_cls(cfg)\njob = submit_workflow(\n workflow, cfg, backend_name=cfg.backend,\n input_ports=tuple(getattr(spec.plugin, \"input_ports\", ())),\n)\nresult = load_result_for_job(job)\nif result is not None:\n print(result.metrics)\n</code></pre> <p>Outputs to inspect:</p> <ul> <li><code>results/summary.csv</code> and <code>results/summary.json</code>: one row per model with prefixed metrics, optional uncertainty-to-error correlation, and binary confusion values. Regression summaries also include OLS slope, intercept, R2, and RMSE diagnostics.</li> <li><code><model-name>/</code>: complete model-zoo subruns with predictions, metrics, results, logs, and model bundles.</li> <li><code>artifacts/<model-name>_model.joblib</code>: pooled copies of generated model bundles.</li> <li><code>results/result_record.json</code>: canonical result exposing <code>summary_csv</code>, <code>summary_json</code>, and <code>log</code> paths.</li> </ul>"},{"location":"plugins/ml/#explore-classifier","title":"<code>explore-classifier</code>","text":"<p>Launches an interactive Streamlit UI for classifier threshold exploration.</p> <p>CLI:</p> <pre><code>caddforge run ml explore-classifier --config explore_classifier.yaml\n</code></pre> <p>YAML:</p> <pre><code>job_id: existing_model_job_id\nrun_base: runs\nmodel_name: random_forest\npositive_class: 1\n</code></pre> <p>Python:</p> <pre><code>from caddforge.core.execution import load_result_for_job, submit_workflow\nfrom caddforge.plugins import get_tool\n\nspec = get_tool(\"ml\", \"explore-classifier\")\ncfg = spec.config_cls(\n job_id=\"existing_model_job_id\",\n run_base=\"runs\",\n model_name=\"random_forest\",\n backend=\"local\",\n)\nworkflow = spec.workflow_cls(cfg)\njob = submit_workflow(\n workflow, cfg, backend_name=cfg.backend,\n input_ports=tuple(getattr(spec.plugin, \"input_ports\", ())),\n)\nresult = load_result_for_job(job)\nif result is not None:\n print(result.status.value)\n</code></pre> <p>Outputs to inspect: Streamlit UI session, saved threshold JSON under <code>artifacts/</code>, updated model artifact when possible.</p>"},{"location":"plugins/ml/#compressed-inputs","title":"Compressed inputs","text":"<p>Training, test, and PCA CSV inputs may be supplied as <code>.csv.gz</code>. They are expanded only at runtime, and saved configurations retain the compressed source paths.</p>"},{"location":"plugins/qm/","title":"QM Plugin","text":"<p>Package: <code>caddforge-qm</code></p> <p>Public source tag</p> <p>This page describes <code>caddforge-qm</code> <code>0.3.0</code>, tagged as <code>v0.3.0</code>. Published on PyPI; see the version matrix.</p> <p>The QM plugin currently provides the transfer-free-energy workflow.</p>"},{"location":"plugins/qm/#requirements-and-example-status","title":"Requirements and example status","text":"<p>The PyOCL dependency is installed as <code>pyocl-idor>=0.8.3</code> and imported as <code>pyocl</code>. It requires Java 21 or newer and a configured <code>JAVA_HOME</code>.</p> <p>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.</p>"},{"location":"plugins/qm/#transfer-free-energy","title":"<code>transfer-free-energy</code>","text":"<p>Computes transfer free energy for a ligand in a target solvent using COSMO-RS surfaces.</p> <p>CLI:</p> <pre><code>caddforge show-config qm transfer-free-energy\ncaddforge run qm transfer-free-energy --config transfer_free_energy.yaml\n</code></pre> <p>YAML:</p> <pre><code>ligand_sdf: data/ligand.sdf\nsolvent_sdf: data/octanol.sdf\nn_confs: 3\nbasis: def2-TZVP\nxc: BP86\ndo_geomopt: true\ntemperature_K: 298.15\nstandard_state: x\ncosmo_backend: dft\nxtb_path: xtb\n</code></pre> <p>Python:</p> <pre><code>from caddforge.core.execution import load_result_for_job, submit_workflow\nfrom caddforge.plugins import get_tool\n\nspec = get_tool(\"qm\", \"transfer-free-energy\")\ncfg = spec.config_cls(\n ligand_sdf=\"data/ligand.sdf\",\n solvent_sdf=\"data/octanol.sdf\",\n n_confs=3,\n cosmo_backend=\"dft\",\n backend=\"local\",\n)\nworkflow = spec.workflow_cls(cfg)\njob = submit_workflow(\n workflow, cfg, backend_name=cfg.backend,\n input_ports=tuple(getattr(spec.plugin, \"input_ports\", ())),\n)\nresult = load_result_for_job(job)\nif result is not None:\n print(result.metrics)\n</code></pre> <p>Outputs to inspect: <code>results/results.json</code>, <code>results/cosmo_surfaces/</code>, <code>results/result_record.json</code>.</p> <p>Ligand and solvent structures may be supplied as <code>.sdf.gz</code>. CADDForge expands them in runtime scratch and retains the original compressed paths in saved configuration.</p>"},{"location":"plugins/version-matrix/","title":"CADDForge Version Matrix","text":"<p>These docs describe the public sources in the new repositories. Assigned versions and Git tags below identify reviewed source revisions; package-index publication is recorded separately. Inherited baselines are the copied versions and current bundle pins. Pending entries still await new public release versions and tags.</p> Distribution Inherited baseline New public version Public commit / tag Package-index publication <code>caddforge-core</code> <code>0.21.0</code> <code>0.22.0</code> <code>d7c5f210fea8</code> / <code>v0.22.0</code> Published on PyPI <code>caddforge-datascience</code> <code>0.4.0</code> <code>0.5.0</code> <code>403c1af1e466</code> / <code>v0.5.0</code> Published on PyPI <code>caddforge-descriptors</code> <code>0.10.1</code> <code>0.11.0</code> <code>728f9e9fddd0</code> / <code>v0.11.0</code> Published on PyPI <code>caddforge-docking</code> <code>0.6.1</code> <code>0.7.0</code> <code>2410dbc387f5</code> / <code>v0.7.0</code> Published on PyPI <code>caddforge-fep</code> <code>0.6.0</code> <code>0.7.0</code> <code>d44dcf342b6f</code> / <code>v0.7.0</code> Published on PyPI <code>caddforge-gui</code> <code>0.5.0</code> <code>0.6.0</code> <code>c73c1155ef4a</code> / <code>v0.6.0</code> Published on PyPI <code>caddforge-md</code> <code>0.12.1</code> <code>0.13.0</code> <code>8668328affe3</code> / <code>v0.13.0</code> Not yet on PyPI <code>caddforge-ml</code> <code>0.3.0</code> <code>0.4.0</code> <code>132459f04fdf</code> / <code>v0.4.0</code> Published on PyPI <code>caddforge-qm</code> <code>0.2.1</code> <code>0.3.0</code> <code>fb24d32fc966</code> / <code>v0.3.0</code> Published on PyPI <code>caddforge</code> <code>0.22.1</code> Pending Pending Pending <code>caddforge-docs</code> <code>0.2.1</code> <code>0.3.0</code> <code>v0.3.0</code> Publication pending <p>Publication status was checked on 2026-10-15. All nine public component source tags exist. Eight corresponding versions are published on PyPI; MD <code>0.13.0</code> is tagged and awaiting publication. Documentation <code>0.3.0</code> is tagged <code>v0.3.0</code> and awaits publication. The updated bundle still needs its new release version and tag. A later release may change this package-index status.</p> <p>The public snapshot removes ESOL from descriptors and Glide / Glide WS from docking, including their Python exports and GUI integrations. Those interfaces are not available in this source tree. The copied MD fixes and encoded PheSA reference support remain present. All migrated plugins require <code>caddforge-core>=0.21.0</code>.</p> <p>The bundle's current dependency pins are the baselines above. Its default installation includes the built documentation viewer without documentation authoring or test extras. The bundle must be updated to the new public component versions and validated with <code>caddforge-docs==0.3.0</code> before release. Installing the inherited versions does not install these source changes.</p> <p>Continue each package's existing version sequence and choose unused versions after checking existing artifacts. The bundle's <code>0.x</code> series remains initial development; <code>1.0.0</code> is reserved for a future stable public API commitment. Bundle and component versions evolve independently.</p>"},{"location":"plugins/version-matrix/#pyocl-runtime-dependency","title":"PyOCL runtime dependency","text":"<p>The edited QM, descriptors, and docking sources require the public <code>pyocl-idor</code> distribution, version <code>0.8.3</code> or newer. The Python import package remains <code>pyocl</code>. Java 21 or newer is required. This dependency change is not present in the inherited CADDForge releases above; include it in the new component releases and then update the bundle pins.</p>"},{"location":"plugins/version-matrix/#maintained-third-party-distributions","title":"Maintained third-party distributions","text":"<p>The <code>third-party/</code> source workspace maintains these independent redistributions of the exact upstream revisions currently required by QM and docking. Both initial releases are tagged and published on PyPI. Their repositories include GitHub release workflows.</p> Distribution Released version Upstream source Maintained release / commit <code>caddforge-opencosmors</code> <code>0.0.1</code> openCOSMO-RS_py commit <code>64e42bbfa1d9eda93714d7a346d5cbfdeb8dc5d1</code> (upstream metadata <code>0.0.1</code>) v0.0.1, <code>d582e7b0a1cf</code> <code>caddforge-unidock-tools</code> <code>1.1.3</code> Uni-Dock tag <code>1.1.3</code>, commit <code>caf3161b28f7ceb5aa3b13bc98a505cb76a39db3</code>, subdirectory <code>unidock_tools</code> (upstream Python metadata <code>0.0.1</code>) v1.1.3, <code>449555ac267d</code> <p>These distributions retain the upstream Python code, parameter data, import names, and <code>unidocktools</code> CLI. Uni-Dock Tools includes a rebuilt <code>4.2.9+caddforge.1</code> AutoGrid executable with complete GPL-licensed source, a legacy compatibility patch, and an offline build helper in both artifacts. Its wheel targets <code>manylinux_2_28_x86_64</code> (Linux x86-64, glibc 2.28 or newer). The matching GCC OpenMP runtime is linked statically with its Runtime Library Exception and complete notices, so no separate <code>libgomp</code> installation is needed. The original executable with unverified corresponding source is excluded. See Third-party Notices for the licence split and source-distribution requirements.</p> <p>Both distributions were installed together from public PyPI in a fresh Python 3.12 Linux x86-64 environment. Dependency consistency, imports, command help, the bundled AutoGrid executable and source hashes, and a water/ethanol COSMO-RS numerical regression passed. This validates these dependency releases, not a complete public CADDForge bundle installation.</p> <p>The edited QM and docking sources now require <code>caddforge-opencosmors==0.0.1</code> and <code>caddforge-unidock-tools==1.1.3</code>, respectively. These named requirements replace the previous pinned Git dependencies. Release the affected components, then update and test the bundle pins; the inherited component versions above do not contain this dependency change.</p>"},{"location":"plugins/version-matrix/#maintenance-rule","title":"Maintenance rule","text":"<p>When the reviewed sources are committed and tagged, replace the pending entries with the actual public versions, commits, and tags, and record publication status separately. Update bundle pins, installation examples, plugin callouts, and release notes in the same release work. Do not recreate historical tags on new initial commits or label inherited baselines as releases of this snapshot.</p> <p>Local validation may use temporary VCS version overrides at the inherited baselines. Such overrides do not assign release versions; never publish those validation artifacts.</p>"},{"location":"reference/","title":"Reference","text":"<p>Use reference pages while configuring a known workflow. Start with tutorials if you are learning the runtime for the first time.</p> <ul> <li>Public Python API: reviewed core symbols, signatures, and declared fields.</li> <li>CLI: commands and common options; <code>--help</code> reflects the installed version.</li> <li>Plugin catalog: public scientific workflows.</li> <li>Version matrix: tags, reviewed revisions, and candidates.</li> <li>Example library: downloadable configs and validation status.</li> <li>Pipeline specification and built-ins.</li> </ul> <p><code>caddforge show-config <section> <tool></code> prints a configuration template from an installed plugin. Configurations use Pydantic validation; fields and defaults are versioned with the plugin. A template is not a guarantee that an engine or input file is available. Supply paths and scientific preparation appropriate to the method.</p>"},{"location":"reference/api/","title":"Public Python API","text":"<p>Generated from the reviewed core sources using <code>scripts/update_reference.py</code>. Builds consume this checked-in reference and do not import or discover plugins. Version and tag provenance are recorded in the version matrix.</p> <p>Use plugin contracts to obtain tool configurations and workflows. Call <code>submit_workflow</code> for backend-aware execution; direct workflow calls are intended for tests and custom inline execution. See the first workflow.</p> <p><code>Dataset.from_file()</code> currently raises <code>NotImplementedError</code>. A frozen dataclass prevents field reassignment; it does not make nested molecule objects or lists immutable.</p>"},{"location":"reference/api/#caddforgeconfigbase","title":"<code>caddforge.config.base</code>","text":""},{"location":"reference/api/#baseconfig","title":"<code>BaseConfig</code>","text":"<pre><code>class BaseConfig(BaseModel)\n</code></pre> <pre><code>Common config options shared across tools.\n</code></pre> <p>Declared fields and enum values (inherited fields are documented on the base class):</p> <pre><code>run_directory: Path | None = None\nregistry_path: Path | None = None\nbackend: str = 'local'\nslurm_partition: str | None = None\nslurm_time: str | None = None\nslurm_account: str | None = None\nsbatch_options: list[str] = Field(default_factory=list)\nslurm_setup_commands: list[str] = Field(default_factory=list)\njob_name: str | None = None\nexport_results: bool = False\nslurm_nodes: int = 1\nslurm_ntasks: int = 1\nslurm_cpus_per_task: int = 1\nslurm_env_pack_path: Path | None = None\nslurm_env_cache_enabled: bool = True\nslurm_env_cache_dir: Path | None = None\nslurm_wheelhouse_path: Path | None = None\nslurm_requirements_path: Path | None = None\n</code></pre>"},{"location":"reference/api/#from_yaml","title":"<code>from_yaml</code>","text":"<pre><code>def from_yaml(cls, path: str | Path) -> 'BaseConfig'\n</code></pre> <pre><code>Load configuration from a YAML file.\n\nArgs:\n path: Path to the YAML config file.\n\nReturns:\n BaseConfig: Parsed and validated configuration.\n</code></pre>"},{"location":"reference/api/#from_json","title":"<code>from_json</code>","text":"<pre><code>def from_json(cls, path: str | Path) -> 'BaseConfig'\n</code></pre> <pre><code>Load configuration from a JSON file.\n\nArgs:\n path: Path to the JSON config file.\n\nReturns:\n BaseConfig: Parsed and validated configuration.\n</code></pre>"},{"location":"reference/api/#example_dict","title":"<code>example_dict</code>","text":"<pre><code>def example_dict(cls) -> dict[str, Any]\n</code></pre> <pre><code>Return a dict with defaults or placeholders for required fields.\n\nReturns:\n dict[str, Any]: Example config mapping.\n</code></pre>"},{"location":"reference/api/#caddforgecoredataset","title":"<code>caddforge.core.dataset</code>","text":""},{"location":"reference/api/#dataset","title":"<code>Dataset</code>","text":"<pre><code>class Dataset()\n</code></pre> <pre><code>Immutable dataset consisting of entries and optional labels.\n\nArgs:\n entries: Sequence of entries (typically molecule-like objects).\n labels: Optional sequence of labels aligned with entries.\n source: Optional provenance string (e.g., file path or database tag).\n</code></pre> <p>Declared fields and enum values (inherited fields are documented on the base class):</p> <pre><code>entries: Sequence[object]\nlabels: Sequence[object] | None = None\nsource: str | None = None\n</code></pre>"},{"location":"reference/api/#from_file","title":"<code>from_file</code>","text":"<pre><code>def from_file(cls, path: str | Path) -> 'Dataset'\n</code></pre> <pre><code>Create a dataset from a file path.\n\nArgs:\n path: Path to the dataset file.\n\nReturns:\n Dataset: Parsed dataset instance.\n\nRaises:\n NotImplementedError: Always, until a concrete loader is implemented.\n</code></pre>"},{"location":"reference/api/#from_molecules","title":"<code>from_molecules</code>","text":"<pre><code>def from_molecules(cls, molecules: Iterable[object], *, source: str | None=None) -> 'Dataset'\n</code></pre> <pre><code>Build a dataset from an iterable of molecule-like objects.\n\nArgs:\n molecules: Iterable of molecule-like objects.\n source: Optional provenance string.\n\nReturns:\n Dataset: New dataset with entries populated and labels empty.\n</code></pre>"},{"location":"reference/api/#moleculeset","title":"<code>MoleculeSet</code>","text":"<pre><code>class MoleculeSet()\n</code></pre> <pre><code>Immutable collection of molecules.\n\nArgs:\n molecules: Sequence of molecule-like objects.\n source: Optional provenance string (e.g., file path or database tag).\n</code></pre> <p>Declared fields and enum values (inherited fields are documented on the base class):</p> <pre><code>molecules: Sequence[object]\nsource: str | None = None\n</code></pre>"},{"location":"reference/api/#caddforgecoremodel","title":"<code>caddforge.core.model</code>","text":""},{"location":"reference/api/#model","title":"<code>Model</code>","text":"<pre><code>class Model(ABC)\n</code></pre> <pre><code>Abstract base class for models used across tools.\n</code></pre>"},{"location":"reference/api/#fit","title":"<code>fit</code>","text":"<pre><code>def fit(self, dataset: Dataset) -> None\n</code></pre> <pre><code>Train the model with the provided dataset.\n\nArgs:\n dataset: Dataset containing training data.\n</code></pre>"},{"location":"reference/api/#predict","title":"<code>predict</code>","text":"<pre><code>def predict(self, data: Dataset | MoleculeSet)\n</code></pre> <pre><code>Generate predictions for a dataset or molecule set.\n\nArgs:\n data: Dataset or MoleculeSet to predict on.\n\nReturns:\n Any: Tool-specific prediction output.\n</code></pre>"},{"location":"reference/api/#caddforgecorejob","title":"<code>caddforge.core.job</code>","text":""},{"location":"reference/api/#jobstatus","title":"<code>JobStatus</code>","text":"<pre><code>class JobStatus(str, Enum)\n</code></pre> <pre><code>Enumeration of job lifecycle states.\n</code></pre> <p>Declared fields and enum values (inherited fields are documented on the base class):</p> <pre><code>PENDING = 'PENDING'\nRUNNING = 'RUNNING'\nCOMPLETED = 'COMPLETED'\nFAILED = 'FAILED'\nCANCELLED = 'CANCELLED'\n</code></pre>"},{"location":"reference/api/#job","title":"<code>Job</code>","text":"<pre><code>class Job()\n</code></pre> <pre><code>Canonical representation of a workflow execution.\n\nArgs:\n id: Unique job identifier.\n tool_name: Registered tool name (without section).\n backend: Backend name used to execute the job.\n status: Current job status.\n created_at: Timestamp when the job was created.\n started_at: Timestamp when execution began.\n finished_at: Timestamp when execution completed.\n run_directory: Path to the job run directory.\n metadata: Tool/backend-specific metadata.\n</code></pre> <p>Declared fields and enum values (inherited fields are documented on the base class):</p> <pre><code>id: str\ntool_name: str\nbackend: str = 'unsubmitted'\nstatus: JobStatus = JobStatus.PENDING\ncreated_at: datetime = field(default_factory=lambda: datetime.now().astimezone())\nstarted_at: Optional[datetime] = None\nfinished_at: Optional[datetime] = None\nrun_directory: Optional[Path] = None\nmetadata: dict[str, Any] = field(default_factory=dict)\n</code></pre>"},{"location":"reference/api/#to_metadata_dict","title":"<code>to_metadata_dict</code>","text":"<pre><code>def to_metadata_dict(self) -> dict[str, Any]\n</code></pre> <pre><code>Serialize job metadata to a JSON-friendly dict.\n\nReturns:\n dict[str, Any]: Serialized metadata suitable for JSON encoding.\n</code></pre>"},{"location":"reference/api/#from_metadata_dict","title":"<code>from_metadata_dict</code>","text":"<pre><code>def from_metadata_dict(cls, data: dict[str, Any]) -> 'Job'\n</code></pre> <pre><code>Deserialize job metadata produced by `to_metadata_dict`.\n\nArgs:\n data: Serialized job metadata.\n\nReturns:\n Job: Deserialized Job instance.\n</code></pre>"},{"location":"reference/api/#save_metadata","title":"<code>save_metadata</code>","text":"<pre><code>def save_metadata(self, path: Path) -> None\n</code></pre> <pre><code>Persist job metadata to disk.\n\nArgs:\n path: Target metadata JSON file path.\n</code></pre>"},{"location":"reference/api/#caddforgecoreresult","title":"<code>caddforge.core.result</code>","text":""},{"location":"reference/api/#result","title":"<code>Result</code>","text":"<pre><code>class Result()\n</code></pre> <pre><code>Standardized result object produced by workflows.\n\nArgs:\n job_id: Job identifier.\n tool_name: Tool name that produced the result.\n status: Completion status for the job.\n metrics: Tool-defined metrics (simple scalar values).\n paths: Paths to key output artifacts.\n extra: Tool-defined extra metadata payload.\n</code></pre> <p>Declared fields and enum values (inherited fields are documented on the base class):</p> <pre><code>job_id: str\ntool_name: str\nstatus: JobStatus\nmetrics: dict[str, float | int | str] = field(default_factory=dict)\npaths: dict[str, Path] = field(default_factory=dict)\nextra: dict[str, Any] = field(default_factory=dict)\n</code></pre>"},{"location":"reference/api/#to_dict","title":"<code>to_dict</code>","text":"<pre><code>def to_dict(self) -> dict[str, Any]\n</code></pre> <pre><code>Serialize to a JSON-friendly dictionary.\n\nReturns:\n dict[str, Any]: Serialized representation of the result.\n</code></pre>"},{"location":"reference/api/#from_dict","title":"<code>from_dict</code>","text":"<pre><code>def from_dict(cls, data: dict[str, Any]) -> 'Result'\n</code></pre> <pre><code>Construct a Result from a dictionary representation.\n\nArgs:\n data: Serialized result dictionary.\n\nReturns:\n Result: Deserialized Result instance.\n</code></pre>"},{"location":"reference/api/#save","title":"<code>save</code>","text":"<pre><code>def save(self, path: Path) -> None\n</code></pre> <pre><code>Persist result to disk as JSON.\n\nArgs:\n path: Target JSON file path.\n</code></pre>"},{"location":"reference/api/#caddforgecoreworkflow","title":"<code>caddforge.core.workflow</code>","text":""},{"location":"reference/api/#workflow","title":"<code>Workflow</code>","text":"<pre><code>class Workflow(ABC)\n</code></pre> <pre><code>Abstract workflow run by CADDForge tools.\n</code></pre> <p>Declared fields and enum values (inherited fields are documented on the base class):</p> <pre><code>tool_name: str\n</code></pre>"},{"location":"reference/api/#prepare_job","title":"<code>prepare_job</code>","text":"<pre><code>def prepare_job(self) -> Job\n</code></pre> <pre><code>Build a job object with metadata and paths.\n\nReturns:\n Job: Prepared job instance ready for submission.\n</code></pre>"},{"location":"reference/api/#run","title":"<code>run</code>","text":"<pre><code>def run(self, job: Job, context: RuntimeContext | None=None) -> Result\n</code></pre> <pre><code>Execute the workflow logic and return a result.\n\nArgs:\n job: Job metadata for the execution.\n context: Backend-neutral runtime details supplied by the executor.\n\nReturns:\n Result: Standardized workflow result.\n</code></pre>"},{"location":"reference/api/#caddforgecoreexecution","title":"<code>caddforge.core.execution</code>","text":""},{"location":"reference/api/#backendoptions","title":"<code>BackendOptions</code>","text":"<pre><code>class BackendOptions()\n</code></pre> <pre><code>Backend-specific options supplied by CLI or GUI callers.\n</code></pre> <p>Declared fields and enum values (inherited fields are documented on the base class):</p> <pre><code>slurm_partition: str | None = None\nslurm_time: str | None = None\nslurm_account: str | None = None\nslurm_nodes: int | None = None\nslurm_ntasks: int | None = None\nslurm_cpus_per_task: int | None = None\nslurm_setup: list[str] = field(default_factory=list)\nslurm_env_pack: Path | None = None\nslurm_wheelhouse: Path | None = None\nslurm_requirements: Path | None = None\nextra_sbatch_options: list[str] = field(default_factory=list)\n</code></pre>"},{"location":"reference/api/#submit_workflow","title":"<code>submit_workflow</code>","text":"<pre><code>def submit_workflow(workflow, cfg: BaseConfig, *, backend_name: str, runs_root: Path | str=Path('runs'), registry_path: Path | None=None, job_name: str | None=None, submission_cwd: Path | None=None, local_async: bool=False, backend_options: BackendOptions | None=None, emit: EmitFn | None=None, prepared_job_callback: Callable[[Job], None] | None=None, input_ports: tuple[InputPort, ...]=()) -> Job\n</code></pre> <pre><code>Prepare a workflow job and submit it through the selected backend.\n</code></pre>"},{"location":"reference/api/#load_result_for_job","title":"<code>load_result_for_job</code>","text":"<pre><code>def load_result_for_job(job: Job) -> Result | None\n</code></pre> <pre><code>Load the canonical result for a job if it exists.\n</code></pre>"},{"location":"reference/api/#caddforgecorebackend","title":"<code>caddforge.core.backend</code>","text":""},{"location":"reference/api/#backend","title":"<code>Backend</code>","text":"<pre><code>class Backend(ABC)\n</code></pre> <pre><code>Abstract compute backend.\n</code></pre> <p>Declared fields and enum values (inherited fields are documented on the base class):</p> <pre><code>name: str\n</code></pre>"},{"location":"reference/api/#submit","title":"<code>submit</code>","text":"<pre><code>def submit(self, job: Job, run_callable: Callable[[Job], Result]) -> JobHandle\n</code></pre> <pre><code>Submit a job for execution using the provided callable.\n\nArgs:\n job: Job metadata to register and execute.\n run_callable: Callable that performs the work and returns a Result.\n\nReturns:\n JobHandle: Backend-specific handle for tracking execution.\n</code></pre>"},{"location":"reference/api/#submit_shell_commands","title":"<code>submit_shell_commands</code>","text":"<pre><code>def submit_shell_commands(self, job: Job, commands: Iterable[str]) -> JobHandle\n</code></pre> <pre><code>Submit a shell-command job if the backend supports it.\n\nArgs:\n job: Job metadata to register and execute.\n commands: Shell commands to execute in order.\n\nReturns:\n JobHandle: Backend-specific handle for tracking execution.\n\nRaises:\n NotImplementedError: If the backend does not implement shell jobs.\n</code></pre>"},{"location":"reference/api/#query_status","title":"<code>query_status</code>","text":"<pre><code>def query_status(self, handle: JobHandle) -> JobStatus\n</code></pre> <pre><code>Return current status of a submitted job.\n\nArgs:\n handle: Backend-specific job handle.\n\nReturns:\n JobStatus: Current status for the job.\n</code></pre>"},{"location":"reference/api/#cancel","title":"<code>cancel</code>","text":"<pre><code>def cancel(self, handle: JobHandle) -> None\n</code></pre> <pre><code>Cancel a submitted job if supported.\n\nArgs:\n handle: Backend-specific job handle.\n</code></pre>"},{"location":"reference/api/#caddforgecoreartifacts","title":"<code>caddforge.core.artifacts</code>","text":""},{"location":"reference/api/#artifactkind","title":"<code>ArtifactKind</code>","text":"<pre><code>class ArtifactKind(str, Enum)\n</code></pre> <pre><code>Portable artifact categories understood by the core pipeline layer.\n</code></pre> <p>Declared fields and enum values (inherited fields are documented on the base class):</p> <pre><code>FILE = 'file'\nDIRECTORY = 'directory'\nMOLECULE_COLLECTION = 'molecule_collection'\nPOSE_COLLECTION = 'pose_collection'\nPOSE_MANIFEST = 'pose_manifest'\nTABULAR_SCORES = 'tabular_scores'\nPROTEIN_STRUCTURE = 'protein_structure'\nCOMPLEX_STRUCTURE = 'complex_structure'\n</code></pre>"},{"location":"reference/api/#artifactref","title":"<code>ArtifactRef</code>","text":"<pre><code>class ArtifactRef()\n</code></pre> <pre><code>Reference to a workflow artifact stored on disk.\n\nLarge data stays on disk; the pipeline engine passes these lightweight\nreferences between stages and resolves them into concrete config paths.\n</code></pre> <p>Declared fields and enum values (inherited fields are documented on the base class):</p> <pre><code>id: str\nkind: ArtifactKind | str\nproducer_stage_id: str\npath: Path | None = None\npaths: list[Path] = field(default_factory=list)\nproducer_job_id: str | None = None\nmetadata: dict[str, Any] = field(default_factory=dict)\n</code></pre>"},{"location":"reference/api/#primary_path","title":"<code>primary_path</code>","text":"<pre><code>def primary_path(self) -> Path\n</code></pre> <pre><code>Return the single concrete path for config binding.\n</code></pre>"},{"location":"reference/api/#to_dict_1","title":"<code>to_dict</code>","text":"<pre><code>def to_dict(self) -> dict[str, Any]\n</code></pre> <pre><code>Serialize to a JSON-friendly dictionary.\n</code></pre>"},{"location":"reference/api/#from_dict_1","title":"<code>from_dict</code>","text":"<pre><code>def from_dict(cls, data: dict[str, Any]) -> ArtifactRef\n</code></pre> <pre><code>Deserialize an artifact reference from a dictionary.\n</code></pre>"},{"location":"reference/api/#inputport","title":"<code>InputPort</code>","text":"<pre><code>class InputPort()\n</code></pre> <pre><code>Declarative input accepted by a tool plugin.\n\n``config_field`` is the stable top-level pipeline binding target.\n``runtime_selector`` may identify nested path values for execution-time\nstaging without changing that pipeline contract.\n</code></pre> <p>Declared fields and enum values (inherited fields are documented on the base class):</p> <pre><code>name: str\nkind: ArtifactKind | str\nconfig_field: str\nrequired: bool = True\ndescription: str | None = None\ncompression: InputCompressionPolicy | str = InputCompressionPolicy.TRANSPARENT_GZIP\nruntime_selector: str | None = None\n</code></pre>"},{"location":"reference/api/#outputport","title":"<code>OutputPort</code>","text":"<pre><code>class OutputPort()\n</code></pre> <pre><code>Declarative output produced by a tool plugin.\n\n``config_field`` identifies the optional Path-typed config field that asks\nthe workflow where to write this result. Declaring it enables transparent\ngzip output handling at the pipeline boundary.\n</code></pre> <p>Declared fields and enum values (inherited fields are documented on the base class):</p> <pre><code>name: str\nkind: ArtifactKind | str\nresult_path_key: str\ndescription: str | None = None\nrequired: bool = True\npath_collection_column: str | None = None\nconfig_field: str | None = None\n</code></pre>"},{"location":"reference/api/#splitcontract","title":"<code>SplitContract</code>","text":"<pre><code>class SplitContract()\n</code></pre> <pre><code>Central description of a splittable workflow input.\n</code></pre> <p>Declared fields and enum values (inherited fields are documented on the base class):</p> <pre><code>input_field: str\nkind: ArtifactKind | str = ArtifactKind.MOLECULE_COLLECTION\nstrategy: Literal['sdf', 'csv', 'tsv', 'lines', 'auto'] = 'auto'\nmerge_outputs: dict[str, Literal['csv', 'tsv', 'sdf', 'jsonl', 'manifest', 'auto']] = field(default_factory=dict)\nitem_path_field: str | None = None\n</code></pre>"},{"location":"reference/api/#caddforgecoretool","title":"<code>caddforge.core.tool</code>","text":""},{"location":"reference/api/#toolplugin","title":"<code>ToolPlugin</code>","text":"<pre><code>class ToolPlugin(Protocol)\n</code></pre> <pre><code>Protocol that tool plugins should satisfy.\n\nAttributes:\n section: Section/group name for CLI organization.\n name: Tool name within its section.\n config_cls: Pydantic config model used by the tool.\n workflow_cls: Workflow class executed by the tool.\n</code></pre> <p>Declared fields and enum values (inherited fields are documented on the base class):</p> <pre><code>section: str\nname: str\nconfig_cls: Type[BaseConfig]\nworkflow_cls: Type[Workflow]\ninput_ports: tuple[InputPort, ...] = ()\noutput_ports: tuple[OutputPort, ...] = ()\nsplit_contracts: tuple[SplitContract, ...] = ()\n</code></pre>"},{"location":"reference/api/#toolspec","title":"<code>ToolSpec</code>","text":"<pre><code>class ToolSpec()\n</code></pre> <pre><code>Concrete description of a discovered tool plugin.\n\nArgs:\n section: Section/group name for CLI organization.\n name: Tool name within its section.\n config_cls: Pydantic config model used by the tool.\n workflow_cls: Workflow class executed by the tool.\n plugin: Loaded plugin instance.\n</code></pre> <p>Declared fields and enum values (inherited fields are documented on the base class):</p> <pre><code>section: str\nname: str\nconfig_cls: Type[BaseConfig]\nworkflow_cls: Type[Workflow]\nplugin: ToolPlugin\n</code></pre>"},{"location":"reference/api/#caddforgeplugins","title":"<code>caddforge.plugins</code>","text":""},{"location":"reference/api/#get_tool","title":"<code>get_tool</code>","text":"<pre><code>def get_tool(section: str, name: str) -> ToolSpec\n</code></pre> <pre><code>Return the tool spec for a given section/tool name.\n\nArgs:\n section: Section/group name.\n name: Tool name within the section.\n\nReturns:\n ToolSpec: Discovered tool specification.\n\nRaises:\n KeyError: If the section or tool name is not registered.\n</code></pre>"},{"location":"reference/api/#caddforgepipelinespec","title":"<code>caddforge.pipeline.spec</code>","text":""},{"location":"reference/api/#pipelinespec","title":"<code>PipelineSpec</code>","text":"<pre><code>class PipelineSpec(BaseModel)\n</code></pre> <pre><code>Top-level declarative pipeline specification.\n</code></pre> <p>Declared fields and enum values (inherited fields are documented on the base class):</p> <pre><code>version: int = 1\nname: str = 'pipeline'\ndefaults: PipelineDefaults = Field(default_factory=PipelineDefaults)\nstages: list[PipelineStageSpec]\n</code></pre>"},{"location":"reference/api/#pipelinestagespec","title":"<code>PipelineStageSpec</code>","text":"<pre><code>class PipelineStageSpec(BaseModel)\n</code></pre> <pre><code>One stage in a CADDForge pipeline DAG.\n</code></pre> <p>Declared fields and enum values (inherited fields are documented on the base class):</p> <pre><code>id: str\ntool: str | None = None\ntype: str | None = None\nconfig: str | Path | dict[str, Any] | None = None\ninputs: dict[str, str] = Field(default_factory=dict)\noutputs: dict[str, str | dict[str, Any]] = Field(default_factory=dict)\nscatter: ScatterSpec | None = None\ndepends_on: list[str] = Field(default_factory=list)\n</code></pre>"},{"location":"reference/api/#pipelinedefaults","title":"<code>PipelineDefaults</code>","text":"<pre><code>class PipelineDefaults(BaseModel)\n</code></pre> <pre><code>Defaults applied to tool stages.\n</code></pre> <p>Declared fields and enum values (inherited fields are documented on the base class):</p> <pre><code>backend: str = 'local'\nrun_directory: Path | None = None\nregistry_path: Path | None = None\njob_name: str | None = None\nexport_results: bool = False\n</code></pre>"},{"location":"reference/api/#scatterspec","title":"<code>ScatterSpec</code>","text":"<pre><code>class ScatterSpec(BaseModel)\n</code></pre> <pre><code>Fan-out settings for a splittable stage.\n</code></pre> <p>Declared fields and enum values (inherited fields are documented on the base class):</p> <pre><code>by: str\nchunks: int | None = Field(default=None, ge=1)\nchunk_size: int | None = Field(default=None, ge=1)\nstrategy: Literal['auto', 'sdf', 'csv', 'tsv', 'lines'] = 'auto'\n</code></pre>"},{"location":"reference/api/#load_pipeline_spec","title":"<code>load_pipeline_spec</code>","text":"<pre><code>def load_pipeline_spec(path: str | Path) -> PipelineSpec\n</code></pre> <pre><code>Load a pipeline spec from YAML or JSON.\n</code></pre>"},{"location":"reference/api/#caddforgepipelineexecutor","title":"<code>caddforge.pipeline.executor</code>","text":""},{"location":"reference/api/#pipelineexecutionresult","title":"<code>PipelineExecutionResult</code>","text":"<pre><code>class PipelineExecutionResult()\n</code></pre> <pre><code>Summary returned after a pipeline run.\n</code></pre> <p>Declared fields and enum values (inherited fields are documented on the base class):</p> <pre><code>pipeline_job: Job\nstage_jobs: dict[str, list[Job]] = field(default_factory=dict)\nartifacts: dict[str, ArtifactRef] = field(default_factory=dict)\n</code></pre>"},{"location":"reference/api/#run_pipeline","title":"<code>run_pipeline</code>","text":"<pre><code>def run_pipeline(path: str | Path, *, emit: EmitFn | None=None, poll_interval_seconds: int=10, wait_timeout_seconds: int | None=None, backend_options: BackendOptions | None=None) -> PipelineExecutionResult\n</code></pre> <pre><code>Load and run a pipeline spec from disk.\n</code></pre>"}]}
|