Skip to content

Add structureclusterupdate workflow - #588

Open
genomewalker wants to merge 4 commits into
steineggerlab:masterfrom
genomewalker:feature/structureclusterupdate
Open

Add structureclusterupdate workflow#588
genomewalker wants to merge 4 commits into
steineggerlab:masterfrom
genomewalker:feature/structureclusterupdate

Conversation

@genomewalker

Copy link
Copy Markdown

This PR adds structureclusterupdate, an incremental update workflow for
structural cluster databases. Given an existing cluster database and a new
(larger) sequence database containing additional structures, it efficiently
updates the cluster assignments without re-running a full clustering from
scratch.

Approach

Uses diffseqdbs to partition sequences into three groups by integer key:

  • mappingSeqs — sequences present in both old and new DB: cluster
    representatives are remapped to new key space via renamedbkeys
  • newSeqs — sequences only in new DB: searched against existing cluster
    representatives with structural alignment (structurealign), then assigned
    or seeded into new clusters
  • removedSeqs — sequences only in old DB: pruned from cluster assignments
    (or re-appended with new keys when RECOVER_DELETED=1)

All prefiltering uses the 3Di alphabet (--seed-sub-mat aa:3di.out) and
alignment uses structurealign, so the update is fully structure-based
throughout.

Copilot AI review requested due to automatic review settings May 19, 2026 10:41

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds a new structureclusterupdate workflow command to extend an existing structural clustering with new sequences, driven by a bundled shell pipeline.

Changes:

  • Introduces structureclusterupdate C++ entrypoint that prepares parameters, temp directory handling, and executes the embedded workflow script.
  • Registers the new command in the CLI command list and build system.
  • Adds and compiles a new data/structureclusterupdate.sh workflow script resource.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/workflow/StructureClusterUpdate.cpp Implements the new workflow command wrapper and script execution.
src/workflow/CMakeLists.txt Includes the new workflow source in the build.
src/LocalCommandDeclarations.h Declares the new command entrypoint for linkage.
src/FoldseekBase.cpp Registers the new structureclusterupdate command in the CLI.
data/structureclusterupdate.sh Adds the pipeline logic for updating clustering with new sequences.
data/CMakeLists.txt Compiles the new script into the binary resources.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +10 to +20
void setStructureClusterUpdateDefaults(LocalParameters *p) {
p->covThr = 0.8;
p->evalThr = 0.001;
p->sortByStructureBits = 0;
p->alignmentMode = Parameters::ALIGNMENT_MODE_SCORE_COV_SEQID;
p->compBiasCorrection = 0;
}

void setStructureClusterUpdateMustPassAlong(LocalParameters *p) {
p->PARAM_ALIGNMENT_MODE.wasSet = true;
}
Comment on lines +54 to +55
cmd.addVariable("REMOVE_TMP", par.removeTmpFiles ? "TRUE" : NULL);
cmd.addVariable("RECOVER_DELETED", par.recoverDeleted ? "TRUE" : NULL);
Comment on lines +89 to +93
cmd.execProgram(program.c_str(), par.filenames);

// Unreachable
assert(false);
return EXIT_FAILURE;
Comment thread data/structureclusterupdate.sh Outdated
Comment on lines +259 to +264
if notExists "$NEWCLUST"; then
log "=== Merge updated clustering with new clusters"
# shellcheck disable=SC2086
"$MMSEQS" concatdbs "${UPDATEDCLUST}" "${TMP_PATH}/newClusters" "$NEWCLUST" --preserve-keys ${THREADS_PAR} \
|| fail "concatdbs died"
fi
Comment thread data/structureclusterupdate.sh Outdated
}

# check number of input variables
[ "$#" -ne 6 ] && echo "Please provide <i:oldSequenceDB> <i:newSequenceDB> <i:oldClusteringDB> <o:newMappedSequenceDB> <o:newClusteringDB> <o:tmpDir>" && exit 1
@fawaz-dabbaghieh

Copy link
Copy Markdown

Hi Antonio, thank you very much for adding this. I looked a bit through the code and ran it a couple of times. I have a couple of comments:

