Static-mode Aggrescan3D scoring for PDB and mmCIF structures on a Slurm cluster. The workflow analyzes binder chain B by default, preserves residue-level results, processes several structures concurrently within one persistent Slurm allocation, and reuses only validated existing outputs.
The scientific runtime is pulled from:
docker://lcbio/a3d_server:latest
The validated image contains:
Aggrescan3D 0.9.11
Python 2.7
CLI: /opt/conda/bin/aggrescan
Architecture: x86_64 / amd64
The Docker image runscript starts its original server-oriented command. Therefore this workflow uses apptainer exec, not apptainer run, to invoke Aggrescan3D:
apptainer exec aggrescan3d.sif /opt/conda/bin/aggrescan --helpHost-side Python 3 code handles PDB/mmCIF parsing, chain extraction, CIF-to-PDB conversion, output parsing, metadata, cache validation, concurrency, and Slurm submission. Only the legacy Aggrescan3D calculation runs inside the SIF.
Implemented:
- Aggrescan3D static mode;
- PDB,
.cif, and.mmcifinputs; - chain
Bby default; - multiple selected chains with
--chains A,B; - all first-model chains with
--all-chains; - FreeSASA, which is the image default because
--naccessis not used; - residue-level normalized output;
- per-structure summary metrics;
- manifest-driven concurrent execution;
- one persistent Slurm allocation rather than a job array;
- validated cache reuse;
- atomic output replacement;
- per-output locking to prevent simultaneous writers.
Not implemented:
- dynamic mode;
- CABS-flex;
- FoldX optimization or stability calculations;
- mutation design;
- automated solubility-enhancing mutation selection;
- the Aggrescan3D web interface;
- job arrays;
- database-backed result tracking;
- cross-model plotting or ranking.
See SPEC.md for the complete behavioral contract.
aggrescan3d_common.py
build_container.sh
CHANGELOG.md
README.md
requirements.txt
run_aggrescan3d_manifest.py
run_aggrescan3d_single.py
run_tests.sh
SOURCE_IMAGE.txt
SPEC.md
submit_aggrescan3d.py
tests/
Generated files such as aggrescan3d.sif, image metadata, test manifests, Slurm logs, and per-structure result directories are excluded by .gitignore.
- Apptainer with OCI image pull support;
- Slurm for
submit_aggrescan3d.py; - Python 3.9 or newer is recommended;
- enough local or shared storage for the SIF and per-structure results.
Gemmi is required on the login and compute nodes:
python3 -m pip install --user -r requirements.txt
python3 -c 'import gemmi; print(gemmi.__version__)'The current pin is:
gemmi==0.7.4
The Python interpreter used to submit the job must resolve to an interpreter that can also import Gemmi on the compute node.
From the workflow directory:
chmod +x build_container.sh
./build_container.shbuild_container.sh uses apptainer pull, not a %post container build. This avoids reconstructing Aggrescan3D's obsolete Python 2 dependency stack.
The script validates:
- container architecture;
- Python version;
- installed Aggrescan3D package version;
- availability of
/opt/conda/bin/aggrescan; - successful CLI help execution.
It creates:
aggrescan3d.sif
aggrescan3d_image_metadata.json
The metadata file records the source image, architecture, Python version, Aggrescan3D version, executable path, and SIF SHA-256 checksum. Docker's latest tag is mutable, so retain this metadata with the deployed SIF even though both are normally excluded from Git.
Manual checks:
apptainer exec aggrescan3d.sif uname -m
apptainer exec aggrescan3d.sif \
/opt/conda/bin/python --version
apptainer exec aggrescan3d.sif \
/opt/conda/bin/conda list | grep -i '^aggrescan3d '
apptainer exec aggrescan3d.sif \
/opt/conda/bin/aggrescan --helpDo not use:
apptainer run aggrescan3d.sif --helpThe inherited image runscript treats --help as the executable name and fails.
PDB:
./run_aggrescan3d_single.py /absolute/path/model.pdbmmCIF:
./run_aggrescan3d_single.py /absolute/path/model.cifThe wrapper reads the first structural model, extracts chain B by default, writes a selected-chain PDB, executes Aggrescan3D, and parses its output.
Alternative selections:
./run_aggrescan3d_single.py model.pdb --chains A,B
./run_aggrescan3d_single.py model.cif --all-chainsForce recalculation:
./run_aggrescan3d_single.py model.cif --forceRecord or recompute the full SIF checksum during the run:
./run_aggrescan3d_single.py model.cif --container-checksumOverride the default container:
./run_aggrescan3d_single.py model.cif \
--container /path/to/aggrescan3d.sifThe default container path is:
/work/users/d/b/dbortone/binder_design/scripts/aggrescan3d/aggrescan3d.sif
A direct manifest run is useful for local validation or execution inside an existing allocation:
./run_aggrescan3d_manifest.py \
manifest.txt \
results.tsv \
--cpus 4mkdir -p slurm_logs
./submit_aggrescan3d.py \
manifest.txt \
slurm_logs \
results.tsv \
--job-suffix cd45Inspect the generated sbatch command without submitting:
./submit_aggrescan3d.py \
manifest.txt \
slurm_logs \
results.tsv \
--job-suffix cd45 \
--dry-runstructure Input .pdb, .cif, or .mmcif file
--chains B Comma-separated chain IDs; default B
--all-chains Analyze all chains in the first model
--output-dir PATH Override INPUT.aggrescan3d
--container PATH Override the default SIF
--apptainer COMMAND Override the Apptainer executable name/path
--distance {5,10} A3D neighborhood distance in Å; default 10
--hotspot-threshold X Hotspot cutoff; score must be > X; default 0.0
--verbosity {0,1,2,3,4} Aggrescan3D verbosity; default 3
--container-checksum Compute/store the SIF SHA-256 during the run
--force Recalculate even when existing output is valid
The wrapper uses Gemmi to parse both PDB and mmCIF inputs. It then:
- selects the first model;
- verifies the requested chains exist;
- requires one-character chain IDs because the selected structure is written as PDB;
- requires each selected chain to contain at least one CA-bearing residue;
- requires at least four selected CA-bearing residues in total;
- writes
selected_input.pdbwithout modifying the source file.
Chain extraction is intentional. Scoring only the binder avoids having a target mask aggregation-prone binder surfaces at a predicted interface.
The wrapper does not pass Aggrescan3D's --chain option because the temporary input PDB already contains only the requested chains.
The effective scientific command is equivalent to:
apptainer exec \
--cleanenv \
--bind <required paths> \
--env OMP_NUM_THREADS=1 \
--env OPENBLAS_NUM_THREADS=1 \
--env MKL_NUM_THREADS=1 \
--env NUMEXPR_NUM_THREADS=1 \
--env MPLBACKEND=Agg \
aggrescan3d.sif \
/opt/conda/bin/aggrescan \
-i selected_input.pdb \
-w raw \
-D 10 \
-v 3Dynamic mode, FoldX, and NACCESS are not requested. FreeSASA is therefore used by the bundled Aggrescan3D runtime.
Aggrescan3D normally writes progress messages to stderr even when the analysis succeeds. An empty stdout.txt and populated stderr.txt are not themselves errors.
For model.cif, the default result directory is:
model.cif.aggrescan3d/
Expected layout:
model.cif.aggrescan3d/
├── aggrescan3d_residues.tsv
├── aggrescan3d_summary.tsv
├── run_metadata.json
├── selected_input.pdb
├── stdout.txt
├── stderr.txt
└── raw/
├── A3D.csv
├── input.pdb
├── output.pdb
├── config.ini
├── <chain>.png
└── <chain>.svg
The image may vary its nonessential plot filenames for multi-chain inputs. All files produced in the Aggrescan3D work directory are preserved under raw/.
The validated Aggrescan3D 0.9.11 CSV schema is:
protein,chain,residue,residue_name,score
aggrescan3d_residues.tsv contains:
Chain
Residue_Number
Insertion_Code
Residue_Name
A3D_Score
Source_Residue_Field
Source_Residue_Field preserves the unmodified value from A3D.csv. The parser splits a terminal insertion-code letter into Insertion_Code when possible.
aggrescan3d_summary.tsv contains one row with:
Structure_Path
Aggrescan3D_Output_Directory
Analyzed_Chains
Residue_Count
Mean_A3D_Score
Median_A3D_Score
Maximum_A3D_Score
Minimum_A3D_Score
Positive_A3D_Residue_Count
Positive_A3D_Residue_Fraction
Sum_Positive_A3D_Score
Hotspot_Threshold
Hotspot_Residue_Count
Hotspot_Residue_Fraction
Maximum_Hotspot_Run_Length
Status
Error
Definitions:
- positive residue:
A3D_Score > 0.0; - hotspot residue:
A3D_Score > Hotspot_Threshold; - default hotspot threshold:
0.0; - maximum hotspot run: longest run of consecutive hotspot rows within a chain; runs reset at chain boundaries.
With the default threshold, positive-residue and hotspot counts are identical. A different --hotspot-threshold changes only hotspot metrics.
run_metadata.json records:
- schema and workflow status;
- input path, size, and nanosecond modification time;
- requested and analyzed chains;
- analysis mode, A3D distance, and hotspot threshold;
- Aggrescan3D and Python versions;
- solvent-accessibility implementation;
- SIF path, size, modification time, source image, and optional checksum;
- exact execution command;
- start/end timestamps and runtime;
- PDB/mmCIF conversion details;
- available chains and CA-bearing residue counts;
- parsed metrics;
- warnings;
- raw output filenames.
Without --force, an existing result is reused only when all of the following pass:
- output directory exists;
run_metadata.jsonparses;- metadata schema matches the current workflow;
- prior status is successful and exit status is zero;
- input path matches;
- input size and nanosecond modification time match;
- analysis mode is static;
- distance and hotspot threshold match;
- requested chain selection matches;
- SIF path, size, and modification time match;
- residue-level TSV exists and is nonempty;
- summary TSV has the exact expected columns;
- required numeric fields parse;
- summary path and status are reusable.
A valid reused row is returned with:
Status = existing
Any mismatch triggers automatic recomputation. --force always recomputes.
Each output path uses a sibling lock file so two processes cannot write the same result simultaneously. New output is generated in a temporary sibling directory. On success or recorded failure, the temporary directory replaces the prior output using rename-based operations. An existing complete directory is retained if replacement fails midway.
The merged manifest TSV is also written atomically after all workers finish.
A manifest contains one PDB/mmCIF path per line:
# Comments and blank lines are ignored
/absolute/path/model_1.pdb
relative/path/model_2.cif
Rules:
- relative paths are resolved relative to the manifest directory;
- blank lines are ignored;
- lines whose first non-whitespace character is
#are ignored; - duplicate resolved paths are warned about and ignored;
- first-seen ordering is preserved in the final TSV;
- missing files and unsupported extensions generate failed rows rather than stopping other work.
Example manifest generation for Boltz outputs:
find \
/work/users/d/b/dbortone/binder_design/targets/CD45/20260601_de_novo/20_boltz2msa_on_cd45d1/outputs \
-type f \
-path '*/seed_*/*/predictions/*/*.cif' \
-print | sort > manifest.txt./run_aggrescan3d_manifest.py manifest.txt results.tsv [options]Important options:
--container PATH
--cpus N
--chains B
--all-chains
--distance {5,10}
--hotspot-threshold X
--force
--container-checksum
--apptainer COMMAND
--cpus is the maximum number of concurrent structure workers. Each worker is constrained to one CPU where applicable.
A failed structure does not stop the remaining entries. Progress resembles:
[1/200] computed: /path/model_1.cif
[2/200] existing: /path/model_2.cif
[3/200] failed: /path/model_3.cif
computed=197
existing=2
failed=1
final_tsv=/path/results.tsv
The manifest runner exits nonzero when at least one structure fails, after preserving the complete final TSV.
./submit_aggrescan3d.py \
MANIFEST \
SLURM_LOG_DIR \
FINAL_TSV \
[options]Defaults:
container /work/users/d/b/dbortone/binder_design/scripts/aggrescan3d/aggrescan3d.sif
chains B
cpus 4
memory 4G
time 01:00:00
distance 10 Å
hotspot threshold 0.0
force false
mode static
Common overrides:
./submit_aggrescan3d.py manifest.txt slurm_logs results.tsv \
--job-suffix cd45 \
--cpus 8 \
--memory 8G \
--time 02:00:00 \
--partition general \
--account my_account \
--chains B \
--hotspot-threshold 0.0 \
--forceThe submitter creates one job using sbatch --wrap. It requests --cpus-per-task N, then starts at most N concurrent one-CPU workers inside that allocation. It does not use a job array.
Slurm log names are:
%x_%j.out
%x_%j.err
The single-model wrapper derives bind directories from:
- the input structure directory;
- the output directory parent;
- the SIF directory.
Redundant descendants are collapsed. Paths under:
/work/users/d/b/dbortone/binder_design
are represented by one project-root bind when possible. Output paths outside that root receive their own bind.
Per-row status values:
computed Analysis was executed successfully.
existing A validated existing result was reused.
failed Input validation, execution, or output validation failed.
Single-model exit behavior:
0: successful computation or valid cache reuse;1: computation/output-processing failure recorded in the output directory;2: invalid input, missing container, missing Apptainer, or unsupported extension.
Manifest behavior:
- returns
0when no row failed; - returns
1when one or more rows failed; - always writes the complete final TSV after workers finish.
Slurm submitter returns the sbatch command status.
Error text is sanitized to remove tabs and embedded newlines before insertion into the final TSV.
Run local workflow tests:
chmod +x run_tests.sh
./run_tests.shThe tests cover:
- comments, blank lines, relative paths, and duplicate manifest entries;
- PDB chain extraction;
- mmCIF-to-PDB conversion;
- normalized residue parsing;
- summary and hotspot metrics;
- PDB/mmCIF summary equivalence;
- valid existing-output reuse;
- invalidation after input changes;
- invalidation after SIF changes.
These tests mock Apptainer and Aggrescan3D. They verify orchestration logic but do not replace validation with the real SIF.
Validated on June 19, 2026:
- the
lcbio/a3d_serverimage pulled successfully as AMD64; /opt/conda/bin/aggrescan --helpexecuted successfully;- the image reported Aggrescan3D 0.9.11;
- a real PDB complex was reduced to chain B and produced 104 residue scores;
- a real Boltz mmCIF was converted and produced 96 residue scores;
- the raw schema was confirmed as
protein,chain,residue,residue_name,score; - a two-entry PDB/mmCIF manifest completed with two concurrent workers;
- the merged TSV was valid;
- cache reuse returned
existingrows; - Slurm submission completed successfully.
- Only the first model in a multi-model PDB/mmCIF structure is analyzed.
- Selected mmCIF chain IDs must be representable as one-character PDB chain IDs.
- Routine cache validation uses file path, size, and modification time.
--container-checksumadds stronger SIF identity recording but is optional because repeated hashing of a large SIF is expensive. - The source image uses a mutable
latesttag. Reproducibility depends on retaining the generated SIF and metadata checksum. - Aggrescan3D 0.9.11 is legacy Python 2 software. It is intentionally isolated inside the container.
- Static Aggrescan3D scores are one component of developability assessment, not a substitute for experimental aggregation, expression, solubility, or off-target testing.
Analyses should cite the Aggrescan3D method and standalone software as appropriate:
- Zambrano R. et al. AGGRESCAN3D (A3D): server for prediction of aggregation properties of protein structures. Nucleic Acids Research, 2015.
- Kuriata A. et al. Aggrescan3D standalone package for structure-based prediction of protein aggregation properties. Bioinformatics, 2019.
Review the upstream software and container licensing terms before redistribution or commercial deployment. This repository contains workflow code and does not redistribute the SIF through Git.