nx_chem_mass.nx source
↩ module page · 124 lines · 5715 B
1// nx_chem_mass.nx -- C2.8a milestone: monoisotopic mass + ion m/z
2// prediction for LC-MS work.
3//
4// Standard atomic weight (Element.mass_q3 in milli-AMU, C1) is used
5// for nominal molecular weight via nx_chem_mol_weight_q3 (C2.3a).
6// For MS work, the EXACT MASS of the most-abundant isotope is needed
7// instead -- e.g., glucose monoisotopic = 180.0634 Da (not 180.156
8// average) because LC-MS resolves a single isotope peak.
9//
10// This file composes on C1 AtomicData.iso_mass_q4 (Q4 micro-AMU) +
11// C2.3a implicit-H to produce exact-mass + protonated-ion m/z.
12//
13// Units:
14// iso_mass_q4 : exact isotope mass * 10000 (Q4 micro-AMU)
15// monoiso_q4 : whole-molecule monoisotopic mass in Q4
16// proton_mass_q4 : 10073 (= 1.00728 Da, the proton mass; used for
17// [M+H]+ ion m/z which differs from monoisotopic
18// by adding a proton, not a neutral H atom; the
19// difference is the electron mass = 0.00055 Da
20// which is below Q4 precision)
21//
22// Honest gaps (DEFERRED):
23// - Multi-charge ions [M+2H]2+, [M+nH]n+: trivially obtainable
24// by dividing m/z by n; left for caller in MVP.
25// - Isotopologue pattern prediction (M+1, M+2 peaks from natural
26// abundance distribution): C2.8b composes on AtomicData
27// iso_abund_ppm field; requires combinatorial expansion over
28// all C/H/N/O isotopes.
29// - Adduct ions [M+Na]+, [M+K]+, [M+NH4]+ common in ESI+:
30// just substitute proton with respective ion mass.
31
32import "nx_chem.nx"
33import "nx_chem_molecule.nx"
34import "nx_chem_periodic.nx"
35
36// Proton mass in Q4 micro-AMU: 1.00728 Da * 10000 = 10073.
37const NX_PROTON_MASS_Q4: nx_int = 10073
38// Sodium adduct mass (²³Na exact): 22.9898 Da * 10000 = 229898.
39// Subtract proton -> 219825 = Na+ ion contribution for [M+Na]+.
40const NX_SODIUM_MASS_Q4: nx_int = 229898
41const NX_SODIUM_ION_DELTA_Q4: nx_int = 219825
42// Ammonium adduct [NH4]+ exact: N(14.0031) + 4*H(1.00783) - electron =
43// 18.03437. Q4 = 180344.
44const NX_AMMONIUM_ION_Q4: nx_int = 180344
45
46// =================================================================
47// Compute monoisotopic mass of molecule m using the AtomicData table.
48// Returns mass in Q4 micro-AMU (multiply by 0.0001 for Da).
49//
50// Caller MUST run nx_chem_compute_implicit_h(m) FIRST so non-bracket
51// atoms have h_count populated. Caller passes the AtomicData table
52// (typically from nx_chem_atomic_data_table_118()).
53//
54// Algorithm:
55// For each atom i:
56// total += AtomicData[z-1].iso_mass_q4
57// if h_count > 0: total += h_count * AtomicData[0].iso_mass_q4 (H)
58// =================================================================
59func nx_chem_mol_monoisotopic_mass_q4(m: *MolGraph, ad_table: *AtomicData) -> nx_int {
60 let h_data: *AtomicData = ((ad_table as nx_int) + (0 * NX_ATOMIC_DATA_BYTES)) as *AtomicData
61 let h_mass: nx_int = h_data.iso_mass_q4
62 var total: nx_int = 0
63 var i: nx_int = 0
64 while i < m.n_atoms {
65 let a: *Atom = ((m.atoms as nx_int) + (i * NX_ATOM_BYTES)) as *Atom
66 if a.z >= 1 {
67 if a.z <= 118 {
68 let ad: *AtomicData = ((ad_table as nx_int) + ((a.z - 1) * NX_ATOMIC_DATA_BYTES)) as *AtomicData
69 total = total + ad.iso_mass_q4
70 }
71 }
72 if a.h_count > 0 {
73 total = total + (a.h_count * h_mass)
74 }
75 i = i + 1
76 }
77 return total
78}
79
80// =================================================================
81// [M+H]+ ion m/z: monoisotopic mass + proton. Singly charged so
82// m/z == mass + proton (no division by charge).
83// =================================================================
84func nx_chem_mol_mh_plus_q4(m: *MolGraph, ad_table: *AtomicData) -> nx_int {
85 return nx_chem_mol_monoisotopic_mass_q4(m, ad_table) + NX_PROTON_MASS_Q4
86}
87
88// =================================================================
89// [M-H]- ion m/z: monoisotopic mass - proton.
90// =================================================================
91func nx_chem_mol_mh_minus_q4(m: *MolGraph, ad_table: *AtomicData) -> nx_int {
92 return nx_chem_mol_monoisotopic_mass_q4(m, ad_table) - NX_PROTON_MASS_Q4
93}
94
95// =================================================================
96// [M+Na]+ ion m/z: monoisotopic mass + Na - electron.
97// Common in ESI+ when sodium is present in the sample (e.g., MS run
98// with sodium-containing buffer).
99// =================================================================
100func nx_chem_mol_mna_plus_q4(m: *MolGraph, ad_table: *AtomicData) -> nx_int {
101 return nx_chem_mol_monoisotopic_mass_q4(m, ad_table) + NX_SODIUM_ION_DELTA_Q4
102}
103
104// =================================================================
105// [M+NH4]+ ion m/z: monoisotopic mass + NH4+ ion mass.
106// Common in ESI+ with ammonium-acetate buffer.
107// =================================================================
108func nx_chem_mol_mnh4_plus_q4(m: *MolGraph, ad_table: *AtomicData) -> nx_int {
109 return nx_chem_mol_monoisotopic_mass_q4(m, ad_table) + NX_AMMONIUM_ION_Q4
110}
111
112// =================================================================
113// Multi-charge [M+nH]n+ ion m/z: (monoisotopic + n*proton) / n.
114// Common for proteins / large molecules in ESI+ which carry multiple
115// charges in observed spectrum.
116//
117// For n=1, equivalent to [M+H]+. For n=2, m/z is roughly half the
118// singly-charged value.
119// =================================================================
120func nx_chem_mol_multi_charge_mh_q4(m: *MolGraph, ad_table: *AtomicData, n_charge: nx_int) -> nx_int {
121 if n_charge <= 0 { return 0 }
122 let mh_total: nx_int = nx_chem_mol_monoisotopic_mass_q4(m, ad_table) + (n_charge * NX_PROTON_MASS_Q4)
123 return mh_total / n_charge
124}