1- --recover-deleted is implemented but not exposed, I see that the shell script does have a RECOVER_DELETED, but if I run foldseek structureclusterupdate it doesn't show up in the printed help message and if I use --recover-deleted it will fail due to unrecognized parameter.

2- I didn't see in the shell parts that also updates the _ca sidecars, I see explicit remapping of the _h and the _ss files in the database but not the _ca.

3- If we expose the recover deleted branch, I see that in that part in the shell workflow, only the main DB and the _h header DB is concatenated, but it doesn't rebuild or concatenate _ss and _ca for the NEWDB.withOld. I think we need to add _ss handling at least, not sure how important is the _ca

4- The variable RESULT2REPSEQ_PAR is exposed but not used, I think this was used fro mmseqs result2repseq but not in foldseek

5- From my understanding, structural prefiltering is usually done on the 3Di sequences and this command does create 3Di subsets NEWDB.newSeqs_ss and OLDDB.repSeq_ss, but the prefilter step uses the "$MMSEQS" prefilter "${TMP_PATH}/NEWDB.newSeqs" "${TMP_PATH}/OLDDB.repSeq" "${TMP_PATH}/newSeqsPref" ${PREFILTER_PAR} and not the _ssdatabases. Not sure if Foldseek automatically finds the corrisponding _ss database, and my simple regression did work, but my regression test still doesn't check all cases, it's only a simple smoke test now.

I don't think there's anything major that needs to change, I will try to push my regression test before the end of the week, I am a bit sick today.

- expose --recover-deleted via own param list (structureclusterupdate)
  so it shows in help without leaking onto structurecluster
- add _ca everywhere _ss is handled (renamedbkeys/createsubdb for
  newMappedDB, NEWDB.newSeqs, OLDDB.repSeq; recover branch too)
- recover branch: rebuild _ss and _ca for NEWDB.withOld, not just main+_h
- split monolithic notExists guard in recover branch into per-sidecar
  guards so partial-failure retry rebuilds missing sidecars
- fix hash to use par.structureclusterupdate (includes --recover-deleted)
  so tmp dir is not reused across runs that differ only in that flag
- prefilter on _ss subsets to match structurecluster convention
- drop unused RESULT2REPSEQ_PAR
- guard OLCLUST cleanup in non-recover path against missing files
- add _ss/_ca to help example in FoldseekBase.cpp
- fix notExists check to use .dbtype suffix
@genomewalker

Copy link
Copy Markdown
Author

thanks @fawaz-dabbaghieh, all five were on point. pushed an update:

  • recover-deleted: was read and passed to the script, but the command used the shared structureclusterworkflow param list which doesn't carry the flag, so it never showed in help. gave the command its own list (structureclusterworkflow + PARAM_RECOVER_DELETED) so it works without leaking the flag onto structurecluster.
  • _ca: added it everywhere we handle _ss (mapped-db remap, newSeqs subset, rep subset). renamedbkeys/createsubdb don't carry it on their own.
  • recover branch: now rebuilds _ss and _ca for NEWDB.withOld, not just main + _h.
  • RESULT2REPSEQ_PAR: dropped, unused.
  • prefilter on AA: switched to the _ss subsets to match structurecluster and the search workflows. alignment keeps the base names since the aligner pulls _ss/_ca itself.

also cleaned up the new temp dbs and took the bot's notExists "${NEWCLUST}.dbtype" / <tmpDir> fixes.

@genomewalker
genomewalker force-pushed the feature/structureclusterupdate branch from 5ad73aa to 4ff4966 Compare June 16, 2026 15:10
Mirror StructureSearch.cpp GPU block into StructureClusterUpdate.cpp.
Add ungappedprefilter to structureclusterupdate param list.
Branch .sh prefilter call on PREFMODE; pad OLDDB.repSeq for GPU path.
--gpu is opt-in; CPU k-mer prefilter remains the default.
Align new sequences against the padded representative DB and remap the
swapped hit keys back to original rep keys before merging with the old
clustering. The remap read the lookup name column (col2) instead of the
original key (col3), so renamedbkeys got unparseable targets and collapsed
every join-target key to 0 -- all structurally-assigned new sequences then
merged into the single cluster keyed 0 instead of their best-hit cluster.
Use col3 (originalKey) from the mmseqs lookup format.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants