bigbio / bigbio/onsite

Feature Request: Support PTM localization for non-phosphorylation modifications

Open
#57 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Python
Stars
4
Forks
3
PR merge metrics
No merged PRs in 30d

Description

# Feature Request: Support PTM localization for non-phosphorylation modifications

## Overview

onsite is almost entirely **hardcoded for phosphorylation (modification mass 79.96633 Da)** localization. To support other modifications (e.g., acetylation, methylation, oxidation, ubiquitination), a systematic improvment of the code is required.

Below is a layer-by-layer analysis of the phosphorylation-specific hardcoding in the current codebase.

---

## Global Issues (ordered by severity)

### 1. Modification name "Phospho" hardcoded everywhere

All three algorithms have the string `"Phospho"` hardcoded as the modification name:

| Location | Code |
|---|---|
| `onsite/phosphors/phosphors.py:19-26` | `MODIFICATION_NAME = "Phospho"`, `MODIFICATION_TYPES = {"S": "Phospho (S)", ...}` |
| `onsite/lucxor/peptide.py:34` | `_TARGET_MOD_NAMES = {"Phospho", "PhosphoDecoy"}` |
| `onsite/lucxor/psm.py:366-393` | String matching for `"(Phospho)"` and `"(PhosphoDecoy)"` |
| `onsite/ascore/ascore.py:84-85` | `sequence_str.count("(Phospho)")` / `sequence_str.count("(PhosphoDecoy)")` |
| `onsite/ascore/cli.py:307` | `"(Phospho)"` counting, `"(PhosphoDecoy)"` counting |
| `onsite/phosphors/cli.py:333` | `"(Phospho)"` counting |
| `onsite/phosphors/cli.py:403` | `f"{aa}(Phospho)"` and `f"{aa}(PhosphoDecoy)"` pattern matching |
| `onsite/decoy_flr.py:40-41` | `PHOSPHO = "Phospho"`, `PHOSPHO_DECOY = "PhosphoDecoy"` |
| `onsite/onsitec.py:179-181, 485-487` | `target_mods = ("Phospho (S)", "Phospho (T)", "Phospho (Y)")` |
| `onsite/lucxor/cli.py:91` | Default `--target-modifications` is `["Phospho (S)", "Phospho (T)", "Phospho (Y)"]` |

**Impact**: Any modification not named `"Phospho"` (e.g., `"Acetyl"`, `"Methyl"`, `"Oxidation"`) will not be recognized as a target for localization.

### 2. Modifiable residues hardcoded to S/T/Y

All three algorithms only recognize Ser/Thr/Tyr as possible modification sites:

| Location | Code |
|---|---|
| `onsite/ascore/ascore.py:62-64` | `isPhosphoSite(): return residue in ["S", "T", "Y"]` |
| `onsite/phosphors/phosphors.py:26` | `POTENTIAL_SITES = set(["S", "T", "Y"])` |
| `onsite/lucxor/psm.py:243` | `self.target_mod_map = {"S": True, "T": True, "Y": True}` |
| `onsite/lucxor/peptide.py:601-603` | `if aa in ["S", "T", "Y"]` |

**Impact**: Residues commonly modified by other PTMs — K (acetylation, ubiquitination), M (oxidation), C (alkylation), etc. — will never be considered as candidate sites for localization scoring.

### 3. Modification mass hardcoded to 79.96633 Da (HPO₃)

| Location | Code |
|---|---|
| `onsite/lucxor/constants.py:51` | `PHOSPHO_MOD_MASS = mass_provider.get_phospho_mass()` ≈ 79.96633 |
| `onsite/lucxor/constants.py:58-66` | `AA_MASSES.update({"s": AA_MASSES["S"] + PHOSPHO_MOD_MASS, ...})` |
| `onsite/lucxor/constants.py:97` | `DECOY_MASS = PHOSPHO_MOD_MASS` |
| `onsite/phosphors/phosphors.py:740` | `_PHOSPHO_MOD_MASS = 79.96633` |
| `onsite/ascore/ascore.py:697-706` | Sets `"Phospho"` modification via PyOpenMS (implicitly using phospho mass) |
| `onsite/phosphors/phosphors.py:1188-1192` | Mass-based filtering for phospho modification mass |

