Skip to content

feat: add colabfold module - #13066

Open
Bboy010 wants to merge 10 commits into
nf-core:masterfrom
Bboy010:modules/nf-core/colabfold
Open

Bboy010 wants to merge 10 commits into
nf-core:masterfrom
Bboy010:modules/nf-core/colabfold

Conversation

@Bboy010

@Bboy010 Bboy010 commented Sep 30, 2026

Copy link
Copy Markdown
Contributor

New module colabfold: protein structure prediction with ColabFold (AlphaFold2), MSA from an MMseqs2 server.

No existing module covers this (checked modules/nf-core, open/closed PRs and issues for colabfold/alphafold; foldseek/foldcomp/foldmason are search/compression tools, not predictors).

  • Versions emitted through the versions topic channel.
  • Tests: real run on an empty FASTA (no structure emitted) and a stub test on proteomics/interproscan/l_arginase.faa. A real fold needs ~4 GB of weights, an MMseqs2 server and a GPU/hours of CPU, which CI runners lack.
  • nf-core modules lint colabfold: 59 passed, 1 warning (process_gpu label, expected for a GPU module), 0 failed.

PR checklist

  • This comment contains a description of changes (with reason).
  • Followed the module conventions in the contribution docs.
  • Added a resource label.
  • Added a stub test and a snapshot.
  • nf-core modules lint colabfold and nf-test test modules/nf-core/colabfold --profile docker pass locally.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@Bboy010
Bboy010 force-pushed the modules/nf-core/colabfold branch from ff56f5a to 5504259 Compare September 30, 2026 19:22
…zeOutput snapshots

Same adjustments as requested in the tmalign review (nf-core#13065):
- Emit colabfold_batch's own files (pdb, scores json, a3m, png, log)
  instead of copying rank_001 models in a loop and computing a custom
  pLDDT table; per-residue pLDDT is already in the scores json and the
  PDB B-factor column.
- Weights input is now a tuple with meta2.
- Remove the header comment block and pipeline-specific workarounds
  (query-name trimming, unified-memory env, CUDA-image cache path);
  Amber relaxation is now opt-in through ext.args.
- Tests snapshot sanitizeOutput(process.out).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@vagkaratzas vagkaratzas left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Blocking: the container is still a legacy biocontainer; please port it to a Seqera Wave build with nf-core modules containers create colabfold (E1).
  • Blocking: the only non-stub test runs on an empty FASTA, so the tool is never executed and the weights branch is untested; the test name also needs the <organism> - <inputs> - <outputs> form (T10, T5).
  • Smaller points: meta.yml needs args_id, EDAM ontologies and "Optional" on weights; tests should assert process.success before assertAll.
  • Module has the process_gpu label: please also check it against the GPU module docs (GPU test config and tag, ext.use_gpu handling), since the CI GPU jobs were skipped.

Gates: lint, nf-test (conda, docker, singularity) and nextflow lint all pass.

Diff 8.18/10 · Overall 8.18/10
nf-core-reviewer 0.3.0 / vagkaratzas-review (rubric v2) · claude-sonnet-5-5

Comment thread modules/nf-core/colabfold/main.nf Outdated
Comment on lines +7 to +9
container "${workflow.containerEngine in ['singularity', 'apptainer'] && !task.ext.singularity_pull_docker_container
? 'https://depot.galaxyproject.org/singularity/colabfold:1.5.5--pyh7cba7a3_2'
: 'quay.io/biocontainers/colabfold:1.5.5--pyh7cba7a3_2'}"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The container still points at depot.galaxyproject.org / quay.io/biocontainers. Please build a Seqera Wave container with nf-core modules containers create colabfold, which updates this directive, adds the containers: block to meta.yml and the .conda-lock/ files.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in a260a90. The CPU container was built with nf-core modules containers create colabfold, which also added the containers: block and the .conda-lock/ file. The linux/arm64 builds failed twice on the Wave side (an S3 AccessDenied on the arm64 builder, then exit 137), so only linux/amd64 is listed for now; I can rerun the command once those builds go through.

There is also a GPU variant (see the general comment): environment.gpu.yml, built with Wave using --build-template conda/micromamba:v2 and selected with task.accelerator, following the dual-container pattern from the GPU modules guide.

