Add --keep_msas: refresh templates on existing features without redoing MSAs - #634
Conversation
Template search costs minutes; MSA search costs hours. When a template
database moves underneath a set of features the alignments are still perfectly
good and only the template_* block is stale, but the only way to refresh it was
to regenerate everything.
This came up at EMBL: AlphaFold3's fetch_databases.sh pins
pdb_seqres_2022_09_28.fasta, so every local AF3 run had been searching a 2022
snapshot. The refreshed file is a third larger. Features generated against the
old snapshot need new templates, not new MSAs.
--keep_msas updates existing features in --output_dir in place:
* AlphaFold2 - reloads the monomer, re-runs template search, and swaps the
template_* block via the same helper TrueMultimer already used.
* AlphaFold3 - reloads the feature JSON and re-processes it with both MSAs
set and templates=None, which is AF3's own "search for templates only"
branch. (templates=[] means "do not search", and a partially populated
chain is rejected outright, so None is the specific signal required.)
Proteins with no stored features are generated normally, so the flag is safe to
apply to a whole sample sheet. It takes precedence over --skip_existing, and is
rejected with --use_mmseqs2 (MSAs and templates arrive together, with no
separable search) and with --skip_msa (no MSAs to keep).
What AF2 searches with matters. DataPipeline.process builds the template
profile from the uniref90 Stockholm alignment alone, not the merged MSA that
lands in the features, so the on-disk uniref90_hits.sto is preferred. When
--save_msa_files=False deleted it, the alignment is rebuilt from the stored
integer MSA - which is the *merged* alignment and so can yield different hits.
That is a defensible result but not a reproduction of a fresh run, so the
source is logged and recorded on the monomer as template_msa_source rather than
passing silently.
Two related fixes found in the same round:
* Validate precomputed MSAs before reuse. --use_precomputed_msas trusts
whatever it finds, so an alignment left by a killed job is read back as if
complete. An empty uniref90_hits.sto surfaces as StopIteration inside
deduplicate_stockholm_msa naming neither file nor protein; a *partially*
written one silently produces shallower features, which is worse because it
is indistinguishable from a good result. Unsound files are now removed so
they regenerate. Search results (pdb_hits.*) are exempt: an empty one just
means no hits.
* Follow symlinks when recording database provenance. Sites that refresh a
database in place keep AF3's pinned name as a symlink to the current file,
and the release date is parsed out of the file name - so metadata reported
2022-09-28 while 2026-08-19 data was searched. That date ends up in methods
sections. Both ends of the symlink are now recorded.
Verified end to end against the real databases on an existing 74 aa feature
set, in both directions, with hmmsearch querying the live pdb_seqres:
--max_template_date=1990-01-01 6 templates -> none
--max_template_date=2050-01-01 6 templates -> 10 (5 kept, 1 dropped, 5 new)
In both runs the MSA came through byte-identical at (11, 74), deletion matrix
included. The reshuffle in the second run is the reconstruction caveat above:
that feature set had no saved uniref90_hits.sto, so the profile was rebuilt
from the deeper merged alignment.
62 new tests across unit and integration (496 pass in total); the AF3 half runs
against the stubbed folding_input the suite already uses, so it is covered in
environments where AF3 is not built.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 52552bf8db
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if FLAGS.keep_msas and existing is not None: | ||
| reuse_input = _af3_input_keeping_msas(existing, desc) |
There was a problem hiding this comment.
Validate AF3 cached sequence before reusing MSAs
When --keep_msas finds an existing AF3 JSON, this path reuses it based only on the filename/description and never checks that the cached chain sequence matches the current FASTA sequence. If a target is edited but keeps the same description, the pipeline will re-run template search using the old chain and old MSAs, then overwrite the feature file with features for the wrong protein instead of falling back to regeneration as the AF2 path does on sequence mismatch.
Useful? React with 👍 / 👎.
--keep_msas matched an existing AF3 feature file to the input by description alone. A FASTA edited in place but keeping its name would therefore have the cached chain reused: template search would run against the *old* sequence, and the file would be overwritten with features for the wrong protein. The AF2 path already refuses this in _existing_monomer_for_update, which compares the stored monomer's sequence with the input and regenerates on a mismatch. The AF3 path now does the same, so both fall back to full generation rather than half-updating a different protein. The sequence argument is optional, so the helper stays usable standalone. Reported by codex review on #634. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Why
AlphaFold3's
fetch_databases.shpinspdb_seqres_2022_09_28.fasta, so local AF3 runs at EMBL had been searching a 2022 snapshot of PDB seqres. The refreshed file is about a third larger. Features generated against the old snapshot need new templates, not new MSAs — but the only way to refresh them was to regenerate everything, redoing hours of jackhmmer/HHblits work to change minutes of hmmsearch output.What
--keep_msasupdates existing features in--output_dirin place, keeping their MSAs and re-running only the template search.template_*block using the same helper TrueMultimer already used for its single-PDB multimeric templates.templates=None, which is AF3's own "Has MSA, but doesn't have templates. Search for templates only" branch. The distinction matters:templates=[]means "do not search", and a partially populated chain is rejected by AF3 outright, soNoneis the specific signal required.Proteins with no stored features are generated normally, so the flag is safe to apply to a whole sample sheet. It takes precedence over
--skip_existing, and is rejected in combination with--use_mmseqs2(MSAs and templates arrive together, with no separable search) or--skip_msa(no MSAs to keep).One caveat worth knowing
DataPipeline.processbuilds the template profile from the uniref90 alignment alone, not the merged MSA that lands in the features. Souniref90_hits.stois preferred when present. When--save_msa_files=Falsedeleted it, the alignment is rebuilt from the stored integer MSA — which is the merged alignment, giving a deeper profile that can return different hits. That is defensible but is not a reproduction of a fresh run, so the source is logged and recorded on the monomer astemplate_msa_sourcerather than passing silently.Two related fixes found in the same round
Validate precomputed MSAs before reuse.
--use_precomputed_msastrusts whatever it finds, so an alignment left behind by a killed job is read back as if complete. An emptyuniref90_hits.stosurfaces asStopIterationinsidededuplicate_stockholm_msa, naming neither the file nor the protein; a partially written one silently produces shallower features, which is worse because it is indistinguishable from a good result. Unsound files are now removed so they regenerate. Search results (pdb_hits.*) are exempt — an empty one just means no hits were found.Follow symlinks when recording database provenance. Sites that refresh a database in place keep AF3's pinned name as a symlink to the current file, and the release date is parsed out of the file name. Metadata therefore reported
2022-09-28while2026-08-19data was actually searched — and that date ends up in methods sections. Both ends of the symlink are now recorded.Verification
End to end against the real databases on an existing 74 aa feature set, in both directions, with hmmsearch querying the live
pdb_seqres:--max_template_date1990-01-012050-01-01Deletion matrices identical in both. The reshuffle in the second run is the reconstruction caveat above — that feature set had no saved
uniref90_hits.sto.62 new tests across
test/unitandtest/integration; 496 pass in total. The AF3 half runs against the stubbedfolding_inputthe suite already uses, so it stays covered where AF3 is not built.🤖 Generated with Claude Code