diff --git a/alphabase/constants/aa.py b/alphabase/constants/aa.py
index c2334e9e..ef2abb6d 100644
--- a/alphabase/constants/aa.py
+++ b/alphabase/constants/aa.py
@@ -11,12 +11,14 @@
parse_formula,
reset_elements,
)
-from alphabase.yaml_utils import load_yaml
# We use all 128 ASCII code to represent amino acids for flexible extensions in the future.
# The amino acid masses are stored in 128-lengh array :py:data:`AA_ASCII_MASS`.
-# If an ASCII code is not in `AA_Formula`, the mass will be set as a large value to disable MS search.
-AA_Formula: dict = load_yaml(os.path.join(CONST_FILE_FOLDER, "amino_acid.yaml"))
+# If an ASCII code is not in `aa_formula`, the mass will be set as a large value to disable MS search.
+aa_formula: pd.DataFrame = pd.read_csv(
+ os.path.join(CONST_FILE_FOLDER, "amino_acid.tsv"), sep="\t", index_col=0
+)
+
#: AA mass array with ASCII code, mass of 'A' is AA_ASCII_MASS[ord('A')]
AA_ASCII_MASS: np.ndarray = np.ones(128) * 1e8
@@ -28,20 +30,23 @@
def replace_atoms(atom_replace_dict: typing.Dict):
- for aa, formula in list(AA_Formula.items()):
+ for aa, row in aa_formula.iterrows():
+ formula = row["formula"]
atom_comp = dict(parse_formula(formula))
for atom_from, atom_to in atom_replace_dict.items():
if atom_from in atom_comp:
atom_comp[atom_to] = atom_comp[atom_from]
del atom_comp[atom_from]
- AA_Formula[aa] = "".join([f"{atom}({n})" for atom, n in atom_comp.items()])
+ aa_formula.loc[aa, "formula"] = "".join(
+ [f"{atom}({n})" for atom, n in atom_comp.items()]
+ )
def reset_AA_mass() -> np.ndarray:
"""AA mass in np.array with shape (128,)"""
global AA_ASCII_MASS
- for aa, chem in AA_Formula.items():
- AA_ASCII_MASS[ord(aa)] = calc_mass_from_formula(chem)
+ for aa, row in aa_formula.iterrows():
+ AA_ASCII_MASS[ord(aa)] = calc_mass_from_formula(row["formula"])
return AA_ASCII_MASS
@@ -51,15 +56,14 @@ def reset_AA_mass() -> np.ndarray:
def reset_AA_df():
global AA_DF
AA_DF = pd.DataFrame()
- AA_DF["aa"] = [chr(aa) for aa in range(len(AA_ASCII_MASS))]
- AA_DF["formula"] = [""] * len(AA_ASCII_MASS)
- aa_idxes = []
- formulas = []
- for aa, formula in AA_Formula.items():
- aa_idxes.append(ord(aa))
- formulas.append(formula)
- AA_DF.loc[aa_idxes, "formula"] = formulas
+ num_rows = len(AA_ASCII_MASS)
+ AA_DF["aa"] = [chr(aa) for aa in range(num_rows)]
+ AA_DF["formula"] = [""] * num_rows
+ AA_DF["smiles"] = [""] * num_rows
AA_DF["mass"] = AA_ASCII_MASS
+ for aa, row in aa_formula.iterrows():
+ AA_DF.loc[ord(aa), "formula"] = row["formula"]
+ AA_DF.loc[ord(aa), "smiles"] = row["smiles"]
return AA_DF
@@ -69,8 +73,8 @@ def reset_AA_df():
def reset_AA_Composition():
global AA_Composition
AA_Composition = {}
- for aa, formula, _mass in AA_DF.values:
- AA_Composition[aa] = dict(parse_formula(formula))
+ for aa, row in aa_formula.iterrows():
+ AA_Composition[aa] = dict(parse_formula(row["formula"]))
return AA_Composition
@@ -85,12 +89,14 @@ def reset_AA_atoms(atom_replace_dict: typing.Dict = {}):
reset_AA_Composition()
-def update_an_AA(aa: str, formula: str):
+def update_an_AA(aa: str, formula: str, smiles: str = ""):
aa_idx = ord(aa)
+ aa_formula.loc[aa, "formula"] = formula
+ aa_formula.loc[aa, "smiles"] = smiles
AA_DF.loc[aa_idx, "formula"] = formula
+ AA_DF.loc[aa_idx, "smiles"] = smiles
AA_ASCII_MASS[aa_idx] = calc_mass_from_formula(formula)
AA_DF.loc[aa_idx, "mass"] = AA_ASCII_MASS[aa_idx]
- AA_Formula[aa] = formula
AA_Composition[aa] = dict(parse_formula(formula))
diff --git a/alphabase/constants/atom.py b/alphabase/constants/atom.py
index 4d8e5f98..8acb0827 100644
--- a/alphabase/constants/atom.py
+++ b/alphabase/constants/atom.py
@@ -1,8 +1,12 @@
import os
+import re
import typing
+from collections import defaultdict
import numba
import numpy as np
+from rdkit import Chem
+from rdkit.Chem import rdMolDescriptors
from alphabase.constants._const import CONST_FILE_FOLDER, common_const_dict
from alphabase.yaml_utils import load_yaml
@@ -200,3 +204,215 @@ def calc_mass_from_formula(formula: str):
mass of the formula
"""
return np.sum([CHEM_MONO_MASS[elem] * n for elem, n in parse_formula(formula)])
+
+
+class ChemicalCompositonFormula:
+ """
+ Initialize the ChemicalCompositonFormula with a given formula.
+
+ Parameters
+ ----------
+ formula : str
+ The chemical formula as a string.
+
+ Returns
+ -------
+ None
+ """
+
+ def __init__(self, formula=None):
+ self.elements = (
+ defaultdict(int) if formula is None else self._parse_formula(formula)
+ )
+
+ @classmethod
+ def from_smiles(cls, smiles: str) -> "ChemicalCompositonFormula":
+ """
+ Create a ChemicalCompositonFormula instance from a SMILES string.
+
+ Parameters
+ ----------
+ smiles : str
+ The SMILES representation of the molecule.
+
+ Returns
+ -------
+ ChemicalCompositonFormula
+ An instance of the class based on the SMILES string.
+
+ Raises
+ ------
+ ValueError
+ If the SMILES string is invalid and can't be converted to an RDKit molecule.
+ """
+ mol = Chem.MolFromSmiles(smiles)
+ if not mol:
+ raise ValueError(f"Invalid RDKit molecule: {smiles}")
+ formula = rdMolDescriptors.CalcMolFormula(
+ mol, separateIsotopes=True, abbreviateHIsotopes=False
+ )
+ formula = formula.replace("[1H]", "H")
+ return cls._from_rdkit_formula(formula)
+
+ @classmethod
+ def _from_rdkit_formula(cls, formula: str) -> "ChemicalCompositonFormula":
+ """
+ Create a ChemicalCompositonFormula instance from an RDKit formula.
+
+ Parameters
+ ----------
+ formula : str
+ The chemical formula as generated by RDKit.
+
+ Returns
+ -------
+ ChemicalCompositonFormula
+ An instance of the class based on the RDKit formula.
+ """
+ instance = cls.__new__(cls)
+ instance.elements = instance._parse_rdkit_formula(formula)
+ return instance
+
+ def _parse_formula(self, formula) -> dict:
+ """
+ Parse a chemical formula string into a dictionary of elements and their counts.
+
+ Parameters
+ ----------
+ formula : str
+ The chemical formula to parse.
+
+ Returns
+ -------
+ dict
+ A dictionary with elements as keys and their counts as values.
+ """
+ # Expected pattern: (\d+)?: optional isotope number, ([A-Z][a-z]*): element symbol, (?:\(([-]?\d+)\))?: optional count in parentheses
+ # Example: 13C(2)H(3)O(-1) -> [('13', 'C', '2'), ('', 'H', '3'), ('', 'O', '-1')]
+ pattern = r"(\d+)?([A-Z][a-z]*)(?:\(([-]?\d+)\))?"
+ matches = re.findall(pattern, formula)
+ element_counts = defaultdict(int)
+
+ for isotope, element, count in matches:
+ if isotope:
+ element = f"{isotope}{element}"
+ count = int(count) if count else 1
+ element_counts[element] += count
+
+ self._validate_atoms(element_counts)
+ return element_counts
+
+ def _parse_rdkit_formula(self, formula: str) -> dict:
+ """
+ Parse an RDKit-generated formula string into a dictionary of elements and their counts.
+
+ Parameters
+ ----------
+ formula : str
+ The RDKit-generated chemical formula to parse.
+
+ Returns
+ -------
+ dict
+ A dictionary with elements as keys and their counts as values.
+ """
+ # Expected pattern: (\[(\d+)([A-Z][a-z]*)\]|([A-Z][a-z]*)): isotope in square brackets or element symbol, followed by (\d*): optional count
+ # Example: [13C]C2H5OH -> [('[13C]', '13', 'C', '', ''), ('C', '', '', 'C', '2'), ('H', '', '', 'H', '5'), ('O', '', '', 'O', ''), ('H', '', '', 'H', '')]
+ pattern = r"(\[(\d+)([A-Z][a-z]*)\]|([A-Z][a-z]*))(\d*)"
+ matches = re.findall(pattern, formula)
+ element_counts = defaultdict(int)
+
+ for match in matches:
+ count = int(match[4]) if match[4] else 1
+ if match[1]: # noqa: SIM108
+ # Isotope, see 0th element in the example above
+ element = f"{match[1]}{match[2]}"
+ else:
+ # Regular element, see the rest in the example above
+ element = match[3]
+ element_counts[element] += count
+
+ self._validate_atoms(element_counts)
+ return element_counts
+
+ def _validate_atoms(self, element_counts):
+ """
+ Validate the elements in the formula.
+
+ Parameters
+ ----------
+ element_counts : dict
+ The elements and their counts in the formula.
+
+ Raises
+ ------
+ ValueError
+ If the formula contains an unknown element.
+ """
+ for element in element_counts:
+ if element not in CHEM_MONO_MASS:
+ raise ValueError(f"Unknown element: {element}")
+
+ def __str__(self):
+ """
+ Return a string representation of the chemical formula.
+
+ Returns
+ -------
+ str
+ The chemical formula as a string.
+ """
+ return "".join(
+ f"{element}({count})"
+ for element, count in sorted(self.elements.items())
+ if count != 0
+ )
+
+ def __repr__(self):
+ """
+ Return a string representation of the ChemicalCompositonFormula instance.
+
+ Returns
+ -------
+ str
+ A string representation of the instance.
+ """
+ return f"ChemicalCompositonFormula('{self.__str__()}')"
+
+ def __add__(self, other):
+ """
+ Add two ChemicalCompositonFormula instances.
+
+ Parameters
+ ----------
+ other : ChemicalCompositonFormula
+ The other instance to add.
+
+ Returns
+ -------
+ ChemicalCompositonFormula
+ A new instance representing the sum of the two formulas.
+ """
+ result = ChemicalCompositonFormula()
+ for element in set(self.elements.keys()) | set(other.elements.keys()):
+ result.elements[element] = self.elements[element] + other.elements[element]
+ return result
+
+ def __sub__(self, other):
+ """
+ Subtract one ChemicalCompositonFormula instance from another.
+
+ Parameters
+ ----------
+ other : ChemicalCompositonFormula
+ The instance to subtract.
+
+ Returns
+ -------
+ ChemicalCompositonFormula
+ A new instance representing the difference of the two formulas.
+ """
+ result = ChemicalCompositonFormula()
+ for element in set(self.elements.keys()) | set(other.elements.keys()):
+ result.elements[element] = self.elements[element] - other.elements[element]
+ return result
diff --git a/alphabase/constants/const_files/amino_acid.tsv b/alphabase/constants/const_files/amino_acid.tsv
new file mode 100644
index 00000000..c4a0a933
--- /dev/null
+++ b/alphabase/constants/const_files/amino_acid.tsv
@@ -0,0 +1,30 @@
+ formula smiles
+A C(3)H(5)N(1)O(1)S(0) N([Xe])([Xe])[C@@]([H])(C)C(=O)[Rn]
+B C(1000000)
+C C(3)H(5)N(1)O(1)S(1) N([Xe])([Xe])[C@@]([H])(CS)C(=O)[Rn]
+D C(4)H(5)N(1)O(3)S(0) N([Xe])([Xe])[C@@]([H])(CC(=O)O)C(=O)[Rn]
+E C(5)H(7)N(1)O(3)S(0) N([Xe])([Xe])[C@@]([H])(CCC(=O)O)C(=O)[Rn]
+F C(9)H(9)N(1)O(1)S(0) N([Xe])([Xe])[C@@]([H])(Cc1ccccc1)C(=O)[Rn]
+G C(2)H(3)N(1)O(1)S(0) N([Xe])([Xe])CC(=O)[Rn]
+H C(6)H(7)N(3)O(1)S(0) N([Xe])([Xe])[C@@]([H])(CC1=CN=C-N1)C(=O)[Rn]
+I C(6)H(11)N(1)O(1)S(0) N([Xe])([Xe])[C@@]([H])([C@]([H])(CC)C)C(=O)[Rn]
+J C(6)H(11)N(1)O(1)S(0)
+K C(6)H(12)N(2)O(1)S(0) N([Xe])([Xe])[C@@]([H])(CCCCN)C(=O)[Rn]
+L C(6)H(11)N(1)O(1)S(0) N([Xe])([Xe])[C@@]([H])(CC(C)C)C(=O)[Rn]
+M C(5)H(9)N(1)O(1)S(1) N([Xe])([Xe])[C@@]([H])(CCSC)C(=O)[Rn]
+N C(4)H(6)N(2)O(2)S(0) N([Xe])([Xe])[C@@]([H])(CC(=O)N)C(=O)[Rn]
+O C(12)H(19)N(3)O(2) C[C@@H]1CC=N[C@H]1C(=O)NCCCC[C@@H](C(=O)[Rn])N([Xe])([Xe])
+P C(5)H(7)N(1)O(1)S(0) N1([Xe])[C@@]([H])(CCC1)C(=O)[Rn]
+Q C(5)H(8)N(2)O(2)S(0) N([Xe])([Xe])[C@@]([H])(CCC(=O)N)C(=O)[Rn]
+R C(6)H(12)N(4)O(1)S(0) N([Xe])([Xe])[C@@]([H])(CCCNC(=N)N)C(=O)[Rn]
+S C(3)H(5)N(1)O(2)S(0) N([Xe])([Xe])[C@@]([H])(CO)C(=O)[Rn]
+T C(4)H(7)N(1)O(2)S(0) N([Xe])([Xe])[C@@]([H])([C@]([H])(O)C)C(=O)[Rn]
+U C(3)H(5)N(1)O(1)Se(1) N([Xe])([Xe])[C@@]([H])(C[Se][H])C(=O)[Rn]
+V C(5)H(9)N(1)O(1)S(0) N([Xe])([Xe])[C@@]([H])(C(C)C)C(=O)[Rn]
+W C(11)H(10)N(2)O(1)S(0) N([Xe])([Xe])[C@@]([H])(CC(=CN2)C1=C2C=CC=C1)C(=O)[Rn]
+X C(1000000)
+Y C(9)H(9)N(1)O(2)S(0) N([Xe])([Xe])[C@@]([H])(Cc1ccc(O)cc1)C(=O)[Rn]
+Z C(1000000)
+s C(3)H(5)N(1)O(2)S(0)
+t C(4)H(8)N(1)O(5)P(1)
+y C(9)H(9)N(1)O(2)S(0)
diff --git a/alphabase/constants/const_files/amino_acid.yaml b/alphabase/constants/const_files/amino_acid.yaml
deleted file mode 100644
index 5d3ef0b1..00000000
--- a/alphabase/constants/const_files/amino_acid.yaml
+++ /dev/null
@@ -1,40 +0,0 @@
-# https://en.wikipedia.org/wiki/Proteinogenic_amino_acid
-# B, X, Z are the placeholders for future
-A: 'C(3)H(5)N(1)O(1)S(0)'
-B: 'C(1000000)'
-C: 'C(3)H(5)N(1)O(1)S(1)'
-D: 'C(4)H(5)N(1)O(3)S(0)'
-E: 'C(5)H(7)N(1)O(3)S(0)'
-F: 'C(9)H(9)N(1)O(1)S(0)'
-G: 'C(2)H(3)N(1)O(1)S(0)'
-H: 'C(6)H(7)N(3)O(1)S(0)'
-I: 'C(6)H(11)N(1)O(1)S(0)'
-# I/L
-J: 'C(6)H(11)N(1)O(1)S(0)'
-K: 'C(6)H(12)N(2)O(1)S(0)'
-L: 'C(6)H(11)N(1)O(1)S(0)'
-M: 'C(5)H(9)N(1)O(1)S(1)'
-N: 'C(4)H(6)N(2)O(2)S(0)'
-# Pyrrolysine
-O: 'C(12)H(19)N(3)O(2)'
-P: 'C(5)H(7)N(1)O(1)S(0)'
-Q: 'C(5)H(8)N(2)O(2)S(0)'
-R: 'C(6)H(12)N(4)O(1)S(0)'
-S: 'C(3)H(5)N(1)O(2)S(0)'
-T: 'C(4)H(7)N(1)O(2)S(0)'
-# Selenocysteine
-U: 'C(3)H(5)N(1)O(1)Se(1)'
-V: 'C(5)H(9)N(1)O(1)S(0)'
-W: 'C(11)H(10)N(2)O(1)S(0)'
-X: 'C(1000000)'
-Y: 'C(9)H(9)N(1)O(2)S(0)'
-Z: 'C(1000000)'
-# Any other ASCII chars could be the placeholders for future usage.
-# For example:
-# phospho site-specific search (only lower case 'sty' can be modified)
-# s is S
-s: 'C(3)H(5)N(1)O(2)S(0)'
-# t is T
-t: 'C(4)H(8)N(1)O(5)P(1)'
-# y is Y
-y: 'C(9)H(9)N(1)O(2)S(0)'
diff --git a/alphabase/constants/const_files/modification.tsv b/alphabase/constants/const_files/modification.tsv
index 6d28216b..705c5886 100644
--- a/alphabase/constants/const_files/modification.tsv
+++ b/alphabase/constants/const_files/modification.tsv
@@ -1,16 +1,16 @@
mod_name unimod_mass unimod_avge_mass composition unimod_modloss modloss_composition classification unimod_id smiles modloss_importance
Acetyl@T 42.010565 42.0367 H(2)C(2)O(1) 0.0 Post-translational 1 0.0
-Acetyl@Protein_N-term 42.010565 42.0367 H(2)C(2)O(1) 0.0 Post-translational 1 0.0
+Acetyl@Protein_N-term 42.010565 42.0367 H(2)C(2)O(1) 0.0 Post-translational 1 C(=O)C 0.0
Acetyl@S 42.010565 42.0367 H(2)C(2)O(1) 0.0 Post-translational 1 0.0
Acetyl@C 42.010565 42.0367 H(2)C(2)O(1) 0.0 Post-translational 1 0.0
-Acetyl@Any_N-term 42.010565 42.0367 H(2)C(2)O(1) 0.0 Multiple 1 0.0
-Acetyl@K 42.010565 42.0367 H(2)C(2)O(1) 0.0 Multiple 1 0.0
+Acetyl@Any_N-term 42.010565 42.0367 H(2)C(2)O(1) 0.0 Multiple 1 C(=O)C 0.0
+Acetyl@K 42.010565 42.0367 H(2)C(2)O(1) 0.0 Multiple 1 CC(=O)NCCCC[C@H](N([Xe])([Xe]))C(=O)[Rn] 0.0
Acetyl@Y 42.010565 42.0367 H(2)C(2)O(1) 0.0 Chemical derivative 1 0.0
Acetyl@H 42.010565 42.0367 H(2)C(2)O(1) 0.0 Chemical derivative 1 0.0
Acetyl@R 42.010565 42.0367 H(2)C(2)O(1) 0.0 Artefact 1 0.0
-Amidated@Any_C-term -0.984016 -0.9848 H(1)N(1)O(-1) 0.0 Artefact 2 0.0
-Amidated@Protein_C-term -0.984016 -0.9848 H(1)N(1)O(-1) 0.0 Post-translational 2 0.0
-Biotin@Any_N-term 226.077598 226.2954 H(14)C(10)N(2)O(2)S(1) 0.0 Chemical derivative 3 0.0
+Amidated@Any_C-term -0.984016 -0.9848 H(1)N(1)O(-1) 0.0 Artefact 2 N 0.0
+Amidated@Protein_C-term -0.984016 -0.9848 H(1)N(1)O(-1) 0.0 Post-translational 2 N 0.0
+Biotin@Any_N-term 226.077598 226.2954 H(14)C(10)N(2)O(2)S(1) 0.0 Chemical derivative 3 C(=O)CCCCC1SCC2NC(=O)NC21 0.0
Biotin@K 226.077598 226.2954 H(14)C(10)N(2)O(2)S(1) 0.0 Post-translational 3 0.0
Carbamidomethyl@Y 57.021464 57.0513 H(3)C(2)N(1)O(1) 0.0 Artefact 4 0.0
Carbamidomethyl@T 57.021464 57.0513 H(3)C(2)N(1)O(1) 0.0 Artefact 4 0.0
@@ -18,32 +18,32 @@ Carbamidomethyl@S 57.021464 57.0513 H(3)C(2)N(1)O(1) 0.0 Artefact 4 0.0
Carbamidomethyl@E 57.021464 57.0513 H(3)C(2)N(1)O(1) 0.0 Artefact 4 0.0
Carbamidomethyl@D 57.021464 57.0513 H(3)C(2)N(1)O(1) 0.0 Artefact 4 0.0
Carbamidomethyl@H 57.021464 57.0513 H(3)C(2)N(1)O(1) 0.0 Artefact 4 0.0
-Carbamidomethyl@Any_N-term 57.021464 57.0513 H(3)C(2)N(1)O(1) 0.0 Artefact 4 0.0
+Carbamidomethyl@Any_N-term 57.021464 57.0513 H(3)C(2)N(1)O(1) 0.0 Artefact 4 C(=O)NC 0.0
Carbamidomethyl@K 57.021464 57.0513 H(3)C(2)N(1)O(1) 0.0 Artefact 4 0.0
-Carbamidomethyl@C 57.021464 57.0513 H(3)C(2)N(1)O(1) 0.0 Chemical derivative 4 0.0
+Carbamidomethyl@C 57.021464 57.0513 H(3)C(2)N(1)O(1) 0.0 Chemical derivative 4 C(C(C(=O)[Rn])N([Xe])([Xe]))SCC(=O)N 0.0
Carbamidomethyl@U 57.021464 57.0513 H(3)C(2)N(1)O(1) 0.0 Chemical derivative 4 0.0
-Carbamidomethyl@M 57.021464 57.0513 H(3)C(2)N(1)O(1) 105.024835 H(7)C(3)N(1)O(1)S(1) Chemical derivative 4 0.5
+Carbamidomethyl@M 57.021464 57.0513 H(3)C(2)N(1)O(1) 105.024835 H(7)C(3)N(1)O(1)S(1) Chemical derivative 4 CS(CCC(N([Xe])([Xe]))C([Rn])=O)=CC(N)=O 0.5
Carbamyl@Y 43.005814 43.0247 H(1)C(1)N(1)O(1) 0.0 Chemical derivative 5 0.0
Carbamyl@T 43.005814 43.0247 H(1)C(1)N(1)O(1) 0.0 Chemical derivative 5 0.0
Carbamyl@S 43.005814 43.0247 H(1)C(1)N(1)O(1) 0.0 Chemical derivative 5 0.0
Carbamyl@M 43.005814 43.0247 H(1)C(1)N(1)O(1) 0.0 Artefact 5 0.0
Carbamyl@C 43.005814 43.0247 H(1)C(1)N(1)O(1) 0.0 Artefact 5 0.0
Carbamyl@R 43.005814 43.0247 H(1)C(1)N(1)O(1) 0.0 Artefact 5 0.0
-Carbamyl@Any_N-term 43.005814 43.0247 H(1)C(1)N(1)O(1) 0.0 Multiple 5 0.0
+Carbamyl@Any_N-term 43.005814 43.0247 H(1)C(1)N(1)O(1) 0.0 Multiple 5 C(=O)N 0.0
Carbamyl@K 43.005814 43.0247 H(1)C(1)N(1)O(1) 0.0 Multiple 5 0.0
-Carbamyl@Protein_N-term 43.005814 43.0247 H(1)C(1)N(1)O(1) 0.0 Post-translational 5 0.0
+Carbamyl@Protein_N-term 43.005814 43.0247 H(1)C(1)N(1)O(1) 0.0 Post-translational 5 C(=O)N 0.0
Carboxymethyl@Any_N-term 58.005479 58.0361 H(2)C(2)O(2) 0.0 Artefact 6 0.0
Carboxymethyl@K 58.005479 58.0361 H(2)C(2)O(2) 0.0 Artefact 6 0.0
Carboxymethyl@C 58.005479 58.0361 H(2)C(2)O(2) 0.0 Chemical derivative 6 0.0
Carboxymethyl@W 58.005479 58.0361 H(2)C(2)O(2) 0.0 Chemical derivative 6 0.0
Carboxymethyl@U 58.005479 58.0361 H(2)C(2)O(2) 0.0 Chemical derivative 6 0.0
-Deamidated@Q 0.984016 0.9848 H(-1)N(-1)O(1) 0.0 Artefact 7 0.0
+Deamidated@Q 0.984016 0.9848 H(-1)N(-1)O(1) 0.0 Artefact 7 C(CC(=O)O)[C@@H](C(=O)[Rn])N([Xe])([Xe]) 0.0
Deamidated@R 0.984016 0.9848 H(-1)N(-1)O(1) 43.005814 H(1)C(1)N(1)O(1) Post-translational 7 0.5
-Deamidated@N 0.984016 0.9848 H(-1)N(-1)O(1) 0.0 Artefact 7 0.0
+Deamidated@N 0.984016 0.9848 H(-1)N(-1)O(1) 0.0 Artefact 7 C([C@@H](C(=O)[Rn])N([Xe])([Xe]))C(=O)O 0.0
Deamidated@F^Protein_N-term 0.984016 0.9848 H(-1)N(-1)O(1) 0.0 Post-translational 7 0.0
ICAT-G@C 486.251206 486.6253 H(38)C(22)N(4)O(6)S(1) 0.0 Isotopic label 8 0.0
ICAT-G:2H(8)@C 494.30142 494.6746 H(30)2H(8)C(22)N(4)O(6)S(1) 0.0 Isotopic label 9 0.0
-Met->Hse@M^Any_C-term -29.992806 -30.0922 H(-2)C(-1)O(1)S(-1) 0.0 Chemical derivative 10 0.0
+Met->Hse@M^Any_C-term -29.992806 -30.0922 H(-2)C(-1)O(1)S(-1) 0.0 Chemical derivative 10 N([Xe])([Xe])[C@H](C(=O)[Rn])CCO 0.0
Met->Hsl@M^Any_C-term -48.003371 -48.1075 H(-4)C(-1)S(-1) 0.0 Chemical derivative 11 0.0
ICAT-D:2H(8)@C 450.275205 450.6221 H(26)2H(8)C(20)N(4)O(5)S(1) 0.0 Isotopic label 12 0.0
ICAT-D@C 442.224991 442.5728 H(34)C(20)N(4)O(5)S(1) 0.0 Isotopic label 13 0.0
@@ -55,9 +55,9 @@ Phospho@K 79.966331 79.9799 H(1)O(3)P(1) 0.0 Post-translational 21 0.0
Phospho@H 79.966331 79.9799 H(1)O(3)P(1) 0.0 Post-translational 21 0.0
Phospho@C 79.966331 79.9799 H(1)O(3)P(1) 0.0 Post-translational 21 0.0
Phospho@D 79.966331 79.9799 H(1)O(3)P(1) 0.0 Post-translational 21 0.0
-Phospho@Y 79.966331 79.9799 H(1)O(3)P(1) 0.0 Post-translational 21 0.0
-Phospho@T 79.966331 79.9799 H(1)O(3)P(1) 97.976896 H(3)O(4)P(1) Post-translational 21 10000000.0
-Phospho@S 79.966331 79.9799 H(1)O(3)P(1) 97.976896 H(3)O(4)P(1) Post-translational 21 100000000.0
+Phospho@Y 79.966331 79.9799 H(1)O(3)P(1) 0.0 Post-translational 21 C1=CC(=CC=C1CC(C(=O)[Rn])N([Xe])([Xe]))OP(=O)(O)O 0.0
+Phospho@T 79.966331 79.9799 H(1)O(3)P(1) 97.976896 H(3)O(4)P(1) Post-translational 21 CC(C(C(=O)[Rn])N([Xe])([Xe]))OP(=O)(O)O 10000000.0
+Phospho@S 79.966331 79.9799 H(1)O(3)P(1) 97.976896 H(3)O(4)P(1) Post-translational 21 O=P(O)(O)OC[C@@H](C(=O)[Rn])N([Xe])([Xe]) 100000000.0
Methamidophos-S@Y 108.975121 109.0873 H(4)C(1)N(1)O(1)P(1)S(1) 0.0 Chemical derivative 2007 0.0
Methamidophos-S@T 108.975121 109.0873 H(4)C(1)N(1)O(1)P(1)S(1) 0.0 Chemical derivative 2007 0.0
Methamidophos-S@S 108.975121 109.0873 H(4)C(1)N(1)O(1)P(1)S(1) 0.0 Chemical derivative 2007 0.0
@@ -73,27 +73,27 @@ Dehydrated@Q^Protein_C-term -18.010565 -18.0153 H(-2)O(-1) 0.0 Post-translation
Dehydrated@C^Any_N-term -18.010565 -18.0153 H(-2)O(-1) 0.0 Artefact 23 0.0
Propionamide@C 71.037114 71.0779 H(5)C(3)N(1)O(1) 0.0 Artefact 24 0.0
Propionamide@K 71.037114 71.0779 H(5)C(3)N(1)O(1) 0.0 Chemical derivative 24 0.0
-Propionamide@Any_N-term 71.037114 71.0779 H(5)C(3)N(1)O(1) 0.0 Chemical derivative 24 0.0
-Pyridylacetyl@Any_N-term 119.037114 119.1207 H(5)C(7)N(1)O(1) 0.0 Chemical derivative 25 0.0
+Propionamide@Any_N-term 71.037114 71.0779 H(5)C(3)N(1)O(1) 0.0 Chemical derivative 24 CCC(N)=O 0.0
+Pyridylacetyl@Any_N-term 119.037114 119.1207 H(5)C(7)N(1)O(1) 0.0 Chemical derivative 25 C(=O)Cc1ccccn1 0.0
Pyridylacetyl@K 119.037114 119.1207 H(5)C(7)N(1)O(1) 0.0 Chemical derivative 25 0.0
Pyro-carbamidomethyl@C^Any_N-term 39.994915 40.0208 C(2)O(1) 0.0 Artefact 26 0.0
-Glu->pyro-Glu@E^Any_N-term -18.010565 -18.0153 H(-2)O(-1) 0.0 Artefact 27 0.0
-Gln->pyro-Glu@Q^Any_N-term -17.026549 -17.0305 H(-3)N(-1) 0.0 Artefact 28 0.0
+Glu->pyro-Glu@E^Any_N-term -18.010565 -18.0153 H(-2)O(-1) 0.0 Artefact 27 O=C([Rn])[C@H]1N([Xe])C(=O)CC1 0.0
+Gln->pyro-Glu@Q^Any_N-term -17.026549 -17.0305 H(-3)N(-1) 0.0 Artefact 28 O=C([Rn])[C@H]1N([Xe])C(=O)CC1 0.0
SMA@Any_N-term 127.063329 127.1412 H(9)C(6)N(1)O(2) 0.0 Chemical derivative 29 0.0
SMA@K 127.063329 127.1412 H(9)C(6)N(1)O(2) 0.0 Chemical derivative 29 0.0
Cation:Na@D 21.981943 21.9818 H(-1)Na(1) 0.0 Artefact 30 0.0
-Cation:Na@Any_C-term 21.981943 21.9818 H(-1)Na(1) 0.0 Artefact 30 0.0
+Cation:Na@Any_C-term 21.981943 21.9818 H(-1)Na(1) 0.0 Artefact 30 O[Na] 0.0
Cation:Na@E 21.981943 21.9818 H(-1)Na(1) 0.0 Artefact 30 0.0
-Pyridylethyl@C 105.057849 105.1372 H(7)C(7)N(1) 0.0 Chemical derivative 31 0.0
+Pyridylethyl@C 105.057849 105.1372 H(7)C(7)N(1) 0.0 Chemical derivative 31 C1=CN=CC=C1CCSCC(C(=O)[Rn])N([Xe])([Xe]) 0.0
Methyl@E 14.01565 14.0266 H(2)C(1) 0.0 Post-translational 34 0.0
Methyl@D 14.01565 14.0266 H(2)C(1) 0.0 Post-translational 34 0.0
-Methyl@Any_C-term 14.01565 14.0266 H(2)C(1) 0.0 Multiple 34 0.0
-Methyl@Protein_N-term 14.01565 14.0266 H(2)C(1) 0.0 Post-translational 34 0.0
+Methyl@Any_C-term 14.01565 14.0266 H(2)C(1) 0.0 Multiple 34 OC 0.0
+Methyl@Protein_N-term 14.01565 14.0266 H(2)C(1) 0.0 Post-translational 34 C 0.0
Methyl@L 14.01565 14.0266 H(2)C(1) 0.0 Post-translational 34 0.0
Methyl@I 14.01565 14.0266 H(2)C(1) 0.0 Post-translational 34 0.0
Methyl@R 14.01565 14.0266 H(2)C(1) 0.0 Post-translational 34 0.0
Methyl@Q 14.01565 14.0266 H(2)C(1) 0.0 Post-translational 34 0.0
-Methyl@Any_N-term 14.01565 14.0266 H(2)C(1) 0.0 Chemical derivative 34 0.0
+Methyl@Any_N-term 14.01565 14.0266 H(2)C(1) 0.0 Chemical derivative 34 C 0.0
Methyl@N 14.01565 14.0266 H(2)C(1) 0.0 Post-translational 34 0.0
Methyl@K 14.01565 14.0266 H(2)C(1) 0.0 Post-translational 34 0.0
Methyl@H 14.01565 14.0266 H(2)C(1) 0.0 Post-translational 34 0.0
@@ -113,23 +113,23 @@ Oxidation@C 15.994915 15.9994 O(1) 0.0 Post-translational 35 0.0
Oxidation@H 15.994915 15.9994 O(1) 0.0 Artefact 35 0.0
Oxidation@V 15.994915 15.9994 O(1) 0.0 Chemical derivative 35 0.0
Oxidation@R 15.994915 15.9994 O(1) 0.0 Post-translational 35 0.0
-Oxidation@M 15.994915 15.9994 O(1) 63.998285 H(4)C(1)O(1)S(1) Artefact 35 0.5
+Oxidation@M 15.994915 15.9994 O(1) 63.998285 H(4)C(1)O(1)S(1) Artefact 35 O=C([Rn])C(N([Xe])([Xe]))CCS(=O)C 0.5
Oxidation@Y 15.994915 15.9994 O(1) 0.0 Post-translational 35 0.0
Oxidation@F 15.994915 15.9994 O(1) 0.0 Artefact 35 0.0
Oxidation@P 15.994915 15.9994 O(1) 0.0 Post-translational 35 0.0
Oxidation@N 15.994915 15.9994 O(1) 0.0 Post-translational 35 0.0
Oxidation@K 15.994915 15.9994 O(1) 0.0 Post-translational 35 0.0
Oxidation@D 15.994915 15.9994 O(1) 0.0 Post-translational 35 0.0
-Dimethyl@Protein_N-term 28.0313 28.0532 H(4)C(2) 0.0 Isotopic label 36 0.0
+Dimethyl@Protein_N-term 28.0313 28.0532 H(4)C(2) 0.0 Isotopic label 36 C 0.0
Dimethyl@P^Protein_N-term 28.0313 28.0532 H(4)C(2) 0.0 Post-translational 36 0.0
Dimethyl@N 28.0313 28.0532 H(4)C(2) 0.0 Post-translational 36 0.0
-Dimethyl@Any_N-term 28.0313 28.0532 H(4)C(2) 0.0 Isotopic label 36 0.0
-Dimethyl@K 28.0313 28.0532 H(4)C(2) 0.0 Multiple 36 0.0
+Dimethyl@Any_N-term 28.0313 28.0532 H(4)C(2) 0.0 Isotopic label 36 C 0.0
+Dimethyl@K 28.0313 28.0532 H(4)C(2) 0.0 Multiple 36 CN(C)CCCC[C@H](N([Xe])([Xe]))C(=O)[Rn] 0.0
Dimethyl@R 28.0313 28.0532 H(4)C(2) 0.0 Post-translational 36 0.0
Trimethyl@A^Protein_N-term 42.04695 42.0797 H(6)C(3) 0.0 Post-translational 37 0.0
Trimethyl@R 42.04695 42.0797 H(6)C(3) 0.0 Chemical derivative 37 0.0
Trimethyl@K 42.04695 42.0797 H(6)C(3) 59.073499 H(9)C(3)N(1) Post-translational 37 0.5
-Methylthio@C 45.987721 46.0916 H(2)C(1)S(1) 0.0 Multiple 39 0.0
+Methylthio@C 45.987721 46.0916 H(2)C(1)S(1) 0.0 Multiple 39 CSSC[C@H](N([Xe])([Xe]))C([Rn])=O 0.0
Methylthio@N 45.987721 46.0916 H(2)C(1)S(1) 0.0 Post-translational 39 0.0
Methylthio@D 45.987721 46.0916 H(2)C(1)S(1) 0.0 Post-translational 39 0.0
Methylthio@K 45.987721 46.0916 H(2)C(1)S(1) 0.0 Artefact 39 0.0
@@ -186,11 +186,11 @@ Acetyl:2H(3)@H 45.029395 45.0552 H(-1)2H(3)C(2)O(1) 0.0 Isotopic label 56 0.0
Acetyl:2H(3)@Any_N-term 45.029395 45.0552 H(-1)2H(3)C(2)O(1) 0.0 Isotopic label 56 0.0
Acetyl:2H(3)@K 45.029395 45.0552 H(-1)2H(3)C(2)O(1) 0.0 Isotopic label 56 0.0
Acetyl:2H(3)@Protein_N-term 45.029395 45.0552 H(-1)2H(3)C(2)O(1) 0.0 Isotopic label 56 0.0
-Propionyl@Protein_N-term 56.026215 56.0633 H(4)C(3)O(1) 0.0 Multiple 58 0.0
+Propionyl@Protein_N-term 56.026215 56.0633 H(4)C(3)O(1) 0.0 Multiple 58 C(=O)CC 0.0
Propionyl@T 56.026215 56.0633 H(4)C(3)O(1) 0.0 Isotopic label 58 0.0
Propionyl@S 56.026215 56.0633 H(4)C(3)O(1) 0.0 Chemical derivative 58 0.0
-Propionyl@K 56.026215 56.0633 H(4)C(3)O(1) 0.0 Isotopic label 58 0.0
-Propionyl@Any_N-term 56.026215 56.0633 H(4)C(3)O(1) 0.0 Isotopic label 58 0.0
+Propionyl@K 56.026215 56.0633 H(4)C(3)O(1) 0.0 Isotopic label 58 CCC(=O)NCCCCC(C(=O)[Rn])N([Xe])([Xe]) 0.0
+Propionyl@Any_N-term 56.026215 56.0633 H(4)C(3)O(1) 0.0 Isotopic label 58 C(=O)CC 0.0
Propionyl:13C(3)@Any_N-term 59.036279 59.0412 H(4)13C(3)O(1) 0.0 Isotopic label 59 0.0
Propionyl:13C(3)@K 59.036279 59.0412 H(4)13C(3)O(1) 0.0 Isotopic label 59 0.0
GIST-Quat@Any_N-term 127.099714 127.1842 H(13)C(7)N(1)O(1) 59.073499 H(9)C(3)N(1) Isotopic label 60 0.5
@@ -203,7 +203,7 @@ GIST-Quat:2H(9)@Any_N-term 136.156205 136.2397 H(4)2H(9)C(7)N(1)O(1) 68.12999 2H
GIST-Quat:2H(9)@K 136.156205 136.2397 H(4)2H(9)C(7)N(1)O(1) 68.12999 2H(9)C(3)N(1) Isotopic label 63 0.5
Succinyl@Protein_N-term 100.016044 100.0728 H(4)C(4)O(3) 0.0 Post-translational 64 0.0
Succinyl@Any_N-term 100.016044 100.0728 H(4)C(4)O(3) 0.0 Isotopic label 64 0.0
-Succinyl@K 100.016044 100.0728 H(4)C(4)O(3) 0.0 Isotopic label 64 0.0
+Succinyl@K 100.016044 100.0728 H(4)C(4)O(3) 0.0 Isotopic label 64 C(CCN)CC(C(=O)[Rn])N([Xe])C(=O)CCC(=O)O 0.0
Succinyl:2H(4)@Any_N-term 104.041151 104.0974 2H(4)C(4)O(3) 0.0 Isotopic label 65 0.0
Succinyl:2H(4)@K 104.041151 104.0974 2H(4)C(4)O(3) 0.0 Isotopic label 65 0.0
Succinyl:13C(4)@Any_N-term 104.029463 104.0434 H(4)13C(4)O(3) 0.0 Isotopic label 66 0.0
@@ -239,7 +239,7 @@ IMID@K 68.037448 68.0773 H(4)C(3)N(2) 0.0 Isotopic label 94 0.0
IMID:2H(4)@K 72.062555 72.1019 2H(4)C(3)N(2) 0.0 Isotopic label 95 0.0
Lysbiotinhydrazide@K 241.088497 241.31 H(15)C(10)N(3)O(2)S(1) 0.0 Chemical derivative 353 0.0
Propionamide:2H(3)@C 74.055944 74.0964 H(2)2H(3)C(3)N(1)O(1) 0.0 Isotopic label 97 0.0
-Nitro@Y 44.985078 44.9976 H(-1)N(1)O(2) 0.0 Chemical derivative 354 0.0
+Nitro@Y 44.985078 44.9976 H(-1)N(1)O(2) 0.0 Chemical derivative 354 O=[N+]([O-])c1cc(ccc1O)C[C@@H](C(=O)[Rn])N([Xe])([Xe]) 0.0
Nitro@W 44.985078 44.9976 H(-1)N(1)O(2) 0.0 Chemical derivative 354 0.0
Nitro@F 44.985078 44.9976 H(-1)N(1)O(2) 0.0 Artefact 354 0.0
ICAT-C@C 227.126991 227.2603 H(17)C(10)N(3)O(3) 0.0 Isotopic label 105 0.0
@@ -273,7 +273,7 @@ Formyl@Any_N-term 27.994915 28.0101 C(1)O(1) 0.0 Artefact 122 0.0
Formyl@S 27.994915 28.0101 C(1)O(1) 0.0 Artefact 122 0.0
ICAT-H@C 345.097915 345.7754 H(20)C(15)N(1)O(6)Cl(1) 0.0 Isotopic label 123 0.0
ICAT-H:13C(6)@C 351.118044 351.7313 H(20)C(9)13C(6)N(1)O(6)Cl(1) 0.0 Isotopic label 124 0.0
-Cation:K@Any_C-term 37.955882 38.0904 H(-1)K(1) 0.0 Artefact 530 0.0
+Cation:K@Any_C-term 37.955882 38.0904 H(-1)K(1) 0.0 Artefact 530 O[K] 0.0
Cation:K@E 37.955882 38.0904 H(-1)K(1) 0.0 Artefact 530 0.0
Cation:K@D 37.955882 38.0904 H(-1)K(1) 0.0 Artefact 530 0.0
Xlink:DTSSP[88]@Protein_N-term 87.998285 88.1283 H(4)C(3)O(1)S(1) 0.0 Chemical derivative 126 0.0
@@ -351,8 +351,8 @@ NBS:13C(6)@W 159.008578 159.1144 H(3)13C(6)N(1)O(2)S(1) 0.0 Chemical derivative
Methyl:2H(3)13C(1)@K 18.037835 18.0377 H(-1)2H(3)13C(1) 0.0 Isotopic label 329 0.0
Methyl:2H(3)13C(1)@R 18.037835 18.0377 H(-1)2H(3)13C(1) 0.0 Isotopic label 329 0.0
Methyl:2H(3)13C(1)@Any_N-term 18.037835 18.0377 H(-1)2H(3)13C(1) 0.0 Isotopic label 329 0.0
-Dimethyl:2H(6)13C(2)@Protein_N-term 36.07567 36.0754 H(-2)2H(6)13C(2) 0.0 Isotopic label 330 0.0
-Dimethyl:2H(6)13C(2)@Any_N-term 36.07567 36.0754 H(-2)2H(6)13C(2) 0.0 Isotopic label 330 0.0
+Dimethyl:2H(6)13C(2)@Protein_N-term 36.07567 36.0754 H(-2)2H(6)13C(2) 0.0 Isotopic label 330 [13C]([2H])([2H])([2H]) 0.0
+Dimethyl:2H(6)13C(2)@Any_N-term 36.07567 36.0754 H(-2)2H(6)13C(2) 0.0 Isotopic label 330 [13C]([2H])([2H])([2H]) 0.0
Dimethyl:2H(6)13C(2)@R 36.07567 36.0754 H(-2)2H(6)13C(2) 0.0 Isotopic label 330 0.0
Dimethyl:2H(6)13C(2)@K 36.07567 36.0754 H(-2)2H(6)13C(2) 0.0 Isotopic label 330 0.0
NBS@W 152.988449 153.1585 H(3)C(6)N(1)O(2)S(1) 0.0 Chemical derivative 172 0.0
@@ -378,8 +378,8 @@ QAT:2H(3)@C 174.168569 174.2784 H(16)2H(3)C(9)N(2)O(1) 0.0 Isotopic label 196
Label:18O(2)@Any_C-term 4.008491 3.9995 O(-2)18O(2) 0.0 Isotopic label 193 0.0
AccQTag@Any_N-term 170.048013 170.1674 H(6)C(10)N(2)O(1) 0.0 Chemical derivative 194 0.0
AccQTag@K 170.048013 170.1674 H(6)C(10)N(2)O(1) 0.0 Chemical derivative 194 0.0
-Dimethyl:2H(4)@Protein_N-term 32.056407 32.0778 2H(4)C(2) 0.0 Isotopic label 199 0.0
-Dimethyl:2H(4)@Any_N-term 32.056407 32.0778 2H(4)C(2) 0.0 Isotopic label 199 0.0
+Dimethyl:2H(4)@Protein_N-term 32.056407 32.0778 2H(4)C(2) 0.0 Isotopic label 199 C([2H])([2H])([1H]) 0.0
+Dimethyl:2H(4)@Any_N-term 32.056407 32.0778 2H(4)C(2) 0.0 Isotopic label 199 C([2H])([2H])([1H]) 0.0
Dimethyl:2H(4)@K 32.056407 32.0778 2H(4)C(2) 0.0 Isotopic label 199 0.0
Dimethyl:2H(4)@R 32.056407 32.0778 2H(4)C(2) 0.0 Isotopic label 199 0.0
EQAT@C 184.157563 184.2786 H(20)C(10)N(2)O(1) 0.0 Chemical derivative 197 0.0
@@ -475,7 +475,7 @@ Ethanolyl@C 44.026215 44.0526 H(4)C(2)O(1) 0.0 Chemical derivative 278 0.0
Ethanolyl@R 44.026215 44.0526 H(4)C(2)O(1) 0.0 Chemical derivative 278 0.0
Label:13C(6)15N(2)+Dimethyl@K 36.045499 35.9959 H(4)C(-4)13C(6)N(-2)15N(2) 0.0 Isotopic label 987 0.0
HMVK@C 86.036779 86.0892 H(6)C(4)O(2) 0.0 Chemical derivative 371 0.0
-Ethyl@Any_C-term 28.0313 28.0532 H(4)C(2) 0.0 Chemical derivative 280 0.0
+Ethyl@Any_C-term 28.0313 28.0532 H(4)C(2) 0.0 Chemical derivative 280 OCC 0.0
Ethyl@Protein_N-term 28.0313 28.0532 H(4)C(2) 0.0 Chemical derivative 280 0.0
Ethyl@E 28.0313 28.0532 H(4)C(2) 0.0 Artefact 280 0.0
Ethyl@Any_N-term 28.0313 28.0532 H(4)C(2) 0.0 Multiple 280 0.0
@@ -589,7 +589,7 @@ ICPL@Any_N-term 105.021464 105.0941 H(3)C(6)N(1)O(1) 0.0 Isotopic label 365 0.
Deamidated:18O(1)@Q 2.988261 2.9845 H(-1)N(-1)18O(1) 0.0 Isotopic label 366 0.0
Deamidated:18O(1)@N 2.988261 2.9845 H(-1)N(-1)18O(1) 0.0 Isotopic label 366 0.0
Arg->Orn@R -42.021798 -42.04 H(-2)C(-1)N(-2) 0.0 Artefact 372 0.0
-Cation:Cu[I]@Any_C-term 61.921774 62.5381 H(-1)Cu(1) 0.0 Artefact 531 0.0
+Cation:Cu[I]@Any_C-term 61.921774 62.5381 H(-1)Cu(1) 0.0 Artefact 531 O[Cu] 0.0
Cation:Cu[I]@E 61.921774 62.5381 H(-1)Cu(1) 0.0 Artefact 531 0.0
Cation:Cu[I]@D 61.921774 62.5381 H(-1)Cu(1) 0.0 Artefact 531 0.0
Cation:Cu[I]@H 61.921774 62.5381 H(-1)Cu(1) 0.0 Artefact 531 0.0
@@ -761,10 +761,10 @@ Diethyl@Any_N-term 56.0626 56.1063 H(8)C(4) 0.0 Chemical derivative 518 0.0
Diethyl@K 56.0626 56.1063 H(8)C(4) 0.0 Chemical derivative 518 0.0
LG-Hlactam-K@Protein_N-term 348.193674 348.4333 H(28)C(20)O(5) 0.0 Post-translational 504 0.0
LG-Hlactam-K@K 348.193674 348.4333 H(28)C(20)O(5) 0.0 Post-translational 504 0.0
-Dimethyl:2H(4)13C(2)@Protein_N-term 34.063117 34.0631 2H(4)13C(2) 0.0 Isotopic label 510 0.0
+Dimethyl:2H(4)13C(2)@Protein_N-term 34.063117 34.0631 2H(4)13C(2) 0.0 Isotopic label 510 [13C]([2H])([2H])([1H]) 0.0
Dimethyl:2H(4)13C(2)@R 34.063117 34.0631 2H(4)13C(2) 0.0 Isotopic label 510 0.0
Dimethyl:2H(4)13C(2)@K 34.063117 34.0631 2H(4)13C(2) 0.0 Isotopic label 510 0.0
-Dimethyl:2H(4)13C(2)@Any_N-term 34.063117 34.0631 2H(4)13C(2) 0.0 Isotopic label 510 0.0
+Dimethyl:2H(4)13C(2)@Any_N-term 34.063117 34.0631 2H(4)13C(2) 0.0 Isotopic label 510 [13C]([2H])([2H])([1H]) 0.0
C8-QAT@Any_N-term 227.224915 227.3862 H(29)C(14)N(1)O(1) 0.0 Chemical derivative 513 0.0
C8-QAT@K 227.224915 227.3862 H(29)C(14)N(1)O(1) 0.0 Chemical derivative 513 0.0
Hex(2)@R 324.105647 324.2812 H(20)C(12)O(10) 0.0 Other glycosylation 512 0.0
@@ -1055,7 +1055,7 @@ Biotin:Aha-DADPS@M 922.465403 923.2022 H(70)C(42)N(8)O(11)S(1)Si(1) 0.0 Chemica
NO_SMX_SIMD@C 267.031377 267.2612 H(9)C(10)N(3)O(4)S(1) 0.0 Chemical derivative 746 0.0
Malonyl@C 86.000394 86.0462 H(2)C(3)O(3) 0.0 Chemical derivative 747 0.0
Malonyl@S 86.000394 86.0462 H(2)C(3)O(3) 0.0 Chemical derivative 747 0.0
-Malonyl@K 86.000394 86.0462 H(2)C(3)O(3) 0.0 Post-translational 747 0.0
+Malonyl@K 86.000394 86.0462 H(2)C(3)O(3) 0.0 Post-translational 747 N([Xe])([Xe])[C@@H](CCCC(NC(=O)CC(=O)O))C(=O)[Rn] 0.0
3sulfo@Any_N-term 183.983029 184.1693 H(4)C(7)O(4)S(1) 0.0 Chemical derivative 748 0.0
trifluoro@L 53.971735 53.9714 H(-3)F(3) 0.0 Non-standard residue 750 0.0
TNBS@Any_N-term 210.986535 211.0886 H(1)C(6)N(3)O(6) 0.0 Chemical derivative 751 0.0
@@ -1107,8 +1107,8 @@ cGMP@C 343.031785 343.1895 H(10)C(10)N(5)O(7)P(1) 0.0 Post-translational 849 0
cGMP+RMP-loss@C 150.041585 150.1182 H(4)C(5)N(5)O(1) 0.0 Post-translational 851 0.0
cGMP+RMP-loss@S 150.041585 150.1182 H(4)C(5)N(5)O(1) 0.0 Post-translational 851 0.0
mTRAQ@Y 140.094963 140.183 H(12)C(7)N(2)O(1) 0.0 Isotopic label 888 0.0
-mTRAQ@Any_N-term 140.094963 140.183 H(12)C(7)N(2)O(1) 0.0 Isotopic label 888 0.0
-mTRAQ@K 140.094963 140.183 H(12)C(7)N(2)O(1) 0.0 Isotopic label 888 0.0
+mTRAQ@Any_N-term 140.094963 140.183 H(12)C(7)N(2)O(1) 0.0 Isotopic label 888 C(=O)CN1CCN(CC1)C 0.0
+mTRAQ@K 140.094963 140.183 H(12)C(7)N(2)O(1) 0.0 Isotopic label 888 [H]N(CCCC[C@H](N([Xe])([Xe]))C(=O)[Rn])C(=O)CN1CCN(C)CC1 0.0
mTRAQ@H 140.094963 140.183 H(12)C(7)N(2)O(1) 0.0 Isotopic label 888 0.0
mTRAQ@S 140.094963 140.183 H(12)C(7)N(2)O(1) 0.0 Isotopic label 888 0.0
mTRAQ@T 140.094963 140.183 H(12)C(7)N(2)O(1) 0.0 Isotopic label 888 0.0
@@ -1129,8 +1129,8 @@ mTRAQ:13C(3)15N(1)@S 144.102063 144.1544 H(12)C(4)13C(3)N(1)15N(1)O(1) 0.0 Isot
mTRAQ:13C(3)15N(1)@T 144.102063 144.1544 H(12)C(4)13C(3)N(1)15N(1)O(1) 0.0 Isotopic label 889 0.0
mTRAQ:13C(3)15N(1)@H 144.102063 144.1544 H(12)C(4)13C(3)N(1)15N(1)O(1) 0.0 Isotopic label 889 0.0
mTRAQ:13C(3)15N(1)@Y 144.102063 144.1544 H(12)C(4)13C(3)N(1)15N(1)O(1) 0.0 Isotopic label 889 0.0
-mTRAQ:13C(3)15N(1)@Any_N-term 144.102063 144.1544 H(12)C(4)13C(3)N(1)15N(1)O(1) 0.0 Isotopic label 889 0.0
-mTRAQ:13C(3)15N(1)@K 144.102063 144.1544 H(12)C(4)13C(3)N(1)15N(1)O(1) 0.0 Isotopic label 889 0.0
+mTRAQ:13C(3)15N(1)@Any_N-term 144.102063 144.1544 H(12)C(4)13C(3)N(1)15N(1)O(1) 0.0 Isotopic label 889 C(=O)[13C]([H])([H])[15N]1[13C]([H])([H])[13C]([H])([H])N(CC1)C 0.0
+mTRAQ:13C(3)15N(1)@K 144.102063 144.1544 H(12)C(4)13C(3)N(1)15N(1)O(1) 0.0 Isotopic label 889 [H]N(CCCC[C@H](N([Xe])([Xe]))C(=O)[Rn])C(=O)[13CH2][15N]1CCN(C)[13CH2][13CH2]1 0.0
Methyl-PEO12-Maleimide@C 710.383719 710.8073 H(58)C(32)N(2)O(15) 0.0 Chemical derivative 891 0.0
MDCC@C 383.148121 383.3978 H(21)C(20)N(3)O(5) 0.0 Chemical derivative 887 0.0
QQQTGG@K 599.266339 599.5942 H(37)C(23)N(9)O(10) 0.0 Other 877 0.0
@@ -1198,7 +1198,7 @@ LG-anhyropyrrole@K 298.19328 298.4192 H(26)C(20)O(2) 0.0 Post-translational 948
3-deoxyglucosone@R 144.042259 144.1253 H(8)C(6)O(4) 0.0 Multiple 949 0.0
Cation:Li@D 6.008178 5.9331 H(-1)Li(1) 0.0 Artefact 950 0.0
Cation:Li@E 6.008178 5.9331 H(-1)Li(1) 0.0 Artefact 950 0.0
-Cation:Li@Any_C-term 6.008178 5.9331 H(-1)Li(1) 0.0 Artefact 950 0.0
+Cation:Li@Any_C-term 6.008178 5.9331 H(-1)Li(1) 0.0 Artefact 950 O[Li] 0.0
Cation:Ca[II]@Any_C-term 37.946941 38.0621 H(-2)Ca(1) 0.0 Artefact 951 0.0
Cation:Ca[II]@E 37.946941 38.0621 H(-2)Ca(1) 0.0 Artefact 951 0.0
Cation:Ca[II]@D 37.946941 38.0621 H(-2)Ca(1) 0.0 Artefact 951 0.0
@@ -1577,7 +1577,7 @@ UgiJoullieProGlyProGly@E 308.148455 308.333 H(20)C(14)N(4)O(4) 0.0 Chemical der
Arg-loss@R^Any_C-term -156.101111 -156.1857 H(-12)C(-6)N(-4)O(-1) 0.0 Other 1287 0.0
Arg@Any_N-term 156.101111 156.1857 H(12)C(6)N(4)O(1) 0.0 Other 1288 0.0
IMEHex(2)NeuAc(1)@K 688.199683 688.6527 H(40)C(25)N(2)O(18)S(1) 0.0 Other glycosylation 1286 0.0
-Butyryl@K 70.041865 70.0898 H(6)C(4)O(1) 0.0 Post-translational 1289 0.0
+Butyryl@K 70.041865 70.0898 H(6)C(4)O(1) 0.0 Post-translational 1289 CCCC(=O)NCCCCC(C(=O)[Rn])N([Xe])([Xe]) 0.0
Dicarbamidomethyl@K 114.042927 114.1026 H(6)C(4)N(2)O(2) 0.0 Artefact 1290 0.0
Dicarbamidomethyl@H 114.042927 114.1026 H(6)C(4)N(2)O(2) 0.0 Artefact 1290 0.0
Dicarbamidomethyl@C 114.042927 114.1026 H(6)C(4)N(2)O(2) 0.0 Artefact 1290 0.0
@@ -1595,8 +1595,8 @@ Label:13C(4)15N(1)@D 5.010454 4.964 C(-4)13C(4)N(-1)15N(1) 0.0 Isotopic label 1
Label:2H(10)@L 10.062767 10.0616 H(-10)2H(10) 0.0 Isotopic label 1299 0.0
Label:2H(4)13C(1)@R 5.028462 5.0173 H(-4)2H(4)C(-1)13C(1) 0.0 Isotopic label 1300 0.0
Lys@Any_N-term 128.094963 128.1723 H(12)C(6)N(2)O(1) 0.0 Other 1301 0.0
-mTRAQ:13C(6)15N(2)@K 148.109162 148.1257 H(12)C(1)13C(6)15N(2)O(1) 0.0 Isotopic label 1302 0.0
-mTRAQ:13C(6)15N(2)@Any_N-term 148.109162 148.1257 H(12)C(1)13C(6)15N(2)O(1) 0.0 Isotopic label 1302 0.0
+mTRAQ:13C(6)15N(2)@K 148.109162 148.1257 H(12)C(1)13C(6)15N(2)O(1) 0.0 Isotopic label 1302 [H]N(CCCC[C@H](N([Xe])([Xe]))C(=O)[Rn])C(=O)[13CH2][15N]1[13CH2][13CH2][15N]([13CH3])[13CH2][13CH2]1 0.0
+mTRAQ:13C(6)15N(2)@Any_N-term 148.109162 148.1257 H(12)C(1)13C(6)15N(2)O(1) 0.0 Isotopic label 1302 C(=O)[13C]([H])([H])[15N]1[13C]([H])([H])[13C]([H])([H])[15N]([13C]([H])([H])[13C]1([H])([H]))[13C]([H])([H])([H]) 0.0
mTRAQ:13C(6)15N(2)@Y 148.109162 148.1257 H(12)C(1)13C(6)15N(2)O(1) 0.0 Isotopic label 1302 0.0
mTRAQ:13C(6)15N(2)@H 148.109162 148.1257 H(12)C(1)13C(6)15N(2)O(1) 0.0 Isotopic label 1302 0.0
mTRAQ:13C(6)15N(2)@S 148.109162 148.1257 H(12)C(1)13C(6)15N(2)O(1) 0.0 Isotopic label 1302 0.0
@@ -1611,8 +1611,8 @@ Propyl@D 42.04695 42.0797 H(6)C(3) 0.0 Chemical derivative 1305 0.0
Propyl@K 42.04695 42.0797 H(6)C(3) 0.0 Isotopic label 1305 0.0
Propyl@Any_N-term 42.04695 42.0797 H(6)C(3) 0.0 Isotopic label 1305 0.0
Propyl@E 42.04695 42.0797 H(6)C(3) 0.0 Chemical derivative 1305 0.0
-Propyl@Any_C-term 42.04695 42.0797 H(6)C(3) 0.0 Chemical derivative 1305 0.0
-Propyl@Protein_C-term 42.04695 42.0797 H(6)C(3) 0.0 Chemical derivative 1305 0.0
+Propyl@Any_C-term 42.04695 42.0797 H(6)C(3) 0.0 Chemical derivative 1305 OCCC 0.0
+Propyl@Protein_C-term 42.04695 42.0797 H(6)C(3) 0.0 Chemical derivative 1305 OCCC 0.0
Propyl:2H(6)@Any_N-term 48.084611 48.1167 2H(6)C(3) 0.0 Isotopic label 1306 0.0
Propyl:2H(6)@K 48.084611 48.1167 2H(6)C(3) 0.0 Isotopic label 1306 0.0
Propiophenone@C 132.057515 132.1592 H(8)C(9)O(1) 0.0 Chemical derivative 1310 0.0
@@ -1677,7 +1677,7 @@ HN2_mustard@H 101.084064 101.1469 H(11)C(5)N(1)O(1) 0.0 Post-translational 1388
HN2_mustard@K 101.084064 101.1469 H(11)C(5)N(1)O(1) 0.0 Post-translational 1388 0.0
HN2_mustard@C 101.084064 101.1469 H(11)C(5)N(1)O(1) 0.0 Post-translational 1388 0.0
NEM:2H(5)+H2O@C 148.089627 148.1714 H(4)2H(5)C(6)N(1)O(3) 0.0 Chemical derivative 1358 0.0
-Crotonyl@K 68.026215 68.074 H(4)C(4)O(1) 0.0 Post-translational 1363 0.0
+Crotonyl@K 68.026215 68.074 H(4)C(4)O(1) 0.0 Post-translational 1363 CC=CC(=O)NCCCCC(C(=O)[Rn])N([Xe])([Xe]) 0.0
O-Et-N-diMePhospho@S 135.044916 135.1015 H(10)C(4)N(1)O(2)P(1) 0.0 Chemical derivative 1364 0.0
N-dimethylphosphate@S 107.013615 107.0483 H(6)C(2)N(1)O(2)P(1) 0.0 Chemical derivative 1365 0.0
phosphoRibosyl@E 212.00859 212.0945 H(9)C(5)O(7)P(1) 0.0 Post-translational 1356 0.0
@@ -2770,4 +2770,28 @@ NQTGG@K 457.192111 457.4384 H(27)C(17)N(7)O(8) 0.0 Other 2084 0.0
DVFQQQTGG@K 960.43011 960.9865 H(60)C(41)N(12)O(15) 0.0 Other 2085 0.0
iST-NHS_specific_cysteine_modification@C 113.084064 113.1576 H(11)C(6)N(1)O(1) 0.0 Chemical derivative 2086 0.0
Label:13C(2)15N(1)@G 3.003745 2.9787 C(-2)13C(2)N(-1)15N(1) 0.0 Isotopic label 2088 0.0
-GlyGly@K 114.042927 114.1026 H(6)C(4)N(2)O(2) 0.0 Multiple 121 1000000.0
+GlyGly@K 114.042927 114.1026 H(6)C(4)N(2)O(2) 0.0 Multiple 121 NCC(=O)NCC(=O)NCCCC[C@H](N([Xe])([Xe]))C([Rn])=O 1000000.0
+Pro->(2S,4R)-4-fluoroproline@P 0.0 0.0 F(1)H(-1) 0.0 User-added 0 F[C@@H]1C[C@H](N([Xe])C1)C(=O)[Rn] 0.0
+Pro->(2S,4S)-4fluoroproline@P 0.0 0.0 F(1)H(-1) 0.0 User-added 0 F[C@H]1C[C@H](N([Xe])C1)C(=O)[Rn] 0.0
+Pro->(2S)-1,3-thiazolidine-2-carboxylic_acid@P 0.0 0.0 C(-1)H(-2)S(1) 0.0 User-added 0 S1[C@H](N([Xe])CC1)C(=O)[Rn] 0.0
+Pro->(4R)-1,3-Thiazolidine-4-carboxylic_acid@P 0.0 0.0 C(-1)H(-2)S(1) 0.0 User-added 0 S1CN([Xe])[C@@H](C1)C(=O)[Rn] 0.0
+Pro->(2S,4R)-4-hydroxyproline@P 0.0 0.0 O(1) 0.0 User-added 0 O[C@@H]1C[C@H](N([Xe])C1)C(=O)[Rn] 0.0
+Pro->(DL)-pipecolic_acid@P 0.0 0.0 C(1)H(2) 0.0 User-added 0 C1CCN([Xe])C(C1)C(=O)[Rn] 0.0
+Pro->3,4-Dehydro-L-proline@P 0.0 0.0 H(-2) 0.0 User-added 0 C1C=CC(N1([Xe]))C(=O)[Rn] 0.0
+Pro->(1S,3S,5S)-2-Azabicyclo[3.1.0]hexane-3-carboxylic_acid@P 0.0 0.0 C(1) 0.0 User-added 0 [C@H]12N([Xe])[C@@H](C[C@@H]2C1)C(=O)[Rn] 0.0
+Pro->(1R,3S,5R)-2-Azabicyclo[3.1.0]hexane-3-carboxylic_acid@P 0.0 0.0 C(1) 0.0 User-added 0 [C@@H]12N([Xe])[C@@H](C[C@H]2C1)C(=O)[Rn] 0.0
+Pro->(2S,3aS,7aS)-Octahydro-1H-indole-2-carboxylic_acid@P 0.0 0.0 C(4)H(6) 0.0 User-added 0 N1([Xe])[C@@H](C[C@@H]2CCCC[C@H]12)C(=O)[Rn] 0.0
+Pro->(DL)-5-trifluoromethylproline@P 0.0 0.0 C(1)F(3)H(-1) 0.0 User-added 0 FC(C1CCC(N1([Xe]))C(=O)[Rn])(F)F 0.0
+mTRAQ@Protein_N-term 0.0 0.0 C(7)H(12)N(2)O(1) 0.0 User-added 0 C(=O)CN1CCN(CC1)C 0.0
+mTRAQ:13C(3)15N(1)@Protein_N-term 0.0 0.0 13C(3)15N(1)C(4)H(12)N(1)O(1) 0.0 User-added 0 C(=O)[13C]([H])([H])[15N]1[13C]([H])([H])[13C]([H])([H])N(CC1)C 0.0
+mTRAQ:13C(6)15N(2)@Protein_N-term 0.0 0.0 13C(6)15N(2)C(1)H(12)O(1) 0.0 User-added 0 C(=O)[13C]([H])([H])[15N]1[13C]([H])([H])[13C]([H])([H])[15N]([13C]([H])([H])[13C]1([H])([H]))[13C]([H])([H])([H]) 0.0
+Biotin@Protein_N-term 0.0 0.0 C(10)H(14)N(2)O(2)S(1) 0.0 User-added 0 C(=O)CCCCC1SCC2NC(=O)NC21 0.0
+Carbamidomethyl@Protein_N-term 0.0 0.0 C(2)H(3)N(1)O(1) 0.0 User-added 0 C(=O)NC 0.0
+Propionamide@Protein_N-term 0.0 0.0 C(3)H(5)N(1)O(1) 0.0 User-added 0 CCC(N)=O 0.0
+Pyridylacetyl@Protein_N-term 0.0 0.0 C(7)H(5)N(1)O(1) 0.0 User-added 0 C(=O)Cc1ccccn1 0.0
+Methyl@Protein_C-term 0.0 0.0 C(1)H(2) 0.0 User-added 0 OC 0.0
+Ethyl@Protein_C-term 0.0 0.0 C(2)H(4) 0.0 User-added 0 OCC 0.0
+Cation:Na@Protein_C-term 0.0 0.0 H(-1)Na(1) 0.0 User-added 0 O[Na] 0.0
+Cation:K@Protein_C-term 0.0 0.0 H(-1)K(1) 0.0 User-added 0 O[K] 0.0
+Cation:Cu[I]@Protein_C-term 0.0 0.0 Cu(1)H(-1) 0.0 User-added 0 O[Cu] 0.0
+Cation:Li@Protein_C-term 0.0 0.0 H(-1)Li(1) 0.0 User-added 0 O[Li] 0.0
diff --git a/alphabase/constants/modification.py b/alphabase/constants/modification.py
index 58ee7146..4738628c 100644
--- a/alphabase/constants/modification.py
+++ b/alphabase/constants/modification.py
@@ -362,12 +362,70 @@ def calc_modloss_mass(
return _calc_modloss(mod_losses[::-1])[-3:0:-1]
+def _check_mass_sanity(
+ mod_name: str,
+ composition: str,
+ smiles: str,
+):
+ """
+ Check if the mass of the modification is consistent with the formula.
+
+ Parameters
+ ----------
+ mod_name : str
+ Modification name (e.g. Mod@S)
+
+ composition : str
+ Composition formula (e.g. "H(4)O(2)"), used to calculate the mass of the modification
+
+ smiles : str
+ SMILES string of the modification, used to calculate and compare the mass of the modification
+
+ Raises
+ ------
+ ValueError
+ If the mass of the modification is inconsistent with the formula
+ """
+ if not smiles or mod_name not in MOD_MASS:
+ return
+ composition_mass = calc_mass_from_formula(composition)
+ if not np.allclose(composition_mass, MOD_MASS[mod_name], atol=1e-5):
+ raise ValueError(
+ f"Modification mass of {mod_name} is inconsistent with the composition formula: {composition}, df version {MOD_DF.loc[mod_name,['composition']]}"
+ f" calculated_mass={composition_mass}, mod_mass={MOD_MASS[mod_name]}"
+ )
+
+
def _add_a_new_modification(
- mod_name: str, composition: str, modloss_composition: str = ""
+ mod_name: str,
+ composition: str,
+ modloss_composition: str = "",
+ smiles: str = "",
):
"""
- Add a new modification into :data:`MOD_DF`.
+ Add a new modification into :data:`MOD_DF` or update SMILES if modification already exists.
+
+ Parameters
+ ----------
+ mod_name : str
+ Modification name (e.g. Mod@S)
+
+ composition : str
+ Composition formula (e.g. "H(4)O(2)")
+
+ modloss_composition : str
+ Composition formula of the modification loss (e.g. "H(2)O(1)")
+
+ smiles : str
+ SMILES string of the modification
"""
+ _check_mass_sanity(mod_name, composition, smiles)
+
+ if mod_name in MOD_DF.index:
+ # If the modification already exists, only update the SMILES
+ MOD_DF.loc[mod_name, "smiles"] = smiles
+ return
+
MOD_DF.loc[
mod_name,
[
@@ -376,11 +434,14 @@ def _add_a_new_modification(
"modloss_composition",
"classification",
"unimod_id",
+ "smiles",
],
- ] = [mod_name, composition, modloss_composition, "User-added", 0]
+ ] = [mod_name, composition, modloss_composition, "User-added", 0, smiles]
+ composition_mass = calc_mass_from_formula(composition)
+ modloss_mass = calc_mass_from_formula(modloss_composition)
MOD_DF.loc[mod_name, ["mass", "modloss"]] = (
- calc_mass_from_formula(composition),
- calc_mass_from_formula(modloss_composition),
+ composition_mass,
+ modloss_mass,
)
if MOD_DF.loc[mod_name, "modloss"] > 0:
MOD_DF.loc[mod_name, "modloss_importance"] = 1e10
diff --git a/alphabase/smiles/__init__.py b/alphabase/smiles/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/alphabase/smiles/smiles.py b/alphabase/smiles/smiles.py
new file mode 100644
index 00000000..4f264069
--- /dev/null
+++ b/alphabase/smiles/smiles.py
@@ -0,0 +1,285 @@
+from typing import Dict, Optional
+
+import pandas as pd
+from rdkit import Chem
+from rdkit.Chem.rdmolops import ReplaceSubstructs, SanitizeMol
+
+from alphabase.constants.aa import aa_formula
+from alphabase.constants.modification import MOD_DF
+
+
+class AminoAcidModifier:
+ """
+ A class for modifying amino acids with N-terminal and C-terminal modifications.
+ """
+
+ N_TERM_PLACEHOLDER = "[Xe]"
+ C_TERM_PLACEHOLDER = "[Rn]"
+
+ def __init__(self):
+ self._aa_smiles = None
+ self._aa_mols = None
+ self._n_term_modifications = None
+ self._c_term_modifications = None
+ self._ptm_dict = None
+
+ @property
+ def aa_smiles(self) -> Dict[str, str]:
+ """
+ Dictionary of amino acid SMILES.
+
+ Returns
+ -------
+ Dict[str, str]
+ A dictionary with amino acid codes as keys and their SMILES as values.
+ """
+ if self._aa_smiles is None:
+ self._aa_smiles = {
+ aa: aa_formula.loc[aa]["smiles"]
+ for aa in aa_formula.index
+ if not pd.isna(aa_formula.loc[aa]["smiles"])
+ }
+ return self._aa_smiles
+
+ @property
+ def aa_mols(self) -> Dict[str, Chem.Mol]:
+ """
+ Dictionary of amino acid RDKit molecules.
+
+ Returns
+ -------
+ Dict[str, Chem.Mol]
+ A dictionary with amino acid codes as keys and their RDKit molecules as values.
+ """
+ if self._aa_mols is None:
+ self._aa_mols = {
+ aa: Chem.MolFromSmiles(smiles) for aa, smiles in self.aa_smiles.items()
+ }
+ return self._aa_mols
+
+ @property
+ def n_term_modifications(self) -> Dict[str, str]:
+ """
+ Dictionary of N-terminal modifications.
+
+ Returns
+ -------
+ Dict[str, str]
+ A dictionary with modification names as keys and their SMILES as values.
+ """
+ if self._n_term_modifications is None:
+ self._n_term_modifications = self._get_modifications(
+ "@Protein_N-term", "@Any_N-term"
+ )
+ return self._n_term_modifications
+
+ @property
+ def c_term_modifications(self) -> Dict[str, str]:
+ """
+ Dictionary of C-terminal modifications.
+
+ Returns
+ -------
+ Dict[str, str]
+ A dictionary with modification names as keys and their SMILES as values.
+ """
+ if self._c_term_modifications is None:
+ self._c_term_modifications = self._get_modifications(
+ "@Protein_C-term", "@Any_C-term"
+ )
+ return self._c_term_modifications
+
+ @property
+ def ptm_dict(self) -> Dict[str, str]:
+ """
+ Dictionary of post-translational modifications (PTMs).
+
+ Returns
+ -------
+ Dict[str, str]
+ A dictionary with modification names as keys and their SMILES as values.
+ """
+ if self._ptm_dict is None:
+ self._ptm_dict = {
+ name: smiles
+ for name, smiles in self._get_modifications("@").items()
+ if name not in self.n_term_modifications
+ and name not in self.c_term_modifications
+ }
+ return self._ptm_dict
+
+ def _get_modifications(self, *terms: str) -> Dict[str, str]:
+ """
+ Helper method to get modifications based on specific terms.
+
+ Parameters
+ ----------
+ *terms : str
+ Terms to filter modifications by.
+
+ Returns
+ -------
+ Dict[str, str]
+ A dictionary of modifications with names as keys and SMILES as values.
+ """
+ mod_df_smiles = MOD_DF[MOD_DF["smiles"] != ""]
+ return {
+ name: smiles
+ for name, smiles in zip(mod_df_smiles["mod_name"], mod_df_smiles["smiles"])
+ if any(term in name for term in terms)
+ }
+
+ def validate_correct_args(
+ self,
+ aa_smiles: str,
+ n_term_mod: Optional[str] = None,
+ c_term_mod: Optional[str] = None,
+ ) -> None:
+ """
+ Validate the amino acid, N-terminal modification, and C-terminal modification.
+
+ Parameters
+ ----------
+ aa_smiles : str
+ SMILES of an amino acid with or without PTMs, must have the placeholder atoms [Xe] for N-term and [Rn] for C-term.
+ n_term_mod : Optional[str], optional
+ N-terminal modification name. Options are the keys in the n_term_modifications dict.
+ c_term_mod : Optional[str], optional
+ C-terminal modification name. Options are the keys in the c_term_modifications dict.
+
+ Raises
+ ------
+ ValueError
+ If the amino acid is invalid, or if N-terminal / C-terminal modification is not recognized.
+ """
+ errors = []
+
+ if self.N_TERM_PLACEHOLDER not in aa_smiles:
+ errors.append(
+ f"The amino acid {aa_smiles} must contain the N-terminal placeholder [{self.N_TERM_PLACEHOLDER}]."
+ )
+ if self.C_TERM_PLACEHOLDER not in aa_smiles:
+ errors.append(
+ f"The amino acid {aa_smiles} must contain the C-terminal placeholder [{self.C_TERM_PLACEHOLDER}]."
+ )
+
+ if n_term_mod is not None and n_term_mod not in self.n_term_modifications:
+ errors.append(f"Unrecognized N-terminal modification: {n_term_mod}")
+
+ if c_term_mod is not None and c_term_mod not in self.c_term_modifications:
+ errors.append(f"Unrecognized C-terminal modification: {c_term_mod}")
+
+ mol = Chem.MolFromSmiles(aa_smiles)
+ if mol is None:
+ errors.append(f"Invalid amino acid SMILES: {aa_smiles}")
+
+ if errors:
+ raise ValueError("\n".join(errors))
+
+ def modify_amino_acid(
+ self,
+ aa_smiles: str,
+ n_term_mod: Optional[str] = None,
+ c_term_mod: Optional[str] = None,
+ ) -> str:
+ """
+ Modify an amino acid with N-terminal and C-terminal modifications.
+
+ Parameters
+ ----------
+ aa_smiles : str
+ SMILES of an amino acid with or without PTMs, must have the placeholder atoms [Xe] for N-term and [Rn] for C-term.
+ n_term_mod : Optional[str], optional
+ N-terminal modification name. Options are the keys in the n_term_modifications dict.
+ If None, the amino acid is not modified, so using hydrogen atoms.
+ c_term_mod : Optional[str], optional
+ C-terminal modification name. Options are the keys in the c_term_modifications dict.
+ If None, the amino acid is not modified, so using the -OH group.
+
+ Returns
+ -------
+ str
+ SMILES string of the modified amino acid.
+ """
+ self.validate_correct_args(aa_smiles, n_term_mod, c_term_mod)
+ mol = Chem.MolFromSmiles(aa_smiles)
+ n_term_placeholder_mol = Chem.MolFromSmiles(
+ self.N_TERM_PLACEHOLDER, sanitize=False
+ )
+ c_term_placeholder_mol = Chem.MolFromSmiles(
+ self.C_TERM_PLACEHOLDER, sanitize=False
+ )
+
+ mol = self._apply_n_terminal_modification(
+ mol, n_term_placeholder_mol, n_term_mod
+ )
+ mol = self._apply_c_terminal_modification(
+ mol, c_term_placeholder_mol, c_term_mod
+ )
+
+ SanitizeMol(mol)
+ return Chem.MolToSmiles(mol)
+
+ def _apply_n_terminal_modification(
+ self, mol: Chem.Mol, n_term_placeholder_mol: Chem.Mol, n_term_mod: Optional[str]
+ ) -> Chem.Mol:
+ """
+ Apply N-terminal modification to the molecule.
+
+ Parameters
+ ----------
+ mol : Chem.Mol
+ The molecule to modify.
+ n_term_placeholder_mol : Chem.Mol
+ The N-terminal placeholder molecule.
+ n_term_mod : Optional[str]
+ The N-terminal modification to apply.
+
+ Returns
+ -------
+ Chem.Mol
+ The modified molecule.
+ """
+ if n_term_mod:
+ n_mod_smiles = self.n_term_modifications[n_term_mod]
+ n_mod_mol = Chem.MolFromSmiles(n_mod_smiles)
+ mol = ReplaceSubstructs(mol, n_term_placeholder_mol, n_mod_mol)[0]
+
+ if "Dimethyl" in n_term_mod:
+ return ReplaceSubstructs(mol, n_term_placeholder_mol, n_mod_mol)[0]
+
+ # replacing all leftover N-terminal placeholders with hydrogen atoms
+ return ReplaceSubstructs(
+ mol,
+ n_term_placeholder_mol,
+ Chem.MolFromSmiles("[H]", sanitize=False),
+ replaceAll=True,
+ )[0]
+
+ def _apply_c_terminal_modification(
+ self, mol: Chem.Mol, c_term_placeholder_mol: Chem.Mol, c_term_mod: Optional[str]
+ ) -> Chem.Mol:
+ """
+ Apply C-terminal modification to the molecule.
+
+ Parameters
+ ----------
+ mol : Chem.Mol
+ The molecule to modify.
+ c_term_placeholder_mol : Chem.Mol
+ The C-terminal placeholder molecule.
+ c_term_mod : Optional[str]
+ The C-terminal modification to apply.
+
+ Returns
+ -------
+ Chem.Mol
+ The modified molecule.
+ """
+ if c_term_mod:
+ c_mod_smiles = self.c_term_modifications[c_term_mod]
+ c_mod_mol = Chem.MolFromSmiles(c_mod_smiles)
+ return ReplaceSubstructs(mol, c_term_placeholder_mol, c_mod_mol)[0]
+ return ReplaceSubstructs(
+ mol, c_term_placeholder_mol, Chem.MolFromSmiles("O", sanitize=False)
+ )[0]
diff --git a/docs/nbs/adding_smiles.ipynb b/docs/nbs/adding_smiles.ipynb
new file mode 100644
index 00000000..e0899474
--- /dev/null
+++ b/docs/nbs/adding_smiles.ipynb
@@ -0,0 +1,885 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Post-Translational Modification (PTM) SMILES Notebook\n",
+ "\n",
+ "This notebook is designed to update a Unimod table with post-translational modifications (PTMs) by adding SMILES formulas for some of the PTMs. We'll also calculate their molecular weights to validate the correctness of the structures against the ground truth data in the table.\n",
+ "\n",
+ "## Setup and Imports\n",
+ "\n",
+ "First, we import the necessary libraries and modules:\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import os\n",
+ "import pandas as pd\n",
+ "from rdkit import Chem\n",
+ "\n",
+ "from alphabase.constants._const import CONST_FILE_FOLDER\n",
+ "from alphabase.constants.atom import ChemicalCompositonFormula\n",
+ "from alphabase.constants.aa import aa_formula\n",
+ "\n",
+ "from alphabase.constants.modification import MOD_DF, add_new_modifications\n",
+ "\n",
+ "from alphabase.smiles.smiles import AminoAcidModifier\n",
+ "\n",
+ "aa_modifier = AminoAcidModifier()\n",
+ "modify_amino_acid = aa_modifier.modify_amino_acid\n",
+ "n_term_modifications_smi = aa_modifier.n_term_modifications\n",
+ "c_term_modifications_smi = aa_modifier.c_term_modifications\n",
+ "ptm_dict_smi = aa_modifier.ptm_dict"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We then define dictionaries with SMILES structures for N-terminal modifications, C-terminal modifications, and PTMs:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "defaultdict(int, {'O': -1, 'H': 2, 'C': 1})"
+ ]
+ },
+ "execution_count": 2,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(ChemicalCompositonFormula(\"C(1)H(4)\") - ChemicalCompositonFormula(\"H(2)O(1)\")).elements"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "ChemicalCompositonFormula('C(1)H(2)O(-1)')"
+ ]
+ },
+ "execution_count": 3,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "ChemicalCompositonFormula(\"C(1)H(2)O(-1)\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "n_term_modifications = {'mTRAQ@Any_N-term': 'C(=O)CN1CCN(CC1)C',\n",
+ " 'mTRAQ:13C(3)15N(1)@Any_N-term': 'C(=O)[13C]([H])([H])[15N]1[13C]([H])([H])[13C]([H])([H])N(CC1)C',\n",
+ " 'mTRAQ:13C(6)15N(2)@Any_N-term': 'C(=O)[13C]([H])([H])[15N]1[13C]([H])([H])[13C]([H])([H])[15N]([13C]([H])([H])[13C]1([H])([H]))[13C]([H])([H])([H])',\n",
+ " 'Acetyl@Any_N-term': 'C(=O)C',\n",
+ " 'Propionyl@Any_N-term': 'C(=O)CC',\n",
+ " 'Biotin@Any_N-term': 'C(=O)CCCCC1SCC2NC(=O)NC21',\n",
+ " 'Carbamidomethyl@Any_N-term': 'C(=O)NC',\n",
+ " 'Carbamyl@Any_N-term': 'C(=O)N',\n",
+ " 'Propionamide@Any_N-term': 'CCC(N)=O',\n",
+ " 'Pyridylacetyl@Any_N-term': 'C(=O)Cc1ccccn1',\n",
+ " 'Methyl@Any_N-term': 'C',\n",
+ " 'Dimethyl@Any_N-term': 'C',\n",
+ " 'Dimethyl:2H(6)13C(2)@Any_N-term': '[13C]([2H])([2H])([2H])',\n",
+ " 'Dimethyl:2H(4)@Any_N-term': 'C([2H])([2H])([1H])',\n",
+ " 'Dimethyl:2H(4)13C(2)@Any_N-term': '[13C]([2H])([2H])([1H])'}\n",
+ "\n",
+ "\n",
+ "c_term_modifications = {'Methyl@Any_C-term': 'OC',\n",
+ " 'Ethyl@Any_C-term': 'OCC',\n",
+ " 'Propyl@Any_C-term': 'OCCC',\n",
+ " 'Amidated@Any_C-term': 'N',\n",
+ " 'Cation:Na@Any_C-term': 'O[Na]',\n",
+ " 'Cation:K@Any_C-term': 'O[K]',\n",
+ " 'Cation:Cu[I]@Any_C-term': 'O[Cu]',\n",
+ " 'Cation:Li@Any_C-term': 'O[Li]'}\n",
+ "\n",
+ "\n",
+ "ptm_dict = {'Carbamidomethyl@C': 'C(C(C(=O)[Rn])N([Xe])([Xe]))SCC(=O)N',\n",
+ " 'Oxidation@M': 'O=C([Rn])C(N([Xe])([Xe]))CCS(=O)C',\n",
+ " 'GlyGly@K': 'NCC(=O)NCC(=O)NCCCC[C@H](N([Xe])([Xe]))C([Rn])=O',\n",
+ " 'Deamidated@N': 'C([C@@H](C(=O)[Rn])N([Xe])([Xe]))C(=O)O',\n",
+ " 'Propionyl@K': 'CCC(=O)NCCCCC(C(=O)[Rn])N([Xe])([Xe])',\n",
+ " 'Deamidated@Q': 'C(CC(=O)O)[C@@H](C(=O)[Rn])N([Xe])([Xe])',\n",
+ " 'Gln->pyro-Glu@Q^Any_N-term': 'O=C([Rn])[C@H]1N([Xe])C(=O)CC1',\n",
+ " 'Glu->pyro-Glu@E^Any_N-term': 'O=C([Rn])[C@H]1N([Xe])C(=O)CC1',\n",
+ " 'Phospho@S': 'O=P(O)(O)OC[C@@H](C(=O)[Rn])N([Xe])([Xe])',\n",
+ " 'Nitro@Y': 'O=[N+]([O-])c1cc(ccc1O)C[C@@H](C(=O)[Rn])N([Xe])([Xe])',\n",
+ " 'Acetyl@K': 'CC(=O)NCCCC[C@H](N([Xe])([Xe]))C(=O)[Rn]',\n",
+ " 'Dimethyl@K': 'CN(C)CCCC[C@H](N([Xe])([Xe]))C(=O)[Rn]',\n",
+ " 'mTRAQ@K': '[H]N(CCCC[C@H](N([Xe])([Xe]))C(=O)[Rn])C(=O)CN1CCN(C)CC1',\n",
+ " 'mTRAQ:13C(3)15N(1)@K': '[H]N(CCCC[C@H](N([Xe])([Xe]))C(=O)[Rn])C(=O)[13CH2][15N]1CCN(C)[13CH2][13CH2]1',\n",
+ " 'mTRAQ:13C(6)15N(2)@K': '[H]N(CCCC[C@H](N([Xe])([Xe]))C(=O)[Rn])C(=O)[13CH2][15N]1[13CH2][13CH2][15N]([13CH3])[13CH2][13CH2]1',\n",
+ " 'Pyridylethyl@C': 'C1=CN=CC=C1CCSCC(C(=O)[Rn])N([Xe])([Xe])',\n",
+ " 'Butyryl@K': 'CCCC(=O)NCCCCC(C(=O)[Rn])N([Xe])([Xe])',\n",
+ " 'Phospho@T': 'CC(C(C(=O)[Rn])N([Xe])([Xe]))OP(=O)(O)O',\n",
+ " 'Methylthio@C': 'CSSC[C@H](N([Xe])([Xe]))C([Rn])=O',\n",
+ " 'Carbamidomethyl@M': 'CS(CCC(N([Xe])([Xe]))C([Rn])=O)=CC(N)=O',\n",
+ " 'Succinyl@K': 'C(CCN)CC(C(=O)[Rn])N([Xe])C(=O)CCC(=O)O',\n",
+ " 'Crotonyl@K': 'CC=CC(=O)NCCCCC(C(=O)[Rn])N([Xe])([Xe])',\n",
+ " 'Phospho@Y': 'C1=CC(=CC=C1CC(C(=O)[Rn])N([Xe])([Xe]))OP(=O)(O)O',\n",
+ " 'Malonyl@K': 'N([Xe])([Xe])[C@@H](CCCC(NC(=O)CC(=O)O))C(=O)[Rn]',\n",
+ " 'Met->Hse@M^Any_C-term': 'N([Xe])([Xe])[C@H](C(=O)[Rn])CCO',\n",
+ " 'Pro->(2S,4R)-4-fluoroproline@P': 'F[C@@H]1C[C@H](N([Xe])C1)C(=O)[Rn]',\n",
+ " 'Pro->(2S,4S)-4fluoroproline@P': 'F[C@H]1C[C@H](N([Xe])C1)C(=O)[Rn]',\n",
+ " 'Pro->(2S)-1,3-thiazolidine-2-carboxylic_acid@P': 'S1[C@H](N([Xe])CC1)C(=O)[Rn]',\n",
+ " 'Pro->(4R)-1,3-Thiazolidine-4-carboxylic_acid@P': 'S1CN([Xe])[C@@H](C1)C(=O)[Rn]',\n",
+ " 'Pro->(2S,4R)-4-hydroxyproline@P': 'O[C@@H]1C[C@H](N([Xe])C1)C(=O)[Rn]',\n",
+ " 'Pro->(DL)-pipecolic_acid@P': 'C1CCN([Xe])C(C1)C(=O)[Rn]',\n",
+ " 'Pro->3,4-Dehydro-L-proline@P': 'C1C=CC(N1([Xe]))C(=O)[Rn]',\n",
+ " 'Pro->(1S,3S,5S)-2-Azabicyclo[3.1.0]hexane-3-carboxylic_acid@P': '[C@H]12N([Xe])[C@@H](C[C@@H]2C1)C(=O)[Rn]',\n",
+ " 'Pro->(1R,3S,5R)-2-Azabicyclo[3.1.0]hexane-3-carboxylic_acid@P': '[C@@H]12N([Xe])[C@@H](C[C@H]2C1)C(=O)[Rn]',\n",
+ " 'Pro->(2S,3aS,7aS)-Octahydro-1H-indole-2-carboxylic_acid@P': 'N1([Xe])[C@@H](C[C@@H]2CCCC[C@H]12)C(=O)[Rn]',\n",
+ " 'Pro->(DL)-5-trifluoromethylproline@P': 'FC(C1CCC(N1([Xe]))C(=O)[Rn])(F)F'}"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "These dictionaries contain the SMILES representations of various modifications.\n",
+ "\n",
+ "## Updating Modification Dictionaries\n",
+ "\n",
+ "We overwrite the existing modification dictionaries with our new SMILES representations (this script can be used when the `modification.tsv` is still not updated, thus regular `n_term_modifications` in `alphabase.smiles.AminoAcidModifier` would be empty):"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "for i in n_term_modifications:\n",
+ " n_term_modifications_smi[i] = n_term_modifications[i]\n",
+ "\n",
+ "for i in c_term_modifications:\n",
+ " c_term_modifications_smi[i] = c_term_modifications[i]\n",
+ "\n",
+ "for i in ptm_dict:\n",
+ " ptm_dict_smi[i] = ptm_dict[i]"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Validation of Amino Acid Formulas\n",
+ "\n",
+ "Next, we validate the amino acid formulas by comparing their chemical compositions:\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "for aa in aa_formula.index:\n",
+ " aa_row = aa_formula.loc[aa]\n",
+ " if pd.isna(aa_row[\"smiles\"]):\n",
+ " continue\n",
+ " aa_smiles = modify_amino_acid(aa_row[\"smiles\"])\n",
+ " chem_composition = ChemicalCompositonFormula.from_smiles(aa_smiles)\n",
+ " assert str(chem_composition - ChemicalCompositonFormula(aa_row[\"formula\"]) - ChemicalCompositonFormula(\"H(2)O(1)\")) == \"\"\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "This step ensures that the chemical compositions derived from SMILES match the known formulas for each amino acid.\n",
+ "\n",
+ "## Processing PTMs\n",
+ "\n",
+ "We then process the PTMs, calculating their compositions and creating a dictionary of modifications to add:\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "{'Carbamidomethyl@C': {'composition': 'C(2)H(3)N(1)O(1)',\n",
+ " 'smiles': 'C(C(C(=O)[Rn])N([Xe])([Xe]))SCC(=O)N'},\n",
+ " 'Oxidation@M': {'composition': 'O(1)',\n",
+ " 'smiles': 'O=C([Rn])C(N([Xe])([Xe]))CCS(=O)C'},\n",
+ " 'GlyGly@K': {'composition': 'C(4)H(6)N(2)O(2)',\n",
+ " 'smiles': 'NCC(=O)NCC(=O)NCCCC[C@H](N([Xe])([Xe]))C([Rn])=O'},\n",
+ " 'Deamidated@N': {'composition': 'H(-1)N(-1)O(1)',\n",
+ " 'smiles': 'C([C@@H](C(=O)[Rn])N([Xe])([Xe]))C(=O)O'},\n",
+ " 'Propionyl@K': {'composition': 'C(3)H(4)O(1)',\n",
+ " 'smiles': 'CCC(=O)NCCCCC(C(=O)[Rn])N([Xe])([Xe])'},\n",
+ " 'Deamidated@Q': {'composition': 'H(-1)N(-1)O(1)',\n",
+ " 'smiles': 'C(CC(=O)O)[C@@H](C(=O)[Rn])N([Xe])([Xe])'},\n",
+ " 'Gln->pyro-Glu@Q^Any_N-term': {'composition': 'H(-3)N(-1)',\n",
+ " 'smiles': 'O=C([Rn])[C@H]1N([Xe])C(=O)CC1'},\n",
+ " 'Glu->pyro-Glu@E^Any_N-term': {'composition': 'H(-2)O(-1)',\n",
+ " 'smiles': 'O=C([Rn])[C@H]1N([Xe])C(=O)CC1'},\n",
+ " 'Phospho@S': {'composition': 'H(1)O(3)P(1)',\n",
+ " 'smiles': 'O=P(O)(O)OC[C@@H](C(=O)[Rn])N([Xe])([Xe])'},\n",
+ " 'Nitro@Y': {'composition': 'H(-1)N(1)O(2)',\n",
+ " 'smiles': 'O=[N+]([O-])c1cc(ccc1O)C[C@@H](C(=O)[Rn])N([Xe])([Xe])'},\n",
+ " 'Acetyl@K': {'composition': 'C(2)H(2)O(1)',\n",
+ " 'smiles': 'CC(=O)NCCCC[C@H](N([Xe])([Xe]))C(=O)[Rn]'},\n",
+ " 'Dimethyl@K': {'composition': 'C(2)H(4)',\n",
+ " 'smiles': 'CN(C)CCCC[C@H](N([Xe])([Xe]))C(=O)[Rn]'},\n",
+ " 'mTRAQ@K': {'composition': 'C(7)H(12)N(2)O(1)',\n",
+ " 'smiles': '[H]N(CCCC[C@H](N([Xe])([Xe]))C(=O)[Rn])C(=O)CN1CCN(C)CC1'},\n",
+ " 'mTRAQ:13C(3)15N(1)@K': {'composition': '13C(3)15N(1)C(4)H(12)N(1)O(1)',\n",
+ " 'smiles': '[H]N(CCCC[C@H](N([Xe])([Xe]))C(=O)[Rn])C(=O)[13CH2][15N]1CCN(C)[13CH2][13CH2]1'},\n",
+ " 'mTRAQ:13C(6)15N(2)@K': {'composition': '13C(6)15N(2)C(1)H(12)O(1)',\n",
+ " 'smiles': '[H]N(CCCC[C@H](N([Xe])([Xe]))C(=O)[Rn])C(=O)[13CH2][15N]1[13CH2][13CH2][15N]([13CH3])[13CH2][13CH2]1'},\n",
+ " 'Pyridylethyl@C': {'composition': 'C(7)H(7)N(1)',\n",
+ " 'smiles': 'C1=CN=CC=C1CCSCC(C(=O)[Rn])N([Xe])([Xe])'},\n",
+ " 'Butyryl@K': {'composition': 'C(4)H(6)O(1)',\n",
+ " 'smiles': 'CCCC(=O)NCCCCC(C(=O)[Rn])N([Xe])([Xe])'},\n",
+ " 'Phospho@T': {'composition': 'H(1)O(3)P(1)',\n",
+ " 'smiles': 'CC(C(C(=O)[Rn])N([Xe])([Xe]))OP(=O)(O)O'},\n",
+ " 'Methylthio@C': {'composition': 'C(1)H(2)S(1)',\n",
+ " 'smiles': 'CSSC[C@H](N([Xe])([Xe]))C([Rn])=O'},\n",
+ " 'Carbamidomethyl@M': {'composition': 'C(2)H(3)N(1)O(1)',\n",
+ " 'smiles': 'CS(CCC(N([Xe])([Xe]))C([Rn])=O)=CC(N)=O'},\n",
+ " 'Succinyl@K': {'composition': 'C(4)H(4)O(3)',\n",
+ " 'smiles': 'C(CCN)CC(C(=O)[Rn])N([Xe])C(=O)CCC(=O)O'},\n",
+ " 'Crotonyl@K': {'composition': 'C(4)H(4)O(1)',\n",
+ " 'smiles': 'CC=CC(=O)NCCCCC(C(=O)[Rn])N([Xe])([Xe])'},\n",
+ " 'Phospho@Y': {'composition': 'H(1)O(3)P(1)',\n",
+ " 'smiles': 'C1=CC(=CC=C1CC(C(=O)[Rn])N([Xe])([Xe]))OP(=O)(O)O'},\n",
+ " 'Malonyl@K': {'composition': 'C(3)H(2)O(3)',\n",
+ " 'smiles': 'N([Xe])([Xe])[C@@H](CCCC(NC(=O)CC(=O)O))C(=O)[Rn]'},\n",
+ " 'Met->Hse@M^Any_C-term': {'composition': 'C(-1)H(-2)O(1)S(-1)',\n",
+ " 'smiles': 'N([Xe])([Xe])[C@H](C(=O)[Rn])CCO'},\n",
+ " 'Pro->(2S,4R)-4-fluoroproline@P': {'composition': 'F(1)H(-1)',\n",
+ " 'smiles': 'F[C@@H]1C[C@H](N([Xe])C1)C(=O)[Rn]'},\n",
+ " 'Pro->(2S,4S)-4fluoroproline@P': {'composition': 'F(1)H(-1)',\n",
+ " 'smiles': 'F[C@H]1C[C@H](N([Xe])C1)C(=O)[Rn]'},\n",
+ " 'Pro->(2S)-1,3-thiazolidine-2-carboxylic_acid@P': {'composition': 'C(-1)H(-2)S(1)',\n",
+ " 'smiles': 'S1[C@H](N([Xe])CC1)C(=O)[Rn]'},\n",
+ " 'Pro->(4R)-1,3-Thiazolidine-4-carboxylic_acid@P': {'composition': 'C(-1)H(-2)S(1)',\n",
+ " 'smiles': 'S1CN([Xe])[C@@H](C1)C(=O)[Rn]'},\n",
+ " 'Pro->(2S,4R)-4-hydroxyproline@P': {'composition': 'O(1)',\n",
+ " 'smiles': 'O[C@@H]1C[C@H](N([Xe])C1)C(=O)[Rn]'},\n",
+ " 'Pro->(DL)-pipecolic_acid@P': {'composition': 'C(1)H(2)',\n",
+ " 'smiles': 'C1CCN([Xe])C(C1)C(=O)[Rn]'},\n",
+ " 'Pro->3,4-Dehydro-L-proline@P': {'composition': 'H(-2)',\n",
+ " 'smiles': 'C1C=CC(N1([Xe]))C(=O)[Rn]'},\n",
+ " 'Pro->(1S,3S,5S)-2-Azabicyclo[3.1.0]hexane-3-carboxylic_acid@P': {'composition': 'C(1)',\n",
+ " 'smiles': '[C@H]12N([Xe])[C@@H](C[C@@H]2C1)C(=O)[Rn]'},\n",
+ " 'Pro->(1R,3S,5R)-2-Azabicyclo[3.1.0]hexane-3-carboxylic_acid@P': {'composition': 'C(1)',\n",
+ " 'smiles': '[C@@H]12N([Xe])[C@@H](C[C@H]2C1)C(=O)[Rn]'},\n",
+ " 'Pro->(2S,3aS,7aS)-Octahydro-1H-indole-2-carboxylic_acid@P': {'composition': 'C(4)H(6)',\n",
+ " 'smiles': 'N1([Xe])[C@@H](C[C@@H]2CCCC[C@H]12)C(=O)[Rn]'},\n",
+ " 'Pro->(DL)-5-trifluoromethylproline@P': {'composition': 'C(1)F(3)H(-1)',\n",
+ " 'smiles': 'FC(C1CCC(N1([Xe]))C(=O)[Rn])(F)F'}}"
+ ]
+ },
+ "execution_count": 7,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "ptms_to_add = {}\n",
+ "\n",
+ "for ptm in ptm_dict:\n",
+ " smi = modify_amino_acid(ptm_dict[ptm])\n",
+ " ptm_formula = ChemicalCompositonFormula.from_smiles(smi)\n",
+ " original_aa = ptm.split(\"@\")[1].split(\"^\")[0]\n",
+ " if original_aa.startswith(\"Any\"):\n",
+ " original_aa = \"A\"\n",
+ " original_aa_brutto_formula = aa_formula.loc[original_aa, \"formula\"]\n",
+ " ptms_to_add[ptm] = {\"composition\": str(ptm_formula - ChemicalCompositonFormula(original_aa_brutto_formula) - ChemicalCompositonFormula(\"H(2)O(1)\")),\n",
+ " \"smiles\": ptm_dict[ptm]}\n",
+ "ptms_to_add"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We update the dataframe with those modifications:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "add_new_modifications(ptms_to_add)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Processing N-terminal Modifications\n",
+ "\n",
+ "Similar to PTMs, we process N-terminal modifications:\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "{'mTRAQ@Any_N-term': {'composition': 'C(7)H(12)N(2)O(1)',\n",
+ " 'smiles': 'C(=O)CN1CCN(CC1)C'},\n",
+ " 'mTRAQ@Protein_N-term': {'composition': 'C(7)H(12)N(2)O(1)',\n",
+ " 'smiles': 'C(=O)CN1CCN(CC1)C'},\n",
+ " 'mTRAQ:13C(3)15N(1)@Any_N-term': {'composition': '13C(3)15N(1)C(4)H(12)N(1)O(1)',\n",
+ " 'smiles': 'C(=O)[13C]([H])([H])[15N]1[13C]([H])([H])[13C]([H])([H])N(CC1)C'},\n",
+ " 'mTRAQ:13C(3)15N(1)@Protein_N-term': {'composition': '13C(3)15N(1)C(4)H(12)N(1)O(1)',\n",
+ " 'smiles': 'C(=O)[13C]([H])([H])[15N]1[13C]([H])([H])[13C]([H])([H])N(CC1)C'},\n",
+ " 'mTRAQ:13C(6)15N(2)@Any_N-term': {'composition': '13C(6)15N(2)C(1)H(12)O(1)',\n",
+ " 'smiles': 'C(=O)[13C]([H])([H])[15N]1[13C]([H])([H])[13C]([H])([H])[15N]([13C]([H])([H])[13C]1([H])([H]))[13C]([H])([H])([H])'},\n",
+ " 'mTRAQ:13C(6)15N(2)@Protein_N-term': {'composition': '13C(6)15N(2)C(1)H(12)O(1)',\n",
+ " 'smiles': 'C(=O)[13C]([H])([H])[15N]1[13C]([H])([H])[13C]([H])([H])[15N]([13C]([H])([H])[13C]1([H])([H]))[13C]([H])([H])([H])'},\n",
+ " 'Acetyl@Any_N-term': {'composition': 'C(2)H(2)O(1)', 'smiles': 'C(=O)C'},\n",
+ " 'Acetyl@Protein_N-term': {'composition': 'C(2)H(2)O(1)', 'smiles': 'C(=O)C'},\n",
+ " 'Propionyl@Any_N-term': {'composition': 'C(3)H(4)O(1)', 'smiles': 'C(=O)CC'},\n",
+ " 'Propionyl@Protein_N-term': {'composition': 'C(3)H(4)O(1)',\n",
+ " 'smiles': 'C(=O)CC'},\n",
+ " 'Biotin@Any_N-term': {'composition': 'C(10)H(14)N(2)O(2)S(1)',\n",
+ " 'smiles': 'C(=O)CCCCC1SCC2NC(=O)NC21'},\n",
+ " 'Biotin@Protein_N-term': {'composition': 'C(10)H(14)N(2)O(2)S(1)',\n",
+ " 'smiles': 'C(=O)CCCCC1SCC2NC(=O)NC21'},\n",
+ " 'Carbamidomethyl@Any_N-term': {'composition': 'C(2)H(3)N(1)O(1)',\n",
+ " 'smiles': 'C(=O)NC'},\n",
+ " 'Carbamidomethyl@Protein_N-term': {'composition': 'C(2)H(3)N(1)O(1)',\n",
+ " 'smiles': 'C(=O)NC'},\n",
+ " 'Carbamyl@Any_N-term': {'composition': 'C(1)H(1)N(1)O(1)',\n",
+ " 'smiles': 'C(=O)N'},\n",
+ " 'Carbamyl@Protein_N-term': {'composition': 'C(1)H(1)N(1)O(1)',\n",
+ " 'smiles': 'C(=O)N'},\n",
+ " 'Propionamide@Any_N-term': {'composition': 'C(3)H(5)N(1)O(1)',\n",
+ " 'smiles': 'CCC(N)=O'},\n",
+ " 'Propionamide@Protein_N-term': {'composition': 'C(3)H(5)N(1)O(1)',\n",
+ " 'smiles': 'CCC(N)=O'},\n",
+ " 'Pyridylacetyl@Any_N-term': {'composition': 'C(7)H(5)N(1)O(1)',\n",
+ " 'smiles': 'C(=O)Cc1ccccn1'},\n",
+ " 'Pyridylacetyl@Protein_N-term': {'composition': 'C(7)H(5)N(1)O(1)',\n",
+ " 'smiles': 'C(=O)Cc1ccccn1'},\n",
+ " 'Methyl@Any_N-term': {'composition': 'C(1)H(2)', 'smiles': 'C'},\n",
+ " 'Methyl@Protein_N-term': {'composition': 'C(1)H(2)', 'smiles': 'C'},\n",
+ " 'Dimethyl@Any_N-term': {'composition': 'C(2)H(4)', 'smiles': 'C'},\n",
+ " 'Dimethyl@Protein_N-term': {'composition': 'C(2)H(4)', 'smiles': 'C'},\n",
+ " 'Dimethyl:2H(6)13C(2)@Any_N-term': {'composition': '13C(2)2H(6)H(-2)',\n",
+ " 'smiles': '[13C]([2H])([2H])([2H])'},\n",
+ " 'Dimethyl:2H(6)13C(2)@Protein_N-term': {'composition': '13C(2)2H(6)H(-2)',\n",
+ " 'smiles': '[13C]([2H])([2H])([2H])'},\n",
+ " 'Dimethyl:2H(4)@Any_N-term': {'composition': '2H(4)C(2)',\n",
+ " 'smiles': 'C([2H])([2H])([1H])'},\n",
+ " 'Dimethyl:2H(4)@Protein_N-term': {'composition': '2H(4)C(2)',\n",
+ " 'smiles': 'C([2H])([2H])([1H])'},\n",
+ " 'Dimethyl:2H(4)13C(2)@Any_N-term': {'composition': '13C(2)2H(4)',\n",
+ " 'smiles': '[13C]([2H])([2H])([1H])'},\n",
+ " 'Dimethyl:2H(4)13C(2)@Protein_N-term': {'composition': '13C(2)2H(4)',\n",
+ " 'smiles': '[13C]([2H])([2H])([1H])'}}"
+ ]
+ },
+ "execution_count": 9,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "nterms_to_add = {}\n",
+ "\n",
+ "\n",
+ "for ptm in n_term_modifications:\n",
+ " original_mod = ptm.split(\"@\")[0]\n",
+ " smi = modify_amino_acid(aa_formula.loc[\"A\", \"smiles\"], n_term_mod=ptm)\n",
+ " ptm_formula = ChemicalCompositonFormula.from_smiles(smi)\n",
+ " original_aa_brutto_formula = aa_formula.loc[\"A\", \"formula\"]\n",
+ " suffixes = [\"Any_N-term\", \"Protein_N-term\"]\n",
+ " for suffix in suffixes:\n",
+ " nterms_to_add[original_mod + \"@\" + suffix] = {\"composition\": str(ptm_formula - ChemicalCompositonFormula(original_aa_brutto_formula) - ChemicalCompositonFormula(\"H(2)O(1)\")),\n",
+ " \"smiles\": n_term_modifications[ptm]}\n",
+ "nterms_to_add"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "add_new_modifications(nterms_to_add)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Processing C-terminal Modifications\n",
+ "\n",
+ "We also process C-terminal modifications in a similar manner:\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "{'Methyl@Any_C-term': {'composition': 'C(1)H(2)', 'smiles': 'OC'},\n",
+ " 'Methyl@Protein_C-term': {'composition': 'C(1)H(2)', 'smiles': 'OC'},\n",
+ " 'Ethyl@Any_C-term': {'composition': 'C(2)H(4)', 'smiles': 'OCC'},\n",
+ " 'Ethyl@Protein_C-term': {'composition': 'C(2)H(4)', 'smiles': 'OCC'},\n",
+ " 'Propyl@Any_C-term': {'composition': 'C(3)H(6)', 'smiles': 'OCCC'},\n",
+ " 'Propyl@Protein_C-term': {'composition': 'C(3)H(6)', 'smiles': 'OCCC'},\n",
+ " 'Amidated@Any_C-term': {'composition': 'H(1)N(1)O(-1)', 'smiles': 'N'},\n",
+ " 'Amidated@Protein_C-term': {'composition': 'H(1)N(1)O(-1)', 'smiles': 'N'},\n",
+ " 'Cation:Na@Any_C-term': {'composition': 'H(-1)Na(1)', 'smiles': 'O[Na]'},\n",
+ " 'Cation:Na@Protein_C-term': {'composition': 'H(-1)Na(1)', 'smiles': 'O[Na]'},\n",
+ " 'Cation:K@Any_C-term': {'composition': 'H(-1)K(1)', 'smiles': 'O[K]'},\n",
+ " 'Cation:K@Protein_C-term': {'composition': 'H(-1)K(1)', 'smiles': 'O[K]'},\n",
+ " 'Cation:Cu[I]@Any_C-term': {'composition': 'Cu(1)H(-1)', 'smiles': 'O[Cu]'},\n",
+ " 'Cation:Cu[I]@Protein_C-term': {'composition': 'Cu(1)H(-1)',\n",
+ " 'smiles': 'O[Cu]'},\n",
+ " 'Cation:Li@Any_C-term': {'composition': 'H(-1)Li(1)', 'smiles': 'O[Li]'},\n",
+ " 'Cation:Li@Protein_C-term': {'composition': 'H(-1)Li(1)', 'smiles': 'O[Li]'}}"
+ ]
+ },
+ "execution_count": 11,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "cterms_to_add = {}\n",
+ "\n",
+ "\n",
+ "for ptm in c_term_modifications:\n",
+ " original_mod = ptm.split(\"@\")[0]\n",
+ " smi = modify_amino_acid(aa_formula.loc[\"A\", \"smiles\"], c_term_mod=ptm)\n",
+ " ptm_formula = ChemicalCompositonFormula.from_smiles(smi)\n",
+ " original_aa_brutto_formula = aa_formula.loc[\"A\", \"formula\"]\n",
+ " suffixes = [\"Any_C-term\", \"Protein_C-term\"]\n",
+ " for suffix in suffixes:\n",
+ " composition = str(ptm_formula - ChemicalCompositonFormula(original_aa_brutto_formula) - ChemicalCompositonFormula(\"H(2)O(1)\"))\n",
+ " cterms_to_add[original_mod + \"@\" + suffix] = {\"composition\": composition,\n",
+ " \"smiles\": c_term_modifications[ptm]}\n",
+ "cterms_to_add"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "add_new_modifications(cterms_to_add)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Final Steps\n",
+ "\n",
+ "Finally, we examine the updated modification database:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "
\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " mod_name | \n",
+ " unimod_mass | \n",
+ " unimod_avge_mass | \n",
+ " composition | \n",
+ " unimod_modloss | \n",
+ " modloss_composition | \n",
+ " classification | \n",
+ " unimod_id | \n",
+ " smiles | \n",
+ " modloss_importance | \n",
+ " mass | \n",
+ " modloss_original | \n",
+ " modloss | \n",
+ "
\n",
+ " \n",
+ " mod_name | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " Acetyl@T | \n",
+ " Acetyl@T | \n",
+ " 42.010565 | \n",
+ " 42.0367 | \n",
+ " H(2)C(2)O(1) | \n",
+ " 0.0 | \n",
+ " | \n",
+ " Post-translational | \n",
+ " 1 | \n",
+ " | \n",
+ " 0.0 | \n",
+ " 42.010565 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ "
\n",
+ " \n",
+ " Acetyl@Protein_N-term | \n",
+ " Acetyl@Protein_N-term | \n",
+ " 42.010565 | \n",
+ " 42.0367 | \n",
+ " H(2)C(2)O(1) | \n",
+ " 0.0 | \n",
+ " | \n",
+ " Post-translational | \n",
+ " 1 | \n",
+ " C(=O)C | \n",
+ " 0.0 | \n",
+ " 42.010565 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ "
\n",
+ " \n",
+ " Acetyl@S | \n",
+ " Acetyl@S | \n",
+ " 42.010565 | \n",
+ " 42.0367 | \n",
+ " H(2)C(2)O(1) | \n",
+ " 0.0 | \n",
+ " | \n",
+ " Post-translational | \n",
+ " 1 | \n",
+ " | \n",
+ " 0.0 | \n",
+ " 42.010565 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ "
\n",
+ " \n",
+ " Acetyl@C | \n",
+ " Acetyl@C | \n",
+ " 42.010565 | \n",
+ " 42.0367 | \n",
+ " H(2)C(2)O(1) | \n",
+ " 0.0 | \n",
+ " | \n",
+ " Post-translational | \n",
+ " 1 | \n",
+ " | \n",
+ " 0.0 | \n",
+ " 42.010565 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ "
\n",
+ " \n",
+ " Acetyl@Any_N-term | \n",
+ " Acetyl@Any_N-term | \n",
+ " 42.010565 | \n",
+ " 42.0367 | \n",
+ " H(2)C(2)O(1) | \n",
+ " 0.0 | \n",
+ " | \n",
+ " Multiple | \n",
+ " 1 | \n",
+ " C(=O)C | \n",
+ " 0.0 | \n",
+ " 42.010565 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ "
\n",
+ " \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ "
\n",
+ " \n",
+ " Ethyl@Protein_C-term | \n",
+ " Ethyl@Protein_C-term | \n",
+ " 0.000000 | \n",
+ " 0.0000 | \n",
+ " C(2)H(4) | \n",
+ " 0.0 | \n",
+ " | \n",
+ " User-added | \n",
+ " 0 | \n",
+ " OCC | \n",
+ " 0.0 | \n",
+ " 28.031300 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ "
\n",
+ " \n",
+ " Cation:Na@Protein_C-term | \n",
+ " Cation:Na@Protein_C-term | \n",
+ " 0.000000 | \n",
+ " 0.0000 | \n",
+ " H(-1)Na(1) | \n",
+ " 0.0 | \n",
+ " | \n",
+ " User-added | \n",
+ " 0 | \n",
+ " O[Na] | \n",
+ " 0.0 | \n",
+ " 21.981944 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ "
\n",
+ " \n",
+ " Cation:K@Protein_C-term | \n",
+ " Cation:K@Protein_C-term | \n",
+ " 0.000000 | \n",
+ " 0.0000 | \n",
+ " H(-1)K(1) | \n",
+ " 0.0 | \n",
+ " | \n",
+ " User-added | \n",
+ " 0 | \n",
+ " O[K] | \n",
+ " 0.0 | \n",
+ " 37.955881 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ "
\n",
+ " \n",
+ " Cation:Cu[I]@Protein_C-term | \n",
+ " Cation:Cu[I]@Protein_C-term | \n",
+ " 0.000000 | \n",
+ " 0.0000 | \n",
+ " Cu(1)H(-1) | \n",
+ " 0.0 | \n",
+ " | \n",
+ " User-added | \n",
+ " 0 | \n",
+ " O[Cu] | \n",
+ " 0.0 | \n",
+ " 61.921773 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ "
\n",
+ " \n",
+ " Cation:Li@Protein_C-term | \n",
+ " Cation:Li@Protein_C-term | \n",
+ " 0.000000 | \n",
+ " 0.0000 | \n",
+ " H(-1)Li(1) | \n",
+ " 0.0 | \n",
+ " | \n",
+ " User-added | \n",
+ " 0 | \n",
+ " O[Li] | \n",
+ " 0.0 | \n",
+ " 6.008178 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
2796 rows × 13 columns
\n",
+ "
"
+ ],
+ "text/plain": [
+ " mod_name unimod_mass \\\n",
+ "mod_name \n",
+ "Acetyl@T Acetyl@T 42.010565 \n",
+ "Acetyl@Protein_N-term Acetyl@Protein_N-term 42.010565 \n",
+ "Acetyl@S Acetyl@S 42.010565 \n",
+ "Acetyl@C Acetyl@C 42.010565 \n",
+ "Acetyl@Any_N-term Acetyl@Any_N-term 42.010565 \n",
+ "... ... ... \n",
+ "Ethyl@Protein_C-term Ethyl@Protein_C-term 0.000000 \n",
+ "Cation:Na@Protein_C-term Cation:Na@Protein_C-term 0.000000 \n",
+ "Cation:K@Protein_C-term Cation:K@Protein_C-term 0.000000 \n",
+ "Cation:Cu[I]@Protein_C-term Cation:Cu[I]@Protein_C-term 0.000000 \n",
+ "Cation:Li@Protein_C-term Cation:Li@Protein_C-term 0.000000 \n",
+ "\n",
+ " unimod_avge_mass composition unimod_modloss \\\n",
+ "mod_name \n",
+ "Acetyl@T 42.0367 H(2)C(2)O(1) 0.0 \n",
+ "Acetyl@Protein_N-term 42.0367 H(2)C(2)O(1) 0.0 \n",
+ "Acetyl@S 42.0367 H(2)C(2)O(1) 0.0 \n",
+ "Acetyl@C 42.0367 H(2)C(2)O(1) 0.0 \n",
+ "Acetyl@Any_N-term 42.0367 H(2)C(2)O(1) 0.0 \n",
+ "... ... ... ... \n",
+ "Ethyl@Protein_C-term 0.0000 C(2)H(4) 0.0 \n",
+ "Cation:Na@Protein_C-term 0.0000 H(-1)Na(1) 0.0 \n",
+ "Cation:K@Protein_C-term 0.0000 H(-1)K(1) 0.0 \n",
+ "Cation:Cu[I]@Protein_C-term 0.0000 Cu(1)H(-1) 0.0 \n",
+ "Cation:Li@Protein_C-term 0.0000 H(-1)Li(1) 0.0 \n",
+ "\n",
+ " modloss_composition classification \\\n",
+ "mod_name \n",
+ "Acetyl@T Post-translational \n",
+ "Acetyl@Protein_N-term Post-translational \n",
+ "Acetyl@S Post-translational \n",
+ "Acetyl@C Post-translational \n",
+ "Acetyl@Any_N-term Multiple \n",
+ "... ... ... \n",
+ "Ethyl@Protein_C-term User-added \n",
+ "Cation:Na@Protein_C-term User-added \n",
+ "Cation:K@Protein_C-term User-added \n",
+ "Cation:Cu[I]@Protein_C-term User-added \n",
+ "Cation:Li@Protein_C-term User-added \n",
+ "\n",
+ " unimod_id smiles modloss_importance mass \\\n",
+ "mod_name \n",
+ "Acetyl@T 1 0.0 42.010565 \n",
+ "Acetyl@Protein_N-term 1 C(=O)C 0.0 42.010565 \n",
+ "Acetyl@S 1 0.0 42.010565 \n",
+ "Acetyl@C 1 0.0 42.010565 \n",
+ "Acetyl@Any_N-term 1 C(=O)C 0.0 42.010565 \n",
+ "... ... ... ... ... \n",
+ "Ethyl@Protein_C-term 0 OCC 0.0 28.031300 \n",
+ "Cation:Na@Protein_C-term 0 O[Na] 0.0 21.981944 \n",
+ "Cation:K@Protein_C-term 0 O[K] 0.0 37.955881 \n",
+ "Cation:Cu[I]@Protein_C-term 0 O[Cu] 0.0 61.921773 \n",
+ "Cation:Li@Protein_C-term 0 O[Li] 0.0 6.008178 \n",
+ "\n",
+ " modloss_original modloss \n",
+ "mod_name \n",
+ "Acetyl@T 0.0 0.0 \n",
+ "Acetyl@Protein_N-term 0.0 0.0 \n",
+ "Acetyl@S 0.0 0.0 \n",
+ "Acetyl@C 0.0 0.0 \n",
+ "Acetyl@Any_N-term 0.0 0.0 \n",
+ "... ... ... \n",
+ "Ethyl@Protein_C-term 0.0 0.0 \n",
+ "Cation:Na@Protein_C-term 0.0 0.0 \n",
+ "Cation:K@Protein_C-term 0.0 0.0 \n",
+ "Cation:Cu[I]@Protein_C-term 0.0 0.0 \n",
+ "Cation:Li@Protein_C-term 0.0 0.0 \n",
+ "\n",
+ "[2796 rows x 13 columns]"
+ ]
+ },
+ "execution_count": 13,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "MOD_DF[\"unimod_id\"] = MOD_DF[\"unimod_id\"].astype(int)\n",
+ "MOD_DF"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "This displays the final, updated database of modifications, including the newly added SMILES representations and calculated compositions.\n",
+ "If needed, we can save the updated database to a file:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# orig_df = pd.read_csv(os.path.join(CONST_FILE_FOLDER, \"modification.tsv\"), sep=\"\\t\", index_col=0)\n",
+ "# MOD_DF[[\"mod_name\", *orig_df.columns]].to_csv(os.path.join(CONST_FILE_FOLDER, \"modification.tsv\"), sep=\"\\t\", index=False)"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "alphabase",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.12.4"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/docs/nbs/tutorial_smiles.ipynb b/docs/nbs/tutorial_smiles.ipynb
new file mode 100644
index 00000000..c4dea1f8
--- /dev/null
+++ b/docs/nbs/tutorial_smiles.ipynb
@@ -0,0 +1,381 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Working with Amino Acids and Post-Translational Modifications using SMILES Notation\n",
+ "\n",
+ "This notebook introduces working with amino acids and post-translational modifications on the molecular level, using the SMILES (Simplified Molecular Input Line Entry System) notation. We'll explore how to represent amino acids, add modifications, and visualize the results using RDKit.\n",
+ "\n",
+ "## Introduction to SMILES and RDKit\n",
+ "\n",
+ "SMILES is a line notation for describing the structure of chemical species using short ASCII strings. For example, the SMILES for ethanol is `CCO`.\n",
+ "\n",
+ "RDKit is an open-source cheminformatics software that we'll use to work with and visualize molecular structures.\n",
+ "\n",
+ "Let's start by importing the necessary functions and data:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from rdkit import Chem\n",
+ "from rdkit.Chem import Draw\n",
+ "\n",
+ "from alphabase.smiles.smiles import AminoAcidModifier\n",
+ "\n",
+ "\n",
+ "aa_modifier = AminoAcidModifier()\n",
+ "modify_amino_acid = aa_modifier.modify_amino_acid\n",
+ "aa_smiles = aa_modifier.aa_smiles\n",
+ "n_term_modifications = aa_modifier.n_term_modifications\n",
+ "c_term_modifications = aa_modifier.c_term_modifications\n",
+ "ptm_dict = aa_modifier.ptm_dict"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Understanding the Data Structure\n",
+ "\n",
+ "Our data is organized into several dictionaries:\n",
+ "\n",
+ "1. `aa_smiles`: Contains SMILES representations of amino acids\n",
+ "2. `n_term_modifications`: Contains N-terminal modifications\n",
+ "3. `c_term_modifications`: Contains C-terminal modifications\n",
+ "4. `ptm_dict`: Contains post-translational modifications\n",
+ "\n",
+ "Let's examine the SMILES representation of an amino acid, such as Lysine (K):"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Lysine SMILES with dummy atoms: N([Xe])([Xe])[C@@]([H])(CCCCN)C(=O)[Rn]\n"
+ ]
+ },
+ {
+ "data": {
+ "image/jpeg": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAEsASwDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD3+iiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAriPHupy2WqeGrQ67Jo1leXUyXV1G8aEKsLMo3SKVHzADp3rt653xB4el1nX/Dt5i3e1064mluI5uSwaFkGBgg8kHnFAHL6L46fT4NUa7vLjX9Nh1OGxsL+3iQvcGRRlfk2q+1vlyo5zXW6L4lGq6peaXc6bd6bqFrGkzQXJjbdG5IVlZGYHlSCM8GuP0nw9Lqmj2qaDf2Vz4dg1yDUdPfe4aOJJSZocbeiuG2+xwcYFdfDotzH49vNdLxfZZtNhtFUE796SSMSRjGMOO/rQBvUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQB5/wDB7914MubL/nz1S7gx6YkJ/wDZq9Arz/4YfuLvxrZf88vEdzIB6K4UivQKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAPP/AAT+4+JPxBs+m26tJwP+ukJP9K9ArxifxHqekfGrxNJoGk/25C1pbm/ht5QJI9g2/L2LDdyvU/ga9A8OeP8Aw94nlNtaXZt9QTiSwvF8m4Q9xsPX8M0AdPRRRQAUUUUAFFFFABRRWZ4kvptL8L6vqFsVE9rZTTRlhkblQsMj6iqhBzkoLd6A9DToritc8bQWngR9SstTsH1MW8TiMSqx3sV3DbnPc8V2ta1cPUpRUpq12191v8xJphRXA+FfGmoajBqEGqrEl1tuLiwkRcLNFHI0ZH+8rLz7MDXUeGNQn1bwrpOo3RU3F1aRTSFRgbmUE4H41eIwdXD35+jt+f8Al94lJPY1qKKK5SgooooAKKKKACiuD174gSS6lJ4e8G2i6xrg4lkB/wBGs+26V+mR/dHPGOvFUILP4o+GF+1tqFj4ricB7izdBbSxt3ET4wR9fwAo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",
+ "image/png": "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",
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 2,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "print(\"Lysine SMILES with dummy atoms:\", aa_smiles[\"K\"])\n",
+ "mol = Chem.MolFromSmiles(aa_smiles[\"K\"])\n",
+ "Draw.MolToImage(mol)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "In this SMILES representation:\n",
+ "- `[Xe]` represents placeholder atoms for the N-terminus\n",
+ "- `[Rn]` represents placeholder atoms for the C-terminus\n",
+ "\n",
+ "These placeholders allow for easy addition of N- and C-terminal modifications."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## N-terminal Modifications\n",
+ "\n",
+ "Let's look at the available N-terminal modifications:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Available N-terminal modifications:\n",
+ "- Acetyl@Protein_N-term\n",
+ "- Acetyl@Any_N-term\n",
+ "- Biotin@Any_N-term\n",
+ "- Carbamidomethyl@Any_N-term\n",
+ "- Carbamyl@Any_N-term\n",
+ "- Carbamyl@Protein_N-term\n",
+ "- Propionamide@Any_N-term\n",
+ "- Pyridylacetyl@Any_N-term\n",
+ "- Methyl@Protein_N-term\n",
+ "- Methyl@Any_N-term\n",
+ "- Dimethyl@Protein_N-term\n",
+ "- Dimethyl@Any_N-term\n",
+ "- Propionyl@Protein_N-term\n",
+ "- Propionyl@Any_N-term\n",
+ "- Dimethyl:2H(6)13C(2)@Protein_N-term\n",
+ "- Dimethyl:2H(6)13C(2)@Any_N-term\n",
+ "- Dimethyl:2H(4)@Protein_N-term\n",
+ "- Dimethyl:2H(4)@Any_N-term\n",
+ "- Dimethyl:2H(4)13C(2)@Protein_N-term\n",
+ "- Dimethyl:2H(4)13C(2)@Any_N-term\n",
+ "- mTRAQ@Any_N-term\n",
+ "- mTRAQ:13C(3)15N(1)@Any_N-term\n",
+ "- mTRAQ:13C(6)15N(2)@Any_N-term\n",
+ "- mTRAQ@Protein_N-term\n",
+ "- mTRAQ:13C(3)15N(1)@Protein_N-term\n",
+ "- mTRAQ:13C(6)15N(2)@Protein_N-term\n",
+ "- Biotin@Protein_N-term\n",
+ "- Carbamidomethyl@Protein_N-term\n",
+ "- Propionamide@Protein_N-term\n",
+ "- Pyridylacetyl@Protein_N-term\n",
+ "\n",
+ "Biotin SMILES: C(=O)CCCCC1SCC2NC(=O)NC21\n"
+ ]
+ },
+ {
+ "data": {
+ "image/jpeg": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAEsASwDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD3+iiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKA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jwZ41vudU+Il0inrFp9hHBj6PyaAPQKy77xLoOl5F/rWnWpHUT3SIfyJrkv+FQ6Lc/8AIX1jxDq+fvLfak5B/BccVqWHwv8ABGm4Nv4Z09iOhnj87/0PNAGVrnxm8G6XYXL2mrRX96kbGGC3R3DsBwCwG0DPU5q98L9IFh4Oh1Ca4S61DWGOoXlyjBg8knOAR2UYH1BrqrfTLC0gaC2sbaGJhtaOOJVUj0IArzi5trv4S6jJqFhHLc+C7mTdd2iZZtNcnmSMd4z3HagD1GioLO8ttQs4byznSe2mQPHLGcqynoQanoAKKKKACmTRJPBJDIMpIpVh6gjBp9FAHBfByV/+FcWllKczafcT2kn1WVsfoRXe1578N2W08ReOtHB/1GstdhfRZ1DD/wBBNehUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAU2SNJY2jkRXjcFWVhkMD1BFOooA52w0u28HW4t9NtxHo+9mMKZPkliSSPbJ/CugR1kRXRgysMgjoaUgMCCAQeCD3rMWFtI8+VXzYhS4i7q3oPauJ8+Hm5bwer/u/8D8vTat/U1KKqG7lhhea5tiiqAR5bbyfbGKq51LUOg+wwHueZCP6Vc8XFWUU230tr872t8w5St4n8YaP4StEm1Sd98rbIbeGMySyt/dVR3+uBXLFvH3jfhF/4RDRn/ib95fyr9OkX8x713drpdpasHSIPKDnzZPmbPTOe34Vcram5uN6is/LX/ITt0MDwv4O0jwhbzppsczTXLBrm5uJTJLOwzgsx+p6YHNb9FFaCCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACo59vkPvjMqkYKAZJH0qSilJXTQGbYRsl24hSeO02fclzw2f4QeQMVpUUVlQpKlDlX9enkNu4UUUVsIKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAP/Z",
+ "image/png": "iVBORw0KGgoAAAANSUhEUgAAASwAAAEsCAIAAAD2HxkiAAAeb0lEQVR4nO3deVRUx54H8F+vguyI4AKKCDQ2LtGBGWeURFETTdScZxKNxqjAE8M4isYYiSGiJu5vFM2Eo+ZEE1+icY2jJDhBowb05YlGRRZbdhQXQGVfm675o5GlARXt7rrdfj/HkyPVza0fOX6pvnXvrRIxxggA+BHzLgDgRYcQAnCGEAJwhhACcIYQAnCGEAJwhhACcIYQAnCGEAJwhhACcIYQAnCGEAJwhhACcIYQAnCGEAJwhhACcIYQAnCGEAJwhhACcIYQAnCGEAJwhhACcIYQAnCGEAJwhhACcIYQAnCGEAJwhhACcIYQAnCGEAJwhhACcIYQAnCGEAJwhhACcIYQAnCGEAJwhhACcIYQAnCGEAJwhhACcIYQAnCGEAJwhhACcIYQAnCGEAJwhhACcIYQAnCGEAJwhhACcIYQAnCGEAJwhhACcIYQAnCGEAJwhhACcIYQAnCGEAJwhhACcIYQAnCGEAJwhhACcIYQAnCGEAJwhhACcIYQAnCGEAJwhhACcIYQAnCGEMILICWF5swhHx9yc6N//Vf64guqqOBdUzMRY4x3DQCGFB9PkyeTnx+9/Tb16EHXrtH27eTqSr//Tra2vIsjQgjBzNXWUr9+NGQIxcaSRNLYmJ5Ofn4UGkpbtnAtrhE+joJZ+7//ozt3KDKyOYFENGAAzZhBe/aQMEYghBDMWnIyEdHAgbrtgwfTgwd086bxK2oLIQSzVlZGXbqQnZ1uu4sLEVFpqfEragshBLNma0u1tVRSott+9y4RtRNOHhBCMGuDBxMRpaXptqemUrdu5OZm/IraQgjBrL36Krm40IYNreZgcnJo3z56/30SifhV1gyXKMDcxcbSlCk0dizNnUsuLnTlCq1ZQ/b2dP68QD6OIoTwArhwgdaupfPnqayM3NxoyhT65BOyt+ddViOEEMxaUhIxRgMHUteuvEvpEM4JwaytWkX/9m8UF9f4ZV0dff01nT/PtSZdUt4FABiSdl5UqWz88sYNCg0lT0/KyOBYlA6MhGC+qqooL49kMvL0bGzRyaQwIIRgvtLTSaMhb2+SyRpbEEIAo2obufR0IqIBA/jU0wGEEMyXNnItQ4iREMCodCKnVlNGBolE5OPDsai2EEIwXzohzMyk2lrq25esrTkW1RZCCGaqpoays0kqJS+vxhZBfhYlhBDMlkpFDQ3k6UldujS2IIQARtXR1ChCCGAkbUOIkRDAqLSRa7okqNGQSiXAqVFCCMFs6Yx72dlUXU2urgJ5hrAlhBDMUX09ZWWRWEwKRWOLUD+LEp6iALNUnpnZpXdvmVwusrRsbBJwCDESghk6kZLSJTf37Rb3iObfv1/bv3+Dry/HqjqCEIIZSk1NJSLvFiGccvq0RVbWH8KblSGEEMxSeno6EQ14FELGmEqlatkiKAghmKG0tDQiUj46A8zNza2oqOjZs6ejoyPXutqHEIK5UavVGRkZIpHI59GHT51MCg1CCOYmMzOztra2b9++1o+elkAIAYyqbeR0ThGFBiEEc6MNoW+LqxEYCQGMqu3U6PXr1wkhBDAanXHv1q1bpaWlTk5O3bt351pXhxBCMCsajUalUrWdGvUV5L0yWgghmJXs7Ozq6mpXV1e7R09LCPyEkBBCMDMmNzVKCCGYmaYQbt26dd++fVVVVcIfCfEoE5gV7bh39OjRnJwckUg0atQobYuQQ4j9CcF8nDhx4t133y0tLSUie3v76urq2tpaInJwcHjw4AHv6jqEj6NgDhITE0eNGjVhwoTS0lJHR8c1a9YUFBT84x//sLW1JSKJRKJWq3nX2DEGYMoSEhJGjRql/cfs5OQUFRVVWlra9OqlS5ccHByIaOrUqfX19RzrfAyEEExVQkLC6NGjO4pfk/Pnz2vHw2nTpqnVauPX+UQIIZielvHr1q1bR/Frcu7cORsbGyKaM2dOQ0OD0ep8SgghmJLff78TEBDQNPqtW7euvLz8ab4xMTFRm8OgoCCh5RAhBNOQkMDGjGEyGXN1Hakd/UpKSjp1hMTERO0ThsHBwYLKIUIIQnfmDHvlFUbEiFi3buzLLzPKysqe7VAJCQnaHIaEhAgnh896nTA1lXJySCqlIUOoZ89nnpsFeIxz52j9eoqNJSKytaWwMIqIIHv75zpmQkLChAkTKisr586du2PHDpFIpI9Kn0+nY3v1KnvpJUbE7O1Zly5MJGJvvcUePtT7rwd4kSUmsokTG0c/Gxu2bJk+/4n9+uuvFhYWIpFo9eozGo3eDvvMOhnCggLm5MQCAlh6OmOM1dez+Hjm4sICAphgBnfgRa1mbS8B1Ne30/gY584ZMH5NTpw4ERgYQ8QWLdL/wTurkyFcvJjZ2rKiolaNx48zInbsmB7LAlM0aRIbPVq30deXBQczxlhsLCNi48e3enXGDBYQ0Pj38+d14/fggQGrPXGCWVgwIeSwk7etxcXR6NHk5NSq8Y03yNaW4uL0+CEZzNWvv9Lhw7qNN27Qq6/Sf/wHxcaSnR1FRVF+Pq1fTw4OBqzktdfop5+oSxeKjqYlSwzY0RN1MoS5udSvn26jSER9+1Jenr5qAjM2cyaFh1N5eatGGxtKTCRra1q2jHJyaOXK5519eUrjxzfmcPNm+ugjY/TYrk6GkDGSSNppl8upoUEvBYF5++wzamigFStaNfbsSQcPGmP0a2vCBDpyhLp0of/+b/r4Y6N23aSTzxP27Ek3b7bTnpdHQ4bopSAwaTk5ugErLGz1pa0trV5NYWH0/vs0bFhz+xtvGKO8dr3+Ou3dS+++S5s2UdeutHKlsQvo5Eg4ahQlJFBtbavGS5eouJgCA/VYFpioigq6erXVn5oa3feEhJCfH4WFkUbDo8T2TJlC+/aRVEqrVtHq1UbvvnPzOMnJTC5n8+axurrGlsJC9i//wry8WHW13meNwLQ8zezovXuMMXbpEpNI2DfftJod5e7gQSaVMiK2erVR++3kSDhoEO3ZQ99/T+7uNGkSjRlD/ftTSQn97/+ShUXje4qL6fRpvf+yAHMybBiFhdGnn1JFBe9SWnj7bdq7l6RSWrGC1qwxXr+df7J+2jTKyqKoKFIo6N//nb75hlJTqWkpq8JC8venyZMpJUW/hYKZ+fxzYox++YV3Ha298w798ANJpRQZSWvXGqnTZ1roycWFQkPbf8nZmQIDadcumjSJkpJ0rygCPGJvT5s20axZvOtoY+pUqq6m4GC6cYPu3aNbt6h//1aXTK5fJwsLcnensjLKzialkuTy5leLiqiggF56qRM96mmNmZZ3gX/1Ffn7U24uzZiB6xYvlPHjadIk3capU0m7+kSfPhQaSpaWzS/NnEnLl7fzLdzNnk1nztCuXXTgAPn5UUhIq1eDghpnUE+fpqFDqaCg1avff09Dh3ayPz2cVxYXs3Hj2P79zS15eczZmRGxTz7Rw/HB7DQ0sOxsdukS7zqeZNs2JpUyqZQdP97cOHw4mz2bMcaOHmVELDu71bds3sw6myp9jISHD1N8PIWEUGpqY0ufPnT4MMnltH49HTighy7AvKSkkIcHvfce7zqegoUFzZ1LCxZQVZWhutBHCENDKSiIKipo4kS6f7+xceRI2riRGKOQELp2TQ+9gBnx8iKxmLKyqL6edylP4YsvqLzcgNcP9bQCd0wMXbtGFy/S9OkUF9d4a1t4OCUn065dNHkyJmmgJUtLcnOjvDzKzSUvL97VPImjI61YQR99RDNn0sCBuq/On09duzZ/mZHR6ePraWLGwoIOH6bu3Sk+vtVtS5ikgQ4oFEREKhXvOp7O/Pnk60thYdR2IQp3d/Lyav7zLJsgPu+pa0sJCUwmYyJRq0ma/PzGSZqICH32BSZuwQJGxP72N951PNa2bczauvHvFy4wsZjt2yfMiZkmLc8Dmy7Wu7nRkSMkl9OGDbR/vz67A1NmWiMhEfn7U1AQLV2q/xkafe9FsWhR4yTNpEnNkzQjRtCmTcQYzZ17r2kGFV5sJhdCIlq/nqqrKTlZz4c1wIYwMTGN54HTpzefBy5cWD937jqlcsSbbz58+FD/nYKpMcUQOjnRunUGOK4ePji3lZfHundnRGz58qa26upqf39/Iho7dqwwtwQAY9JomLU1IxL0Sn0XL7LNm1u1NDSw6GgWG8sYY1lZLDqa6SzAf/kyi47uXC8GW/y3vUma/Px8Z2dnIorAJA0wNnQoI2L//CfvOjqg0bCFC9nlywbvyGD7E2onaYhOf/NNyqNJGjc3tyNHjsjl8g0bNuzHJM0LT/uJ9Pp13nV04NAh2raN3nyTDL61oUEjvm/JEiLy9PR80GLxuq1btxKRlZVVcnKyQXsHgYuK0jllERC1mg0YwIjYzp0G78uwIWw6Dxw3blzL88Dg4GAicnd3L9JZwhReJHv3MiL21lu862jPt98yIubuzmprDd6XwTeEycvL054HftLiiQpM0gBj7NIlRsQGDuRdRxt1dax/f0bE9uwxRnfG2JUpISFBLpeLRKL9mKSBFsrLmUjEunZtUKsFsCNEC9u3MyKmUDDjbLBtpK3RoqOjicja2rrleeDp06dlMlmvXr0eGmyWuqqq6sqVK8uXLx87duyXX34p2F3LX1gjRrxmYWGRlZXFu5Bm1dXMzY0RsYMHjdSj8fYnbPc88MCBA7dv39ZXFw8ePEhISNixY8eyZcsmTpzo4eEhab1U8YgRI/TVF+hFYGAgEcXFxfEupNmWLYyIDR5svC2OjBfCjiZpnk1ZWdnFixf37t27YsWKadOmDR061MrKqu3cr0wm8/HxGTZsmNejB2ZafiQG7sLCwogourOXtw2mooK5uDCixsvxxqGn5wmfgoWFxeHDh/38/OLj4yMjI9d15v6f27dvp6WlZWdnZ2dnp6ampqWl5ebmatqsHevg4ODh4aFUKn19fT08PDw8PHx9fS0ercU4f/78mJiYkJAQpVI5sO1jYcCDQqEgIpVg7l7bto3u3SN/f3r9deN1+qw79T6rc+fOBQYG1tfX79u3b9q0aW3fUFJSkpWV1TJv169fr6ys1HmbXC53dXVtmbdBgwa5uLg8vvfg4ODdu3f369cvKSmpW7duevup4FmdOHFiwoQJgYGBp06d4l0LlZaShwc9eECnThl1QXljh5CItm3bFh4ebmVl9dtvv6nV6qqqqqa8ZWdn5+TktC3JwcGhZd6USqWPj4+k3a1pHqumpiYgIODixYvjxo2Li4t7hiOAfuXk5Hh4ePTu3fvWrVu8a6GoKFq9mgIC6PffjdovhxAS0ezZs/fs2dPuS1ZWVgqFwtvbW6FQ+Pj4eHt7e3t7W1tb66vr/Px8Pz+/oqKi5cuXrzHmMsvQHo1GY2VlVVtbW1paamNjw7GS+/fJw4PKyujsWXr5ZeP2bbzTzxbKy8u1JwMSiWT06NGhoaHr168/duxYVlZWg+HnpBISEmQymc51S+BFe35+iff6h2vXZksk7PXXOXTNJ4Ra1fz2kNmyZQsRWVtbX7t2jVcNoPXWW28R0d69eznWcOfOna5du3p6Tr58ucr4vRvsKYqn0DRvaXyLFi0KCgqqqKiYNGnS/aYVAIAHIUyQrl27tqqqasgQ2UsvWT753frGM4R8xcTE+Pn55ebmTp8+vQErwfHDPYT5+fk7d+4Ui8UrdPY3NZYXN4Ta65bdu3ePj4/n9X8fSAAh/Pzzz2tra2fMmDF48GAuBfCZHRWOxMTEwMBAtVr9448/Tp06lXc5L6LS0lJ7e3srK6vy8nKRSGTk3jMzM5VKpUajSU1N1f46ML4XdyTUGjly5MaNGxljISEhKdhTkQc7OzsXF5fKykoulwpXrVpVX18fFBTEK4FEnC5RCE1QUBARubu7FxcX867lRRQQEEBEwcHBP/30040bN4z2iGlKSopEIpHL5dk6K/ga14v+cVSrpqbm5ZdfTkpKwp00xscYGzBgQFlZ2Z07d7QtMpnMzc1Ne4+U9r8t7wHWo7fffvvw4cMLFizYtm2b3g/+9BDCRvn5+f7+/oWFhZ988slao22UDEQHDx6cOnWqo6Pj+++/f/369evXr+fl5em8RyaTeXl5KVtQKBTylhvkdl5ycvLQoUPlcnlmZmbv3r2f51DPCSFslpiYOGbMmPr6ekzSGE1DQ8PgwYPT0tK2b98+b948bWNtbW1mZmZaWpr2juLU1FSVSqVzGUkqlfbp06fpoRmlUtnR42wdmThx4s8//7x06dKNGzfq80fqPISwla1bty5atMja2vr8+fODBg3iXY75+/vf/z5r1ix3d3eVSvWYka2uri4jI+PxsSSinj17Nn2CVSqVQ4YM6eh+1AsXLgwfPtzKyiorK0u7zApHCKGukJCQXbt2ubu7JyUlOWFPRUOqr68fMGBAVlbWd999N2vWrM5+782bN5syqf1vTU2NzttaxtLDw2PIkCHdu3cnojFjxvz2228rVqxYtWqV3n6eZ4UQ6sIkjdHs3Llz3rx53t7eqampUunzPl9eX19/48aNtLS09PT01NTU9PR0lUpVV1en8zZXV9cePXpcvHjR3t4+OzvbwcHhOft9fghhOzBJYwR1dXUKhSI3N/fAgQPvvPOOIbpQq9X5+flNT6umpqZeuXJF+4C4h4fH2LFjd+zYYYh+O43f1RFBa3eZRtAj7QJ8gwYNMsLDa00aGhoyMzPnzJlDRIsWLTJav4+HEHao3WUaQS+qqqp69epFRMeOHTN+70ePHiWiCRMmGL/rdr3ot609Rnh4eHBwcEVFxeTJk4uLi43QY01NTXJy8sGDB9esWfPGG29YW1vb2dmFhIQYoWsj27Zt2+3bt/38/CZOnGj83rnfMq4D54SPY9BJmocPHzatrKM9b2l32p2Ili1btn79ej12zVdFRYWHh0dRUVF8fPzYsWONX0B9fb2VlVVDQ0NlZSXHh1qbIIRPcPPmTT8/v8LCwoiIiE4t09hS09XnprwlJyeXl5frvE17u5b2AvSAAQNqamqOHTt2+vRpKyurP/74w2yWaVy5cuWqVatGjhyZkJDAqwZvb++MjIyUlBRfX19eNTRBCJ/sics06tDLKqlN5syZ891335nNMo0lJSX9+vUrKSk5c+bMK6+8wquMSZMmxcbGHj58eMqUKbxqaGK8xX9N14gRIzZt2hQeHh4SEmJvb//aa681vWTQVVK1tm/fnpqaevHixenTp5vBdcv169eXlJSMHz+eYwKJSKFQxMbGCuW0kPPEkOmYPn06EUml0ldffXXKlCkvv/xyR7c7ubq6BgYGhoWFRUdHx8XFZWdnP+csfF5envY+j+XC3FDzqRUWFmpXr/wn7z2yd+7cSUSzZ8/mW4YWQvi07t+/37VrV528yeVypVL5zjvvLFu2bMeOHQkJCWVlZYbo/dSpU1Kp1NSvW4aHhxPRX/7yF96FsLNnzxLR8OHDeRfCGELYKVevXp02bdqQIUMWLlwYHx+fn59vzN43b95MprxMY0FBgaWlpVgsvnLlCu9a2N27d4nIwcGBdyGMIYSmRbsCQL9+/UxxBYDQ0FAimj59Ou9CGtnb2xNRYWEh70Jwsd6kaJdpzMnJMbllGnNzc7/99luJRCKche28vb1JGJfsEUJTYrrLNEZFRdXV1c2ePdvHx4d3LY2Ec98MQmhi+vTpc+TIEZlMtm7dugMHDvAu56ncuHFj7969Mpns008/5V1LM4QQnp3JLdMYGRmpVqtDQ0M9PDx419JMOCHExIypMpVlGpOTk8VisYWFxc2bN3nX0kpycjIRKRQK3oVgYsZkmcpeGp9++qlGo5k/f76rqyvvWlrx8vKSSCTZ2dn19fWcS+H9WwCenfDvpLlw4YJIJLK2tr537x7vWtrh7u5ORCqVim8ZGAlNmPAnabS/HRYtWsR9RbN2CeS0ECE0bUKepElMTDx58qSdnd3ixYt519I+hBD0Q7AbnkZGRhLR0qVLHR0dedfSPoQQ9CYmJsbf319QkzRxcXFnz551cnJauHAh71o6hBCC3lhYWBw6dMjZ2Tk+Pv6zzz7jXQ4R0cqVK4koIiKiozWwhUAgIcTsqPlISEiQyWRCeNzpyJEjRNSzZ8/Kykq+lTyeRqPRPt94//59jmVgJDQfApmk0Wg02rXlIyMj2z6BKSgikUh7G3dGRgbHMhBCs8J9kqawsHDlypVXr17t27evSSzWKIRPpFhjxtzExMSkpKQkJSUZek2alluyaFfZuXbt2r1794jIxsZm5MiRXbp0MVDXeoQQgv5pJ2n8/f21kzT62kvj1q1bN27cULWQl5fX7hJyLi4uKpVq7969AQEBTVsOCpYQQoglD83T82x42rQZYNMqcteuXSsrK9N5W9M2nS0XbuzXr59IJPr666+18fvqq6/CwsL09lMZwOXLl4cNG+br68vxLBohNFtPueFpp1ZJbZk3pVJpaWn5+N5FIlFMTMwHH3ygt59K3yorK21sbORyeWVlJa/lJBFCcxYcHLx79257e/ukpCRPT8/S0tLMzMyWeVOpVBUVFTrf1XIhcG3eBg4c2KNHj872Hh0dvXjxYpFItH37du0CM8Lk5uZ269atrKwsXo87IoTmrLq6uk+fPsXFxRKJRCwWt/vMTq9evRQKhbe3t0Kh8PHx8fb2dnd319eYsGXLlg8//FAsFu/evbuze/EazdixY0+dOvXLL79MmDCBSwGYmDFnlpaWx48fHzVqVG1tbUNDg85C4EqlcvDgwba2toYrYPHixRqN5qOPPgoODhaLxTNnzjRcX89MoVCcOnVKpVIhhGAQw4cPLyws3L9/v6+v7/Dhw8ViY18ZXrJkCWNs6dKlc+bMEYlE7733npELeCL+E6Qc79aBF8eGDRuISCKR/PDDD7xr0aXdM1SpVPIqAHfMgDF8/PHHK1asaGhomDVr1o8//si7nFa0qwBzHAkRQjCSVatWRUZGanN49OhPvMshItJoNMePHw8PDxeJRJaWlozTJCXOCcF4Pv/8cyLavn1Lz55rS0rE9vZv8qpEo9EcOnRo9erVqampROTs7BwRESESibgUg0sUYGzXr39RWfmZSCTv3/+wnZ2x96xnjMXGxkZFRV2+fJmI+vTp8+GHH37wwQcc73RFCIGDgoLld++uE4nk/fsfsrObZJxOtfFbuXLln3/+ScKInxZCCHwUFETcvbvBOOOhTvzc3NyWLFkyb968tpuTc4EQAjcFBcvu3t0oEsn79z9iZ/eGIboQePy0EELgiN28ubCw8H/EYsv+/Y/Z2o7V78FjY49FRkZdvXqViNzc3CIiIkJCQrh/+GwLs6PAkcjNbRtjrKjoq6ysyZ6esTY2gXo5blnZyYKCiJMnba5everi4rJ48eLw8HBBjX4tYSQE7lh+/n8VFcWIxV09PWNtbEY/z6FKSo7fubOyquoyEdXV+f755wd//etcAY5+LSGEIAQsP/8/i4q2i8VdPT1/trEZ9QyHKCs7efv28srKJCKSSp1dXD50dl4oFnf4xKNwIIQgECw/P6yoaIdYbOXp+bONzStP/52mGz8thBCEQ5ObG3T//h6JxMbL61crq+FP/IayspO3b39aWXmBiKTS7i4uS0wrflqYmAHhEPftu4uIlZefkUqdiKi8/NSdO2tratKJyNJyUI8eEU1njOYRPy2MhCAsjDWo1fdksl6VlRdUqhF2dhO7dZtDxCor/7CxGWNrO66s7OTt25GVlf+k5vgtEIsFvcrw4yGEIFC3bi25f3/P4MF3RaLmtTbq6++mpPTTaGpkMhcXl4+7d//ApOOnhY+jIFAaTaVGU61WF8tkLk2NMlkPF5dlEolN9+5hZhA/LYyEIFDl5WczMsZIpT2cnRd06zZTJuvNuyJDQQhBuMrLzxYWRpeW/kLUYG8/pW/fnRKJPe+i9A8hBKFTqx8UF399+/YKR8cZ7u67eZejfzgnBKGTSh179FhWU5NeVhbPuxaDwBozYBrU6mLtxUPzg5EQBCov768aTbWNTaBY3LW8/HRp6c99+vwP76IMAueEIFBlZfEPHnxfXX1NrX7YpYu7k9NfHR0Ft3CwXiCEAJzhnBCAM4QQgDOEEIAzhBCAM4QQgDOEEIAzhBCAM4QQgDOEEIAzhBCAM4QQgDOEEIAzhBCAM4QQgDOEEIAzhBCAM4QQgDOEEIAzhBCAM4QQgDOEEIAzhBCAM4QQgDOEEIAzhBCAM4QQgDOEEIAzhBCAM4QQgDOEEIAzhBCAM4QQgDOEEIAzhBCAM4QQgDOEEIAzhBCAM4QQgDOEEIAzhBCAM4QQgDOEEIAzhBCAM4QQgDOEEIAzhBCAM4QQgDOEEIAzhBCAM4QQgDOEEIAzhBCAM4QQgDOEEIAzhBCAM4QQgDOEEIAzhBCAM4QQgDOEEIAzhBCAM4QQgDOEEIAzhBCAM4QQgDOEEIAzhBCAM4QQgLP/B8Ii/VaEAVhPAAAAAElFTkSuQmCC",
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 3,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "print(\"Available N-terminal modifications:\")\n",
+ "for mod in n_term_modifications.keys():\n",
+ " print(f\"- {mod}\")\n",
+ "\n",
+ "# Let's visualize one of these modifications, e.g., Biotin\n",
+ "print(\"\\nBiotin SMILES:\", n_term_modifications[\"Biotin@Any_N-term\"])\n",
+ "biotin_mol = Chem.MolFromSmiles(n_term_modifications[\"Biotin@Any_N-term\"])\n",
+ "Draw.MolToImage(biotin_mol)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## The `modify_amino_acid` Function\n",
+ "\n",
+ "Now, let's explore the `modify_amino_acid` function, which allows us to add modifications to amino acids. This function takes three arguments:\n",
+ "\n",
+ "1. `aa_smiles`: SMILES string of an amino acid\n",
+ "2. `n_term_mod`: N-terminal modification (optional)\n",
+ "3. `c_term_mod`: C-terminal modification (optional)\n",
+ "\n",
+ "Let's see it in action:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Modified Lysine SMILES: [H]N(C(=O)CCCCC1SCC2NC(=O)NC21)[C@@H](CCCCN)C(=O)O\n"
+ ]
+ },
+ {
+ "data": {
+ "image/jpeg": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAEsASwDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD3+iiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigDP/ALXg+0+V5c3l+b5Hn7R5fmf3euevGcYzxnNaFcCuuxt4i/s/yJvs/wDbJh8vzV2+YBv3/dzjdztz1744rvqzpz5rnZjMM6HLdWur/wBeYUUUVocYUUUUAFFFMlljhieWV1jjRSzu5wFA5JJ7CjcB9FNEsbStEHUyKAzIDyAc4JHvg/kajhu7a4EZhuIpBIpZNjg7gCASMdQCQD9RT5WBNRRRSAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKZLLHBC800iRxIpZ3c4CgdSSegoA8fg1C1bx2kQl+c+J5o8bT94RjI6V7HXzTYeIbT/hYsN+6yJo7eKbm5XVWUi2YMgUDf0zxn6GvpVWV1DKQykZBByCKiFNQvbqdWJxdTE8vP8AZVhaKKKs5QooooAKoa5ZS6l4f1KwhKiW5tZYULHADMhAz+dX6KqEnCSkugHOGGXQNQur0zfaUuhbwRrPPiQsDJkL8vP3gQPqSQBWDJa3fgOC01Z7aS80+2szFcwWvzNC7tF5koBxlMR7iOuST3JHoDKrY3KDjnkUpAIweRXXTxji/eV09/NJW+WnUlxKunalZ6vp8N/YXCXFrMu6ORDwR/Q+3arVecavo+qeAb2XXvCtqbrSJHMmpaMnUDvLAOxHde/5Y1W+K3ghNIh1J/EFqI5l3LCCWmB9DGuWB+ornrKmpv2Tbj0vv/SGr21Oyorzw+P/ABBrfy+E/Bd/NGel7qhFrDj+8AfmcfTBqG68JfETXIGl1HxvDpc6/NDbaTbERK3bc7EMw9jxWQz0miuK8I+NLi91CTw34mt00/xLbLkxg/u7tP8AnrCe4PcdRz6HHa0A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Wmi81Ayu0jiKC3gQvLPIeiKo7n8quvc3jo8UMcS3AxgOx9skccii00uOCTz52+0XR5Mrjp/ujtUKvKc1GnG66t6W/wA3/Vwt3ON8KaH4g1PxlL431+KLS3ls/sdvpkR3OIt24GZu7ewHp0xivQaKK6RBRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAVtQ0+01Sxlsr2BZreUYZGH+cH3rBvbKWBri3t4brzFijTTWiLlI8DHJBwMHk7uowOeldPRUSgpHRRxE6Wm67fd/lZ+WgUUUVZzjUjSJdsaKi5zhRgU6iigNwooooAKKKKACiiigAooooAKKKKACiiigCNYUWUy/MXIIyWJwD6enQVJRRSUUtkAUUUUwCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigD/2Q==",
+ "image/png": "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4mTkSGcLrK4KC388ePD+5GTs3187N3eliNhspaWlWSLW0tLM9PSh2tXyxIkXrdZK/x+ogmoZwuuuhO+/L45XPwsLpWtXJ2v4dfZsAcRgkDlzpKBABg8WQOrVEycX2E1IkKlTr37ZocMdMEKoitmc89tvTyUn4/DhR/LzNx050icjY+zp06+cOjU5ORkpKY3z8923Ciu3+QGeeQbPP4+gINSvj7ffxvPPO1le66lT4euLyEi88gqysvDttwgPx9dfY8gQrFqFJ56oYrm5ufD3v/plnTrIz7/mE6owT8/6LVvGX7jwnzp1gjMz37rnnvdq1eoLwGYrFLE0bTrd01P/zUD/kNvifrtYvlx+/tl+PG+e/XHo7t0yapSEhcmCBaLXOPvy5eLlJYCMHSvFxTJunADi7S1Hj1axwD175Lnn7MfFxdK+vT7trPby8lanprY/f/5Di0WP3XYqjzv1ulJiIoYPR2EhQkKwYgWio+Hnh+nTq17gs8+ibVt07YrPP8eTT2LcON2aWr1ZLBdzcj69dCm+TZsk99fOELrYzp0YNgx5eRg0CAkJ8PNDYSG2boXJhP790aBB5UoTwdatOH0agYEos9A9OUcAAyCHDrVr3/6QwVDhbVx1wntCF+vdGzt24PHHUbs2fHyQnY1hwzB2LGrVQkgIFi1CeXvc/yGDAWVeriVdnDs3vbj4oNV6uX79592fQPBK6CZnzqBhQ3h74+230ayZvRu5Zw/mzsWKFaobRwC0FBiU1M1NQt0iIADe3gCQno5Onewfdu6M9HSFjaIyDKoSCIbQ3fz9kZ1tP754EXffrbQ1dFtgCN1r5Ej8+98oKIDZjHfewahRqhtE6vGe0O02bMCnn8JsRnAwxo5V3RpSjyEkUozdUSLFGEIixRhCIsUYQiLFGEIixRhCIsUYQiLFGEIixRhCIsUYQiLFGEIixRhCIsUYQiLFGEIixRhCIsUYQiLFGEIixRhCIsUYQiLFGEIixRhCIsUYQiLFGEIixRhCIsUYQiLFGEIixRhCIsUYQiLFGEIixRhCIsUYQiLFGEIixRhCIsUYQiLFGEIixRhCIsUYQiLFGEIixRhCIsUYQiLFGEIixRhCIsUYQiLFGEIixRhCIsUYQiLFGEIixRhCIsUYQiLFGEIixRhCIsUYQiLFGEIixRhCIsUYQiLFGEIixRhCIsUYQiLFGEIixRhCIsUYQiLFGEIixRhCIsUYQiLFGEIixRhCIsUYQiLFGEIixRhCIsUYQiLFGEIixRhCIsUYQiLFGEIixRhCIsUYQiLFGEIixRhCIsUYQiLFGEIixRhCIsUYQiLFGEIixRhCIsUYQiLFGEIixRhCIsUYQiLFGEIixRhCIsUYQiLFGEIixRhCIsUYQiLFGEIixRhCIsUYQiLFGEIixRhCIsUYQiLF/h9L/ebRgOozxwAAAABJRU5ErkJggg==",
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 4,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "# Modify Lysine with Biotin N-terminal modification\n",
+ "modified_lys = modify_amino_acid(aa_smiles[\"K\"], n_term_mod=\"Biotin@Any_N-term\")\n",
+ "print(\"Modified Lysine SMILES:\", modified_lys)\n",
+ "mod_lys_mol = Chem.MolFromSmiles(modified_lys)\n",
+ "Draw.MolToImage(mod_lys_mol)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "To obtained the unmodified aminoacid, just pass the corresponding SMILES to the function with no additional arguments:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Lysine SMILES: [H]N([H])[C@@H](CCCCN)C(=O)O\n"
+ ]
+ },
+ {
+ "data": {
+ "image/jpeg": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAEsASwDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD3+iiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAK4/4iw3F1pGmWlteTWctxqlvEJ4WwyZJ59+cHFdhWB4vFhDpEWpancSQWumXUV6zRpuJKNwMehJrqwUuXEQa3v669NCZbHKnX7rU9X0OO4LW+oW0OoW9/AjEATJGnIHdTkMp9GqhL4nvP+FNiP+ytaEv9kKPt21dmdg+fdv3Y75xmtHR7zwj458aHV9Gvbj7fb2bxTp5DIjow2hiSPvDOPp9K6l/C8D+CP+EXNxJ5H2IWfnYG7aF25x0zXr1a1GhKEKkGmnFtNNW1k36rVW8iEm7tM2bYk2sJJySi/wAqlrm9TNzBrFhb2GpXDXReLdaKEMaQBv3jvxkZXIBz97GO9dJXh1KfKlK+5omFFFFZDCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACue8d2ovfAeuwnvZSsPqqlh/KuhrP12Brnw/qVugy0trKgHqShFbYefJWhPs0/xE9UeQ/s9W6iLXrgj5swIP/Hz/hXt1eS/s/wxnwdqN4mczXxTkdlRcf8AoRr1qvQzyvCvj6lSDunaz9EkRSVoJMozaLpVxei9n0yzluwQRO8Cs4I6HcRnjtV6iivLcpSsm9jQKKKKkAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKjnnitbeW4ncRwxIXd26KoGST+FSVk+Kf+RQ1r/rwn/8ARbUpOybNKMFOpGD6tIvTX1rb2gu5p0S3O3EhPHzEBfzJH51BqOt6ZpLIt/ewwPJyiM3zMPUDrj3rj9bXXR4MhN1LpxtM2uRFG4kx5seOS2PTtW1ou3/hMfEfnY+1bofLz18nyxjHtu3Z96x9q27Jf1r/AJHf9SpxpupKV0r7eTit7afFrozftbu3vrZLm0nj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wk19CUOQcgR4/Cu6pp32CUXF2krBRRRQIKKKKACiiigCvbWFnZyTSWtpBA87b5mijCmRvViOp5PJqxRRQAUUUUAFFFFABRRRQBn3OhaRealBqN1pdlPfQY8q5kgVpI8HIwxGRg1oUUUAFFFFABRRRQAUUUUAcxf6AdT8YXT3lqJdLudH+ySMSMFjKSVx1zg5zXIQ+DdeeC+tNUtlvkfWbBvMdlIuLWIIhdgT12L8wPU5616tRXoUcyrUlaNun4O/6akOCZxt14OsbLxT4dv9F0SytUt7iY3UtvCkZCGF1GcYJG4j1rcs9Ynm1c6fd6e1rI0LTxHzVfKKwU7sfdPzDjJHXnitasXSNFutO1C6vLnUReSXP32aAKwAPyqCDwqgkBQB1J6kk5uv7aH753cVZb33b9OvXy9R2tsbVFFFcZQUUUUAFFFFABRRRQAVyWtf8lH8Le0V3/6AK62uY1a1uJfH/h65SCVoIYbkSSqhKoSoAyegzWdX4V6r8ztwDSqSb/ln/wCkMh8Kf8jP4sP/AE+R/wDoFdbXMeGbW4g1/wATSzW8saTXitEzoQHGzqpPUfSunopfD9/5hmDTr6do/wDpKCiiitDiCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigArl7u3TXPGM+nXrSNZWdpHKtuJCqyO7MCzYIzgLgA8cmuorK1HRBeX0V/bXk9lfRoYvOhCnehOdrKwIIzyPSoqK6OrC1FTk23ZtWT7Pv38tO5n6Oh0vxXf6NBJI1j9ljuoo3cv5LFmVlBPODtBx25rpazdK0aPTZLi4e4mury5IM1xNjc2OgAAAAGTgAd60qKaaWosVUjUqXi76LXu0tX/Wr3eoUUUVZzBRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQB/9k=",
+ "image/png": "iVBORw0KGgoAAAANSUhEUgAAASwAAAEsCAIAAAD2HxkiAAAUOElEQVR4nO3da3BU5R3H8d/mns0CcjEkBDAGU5AIyEUwkZsCUjBtp0wzdKyZMrZmOhUjwwtjncKKVARGSpgWZug4HdJiEXAqDQpIuIgoCCgClTtICAkXTYAICSGX/ffFCZtkk2yyye7+92x/n/FFcvb20J5vzrPPOdlYRAREpCdEewBE/+8YIZEyRkikjBESKWOERMoYIZEyRkikjBESKWOERMoYIZEyRkikjBESKWOERMoYIZEyRkikjBESKWOERMoYIZEyRkikjBESKWOERMoYIZEyRkikjBESKWOERMoYIZEyRkikjBESKWOERMoYIZEyRkikjBESKWOERMoYIZEyRkikjBESKWOERMoYIZEyRkikjBESKWOERMoYIZEyRkikjBESKWOERMoYIZEyRkikjBESKWOERMoYIZEyRkikjBESKWOERMoYIZEyRkikjBESKWOERMoYIZEyRkikjBESKWOERMoYIZEyRkikjBESKWOERMoYIZEyRkikjBESKWOERMoYIZEyRkikjBESKWOERMoYIZEyRkikjBESKWOERMoYIZEyRkikjBESKWOERMoYIZEyRkikjBESKWOERMoYIZEyRkikjBESKWOERMoYIZEyRkikjBESKWOERMoYIZEyRkikjBG2bPNmLFmCq1ebbHznHWzbBgDnz2PFCty40eTWixexYgVKS/03SAoOjLBl69fj1Vcxd26TjX/+Mz74AACOHsWcObh2rcmtJ05gzhwUF/tvkBQcGGGrevXCe++hoEB7HBTswrQHELgGDkRCAn7/e/z3v4iK0h4NBS8eCd15+21cuYK33tIeBwU1Hgnd6dcPr7yCRYvw7LMYOND11p//HJGRDd/euuXPoVHwYIRtyMnB2rV48UXs2OF6U0YGevdu+PbECaxa5c+hUZBghG2IjMRf/oIf/xibNrne9OyzGDSo4dutWxkhdQQjbNvUqfjFL/DKK3A4tIdCwYgLM+2yfDmuXMH58x48pLAQ69Zh716fjYmCBSNsl759MX++B/fftAnTp+P0abzxBl5+2WfDoqDA6WjLZs5EeXmTLXPmICQEKSkAMGwYli9vsioDYPBgLF+OhAQASEnB9u3o2xelpUhKwooV/ho3mZBFRLTHEMwuXMCTT6KwUHsc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mDBCHgkpuJhwx2WEFFxMuOM2T47TUTIzi/HZEESkxXy/1AsAtbXYuBEff4yrV9GjB9LSMGtWw+ewEpmKCaej5eUYPx6zZuH6dQwZgpgYzJuHlBScOaM9MqKOMOF09PnnsXEj9uzBiBH1W0pKMG4cunXD4cN8Z0imY7YjYVkZ1q7FSy81FAggIQGLFuHIEezZozcyog4yW4QHD6KmBmlprtvHjQOAzz/3/4iIOslsEX7/PQD06+e6PT4e4eG4ds3/IyLqJLNFGB4OABUVrtvv3kVtLSIj/T8iok4yW4TGx40WFrpuLyyESOMPIyUyC7NFOGIE4uKwcaPr9vXrERqKadM0xkTUKWaLMDwcdjs2bcLChaiuBgAR/OtfWLIEWVlITFQeHpHnTHieEMDSpXj9dYSHIykJJSUoK0NWFlascP2b4kRmYM4IAZSVYc8elJbCZsPYsejfX3tARB1k2giJgoXZ3hMSBR1GSKSMERIpY4REyhghkTJGSKSMERIpY4REyhghkTJGSKSMERIpY4REyhghkTJGSKSMERIpY4REyhghkTJGSKSMERIpY4REyhghkTJGSKSMERIpY4REyhghkTJGSKSMERIpY4REyhghkTJGSKSMERIpY4REyhghkTJGSKSMERIpY4REyhghkTJGSKSMERIpY4REyhghkTJGSKSMERIpY4REyhghkTJGSKSMERIpY4REyhghkTJGSKSMERIpY4REyhghkTJGSKSMERIpY4REyhghkTJGSKSMERIpY4REyhghkTJGSKSMERIpY4REyhghkTJGSKSMERIpY4REyhghkTJGSKSMERIpY4REyhghkTJGSKSMERIpY4REyhghkTJGSKSMERIpY4REyhghkTJGSKSMERIpY4REyhghkTJGSKSMERIpY4REyhghkTJGSKSMERIpY4REyhghkTJGSKSMERIp+x+k2xdjRUF+GwAAAABJRU5ErkJggg==",
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 5,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "non_modified_lys = modify_amino_acid(aa_smiles[\"K\"])\n",
+ "print(\"Lysine SMILES:\", non_modified_lys)\n",
+ "Draw.MolToImage(Chem.MolFromSmiles(non_modified_lys))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "As we can see, the Biotin modification has been added to the N-terminus of Lysine.\n",
+ "\n",
+ "## Working with Post-Translational Modifications (PTMs)\n",
+ "\n",
+ "The `ptm_dict` contains various post-translational modifications. Let's examine one:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Phosphorylated Serine SMILES: O=P(O)(O)OC[C@@H](C(=O)[Rn])N([Xe])([Xe])\n"
+ ]
+ },
+ {
+ "data": {
+ "image/jpeg": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAEsASwDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD3+iiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKA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ORgwM3kWy9+Ik78dc13dFJpNWZUJyhJSg7NFWx06y0yAQWNpDbRf3YkCg/XHWrVFFCVtEEpOT5pO7CiiimSFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAUdZtpL3QtQtYf9bNbSRp/vFSB/OsjwrremnwjY+ZdQwNaW6Q3CSuFMTooVgwPTkV0tZ1zoGjXl19qutKspp/+ekkCsx+pI5rOUXzc0TrpVqfsnSq3te91939djzt4HvNFtblXmt4dT8VC4t5E+VxG2QGGRweCRxWt4s0KSw020nbWtVuwL63HlXMqMhzIOcBQf1ruZbS2nEQmt4pBC4kjDoDsYdGGehHrS3FtBdxiO4gjmQMHCyIGAYcg89xWX1fRo7v7WftISSsk27abevoS0UUV0njBRRRQAUUUUAFFFFABRRRQAUUUUAFFFFAGV4n1L+x/CuraluwbWzllU/7QQkfrisj4Zab/AGT8NPD9qV2sbRZmHoZP3h/VqzvjDPIPh5cWEJxPqdzBYxfV5Bn9Aa7m3gjtraK3iGI4kCKPQAYFAElFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUVz3jnXrnwx4K1PWrOOGS4tIw6JMCUJLAc4IPf1oA5/wAef8THx14E0Ucq1/JqDj08hMqT+LGvQa84lv8AWdJ8ZeHrvxNp+i3TXztY217YCVJLdmXdgq5IKnHUHj+fTXHjvwpayxxz+INOjeRmRVadRyGKnPp8wI59KAOhooByMiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAK4r4u/8kq8Qf8AXBf/AENa7WkZVdSrqGU9QRkUAeWDRU8M/EfQrrWb/UNV0+7iMGn3F/cGT7HeEfdxwvzrkA4zkVW0a0tv+FL+Nn8iLfI+qu7bRlipk2k+pGBj6V62yK4AZQwByMjPNAjQIUCLtOcjHBz1oAy/CzM/hDRWYksbCAknufLWtakACgAAADgAUtABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQB//Z",
+ "image/png": "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",
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 6,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "print(\"Phosphorylated Serine SMILES:\", ptm_dict[\"Phospho@S\"])\n",
+ "phos_ser_mol = Chem.MolFromSmiles(ptm_dict[\"Phospho@S\"])\n",
+ "Draw.MolToImage(phos_ser_mol)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We can use the `modify_amino_acid` function with these PTMs as well:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Modified Phosphorylated Serine SMILES: [H]N(C(C)=O)[C@@H](COP(=O)(O)O)C(=O)O\n"
+ ]
+ },
+ {
+ "data": {
+ "image/jpeg": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAEsASwDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD3+iiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAork9MuNd8S2R1W11WPT7WVm+ywC2WTKAkBnJOcnGcDGBVePxbcuukG6eC1c6hNZagMjZujVs4J6AkA/jisvbR3O/8As6rdxi02t1ro7N228mtL6naUVzt14gjfxLoljYX1tNDctMJ1jdXOFjLL06c10VXGSle3Q5atGdJRc/tK/wCLX6BRRRVGQUUUUAFFFFABRRRQAUUUUAFFFFABRXBaJ470uzvNetde123hng1aeOGOdwpWIbdoHtnNangPV5db0e+vJLr7VH/aVykEnGDEHOzHtjFdtbAVqMXOa0Vtddb9iVJM6miiiuIoKKKKACiiigAooqmdV09dWXSjeQDUGiMy228bygON2PTJoAuUUUUAFFFFABRRRQAUUUUAFFFFABRRUN3dRWVnPdzttihjaRz6ADJoGk27I5XR31Tw3YNo0WkS6jFbO6209vPGAVJLBZNzAqRnHQ1BF4WvFGjm7giuHbUpr2/AwUQyK3AB6gEqKveArWUaA+qXK4utVne8k9gx+UfTbg/jXU1zwpqUU2eticZOhXnGFr3d3rq7NPr3benr5HOXmhLH4n0K8sLCGKG3af7Q8SKmA0ZC57nmujooraMVG9up51WtOqoqX2Vb8W/1CiiiqMQooooAKKKKACiiigAooooAKKKKAOV8GJHb3XiS3F1byzHWJp2jikDNGrhdoYdj8p4qTwRam103Ux50Mok1W7lDRPuABlPB9x3FcZaJNomr6/4ttVZktdang1KJRkyWpWM7gPWMkt9C1aPhHxhoGi6U0Wo6nDb/AG3U714HfOxlEx53YwOo6mvexOFqShOVK8ubl2Wt0m2vlv6MyUlfU9IoqK3uYLuBZ7aaOaFxlZI2DKfoRUteE007M1CiiikAUUV5zrHivVPF2qT+GvA8qqsR2ajrmMxWo7pF/fk+nT9QAXvFHja5XVP+EY8JQJqHiKQfvGJ/c2K/35T6+i9f0Bv+EPBNr4YE97cXD6jrl5815qU4+eQ/3V/uoOwHoPQVd8LeFNL8I6X9i02I5c757iQ7pZ37s7dz/KtygAooooAKqare/wBmaPe3/l+Z9lt5Jtm7G7apOM9s4q3WT4oBPhHWgBkmwnwB/wBc2qZO0W0a0IqVWMZbNr8wvta+xeFpNa+z79lqLjyd+M8ZxnH64qG+1+aO8ttP0+x+1388P2gxtL5aRR9NzNg9+AADnBrmtX8ORR/D2e5W91VpBYB/Ka+lZCdo42E4x7VoSTpoPimLVL4Mlhd6dHB9o2krFIjE4bH3QQ3U9xWDqTvrpserHC4flvBczvKy1V7JWWj829NWa+m65PPqj6VqVj9ivhF5yBJfMjlTOCVbA5B6gjvW1XGtePrviiPUtFQXEOn2UypM+VjmmfG1A3cDbkkdM1IPG76cQniTRb3S+xnVfPg/77Xp9MVUaqXxPTv/AFoYVcBObXso62V431T8k3zbWfW1zrqKp6fqun6tD52n3kFyncxODj6jt+NXK2TT1R50oSg+WSswrnfGML6jpCaPFKYpNQlWIsBkhAQWP5DH410VYkH+n+Kp5usVhGIk/wB9uSfy4rnxMnyqEd5O3+f4XOjCNwqe1/l1+fT8bGxDFHbwRwxKFjjUIqjsAMAU+iiuk5m76sKKKKBBRRRQAUVFcXMFnbSXNzMkMESl5JJGCqqjqST0FcBp3iHXvHmvwXHh9307wrZzbpL6SP8AeaiVPKRqw4j7Fuv48UAeiUVxkX9qWupCecS2wvL+e8vJCNyxWkKbI0J5ALYjbHXl/Srdh4uX7DFPqsItyNNbU7llBxbxFv3akcksVDdO6N6gUAdRRTEmjkZlR1LJjcoPK5GRkduKfQAUUVXvr+00yxmvb64jt7WFd8ksjYVR7mgCdmVEZ3YKqjJJOABXn1zr2q+O799M8K3Elno0L7bzWlHLkdY4PU/7X/1s1on1H4qsHxc6b4ODcAgpNqWO/qsX8/8A0H0SzsrbTrOKzs4I4LeFdscca4VR7Cu+lOjh4KorSm9u0fN932Wy632Jd27dCrp+i2emxXscStIL2dri4807t7soVuOmCFHFV9L8KaNpOhnRYLJJNPLs/kTjzF+Y5I+btWzRXL7err7z1s/u2+4dkcRc/DHS4Z2uvD17faBdsck2Mp8pj/tRngj2GKgkvfiJ4cjzcWFn4otlPMlqwtrjbzklT8pPThetd9RW1TG1qsOSq+bzau189/xEopO6OJ0z4q+Gby5FlqE1xomod7XVoTbsP+BH5f1rsHu7aO0a7e4iW2VPMaYuAgXGc56Yx3qDU9H03WrU22qWFteQH+CeIOB9M9DXimh/DHS9W8VeLvDsuoanaWWn3kcyWEFyRBJHKu+MMpznaQeevvXIUdRPqWrfFO5ksNEln03wgjFLrUwNst9jgxw56J2Lf/XB9C0fR9P0HS4NN0u1jtrSEYSNB+pPcnuTya5X4Q3j3Xwz0qKUYns/MtJV/umN2UD8gK7igAooooAKKKKACiiigAooooAKQgMCCAQeCD3paKAMSTwnoxuXure0WzuWGDNa/u2/Tj9KPs2u2H/HvdRX8Q/guBsf8GHX8a26K554aEnzLR91p/wH8zp+tVXpN8y89f8Agr5HPXF9cXF5ZR3Ek2lrIkpdd6Zyu3HJBGOTVzQ7mS4jut8onWOcok4UDzAAOeODjpn2qMrBq2p293GEltrcSxSF14DZHTPXp1q7faet5AkSTzW3lncjQNtwf8K5qUKnM6ifMltrvovl36bmtSVPlVNq3fy1fz/EuUVibdfsPutBqUQ7N+6k/wAKrt410m1vEs9UaXTrhhkC5Qqp+jdPzxXTHExb5ZJxfn/nt+JmsHVn/C9701/Df8Do6KjhniuIllglSWNuVdGDA/QiknuYLVN9xNHEmcbpGCj9a6LnNyu9raktVdR1Kz0mxlvb6dYbeMZZm/kPU+1QatrVrpGkvqMoeaIYCLAu8uScKBj1OBXP6doF9r99HrXihANh3WmmZzHAOzP/AHn/AJfoM5Td+WO510MNFx9rWdoL72+y/V7L7k8mTQ9T+I+oRXeuF7PwpGRJb6YMrJeEdGmP93uFH/1z6JDDFbQRwQRJFDGoVI0UKqgdAAOgp9FWk0tTmqSjKTcVZduwVUutMsr1JVuLWNxLs8w4wXCNuUEjkgHt05Pqat0UyDJOgQi7+0Q3E8TSXwvbja3+uYR+WqE/3RhDj/Z9zToW1azgX7SqX2yCWSRosJI8m4FERThcbcjJI6DPUmtSigDI17xLpfhrSbjUtVnaC3hwDlCS7EcKv94n2rirDw/q3xEv4da8X272ehxN5lhoTH7/AKSXHqfRe355vfGXT/tvw1vrhU3TafJFexexRxn/AMdLV3Fpcx3tlBdRHMc8ayIfZhkfzoAlVVRAiKFVRgADAApaKKACiiigAooooAK8+H/Er+PB7Raxouf96WKT/wCIr0GuL8ZWscHirwhrjZD2169pkHjE6befyqZSUVdmtGlKtNQjv/TKXw5/4l/iPxxoR4+zav8AbEX+6lwgcAe3Br0GuPiij0/4s3DKir/ammq7MByzxtgD8FrsKIyUr+Q6tGVLlv8AaSYUUUVRiFFFFABRRRQAUUUUAFFFFABVHWb3+z9JuLgH51XCf7x4H6mr1ctrl1Le+MNG0OEgxoGvrsYz8i8IPxasa7l7NqG70XzOnCUvaVddldv0Wv8AwB97btY+G7LR4zie6ZYmI7ZOXP8AP866SONYokjQYRFCqPQCudtbxdW8bXkaqTFpMSx7+xlkGTj6Dg10lZYanyyk1srRXov+DcvFuSUYy3fvP1lr+VgqK5tbe8gaG6ginibqkqBlP4GpaK6zkTad0cnP4BsIpmudEu7vRrg8k2kh8tj7oeCPbimaZYW2seKtbl1SFLx7J4raBZ0DCNPLDFgp4BYnOa6+sW+0BptTfUtP1CfT7uRAkzRKrrKB03KwIyOx61jKklZxXyPRpY6pNSjWnq1ZSe61T3WtrK3X7rmfocMemeMdX0qzQJYGCG6WFfuRSMWDBR2zgHFdVWZpGixaT9ol8+a5u7pg9xczEb5CBgDgAAAdABxWnV04uMbM58XVjVq80XfRK/dpJN/P/hwoooqzlCiiigAooooAztf04av4d1PTSAftdrLBz/tKR/WsD4WaidT+GOgzMTvjthbtnqDGTHz/AN812FeffC7/AEF/FWgnj+z9amMS+kUmHT+tAHoNFFFABRRRQAUUUUAFct8Q4Hk8GXc8X+utGjuYz6FXBJ/LNdTVXU7IajpV5YsQBcQvFk9tykf1qKkeaDR0YSr7HEQqPo0/xOX8RTouueENbj/1b3BtyfUTJxn8q7KvPriXT5vDVnotzNfXDaU8QlvbG0eWON4v9rHYdcZxXd2t1BfWsV1ayrLBKoZHU5DCopO8n5nXjqbjSpqz926vbpe6++7JqKKK2PMCiiigAooooAKKKKACiiigArj9KEtjf+JvE+qwSQDeY4VkGCIIl4I/3jz9RWh4lvbtJtL0uynNvNqNwY2nUAtHGqlm2543EDAPvWdq1nL4fexnW/u72yuLqO1u7W+k85XWQ7dw3dCDjjoawqS1v2PUwlK0OVvWp06tJ669LtWXp0Ra8B2UsHhpb25H+l6lK17Mfdzkf+O4rp6REWNFRFCqowFAwAKWtYR5YqJw4ms69WVV9X/S+QUUUVRiFFFFABRRRQAUUUUAFFFFABRRRQAV59pf/Er+OWvWnRNX0qC+HoWiYxH8cHNeg15f8SNUHhXx34Q8SfZbi5Qm4sZoLZQ0sgdRsCjud2TjvQB6hRXK6D8RvCviGQQWmqxRXmdptLv9zMG9NrYyfpmuqoAKKKKACiiigApsoYxOEOHKnafQ06igEcx4Fmgj8FWaF1ja2V0uQxwY5Ax37vQ5yeaXwID/AMI68iArbS3c8lsMYxEZCVx7d/xrQu/DGh392bq60u2lnbBZ2T7+P73r+NaqIsaKiKFVRgKBgAelZQg1a/RWPQxGKp1FPkvebu79N9F3330/EWiiitTzwooooAKKKKACiiigAooooAyNf0ibUo7SezmSG/sphPbvICUJwQVbHOCCRxzVGXTNa1q9sjq6WVrZ2k63HlW8rStNIv3ckquFB5xznFdLRUOmm7nVTxc4RUVbS9n1V97f1pugoooqzlCiiigAooooAKKKKACiiigAooooAKKKxfEHiS10CGNWR7m+nO22s4uZJW+nYe9KUlFXZpSpTrTUKau2P8SeJtL8KaRJqWrXAihX5UQcvK3ZUXuT/wDr4ry+/wBH+IfizVdM8Wz6bYC1sZhcWOgXVw8brjlZGYDHmdDhjgenUV2GjeCprvXx4o8VSi91Rf8AjztDzDYL6IOhbP8AF9OuM129Cd1cmceWTj2PJdY8UeEdXK2fxF8IXGj3LfIJ7223x59EuI+fx4FaOjeEZbRI73wL44nk04Z/0K4lW8tjxwqt1QdOmTXos0EVzC8M8SSxOMMjqGVh7g1yknw28PQ3Mt3o8Mui3cmN0unSGLOM4+X7uOTwAM0pXtoXR5faLndl6X/AP+En17SPl1/w9K8Y63emHzU+pQ/MB9a2NJ8UaJrmBp+owyyf88idsg/4CcGsfZ410UfJJZ69bL/C/wDo8+Pr90/zqtHN4b8U6ollq3h+ez1blwtxblGbbySJF6j6msFOSdr/AH/5rQ9aeGoVIufLousHf5uEveX3pHcUVxmg6efFOlDWtQvr8S3Tu0McF08S26BiqhQpAJwMknNavhO+urvTrqC8lM09heS2bTEYMoQ8MffBGa1jU5rab7HDWwfs1K0ruDtJf5d9dOhvUUUVocQUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABUF1e29l5P2iUR+dKsMef4nboKnrl/G/n/ZtG+zeX5/9rW/l+Znbu+bGcc4qJy5YtnRhaKrVY027XOhuL23tHgSeUI1xJ5UQP8AE+CcfkDVPUPEWk6VcC3vL6OOYru8sAswHqQASB7muc1g63/a/h3+0108Q/2ku37MXLbvLfruHTGav+DwrXHiGWQD7WdVmWQn72wY8sfTbjFR7RuXKjreDp06XtZu+nR+dt7P8i3qmtznRYrvw/bLqkty4jhaNgY1Jz8znPCjHP5cVD4f8MDTJ5NT1Gf7drU4/e3TDhB/cQfwqP1/SovDIWPxD4nitwBZreRlAv3RIY1MmPxxmunpxXP70iK9R4dOhS0Ts2+rTSaT9L6pbvfoFFFFannhRRRQAUUUUAcxbaPruipLaaPPp72DSNJCt0rhoNxJKjbwwySR0PNauhaQui6d9nMxnmkkeaeZhgySMcs2O30rSoqI01F3R01cXUqxalbXV6at+f8AXnuFFFFWcwUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABVW90+21D7P9pQt9nnW4jwxGHXoeOvWrVFJpPcqMnF3i7Mq3en219JayXCFmtZhNEQxG1wCM+/BNUr7w3p9/eteH7Rb3LqFkltbh4WkA6BtpGfxrXopOMXui4V6sLckmirp+m2mlWa2llAsMKknaCSST1JJ5J9zVqiimkloiJSlJuUndsKKKKZIUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQB//9k=",
+ "image/png": "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",
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 7,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "# Modify phosphorylated Serine with Acetyl N-terminal modification\n",
+ "mod_phos_ser = modify_amino_acid(ptm_dict[\"Phospho@S\"], n_term_mod=\"Acetyl@Any_N-term\")\n",
+ "print(\"Modified Phosphorylated Serine SMILES:\", mod_phos_ser)\n",
+ "mod_phos_ser_mol = Chem.MolFromSmiles(mod_phos_ser)\n",
+ "Draw.MolToImage(mod_phos_ser_mol)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Conclusion\n",
+ "\n",
+ "In this tutorial, we've explored how to work with amino acids and post-translational modifications using SMILES notation and the `modify_amino_acid` function. We've seen how to:\n",
+ "\n",
+ "1. Represent amino acids using SMILES\n",
+ "2. Add N-terminal modifications\n",
+ "3. Work with post-translational modifications\n",
+ "4. Visualize molecular structures using RDKit\n",
+ "\n"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "alphabase",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.12.4"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/nbs_tests/constants/aa.ipynb b/nbs_tests/constants/aa.ipynb
index 9c352d60..02b112cd 100644
--- a/nbs_tests/constants/aa.ipynb
+++ b/nbs_tests/constants/aa.ipynb
@@ -2,7 +2,7 @@
"cells": [
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
@@ -29,7 +29,7 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
@@ -38,7 +38,7 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 3,
"metadata": {},
"outputs": [
{
@@ -64,6 +64,7 @@
" | \n",
" aa | \n",
" formula | \n",
+ " smiles | \n",
" mass | \n",
" \n",
" \n",
@@ -72,156 +73,182 @@
" 65 | \n",
" A | \n",
" C(3)H(5)N(1)O(1)S(0) | \n",
+ " N([Xe])([Xe])[C@@]([H])(C)C(=O)[Rn] | \n",
" 7.103711e+01 | \n",
" \n",
" \n",
" 66 | \n",
" B | \n",
" C(1000000) | \n",
+ " NaN | \n",
" 1.200000e+07 | \n",
"
\n",
" \n",
" 67 | \n",
" C | \n",
" C(3)H(5)N(1)O(1)S(1) | \n",
+ " N([Xe])([Xe])[C@@]([H])(CS)C(=O)[Rn] | \n",
" 1.030092e+02 | \n",
"
\n",
" \n",
" 68 | \n",
" D | \n",
" C(4)H(5)N(1)O(3)S(0) | \n",
+ " N([Xe])([Xe])[C@@]([H])(CC(=O)O)C(=O)[Rn] | \n",
" 1.150269e+02 | \n",
"
\n",
" \n",
" 69 | \n",
" E | \n",
" C(5)H(7)N(1)O(3)S(0) | \n",
+ " N([Xe])([Xe])[C@@]([H])(CCC(=O)O)C(=O)[Rn] | \n",
" 1.290426e+02 | \n",
"
\n",
" \n",
" 70 | \n",
" F | \n",
" C(9)H(9)N(1)O(1)S(0) | \n",
+ " N([Xe])([Xe])[C@@]([H])(Cc1ccccc1)C(=O)[Rn] | \n",
" 1.470684e+02 | \n",
"
\n",
" \n",
" 71 | \n",
" G | \n",
" C(2)H(3)N(1)O(1)S(0) | \n",
+ " N([Xe])([Xe])CC(=O)[Rn] | \n",
" 5.702146e+01 | \n",
"
\n",
" \n",
" 72 | \n",
" H | \n",
" C(6)H(7)N(3)O(1)S(0) | \n",
+ " N([Xe])([Xe])[C@@]([H])(CC1=CN=C-N1)C(=O)[Rn] | \n",
" 1.370589e+02 | \n",
"
\n",
" \n",
" 73 | \n",
" I | \n",
" C(6)H(11)N(1)O(1)S(0) | \n",
+ " N([Xe])([Xe])[C@@]([H])([C@]([H])(CC)C)C(=O)[Rn] | \n",
" 1.130841e+02 | \n",
"
\n",
" \n",
" 74 | \n",
" J | \n",
" C(6)H(11)N(1)O(1)S(0) | \n",
+ " NaN | \n",
" 1.130841e+02 | \n",
"
\n",
" \n",
" 75 | \n",
" K | \n",
" C(6)H(12)N(2)O(1)S(0) | \n",
+ " N([Xe])([Xe])[C@@]([H])(CCCCN)C(=O)[Rn] | \n",
" 1.280950e+02 | \n",
"
\n",
" \n",
" 76 | \n",
" L | \n",
" C(6)H(11)N(1)O(1)S(0) | \n",
+ " N([Xe])([Xe])[C@@]([H])(CC(C)C)C(=O)[Rn] | \n",
" 1.130841e+02 | \n",
"
\n",
" \n",
" 77 | \n",
" M | \n",
" C(5)H(9)N(1)O(1)S(1) | \n",
+ " N([Xe])([Xe])[C@@]([H])(CCSC)C(=O)[Rn] | \n",
" 1.310405e+02 | \n",
"
\n",
" \n",
" 78 | \n",
" N | \n",
" C(4)H(6)N(2)O(2)S(0) | \n",
+ " N([Xe])([Xe])[C@@]([H])(CC(=O)N)C(=O)[Rn] | \n",
" 1.140429e+02 | \n",
"
\n",
" \n",
" 79 | \n",
" O | \n",
" C(12)H(19)N(3)O(2) | \n",
+ " C[C@@H]1CC=N[C@H]1C(=O)NCCCC[C@@H](C(=O)[Rn])N... | \n",
" 2.371477e+02 | \n",
"
\n",
" \n",
" 80 | \n",
" P | \n",
" C(5)H(7)N(1)O(1)S(0) | \n",
+ " N1([Xe])[C@@]([H])(CCC1)C(=O)[Rn] | \n",
" 9.705276e+01 | \n",
"
\n",
" \n",
" 81 | \n",
" Q | \n",
" C(5)H(8)N(2)O(2)S(0) | \n",
+ " N([Xe])([Xe])[C@@]([H])(CCC(=O)N)C(=O)[Rn] | \n",
" 1.280586e+02 | \n",
"
\n",
" \n",
" 82 | \n",
" R | \n",
" C(6)H(12)N(4)O(1)S(0) | \n",
+ " N([Xe])([Xe])[C@@]([H])(CCCNC(=N)N)C(=O)[Rn] | \n",
" 1.561011e+02 | \n",
"
\n",
" \n",
" 83 | \n",
" S | \n",
" C(3)H(5)N(1)O(2)S(0) | \n",
+ " N([Xe])([Xe])[C@@]([H])(CO)C(=O)[Rn] | \n",
" 8.703203e+01 | \n",
"
\n",
" \n",
" 84 | \n",
" T | \n",
" C(4)H(7)N(1)O(2)S(0) | \n",
+ " N([Xe])([Xe])[C@@]([H])([C@]([H])(O)C)C(=O)[Rn] | \n",
" 1.010477e+02 | \n",
"
\n",
" \n",
" 85 | \n",
" U | \n",
" C(3)H(5)N(1)O(1)Se(1) | \n",
+ " N([Xe])([Xe])[C@@]([H])(C[Se][H])C(=O)[Rn] | \n",
" 1.509536e+02 | \n",
"
\n",
" \n",
" 86 | \n",
" V | \n",
" C(5)H(9)N(1)O(1)S(0) | \n",
+ " N([Xe])([Xe])[C@@]([H])(C(C)C)C(=O)[Rn] | \n",
" 9.906841e+01 | \n",
"
\n",
" \n",
" 87 | \n",
" W | \n",
" C(11)H(10)N(2)O(1)S(0) | \n",
+ " N([Xe])([Xe])[C@@]([H])(CC(=CN2)C1=C2C=CC=C1)C... | \n",
" 1.860793e+02 | \n",
"
\n",
" \n",
" 88 | \n",
" X | \n",
" C(1000000) | \n",
+ " NaN | \n",
" 1.200000e+07 | \n",
"
\n",
" \n",
" 89 | \n",
" Y | \n",
" C(9)H(9)N(1)O(2)S(0) | \n",
+ " N([Xe])([Xe])[C@@]([H])(Cc1ccc(O)cc1)C(=O)[Rn] | \n",
" 1.630633e+02 | \n",
"
\n",
" \n",
" 90 | \n",
" Z | \n",
" C(1000000) | \n",
+ " NaN | \n",
" 1.200000e+07 | \n",
"
\n",
" \n",
@@ -229,36 +256,64 @@
""
],
"text/plain": [
- " aa formula mass\n",
- "65 A C(3)H(5)N(1)O(1)S(0) 7.103711e+01\n",
- "66 B C(1000000) 1.200000e+07\n",
- "67 C C(3)H(5)N(1)O(1)S(1) 1.030092e+02\n",
- "68 D C(4)H(5)N(1)O(3)S(0) 1.150269e+02\n",
- "69 E C(5)H(7)N(1)O(3)S(0) 1.290426e+02\n",
- "70 F C(9)H(9)N(1)O(1)S(0) 1.470684e+02\n",
- "71 G C(2)H(3)N(1)O(1)S(0) 5.702146e+01\n",
- "72 H C(6)H(7)N(3)O(1)S(0) 1.370589e+02\n",
- "73 I C(6)H(11)N(1)O(1)S(0) 1.130841e+02\n",
- "74 J C(6)H(11)N(1)O(1)S(0) 1.130841e+02\n",
- "75 K C(6)H(12)N(2)O(1)S(0) 1.280950e+02\n",
- "76 L C(6)H(11)N(1)O(1)S(0) 1.130841e+02\n",
- "77 M C(5)H(9)N(1)O(1)S(1) 1.310405e+02\n",
- "78 N C(4)H(6)N(2)O(2)S(0) 1.140429e+02\n",
- "79 O C(12)H(19)N(3)O(2) 2.371477e+02\n",
- "80 P C(5)H(7)N(1)O(1)S(0) 9.705276e+01\n",
- "81 Q C(5)H(8)N(2)O(2)S(0) 1.280586e+02\n",
- "82 R C(6)H(12)N(4)O(1)S(0) 1.561011e+02\n",
- "83 S C(3)H(5)N(1)O(2)S(0) 8.703203e+01\n",
- "84 T C(4)H(7)N(1)O(2)S(0) 1.010477e+02\n",
- "85 U C(3)H(5)N(1)O(1)Se(1) 1.509536e+02\n",
- "86 V C(5)H(9)N(1)O(1)S(0) 9.906841e+01\n",
- "87 W C(11)H(10)N(2)O(1)S(0) 1.860793e+02\n",
- "88 X C(1000000) 1.200000e+07\n",
- "89 Y C(9)H(9)N(1)O(2)S(0) 1.630633e+02\n",
- "90 Z C(1000000) 1.200000e+07"
+ " aa formula \\\n",
+ "65 A C(3)H(5)N(1)O(1)S(0) \n",
+ "66 B C(1000000) \n",
+ "67 C C(3)H(5)N(1)O(1)S(1) \n",
+ "68 D C(4)H(5)N(1)O(3)S(0) \n",
+ "69 E C(5)H(7)N(1)O(3)S(0) \n",
+ "70 F C(9)H(9)N(1)O(1)S(0) \n",
+ "71 G C(2)H(3)N(1)O(1)S(0) \n",
+ "72 H C(6)H(7)N(3)O(1)S(0) \n",
+ "73 I C(6)H(11)N(1)O(1)S(0) \n",
+ "74 J C(6)H(11)N(1)O(1)S(0) \n",
+ "75 K C(6)H(12)N(2)O(1)S(0) \n",
+ "76 L C(6)H(11)N(1)O(1)S(0) \n",
+ "77 M C(5)H(9)N(1)O(1)S(1) \n",
+ "78 N C(4)H(6)N(2)O(2)S(0) \n",
+ "79 O C(12)H(19)N(3)O(2) \n",
+ "80 P C(5)H(7)N(1)O(1)S(0) \n",
+ "81 Q C(5)H(8)N(2)O(2)S(0) \n",
+ "82 R C(6)H(12)N(4)O(1)S(0) \n",
+ "83 S C(3)H(5)N(1)O(2)S(0) \n",
+ "84 T C(4)H(7)N(1)O(2)S(0) \n",
+ "85 U C(3)H(5)N(1)O(1)Se(1) \n",
+ "86 V C(5)H(9)N(1)O(1)S(0) \n",
+ "87 W C(11)H(10)N(2)O(1)S(0) \n",
+ "88 X C(1000000) \n",
+ "89 Y C(9)H(9)N(1)O(2)S(0) \n",
+ "90 Z C(1000000) \n",
+ "\n",
+ " smiles mass \n",
+ "65 N([Xe])([Xe])[C@@]([H])(C)C(=O)[Rn] 7.103711e+01 \n",
+ "66 NaN 1.200000e+07 \n",
+ "67 N([Xe])([Xe])[C@@]([H])(CS)C(=O)[Rn] 1.030092e+02 \n",
+ "68 N([Xe])([Xe])[C@@]([H])(CC(=O)O)C(=O)[Rn] 1.150269e+02 \n",
+ "69 N([Xe])([Xe])[C@@]([H])(CCC(=O)O)C(=O)[Rn] 1.290426e+02 \n",
+ "70 N([Xe])([Xe])[C@@]([H])(Cc1ccccc1)C(=O)[Rn] 1.470684e+02 \n",
+ "71 N([Xe])([Xe])CC(=O)[Rn] 5.702146e+01 \n",
+ "72 N([Xe])([Xe])[C@@]([H])(CC1=CN=C-N1)C(=O)[Rn] 1.370589e+02 \n",
+ "73 N([Xe])([Xe])[C@@]([H])([C@]([H])(CC)C)C(=O)[Rn] 1.130841e+02 \n",
+ "74 NaN 1.130841e+02 \n",
+ "75 N([Xe])([Xe])[C@@]([H])(CCCCN)C(=O)[Rn] 1.280950e+02 \n",
+ "76 N([Xe])([Xe])[C@@]([H])(CC(C)C)C(=O)[Rn] 1.130841e+02 \n",
+ "77 N([Xe])([Xe])[C@@]([H])(CCSC)C(=O)[Rn] 1.310405e+02 \n",
+ "78 N([Xe])([Xe])[C@@]([H])(CC(=O)N)C(=O)[Rn] 1.140429e+02 \n",
+ "79 C[C@@H]1CC=N[C@H]1C(=O)NCCCC[C@@H](C(=O)[Rn])N... 2.371477e+02 \n",
+ "80 N1([Xe])[C@@]([H])(CCC1)C(=O)[Rn] 9.705276e+01 \n",
+ "81 N([Xe])([Xe])[C@@]([H])(CCC(=O)N)C(=O)[Rn] 1.280586e+02 \n",
+ "82 N([Xe])([Xe])[C@@]([H])(CCCNC(=N)N)C(=O)[Rn] 1.561011e+02 \n",
+ "83 N([Xe])([Xe])[C@@]([H])(CO)C(=O)[Rn] 8.703203e+01 \n",
+ "84 N([Xe])([Xe])[C@@]([H])([C@]([H])(O)C)C(=O)[Rn] 1.010477e+02 \n",
+ "85 N([Xe])([Xe])[C@@]([H])(C[Se][H])C(=O)[Rn] 1.509536e+02 \n",
+ "86 N([Xe])([Xe])[C@@]([H])(C(C)C)C(=O)[Rn] 9.906841e+01 \n",
+ "87 N([Xe])([Xe])[C@@]([H])(CC(=CN2)C1=C2C=CC=C1)C... 1.860793e+02 \n",
+ "88 NaN 1.200000e+07 \n",
+ "89 N([Xe])([Xe])[C@@]([H])(Cc1ccc(O)cc1)C(=O)[Rn] 1.630633e+02 \n",
+ "90 NaN 1.200000e+07 "
]
},
- "execution_count": null,
+ "execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
@@ -293,7 +348,7 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
@@ -314,7 +369,7 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
@@ -339,7 +394,7 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
@@ -359,7 +414,7 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 7,
"metadata": {},
"outputs": [
{
@@ -382,7 +437,7 @@
" 453.26996726, 396.24850354, 259.18959168, 146.1055277 ]])}"
]
},
- "execution_count": null,
+ "execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
@@ -404,7 +459,7 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 8,
"metadata": {},
"outputs": [
{
@@ -424,7 +479,7 @@
" 1.28094963e+02]])"
]
},
- "execution_count": null,
+ "execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
@@ -436,7 +491,7 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 9,
"metadata": {},
"outputs": [],
"source": [
@@ -447,16 +502,16 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 13,
"metadata": {},
"outputs": [],
"source": [
"replace_atoms({'N':'15N'})\n",
- "assert '15N' in AA_Formula['A']\n",
- "assert '15N' in AA_Formula['K']\n",
+ "assert '15N' in aa_formula.loc['A'][\"formula\"]\n",
+ "assert '15N' in aa_formula.loc['K'][\"formula\"]\n",
"replace_atoms({\"15N\":'N'})\n",
- "assert '15N' not in AA_Formula['A']\n",
- "assert '15N' not in AA_Formula['K']"
+ "assert '15N' not in aa_formula.loc['A'][\"formula\"]\n",
+ "assert '15N' not in aa_formula.loc['K'][\"formula\"]"
]
},
{
@@ -472,6 +527,18 @@
"display_name": "python3",
"language": "python",
"name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.12.4"
}
},
"nbformat": 4,
diff --git a/nbs_tests/constants/modification.ipynb b/nbs_tests/constants/modification.ipynb
index 355c919a..09e41e0e 100644
--- a/nbs_tests/constants/modification.ipynb
+++ b/nbs_tests/constants/modification.ipynb
@@ -1481,11 +1481,12 @@
"outputs": [],
"source": [
"#| hide\n",
+ "prev_value = (modification.MOD_DF.classification=='User-added').sum()\n",
"add_new_modifications([\n",
" (\"Hello@S\",\"H(2)\"),\n",
" (\"World@S\",\"O(10)\",\"O(3)\")\n",
"])\n",
- "assert (modification.MOD_DF.classification=='User-added').sum()==2\n",
+ "assert (modification.MOD_DF.classification=='User-added').sum() - prev_value == 2\n",
"assert 'Hello@S' in modification.MOD_DF.mod_name\n",
"assert 'World@S' in modification.MOD_DF.mod_name\n",
"assert modification.MOD_DF.loc['World@S','modloss'] > 0\n",
@@ -1504,7 +1505,7 @@
" \"Hi@S\":{'composition':\"H(2)\"},\n",
" \"AlphaX@S\":{'composition':\"O(10)\",'modloss_composition':\"O(3)\"}\n",
"})\n",
- "assert (modification.MOD_DF.classification=='User-added').sum()==4\n",
+ "assert (modification.MOD_DF.classification=='User-added').sum() - prev_value == 4\n",
"assert 'Hi@S' in modification.MOD_DF.mod_name\n",
"assert 'Hi@S' in modification.MOD_DF.index\n",
"assert 'AlphaX@S' in modification.MOD_DF.mod_name\n",
diff --git a/requirements.txt b/requirements.txt
index 8299761f..84de8990 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -16,3 +16,4 @@ dask_expr
pyahocorasick
pyteomics
lxml
+rdkit==2024.3.3
diff --git a/tests/run_tests.sh b/tests/run_tests.sh
index f39c67c6..8a159d9b 100755
--- a/tests/run_tests.sh
+++ b/tests/run_tests.sh
@@ -13,3 +13,4 @@ TUTORIAL_NBS=$(find ../docs/tutorials -name "*.ipynb")
ALL_NBS=$(echo $DOCS_NBS$'\n'$TEST_NBS$'\n'$TUTORIAL_NBS)
python -m pytest --nbmake $(echo $ALL_NBS)
+python -m pytest
diff --git a/tests/test_smiles.py b/tests/test_smiles.py
new file mode 100644
index 00000000..5d611ca1
--- /dev/null
+++ b/tests/test_smiles.py
@@ -0,0 +1,198 @@
+from collections import defaultdict
+
+import pytest
+from rdkit import Chem
+
+from alphabase.constants.atom import ChemicalCompositonFormula
+from alphabase.smiles.smiles import AminoAcidModifier
+
+aa_modifier = AminoAcidModifier()
+modify_amino_acid = aa_modifier.modify_amino_acid
+aa_smiles = aa_modifier.aa_smiles
+n_term_modifications = aa_modifier.n_term_modifications
+c_term_modifications = aa_modifier.c_term_modifications
+ptm_dict = aa_modifier.ptm_dict
+
+
+@pytest.fixture
+def alanine_smiles():
+ return aa_smiles["A"]
+
+
+@pytest.fixture
+def lysine_smiles():
+ return aa_smiles["K"]
+
+
+def test_modify_amino_acid_no_modification(alanine_smiles):
+ result = modify_amino_acid(alanine_smiles)
+ expected = "N[C@@H](C)C(=O)O"
+ assert Chem.MolToSmiles(Chem.MolFromSmiles(result)) == Chem.MolToSmiles(
+ Chem.MolFromSmiles(expected)
+ )
+
+
+def test_modify_amino_acid_n_term_modification(alanine_smiles):
+ result = modify_amino_acid(alanine_smiles, n_term_mod="Acetyl@Any_N-term")
+ expected = "CC(=O)N[C@@H](C)C(=O)O"
+ assert Chem.MolToSmiles(Chem.MolFromSmiles(result)) == Chem.MolToSmiles(
+ Chem.MolFromSmiles(expected)
+ )
+
+
+def test_modify_amino_acid_c_term_modification(alanine_smiles):
+ result = modify_amino_acid(alanine_smiles, c_term_mod="Methyl@Any_C-term")
+ expected = "N[C@@H](C)C(=O)OC"
+ assert Chem.MolToSmiles(Chem.MolFromSmiles(result)) == Chem.MolToSmiles(
+ Chem.MolFromSmiles(expected)
+ )
+
+
+def test_modify_amino_acid_both_modifications(alanine_smiles):
+ result = modify_amino_acid(
+ alanine_smiles, n_term_mod="Acetyl@Any_N-term", c_term_mod="Methyl@Any_C-term"
+ )
+ expected = "CC(=O)N[C@@H](C)C(=O)OC"
+ assert Chem.MolToSmiles(Chem.MolFromSmiles(result)) == Chem.MolToSmiles(
+ Chem.MolFromSmiles(expected)
+ )
+
+
+def test_modify_amino_acid_with_ptm(lysine_smiles):
+ lysine_with_ptm = ptm_dict["Acetyl@K"]
+ result = modify_amino_acid(lysine_with_ptm)
+ expected = "CC(=O)NCCCC[C@H](N)C(=O)O"
+ assert Chem.MolToSmiles(Chem.MolFromSmiles(result)) == Chem.MolToSmiles(
+ Chem.MolFromSmiles(expected)
+ )
+
+
+def test_modify_amino_acid_with_ptm_and_n_term_mod(lysine_smiles):
+ lysine_with_ptm = ptm_dict["Acetyl@K"]
+ result = modify_amino_acid(lysine_with_ptm, n_term_mod="mTRAQ@Any_N-term")
+ expected = "CC(=O)NCCCC[C@H](NC(=O)CN1CCN(C)CC1)C(=O)O"
+ assert Chem.MolToSmiles(Chem.MolFromSmiles(result)) == Chem.MolToSmiles(
+ Chem.MolFromSmiles(expected)
+ )
+
+
+@pytest.mark.parametrize("n_term_mod", n_term_modifications.keys())
+def test_all_n_term_modifications(alanine_smiles, n_term_mod):
+ result = modify_amino_acid(alanine_smiles, n_term_mod=n_term_mod)
+ assert Chem.MolFromSmiles(result) is not None
+
+
+@pytest.mark.parametrize("c_term_mod", c_term_modifications.keys())
+def test_all_c_term_modifications(alanine_smiles, c_term_mod):
+ result = modify_amino_acid(alanine_smiles, c_term_mod=c_term_mod)
+ assert Chem.MolFromSmiles(result) is not None
+
+
+def test_invalid_n_term_modification(alanine_smiles):
+ with pytest.raises(ValueError, match="Unrecognized N-terminal modification"):
+ modify_amino_acid(alanine_smiles, n_term_mod="InvalidMod")
+
+
+def test_invalid_c_term_modification(alanine_smiles):
+ with pytest.raises(ValueError, match="Unrecognized C-terminal modification"):
+ modify_amino_acid(alanine_smiles, c_term_mod="InvalidMod")
+
+
+def test_invalid_amino_acid_smiles():
+ with pytest.raises(ValueError):
+ modify_amino_acid("InvalidSMILES")
+
+
+def test_dimethyl_n_term_modification(alanine_smiles):
+ result = modify_amino_acid(alanine_smiles, n_term_mod="Dimethyl@Any_N-term")
+ expected = "CN(C)[C@@H](C)C(=O)O"
+ assert Chem.MolToSmiles(Chem.MolFromSmiles(result)) == Chem.MolToSmiles(
+ Chem.MolFromSmiles(expected)
+ )
+
+
+@pytest.mark.parametrize("aa, smiles", aa_smiles.items())
+def test_all_amino_acids(aa, smiles):
+ result = modify_amino_acid(smiles)
+ assert Chem.MolFromSmiles(result) is not None
+
+
+@pytest.mark.parametrize("ptm, smiles", ptm_dict.items())
+def test_all_ptms(ptm, smiles):
+ result = modify_amino_acid(smiles)
+ assert Chem.MolFromSmiles(result) is not None
+
+
+@pytest.fixture
+def water_formula():
+ return ChemicalCompositonFormula("H(2)O(1)")
+
+
+@pytest.fixture
+def methane_formula():
+ return ChemicalCompositonFormula("C(1)H(4)")
+
+
+def test_init():
+ formula = ChemicalCompositonFormula("C(6)H(12)O(6)")
+ expected = defaultdict(int, {"C": 6, "H": 12, "O": 6})
+ assert formula.elements == expected
+
+
+def test_init_with_isotopes():
+ formula = ChemicalCompositonFormula("13C(1)C(5)H(12)O(6)")
+ expected = defaultdict(int, {"13C": 1, "C": 5, "H": 12, "O": 6})
+ assert formula.elements == expected
+
+
+def test_from_smiles():
+ formula = ChemicalCompositonFormula.from_smiles("CCO")
+ expected = defaultdict(int, {"C": 2, "H": 6, "O": 1})
+ assert formula.elements == expected
+
+
+def test_str_representation(water_formula):
+ assert str(water_formula) == "H(2)O(1)"
+
+
+def test_repr_representation(water_formula):
+ assert repr(water_formula) == "ChemicalCompositonFormula('H(2)O(1)')"
+
+
+def test_addition(water_formula, methane_formula):
+ result = water_formula + methane_formula
+ expected = ChemicalCompositonFormula("C(1)H(6)O(1)")
+ assert result.elements == expected.elements
+
+
+def test_subtraction(water_formula, methane_formula):
+ result = methane_formula - water_formula
+ expected = ChemicalCompositonFormula("C(1)H(2)O(-1)")
+ assert result.elements == expected.elements
+
+
+def test_parse_formula_with_parentheses():
+ formula = ChemicalCompositonFormula("C(6)H(12)O(6)")
+ expected = defaultdict(int, {"C": 6, "H": 12, "O": 6})
+ assert formula.elements == expected
+
+
+def test_parse_rdkit_formula():
+ formula = ChemicalCompositonFormula._from_rdkit_formula("[13C]CH3OH")
+ expected = defaultdict(int, {"13C": 1, "C": 1, "H": 4, "O": 1})
+ assert formula.elements == expected
+
+
+def test_zero_count_elements():
+ formula = ChemicalCompositonFormula("C(1)H(0)O(2)")
+ assert str(formula) == "C(1)O(2)"
+
+
+def test_error_handling_invalid_rdkit_mol():
+ with pytest.raises(ValueError):
+ ChemicalCompositonFormula.from_smiles("InvalidSMILES")
+
+
+def test_error_unknown_atom():
+ with pytest.raises(ValueError):
+ ChemicalCompositonFormula("C(6)H(12)Atom(6)")