**Impact**: Correct masses for other modifications (e.g., acetylation +42.010565 Da, oxidation +15.994915 Da, methylation +14.015650 Da) cannot be used for theoretical spectrum calculation or fragment matching.

### 4. Neutral losses hardcoded to H₃PO₄ (-97.97690 Da)

The LucXor CID fragmentation model relies heavily on the characteristic neutral loss of phosphorylation:

| Location | Code |
|---|---|
| `onsite/lucxor/constants.py:111-112` | Default `neutral_losses = ["sty -H3PO4 -97.97690"]` |
| `onsite/lucxor/constants.py:140-149` | `NEUTRAL_LOSSES` dict with hardcoded `"H3PO4"`, `"sty": -97.97690` |
| `onsite/lucxor/peptide.py:548-549` | `if nl.startswith("sty")` for neutral loss assignment |
| `onsite/lucxor/peptide.py:371-386` | Converts lowercase `s/t/y` to PyOpenMS format with phospho neutral loss |
| `onsite/onsitec.py:194, 499` | `neutral_losses=("sty -H3PO4 -97.97690",)` |
| `onsite/lucxor/cli.py:97` | Default `--neutral-losses` is `["sty -H3PO4 -97.97690"]` |
| `onsite/lucxor/constants.py:114` | `decoy_neutral_losses = ["X -H3PO4 -97.97690"]` |

**Impact**: Fragment ion scoring for other modifications uses incorrect neutral loss masses, producing distorted localization scores. Other modifications have different neutral loss signatures (e.g., acetylation loses ketene -42.010565 Da from lysine under CID, but this is not the only or most common neutral loss for acetylated peptides).

### 5. Decoy strategy hardcoded to Alanine (A)

The current false localization rate (FLR) estimation relies on Alanine as the only decoy residue:

| Location | Code |
|---|---|
| `onsite/decoy_flr.py:42-43` | `TARGET_RESIDUES = set("STY")`, `DECOY_RESIDUE = "A"` |
| `onsite/ascore/ascore.py:57-59` | `isPhosphoDecoySite(): return residue == "A"` |
| `onsite/phosphors/phosphors.py:23-24` | `"A": "PhosphoDecoy (A)"` |
| `onsite/lucxor/constants.py:168-172` | `DECOY_AMINO_ACIDS = {"S": "A", "T": "V", "Y": "F"}` |

**Impact**: The decoy-amino-acid method couples Alanine to the assumption that S/T/Y are the only target residues. For other modifications, Alanine may not be a suitable decoy; a different decoy residue or an entirely different FLR strategy may be needed.

### 6. Lowercase letter convention hardcoded to s/t/y

LucXor uses lowercase letters to encode modified residues internally, but only defines s/t/y (and a/m):

| Location | Code |
|---|---|
| `onsite/lucxor/peptide.py:371-386` | `_to_pyopenms_format()` only recognizes `s→S(Phospho)`, `t→T(Phospho)`, `y→Y(Phospho)`, `a→A(PhosphoDecoy)` |
| `onsite/lucxor/psm.py:1075-1083` | `_get_mod_map()` checks `aa.upper() in ["S", "T", "Y"]` and `"a"` for Alanine decoy |
| `onsite/lucxor/psm.py:1199-1208` | `_get_mod_positions_from_perm()` checks `aa.upper() in "STY"` and `"a"` |
| `onsite/lucxor/psm.py:1753-1769` | `convert_sequence_to_standard_format()` only checks for lowercase `s/t/y` and `"a"` |
| `onsite/lucxor/constants.py:58-66` | `AA_MASSES` only adds mass entries for `s/t/y/a/m` |

**Impact**: No lowercase letter mappings exist for other modification sites (e.g., `k` for acetylated lysine, `m` for oxidized methionine — though `m` is defined for oxidation, it's not integrated into the full pipeline), so the internal sequence representation cannot propagate other modification information.

Contributor guide

No contributing guide indexed for this repository

Research direction

Start by tracing the phosphorylation-specific constants and defaults in onsite/lucxor/constants.py, onsite/lucxor/cli.py, and onsite/onsitec.py, then compare the site and mass handling in the listed ascore, phosphors, lucxor, and decoy_flr modules. Map how modification names, residues, masses, neutral losses, and decoys flow through all three algorithms. Done means non-phosphorylation PTMs can be configured and localized consistently without phosphorylation-only assumptions.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
bioinformatics
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
28/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.