// A real prediction needs the ~4 GB AlphaFold2 weights, which no CI runner
// has. What can run for real is the empty-input path, where the module
// must succeed and emit no structure.
test("empty fasta") {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test name should follow <organism> - <inputs> - <outputs/config>, e.g. proteomics - empty faa - no structures.

More importantly, this only exercises the empty-input shortcut: colabfold_batch is never run, and neither the --data ${weights} branch nor the no-weights branch is covered. Please add a test that runs the tool for real (a tiny sequence with weights staged via meta2, or a documented way to make it fit in CI), or at least cover the weights-given branch.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed to proteomics - empty faa - no structures (and proteomics - faa - stub).

For a real run, I added tests/main.gpu.nf.test (tag gpu, nextflow.gpu.config sets accelerator = 1). It runs a single-sequence prediction of proteomics/interproscan/l_arginase.faa (--msa-mode single_sequence --num-models 1 --num-recycle 1, so no MSA server is queried) and asserts that a PDB with ATOM records is produced. Locally it passes in ~7 min including the weights download; the prediction itself takes ~40 s on a GPU.

The real test is GPU-only because on CPU the same prediction takes 15 min on 4 cores, after a ~3.5 GB weights download, and would run three times (conda, docker, singularity).

On the weights: the test passes [], which exercises the download branch. The --data branch can't be covered in CI because the AlphaFold2 parameters can't go into nf-core/test-datasets (a single model is >350 MB, above GitHub's 100 MB file limit). It is the same colabfold_batch call with --data <dir> added; I checked it locally with a real prediction against a local weights directory.

Running this for real also caught two bugs that the empty-input and stub tests could not see: a fresh solve of colabfold 1.5.5 pulled dm-haiku 0.0.17, which crashes at the first prediction with jax 0.4.25 (now pinned to 0.0.12 + flax 0.8.2, as in the old biocontainer), and the CUDA 11.8 jaxlib build has no ptxas, so ColabFold skipped every query while still exiting 0 (hence CUDA 12.0).

}

then {
assertAll(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Put a bare assert process.success first, then assertAll(...) holding the snapshot, as in the nf-core template.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in a260a90, here and in the GPU tests.

}

then {
assertAll(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here: bare assert process.success before assertAll(...).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in a260a90.

documentation: https://github.com/sokrypton/ColabFold
doi: 10.1038/s41592-022-01488-1
licence: ["MIT"]
identifier: biotools:colabfold

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add the args_id for the tool, matching the $args variable in main.nf.

Suggested change
identifier: biotools:colabfold
identifier: biotools:colabfold
args_id: "$args"

(Y4)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in a260a90.

Comment thread modules/nf-core/colabfold/meta.yml Outdated
Comment on lines +43 to +49
- weights:
type: directory
description: |
ColabFold data directory containing the AlphaFold2 parameters
(`params/`). Pass `[]` to let colabfold_batch download them into its
own cache on first use.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

weights is optional ([] is accepted), so the description should start with "Optional". Descriptions should also be a single short line.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in a260a90: the description now starts with "Optional" and is a single line, and the other input/output descriptions are shortened too.

Comment thread modules/nf-core/colabfold/meta.yml Outdated
Comment on lines +32 to +37
- fasta:
type: file
description: |
Protein sequences to fold, one prediction per record. An empty file
is accepted and produces no output.
pattern: "*.{fa,faa,fasta}"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Input and output file entries are missing EDAM ontologies (fasta, pdb, json, png, log, weights), one per format in the pattern, each followed by its format name comment (e.g. # FASTA). Use ontologies: [] only where no term exists (e.g. a3m).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in a260a90: FASTA (format_1929), PDB (format_1476), JSON (format_3464), PNG (format_3603) and textual format (format_2330) for the log. ontologies: [] for the a3m output and for weights, which is a directory of AlphaFold2 .npz parameter files with no matching EDAM format.

…t, meta.yml fixes

- Containers: Seqera Wave builds. CPU via `nf-core modules containers create`
  (amd64; the arm64 builds failed on the Wave side). GPU variant from
  environment.gpu.yml (CUDA 12.0, jaxlib 0.4.25 cuda build, built with
  --build-template conda/micromamba:v2), selected with task.accelerator
  following the dual-container pattern of the GPU modules guide.
- Pin dm-haiku=0.0.12 and flax=0.8.2: a fresh solve of colabfold 1.5.5 pulls
  dm-haiku 0.0.17, which fails at the first prediction with jax 0.4.25.
- CUDA 12.0 rather than 11.8: the jaxlib cuda118 build has no ptxas, so
  ColabFold skipped every prediction while still exiting 0.
- Emit the CUDA version on the versions topic (no CUDA available on CPU).
- Tests: real single-sequence prediction of l_arginase in main.gpu.nf.test
  (tag gpu, nextflow.gpu.config sets accelerator = 1); CPU tests renamed to
  <domain> - <inputs> - <outputs>; bare `assert process.success` before
  assertAll.
- meta.yml: args_id, EDAM ontologies, one-line descriptions, weights marked
  Optional.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added size/xl and removed size/m labels Oct 24, 2026
@github-actions

Copy link
Copy Markdown

This PR exceeds the recommended size of 1000 lines. Please make sure you are NOT addressing multiple issues with one PR. Note this PR might be rejected due to its size.

@Bboy010

Bboy010 commented Oct 24, 2026

Copy link
Copy Markdown
Contributor Author

@vagkaratzas all points are addressed in a260a90; replies are in each thread. On the GPU point:

  • The module follows the dual-container pattern from the GPU modules guide: task.accelerator selects environment.gpu.yml and its Wave containers, otherwise the CPU environment is used. ext.use_gpu is no longer used.
  • environment.gpu.yml pins cuda-version=12.0 and jaxlib=0.4.25=*cuda*. 12.0 is the lowest CUDA that works: the cuda118 jaxlib build has no ptxas. The CUDA version is emitted on the versions topic (no CUDA available on the CPU path).
  • tests/main.gpu.nf.test (tag gpu) has a real test and a stub test, with tests/nextflow.gpu.config setting accelerator = 1 and maxForks = 1.

Local checks: nf-test (singularity) CPU and GPU tests pass and their snapshots are stable on a second run; GPU run on an RTX 2060; nf-core modules lint 74 passed, 0 failed (the remaining warning is the process_gpu label); prettier clean.

One note for running it: colabfold sets TF_FORCE_UNIFIED_MEMORY=1 and XLA_PYTHON_CLIENT_MEM_FRACTION=4.0 unless they are already set. That is fine on a 16 GB card like the CI's T4. On small GPUs (6 GB under WSL here) it made the same prediction take an hour instead of 40 s, so I left the tool default in place and only overrode it locally.

@vagkaratzas vagkaratzas left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Thanks, the GPU setup now follows the GPU modules guide: two containers chosen by task.accelerator, cuda-version pinned exactly at the lowest working version, jaxlib=*cuda* build glob, CUDA version emitted on the versions topic, and a gpu-tagged main.gpu.nf.test with real and stub tests and accelerator = 1 / maxForks = 1. The GPU CI jobs pass.
  • Blocking: the module should expose every file colabfold_batch reads or writes. The template inputs and several outputs (config.json, cite.bibtex, PAE JSON, pickles, representations, zip) are missing.
  • Blocking: versions emits are missing for the pinned dm-haiku, flax and jax, and the stub does not match a real run.
  • Blocking (GPU tests): the stable checks need to go inside the snapshot, the test arguments should come through params.module_args, and the stub test name should end with - stub.

Two questions, not blocking:

  • meta.yml containers: and .conda-lock/ only cover the CPU build. If nf-core modules containers create can handle environment.gpu.yml, please add the GPU entries too, so the GPU image is tracked like the CPU one.
  • Was the GPU image built with --build-template conda/micromamba:v2, as the guide recommends for GPU environments?

Diff 7.74/10 · Overall 7.74/10
nf-core-reviewer 0.5.0dev / vagkaratzas-review (rubric v4) · claude-opus-5-5

Comment on lines +11 to +13
input:
tuple val(meta), path(fasta)
tuple val(meta2), path(weights)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

colabfold_batch also reads template files: --custom-template-path (a folder of PDB/mmCIF files), and --pdb-hit-file with --local-pdb-path. Files can't go through ext.args, so please add them as optional inputs (e.g. tuple val(meta3), path(templates)) and pass each flag only when its input is given. --templates itself stays in ext.args.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in a5aaff6. Two optional inputs: tuple val(meta3), path(templates) for --custom-template-path, and tuple val(meta4), path(pdb_hits), path(local_pdb) for --pdb-hit-file / --local-pdb-path. Each flag is only added when its input is given, and --templates stays in ext.args. I checked it with a real run using 1L2Y as a custom template for the Trp-cage sequence: colabfold reports found templates: ['1l2y_A'] and writes *_template_domain_names.json.

Comment thread modules/nf-core/colabfold/main.nf Outdated
Comment on lines +16 to +20
tuple val(meta), path("${prefix}/*_rank_*.pdb") , emit: pdb , optional: true
tuple val(meta), path("${prefix}/*_scores_rank_*.json"), emit: json, optional: true
tuple val(meta), path("${prefix}/*.a3m") , emit: a3m , optional: true
tuple val(meta), path("${prefix}/*.png") , emit: png , optional: true
tuple val(meta), path("${prefix}/log.txt") , emit: log , optional: true

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Several files that colabfold_batch writes are not emitted: config.json, cite.bibtex, *_predicted_aligned_error_v1.json, the *.pickle files (--msa-only, --save-all), the *_repr_*.npy files (--save-single-representations, --save-pair-representations), *.result.zip (--zip) and *_template_domain_names.json (--templates). Please emit them as optional outputs, and update meta.yml and the stub to match.

Suggested change
tuple val(meta), path("${prefix}/*_rank_*.pdb") , emit: pdb , optional: true
tuple val(meta), path("${prefix}/*_scores_rank_*.json"), emit: json, optional: true
tuple val(meta), path("${prefix}/*.a3m") , emit: a3m , optional: true
tuple val(meta), path("${prefix}/*.png") , emit: png , optional: true
tuple val(meta), path("${prefix}/log.txt") , emit: log , optional: true
tuple val(meta), path("${prefix}/*_rank_*.pdb") , emit: pdb , optional: true
tuple val(meta), path("${prefix}/*_scores_rank_*.json") , emit: json , optional: true
tuple val(meta), path("${prefix}/*_predicted_aligned_error_v1.json"), emit: pae , optional: true
tuple val(meta), path("${prefix}/*.a3m") , emit: a3m , optional: true
tuple val(meta), path("${prefix}/*.png") , emit: png , optional: true
tuple val(meta), path("${prefix}/*.pickle") , emit: pickle , optional: true
tuple val(meta), path("${prefix}/*_repr_*.npy") , emit: npy , optional: true
tuple val(meta), path("${prefix}/*.result.zip") , emit: zip , optional: true
tuple val(meta), path("${prefix}/*_template_domain_names.json") , emit: templates, optional: true
tuple val(meta), path("${prefix}/config.json") , emit: config , optional: true
tuple val(meta), path("${prefix}/cite.bibtex") , emit: bibtex , optional: true
tuple val(meta), path("${prefix}/log.txt") , emit: log , optional: true

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in a5aaff6, with your suggested outputs (pae, pickle, npy, zip, templates, config, bibtex), plus meta.yml entries and EDAM terms where they exist (ZIP format_3987, NumPy format_4003, pickle format_4002; [] for BibTeX). I checked the file names against colabfold 1.5.5's source: representations are *_single_repr_*.npy / *_pair_repr_*.npy and the pickles are *_all_*.pickle (--save-all) or <query>.pickle (--msa-only), so the globs match.

tuple val(meta), path("${prefix}/*.png") , emit: png , optional: true
tuple val(meta), path("${prefix}/log.txt") , emit: log , optional: true
tuple val("${task.process}"), val('colabfold'), eval("python -c 'import importlib.metadata as m; print(m.version(\"colabfold\"))'"), topic: versions, emit: versions_colabfold
tuple val("${task.process}"), val('cuda'), eval('python -c "import glob, json, sys; f = glob.glob(sys.prefix + \'/conda-meta/cuda-version-*.json\'); print(json.load(open(f[0]))[\'version\'] if f else \'no CUDA available\')"'), topic: versions, emit: versions_cuda

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dm-haiku and flax are pinned in both environments, and jax in the GPU one, but none of them has a versions emit. Since they are pinned to keep colabfold working, please report them too (then refresh meta.yml with nf-core modules lint colabfold --fix).

Suggested change
tuple val("${task.process}"), val('cuda'), eval('python -c "import glob, json, sys; f = glob.glob(sys.prefix + \'/conda-meta/cuda-version-*.json\'); print(json.load(open(f[0]))[\'version\'] if f else \'no CUDA available\')"'), topic: versions, emit: versions_cuda
tuple val("${task.process}"), val('cuda'), eval('python -c "import glob, json, sys; f = glob.glob(sys.prefix + \'/conda-meta/cuda-version-*.json\'); print(json.load(open(f[0]))[\'version\'] if f else \'no CUDA available\')"'), topic: versions, emit: versions_cuda
tuple val("${task.process}"), val('dm-haiku'), eval("python -c 'import importlib.metadata as m; print(m.version(\"dm-haiku\"))'"), topic: versions, emit: versions_dm_haiku
tuple val("${task.process}"), val('flax'), eval("python -c 'import importlib.metadata as m; print(m.version(\"flax\"))'"), topic: versions, emit: versions_flax
tuple val("${task.process}"), val('jax'), eval("python -c 'import importlib.metadata as m; print(m.version(\"jax\"))'"), topic: versions, emit: versions_jax

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in a5aaff6: versions_dm_haiku, versions_flax and versions_jax, all on the versions topic. The GPU snapshot now records dm-haiku 0.0.12, flax 0.8.2, jax 0.4.25 and cuda 12.0.

Comment on lines +49 to +56
"""
mkdir ${prefix}
touch ${prefix}/${prefix}_unrelaxed_rank_001_alphafold2_ptm_model_1_seed_000.pdb
touch ${prefix}/${prefix}_scores_rank_001_alphafold2_ptm_model_1_seed_000.json
touch ${prefix}/${prefix}.a3m
touch ${prefix}/${prefix}_plddt.png
touch ${prefix}/log.txt
"""

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The stub doesn't match a real run. A real run also writes _coverage.png, _pae.png, the PAE JSON, config.json and cite.bibtex, and an empty FASTA gives no output at all. The stub always creates files and writes only one plot.

Suggested change
"""
mkdir ${prefix}
touch ${prefix}/${prefix}_unrelaxed_rank_001_alphafold2_ptm_model_1_seed_000.pdb
touch ${prefix}/${prefix}_scores_rank_001_alphafold2_ptm_model_1_seed_000.json
touch ${prefix}/${prefix}.a3m
touch ${prefix}/${prefix}_plddt.png
touch ${prefix}/log.txt
"""
mkdir ${prefix}
if [ -s ${fasta} ]; then
touch ${prefix}/${prefix}_unrelaxed_rank_001_alphafold2_ptm_model_1_seed_000.pdb
touch ${prefix}/${prefix}_scores_rank_001_alphafold2_ptm_model_1_seed_000.json
touch ${prefix}/${prefix}_predicted_aligned_error_v1.json
touch ${prefix}/${prefix}.a3m
touch ${prefix}/${prefix}_coverage.png
touch ${prefix}/${prefix}_pae.png
touch ${prefix}/${prefix}_plddt.png
touch ${prefix}/config.json
touch ${prefix}/cite.bibtex
touch ${prefix}/log.txt
fi
"""

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in a5aaff6 with your suggestion: the stub now writes the three plots, the PAE JSON, config.json and cite.bibtex, and nothing when the FASTA is empty.

Comment on lines +32 to +37
assert process.success
assertAll(
{ assert snapshot(sanitizeOutput(process.out, unstableKeys: ['pdb', 'json', 'png', 'log'])).match() },
{ assert path(process.out.pdb[0][1]).readLines().any { it.startsWith('ATOM') } },
{ assert path(process.out.log[0][1]).text.contains('Done') }
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please put the stable checks for the unstable channels inside the snapshot, so a change shows up as a snapshot diff, and add one for json. This also replaces the implicit it with a named closure parameter. The atom count of the unrelaxed model depends only on the sequence, so it stays stable across runs.

Suggested change
assert process.success
assertAll(
{ assert snapshot(sanitizeOutput(process.out, unstableKeys: ['pdb', 'json', 'png', 'log'])).match() },
{ assert path(process.out.pdb[0][1]).readLines().any { it.startsWith('ATOM') } },
{ assert path(process.out.log[0][1]).text.contains('Done') }
)
assert process.success
assertAll(
{ assert snapshot(
sanitizeOutput(process.out, unstableKeys: ['pdb', 'json', 'png', 'log']),
path(process.out.pdb[0][1]).readLines().count { line -> line.startsWith('ATOM') },
path(process.out.json[0][1]).text.contains('plddt'),
path(process.out.log[0][1]).text.contains('Done')
).match() }
)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in a5aaff6: the ATOM count, the plddt check on the scores JSON and the Done check on the log are now in the snapshot, with a named closure parameter. pae, config and bibtex are added to unstableKeys. Two local GPU runs gave the same snapshot.

}
}

test("proteomics - faa - stub - gpu") {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stub test names should end with - stub.

Suggested change
test("proteomics - faa - stub - gpu") {
test("proteomics - faa - gpu - stub") {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed to proteomics - faa - gpu - stub in a5aaff6.

accelerator = 1
// Concurrent GPU tasks on one GPU can deadlock under Singularity
maxForks = 1
ext.args = '--msa-mode single_sequence --num-models 1 --num-recycle 1'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't hardcode test arguments in the config. Pass them through params.module_args, and add params { module_args = '--msa-mode single_sequence --num-models 1 --num-recycle 1' } to the when block of the GPU tests.

Suggested change
ext.args = '--msa-mode single_sequence --num-models 1 --num-recycle 1'
ext.args = { params.module_args ?: '' }

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in a5aaff6: ext.args = { params.module_args ?: '' } in the config, and params { module_args = '--msa-mode single_sequence --num-models 1 --num-recycle 1' } in the when block of both GPU tests.

@vagkaratzas

Copy link
Copy Markdown
Contributor

@JoseEspinosa have a look at this one to see where it is going, and let us know if it would be re-usable in proteinfold ;)

…s, fix tests

- Optional inputs for custom templates (--custom-template-path) and for a PDB
  hit file with a local PDB mirror (--pdb-hit-file, --local-pdb-path); each
  flag is passed only when its input is given, --templates stays in ext.args.
- Emit every file colabfold_batch can write: PAE JSON, pickles, representation
  .npy files, result.zip, template domain names, config.json and cite.bibtex.
- Report the pinned dm-haiku, flax and jax on the versions topic.
- Stub writes the same files as a real run, and nothing for an empty FASTA.
- GPU tests: stable checks inside the snapshot, test arguments through
  params.module_args, stub test renamed to end with "- stub".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Bboy010

Bboy010 commented Oct 25, 2026

Copy link
Copy Markdown
Contributor Author

@vagkaratzas thanks, all points are addressed in a5aaff6 and CI is green, including the GPU jobs. On your two questions:

  1. GPU entries in containers: / .conda-lock/: nf-core modules containers create only reads environment.yml (checked in its help and in its source, nf-core/tools 4.2.0), and the meta-schema keys containers.docker|singularity|conda by platform (linux/amd64, linux/arm64), with no slot for a GPU variant. ribodetector, the reference dual-container module, has no containers: block either. So the GPU image is only referenced in main.nf for now. If you have a preferred key for it (e.g. linux/amd64-gpu), I can add the entries and the lock file by hand.

  2. Yes: both GPU images (Docker and Singularity) were built with wave --conda-file environment.gpu.yml --freeze --build-template conda/micromamba:v2 --platform linux/amd64.

@Bboy010
Bboy010 requested a review from vagkaratzas October 25, 2026 21:19

@vagkaratzas vagkaratzas left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Blocking: the stub names files after prefix, while a real run names them after the FASTA headers.

  • Blocking: the weights, templates and pdb_hits inputs are not tested; only the download path runs in CI.

  • Blocking (the weights download): with weights = [] every task downloads the 3.5 GB AlphaFold2 tarball into its own work directory (HOME=$PWD, so the cache is per task). A pipeline that folds N proteins as N tasks downloads it N times and uses N × 3.5 GB of disk. Please split the module the way caalm and fungtion do:

    • colabfold/downloadmodels downloads the weights (e.g. download_alphafold_params() into the task directory, which also writes params/download_finished.txt). A stub-only test is fine here, since the real download runs in the main module's test.
    • colabfold/colabfold (moved from colabfold/) takes weights as a required input. Its GPU test gets the weights from setup { run("COLABFOLD_DOWNLOADMODELS") { script "../../downloadmodels/main.nf" } }, so the --data path that pipelines will use is the one tested. Rename withName: 'COLABFOLD' in nextflow.gpu.config to the new process name.

    The 3.5 GB download on the GPU runners is fine: no smaller set of weights exists that test-datasets could host, the GPU jobs already finish in 3–5 minutes, and checkm2/databasedownload pulls a similar size from Zenodo. Just keep it out of the CPU tests.

Not blocking: the empty-FASTA guard in script exists mainly so the CPU file has a non-stub test, and that test only checks versions. With weights required, you can drop both the guard and that test and keep the CPU file stub-only, since the real run lives in main.gpu.nf.test.

Diff 9.19/10 · Overall 9.19/10
nf-core-reviewer 0.5.0 / vagkaratzas-review (rubric v4) · claude-opus-5-5

Comment added by Claude Opus 5.5

Comment on lines +66 to +82
stub:
prefix = task.ext.prefix ?: "${meta.id}"
"""
mkdir ${prefix}
if [ -s ${fasta} ]; then
touch ${prefix}/${prefix}_unrelaxed_rank_001_alphafold2_ptm_model_1_seed_000.pdb
touch ${prefix}/${prefix}_scores_rank_001_alphafold2_ptm_model_1_seed_000.json
touch ${prefix}/${prefix}_predicted_aligned_error_v1.json
touch ${prefix}/${prefix}.a3m
touch ${prefix}/${prefix}_coverage.png
touch ${prefix}/${prefix}_pae.png
touch ${prefix}/${prefix}_plddt.png
touch ${prefix}/config.json
touch ${prefix}/cite.bibtex
touch ${prefix}/log.txt
fi
"""

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The stub names files after prefix, but colabfold_batch names them after each FASTA header (safe_filename keeps [A-Za-z0-9_.-], everything else becomes _). The GPU snapshot shows it: the real run writes GI_225038609_EFDID_719595_FULL.a3m, the stub writes test.a3m. That's on me, my earlier suggestion had the same mistake. Deriving the names from the headers fixes it and also gives one set of files per record.

Suggested change
stub:
prefix = task.ext.prefix ?: "${meta.id}"
"""
mkdir ${prefix}
if [ -s ${fasta} ]; then
touch ${prefix}/${prefix}_unrelaxed_rank_001_alphafold2_ptm_model_1_seed_000.pdb
touch ${prefix}/${prefix}_scores_rank_001_alphafold2_ptm_model_1_seed_000.json
touch ${prefix}/${prefix}_predicted_aligned_error_v1.json
touch ${prefix}/${prefix}.a3m
touch ${prefix}/${prefix}_coverage.png
touch ${prefix}/${prefix}_pae.png
touch ${prefix}/${prefix}_plddt.png
touch ${prefix}/config.json
touch ${prefix}/cite.bibtex
touch ${prefix}/log.txt
fi
"""
stub:
prefix = task.ext.prefix ?: "${meta.id}"
"""
mkdir ${prefix}
grep '^>' ${fasta} | sed 's/^>//; s/[^A-Za-z0-9_.-]/_/g' | while read -r name; do
touch ${prefix}/\${name}_unrelaxed_rank_001_alphafold2_ptm_model_1_seed_000.pdb
touch ${prefix}/\${name}_scores_rank_001_alphafold2_ptm_model_1_seed_000.json
touch ${prefix}/\${name}_predicted_aligned_error_v1.json
touch ${prefix}/\${name}.a3m
touch ${prefix}/\${name}_coverage.png
touch ${prefix}/\${name}_pae.png
touch ${prefix}/\${name}_plddt.png
done
if [ -s ${fasta} ]; then
touch ${prefix}/config.json
touch ${prefix}/cite.bibtex
touch ${prefix}/log.txt
fi
"""

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, with your suggestion. For an A3M input the name comes from the file name, as colabfold does it. The stub snapshot now shows GI_225038609_EFDID_719595_FULL.a3m, the same name as the real run.

Comment thread modules/nf-core/colabfold/meta.yml Outdated
e.g. [ id:'alphafold2_params' ]
- weights:
type: directory
description: Optional ColabFold data directory with the AlphaFold2 parameters in `params/`; pass `[]` to download them

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

colabfold_batch checks for an empty file, params/download_finished.txt, which colabfold's own downloader creates when it finishes. It checks even when --data is given. If the file is missing (e.g. weights extracted by hand, or reused from proteinfold), colabfold downloads the 3.5 GB again into the staged weights folder. That writes through the symlink into the upstream folder, or fails on read-only storage. Please say this in the description, so users who bring their own weights know to add the file.

Suggested change
description: Optional ColabFold data directory with the AlphaFold2 parameters in `params/`; pass `[]` to download them
description: ColabFold data directory with the AlphaFold2 parameters in `params/`. It must contain the empty `params/download_finished.txt` that colabfold's downloader creates (e.g. via `colabfold/downloadmodels`); without it colabfold downloads the weights again

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Applied as suggested.

[ id:'test' ],
file(params.modules_testdata_base_path + 'proteomics/interproscan/l_arginase.faa', checkIfExists: true)
]
input[1] = [ [:], [] ]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No test passes weights, templates or pdb_hits, so the --data, --custom-template-path and --pdb-hit-file / --local-pdb-path branches never run in CI. Once the weights come from a setup block (see the review body), the --data path is tested. For templates, the cheapest test is a second GPU test with the 20-residue Trp-cage sequence and the 1L2Y template you already tried locally (both small enough for test-datasets), with --templates in module_args.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done: the weights come from the setup block, and two new GPU tests cover templates and pdb_hits / local_pdb on Trp-cage (details in the general comment). Test data: nf-core/test-datasets#2354.

Comment thread modules/nf-core/colabfold/meta.yml Outdated
type: file
description: Optional BLAST-m8 PDB hit file for the input MSA, e.g. from colabfold_search (`--pdb-hit-file`; needs `--templates` in `ext.args`)
pattern: "*.m8"
ontologies: []

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

*.m8 is a tab-separated BLAST table; TSV (format_3475) fits until EDAM gets a specific term.

Suggested change
ontologies: []
ontologies:
- edam: http://edamontology.org/format_3475 # TSV

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Applied (TSV, format_3475).

@vagkaratzas

Copy link
Copy Markdown
Contributor

@Bboy010 thanks, getting there!
We need to get this one right, so other protein folding modules can then follow its template ;)
Have a look at the suggested caalm and fungtion modules to see how they have a helper module downloading weights, which they then use as setup in nf-tests, and can also be used in pipelines together.

Bboy010 and others added 2 commits October 28, 2026 18:53
… test every input

- New colabfold/downloadmodels (COLABFOLD_DOWNLOADMODELS): downloads a
  parameter set once with colabfold's own download_alphafold_params(),
  which also writes the params/download*_finished.txt marker. Stub test.
- colabfold/colabfold (COLABFOLD_COLABFOLD, moved from colabfold/):
  weights are a required input (--data); the empty-FASTA guard and its
  test are gone, the CPU test file is stub-only.
- templates are staged as files into templates/, so the pdb70 index and
  PDB->mmCIF conversions colabfold writes there stay in the task dir.
- The input also accepts a single A3M (colabfold names that query after
  the file); with a FASTA, --templates always queries the MSA server.
- Stub names the files after the FASTA headers (or the A3M file name).
- GPU tests take the weights from a setup block running
  COLABFOLD_DOWNLOADMODELS, and cover custom templates (1L2Y) and
  pdb_hits + local_pdb (via UNTAR) on Trp-cage; test data in
  nf-core/test-datasets#2354.
- meta.yml: weights description (download marker), TSV for *.m8.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Bboy010

Bboy010 commented Oct 28, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @vagkaratzas! The split is done in e22eedc. I followed caalm/fungtion:

  • colabfold/downloadmodels downloads one parameter set with colabfold's own download_alphafold_params(), so params/download_finished.txt gets written. The model_type input takes alphafold2_ptm, the multimer sets or deepfold_v1. The test is stub-only. I also ran the real download locally: 3.5 GB in about 6 minutes, with the 10 .npz files and the marker.

  • colabfold/colabfold takes the weights as a required input (--data). The empty-FASTA guard and its test are gone, and the CPU test file is stub-only.

  • GPU tests get the weights from setup { run("COLABFOLD_DOWNLOADMODELS") }. There are three real tests:

    • the l_arginase FASTA in single-sequence mode;
    • Trp-cage with the 1L2Y custom template;
    • Trp-cage with pdb_hits and local_pdb (the local PDB comes from a tarball, unpacked by UNTAR in the setup).

    Both template tests check that the log says found templates: ['1l2y_A'], and they snapshot *_template_domain_names.json. All four tests pass twice in a row locally on an RTX 2060, in about 4 minutes for the file.

  • Test data: Add Trp-cage / 1L2Y test data for the colabfold module test-datasets#2354 adds four files, under 35 KB in total. Until it is merged, the tests that read them fail in CI.

Two changes go beyond your suggestions, so please take a look:

  1. With a FASTA input, --templates always calls the MSA server, even with --msa-mode single_sequence: get_msa_and_templates runs run_mmseqs2 whenever there are no a3m lines. So the template tests use a single-sequence A3M, and the input now also accepts *.a3m. Colabfold names an A3M query after the file, and the stub does the same.
  2. templates is now a list of files staged into templates/ instead of a directory. Colabfold writes its pdb70 index into that folder and converts .pdb files to .cif there. With a staged directory, those writes went through the symlink into the user's folder, the same problem you raised for the weights.

@vagkaratzas

Copy link
Copy Markdown
Contributor

Fixes failing tests please and I'll have another look!

@Bboy010

Bboy010 commented Oct 29, 2026

Copy link
Copy Markdown
Contributor Author

Fixes failing tests please and I'll have another look!

Thanks @vagkaratzas! The code is not what fails: every failing test reads trp_cage.a3m or local_pdb.tar.gz, which are added by nf-core/test-datasets#2354. In the same jobs the other tests pass, including the real GPU tests with the 3.5 GB weights download. The template tests pass locally with those files.

#2354 is green (pre-commit) but has no reviewer yet. Could you, or someone with access, have a look at it? Once it is merged I'll re-run the failed jobs here.

@vagkaratzas

Copy link
Copy Markdown
Contributor

Merged test-datasets and merged main branch to rerun tests anew.

@vagkaratzas vagkaratzas left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Thanks, the split worked out well. colabfold/downloadmodels feeds the GPU tests through setup, so they now test the --data path that pipelines will use, plus the custom-template and pdb_hits/local_pdb paths. The stub names match a real run. CI is green, GPU jobs included.
  • Blocking: colabfold/downloadmodels has no versions emits for the pinned dm-haiku and flax.
  • Small: the local_pdb directory entry in meta.yml should have ontologies: [].

Diff 9.52/10 · Overall 9.52/10
nf-core-reviewer 0.5.0 / vagkaratzas-review (rubric v4) · claude-opus-5-5


output:
tuple val(meta), path("weights"), emit: weights
tuple val("${task.process}"), val('colabfold'), eval("python -c 'import importlib.metadata as m; print(m.version(\"colabfold\"))'"), topic: versions, emit: versions_colabfold

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dm-haiku and flax are pinned in this environment.yml too, so they need their own versions emits here, the same as in colabfold/colabfold. Then run nf-core modules lint colabfold/downloadmodels --fix to update meta.yml.

Suggested change
tuple val("${task.process}"), val('colabfold'), eval("python -c 'import importlib.metadata as m; print(m.version(\"colabfold\"))'"), topic: versions, emit: versions_colabfold
tuple val("${task.process}"), val('colabfold'), eval("python -c 'import importlib.metadata as m; print(m.version(\"colabfold\"))'"), topic: versions, emit: versions_colabfold
tuple val("${task.process}"), val('dm-haiku'), eval("python -c 'import importlib.metadata as m; print(m.version(\"dm-haiku\"))'"), topic: versions, emit: versions_dm_haiku
tuple val("${task.process}"), val('flax'), eval("python -c 'import importlib.metadata as m; print(m.version(\"flax\"))'"), topic: versions, emit: versions_flax

Comment on lines +73 to +74
ontologies:
- edam: http://edamontology.org/format_1477 # mmCIF

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

local_pdb is a directory, and directory entries get ontologies: [].

Suggested change
ontologies:
- edam: http://edamontology.org/format_1477 # mmCIF
ontologies: []

@vagkaratzas vagkaratzas left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, preemptively approving this round. Just make sure to fix the last two issues before merging :D

Bboy010 and others added 2 commits October 30, 2026 20:52
…logy for local_pdb

- colabfold/downloadmodels: versions_dm_haiku and versions_flax emits,
  as in colabfold/colabfold (both are pinned in environment.yml).
- colabfold/colabfold meta.yml: local_pdb is a directory, so
  `ontologies: []`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Bboy010

Bboy010 commented Oct 30, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @vagkaratzas! Both fixed in c34377f: colabfold/downloadmodels now emits the dm-haiku and flax versions, and local_pdb has ontologies: []. CI is green, GPU jobs included.

@Bboy010
Bboy010 requested a review from vagkaratzas October 30, 2026 21:41

@vagkaratzas vagkaratzas left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @Bboy010! Good job!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants