Repository navigation
Drop residues missing from the residue table on request, and name them when raising #37
Copy link
Copy link
Closed
Description
User Story
As a user calculating surface features for many predicted structures
I want Prodes to leave out residues it has no data for, with a warning that names them, and to be able to ask for an error instead
So that one unknown residue in one model does not stop the calculation for that structure
Description
- Problem: a structure predicted from a sequence that contains
Xholds a residue namedUNK. Boltz-2, a structure prediction model that writes mmCIF files, builds it with the atoms N, CA, C, O and CB. Prodes reads every residue's properties (radii, aromatic carbons, pKa, hydrophobicity) from the residue table inprodes/data/general_data.json, which has noUNKentry, so the run stops withKeyError: UNK is not found in the list with residues. - Example: an antibody registered in the FDA Global Substance Registration System (GSRS) under the Unique Ingredient Identifier (UNII)
105J35OE21. GSRS gives its heavy chain sequence as starting withX, at the position where other records of the same antibody have a glutamine, so the residue is most likely an N-terminal pyroglutamate. In the Boltz-2 model built from the GSRS sequences, residue 1 of both heavy chains, A and B, isUNK. Of 7012 Boltz-2 models built from GSRS protein sequences in one downstream project, 103 had at least oneXin the submitted sequences, counted from the sequences each model's input record lists. - Current behaviour: the
KeyErroris raised during the surface calculation, after the structure has been read, and it names the residue type but not the chain or residue number. - Desired behaviour: by default, Prodes removes those residues when the structure is read, logs a warning that lists each one by chain, name and number, records them in the run record (
prodes_run.jsonin the output bundle, written byrun_metadatainsrc/prodes/output.py), and calculates the features from the remaining residues. An unattended caller, such as an agent, then gets features rather than an error for the sake of one or two residues. With the option set to False, Prodes raises aValueErrorlisting them instead.
Technical Context
parse_pdb_textinsrc/prodes/io/parser.pyreads all records, keeps the first model, keeps one conformation per residue where the file models several (elect_conformers, the conformer election, which keeps the best occupied one) and builds theStructureobject. The residue name is final only after the conformer election, because a file can model one position as two different amino acids, and the conformer election chooses one.build_structuregives the first residue of each chain the N terminus (parser.py:359), and the last residue of each chain the C terminus. If an unknown first residue were removed beforebuild_structure, the second residue would become the N terminus and would be given an N-terminal charge that it does not have in the molecule. For a pyroglutamate there is no free N-terminal amine at all.calculate()insrc/prodes/run.pyis called both from the CLI and in-process by other code. The CLI passes its arguments tocalculate()by position, andtests/test_cli.pypins that order, so a new argument is appended at the end of thecalculate()signature and at the end of the argument list the CLI builds.- The run record written by
run_metadataalready carriesmodels,alternate_conformersandinferred_elementsfor the same reason: each says that the features describe less than, or something other than, the whole file.
Implementation Suggestion
- In
parse_pdb_text, after the conformer election, find the residues whose name is not indata.all_residues(). - With the option False: raise a
ValueErrorthat lists each one as chain, residue name and residue number with insertion code, and names the option that removes them. - With the option True, the default (
drop_unknown_residuesinparse_pdb_text,PDBparser.parse,prodes.readandcalculate(),--no-drop-unknown-residueson the CLI to turn it off): remove the records of those residues, print one line naming them, and store the chain, name and number of each on theStructureobject, so thatcalculate()can write them toprodes_run.json. - A removed residue takes its terminus with it: when the first residue of a chain is removed, no residue of that chain is given the N terminus, and the same for the last residue and the C terminus. The atoms that carried that charge are the ones removed.
- Raise if removing them leaves no residue.
- Add the list of removed residues to the run record, as an empty list when none were removed.
- Document the option in the README section "Input files".
Acceptance Criteria
- By default, a PDB file holding a residue that is not in the residue table gives a feature table, and the warning and the run record both list the removed residues.
- With the option False, the same file raises a
ValueErrorwhen it is read, and the message names each such residue by chain, name and number. - When residues are removed, a chain whose first residue was removed has no residue with the N terminus, and a chain whose last residue was removed has no residue with the C terminus.
- A file with no unknown residues gives the same features with and without the option.
- A file in which every residue is unknown raises, whatever the option.
- The option can be used from the CLI and from
calculate().
Out of Scope
- Adding a
UNKentry, or entries for modified residues such asPCA, to the residue table. Any values there (radius, hydrophobicity, pKa) would be invented. - HETATM records, which are already read separately and are not affected.
- Changing the sequence a structure is predicted from. That belongs to the code that submits the prediction.
Testing Considerations
- A small test structure with an
UNKresidue at the start of a chain, in the middle of a chain and at the end of a chain. - A test that the conformer election runs before the check: a position modelled as one known amino acid at higher occupancy and
UNKat lower occupancy is kept, and the reverse is removed. - The CLI argument order test in
tests/test_cli.py.
Dependencies
- None.
Estimation
Story Points: 3
Priority
Medium
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels
Activity
Merge pull request #38 from datacatalysis/37-drop-unknown-residues
teese commentedon Oct 24, 2026
Merged in #38 (merge commit 9476015, branch 37-drop-unknown-residues), released as 11.0.0. Each acceptance criterion was checked against main after the merge and is covered by tests/test_unknown_residues.py and tests/test_cli.py: by default a residue outside the 20 standard amino acids is left out with a warning and listed in prodes_run.json as removed_residues; drop_unknown_residues=False (--no-drop-unknown-residues) raises a ValueError naming each by chain, name and number; a chain whose end residue was removed has no terminus at that end; a file with no unknown residue gives the same structure and atom charges either way; a file with no standard residue raises either way; the option works from the CLI and from calculate().