CandidateSearch 1.1.2
Proof-of-concept implementation of a search engine that uses sparse matrix multiplication to identify the best peptide candidates for a given mass spectrum.
Loading...
Searching...
No Matches
IonCalculator.cs
Go to the documentation of this file.
1using System.Text;
3
5{
6 public static class IonCalculator
7 {
8 static double massH_MonoMass = ChemicalUtils.Chemicals["H"].MonoMass;
9 static double massProton_MonoMass = ChemicalUtils.Chemicals["p"].MonoMass;
10 static double massO_MonoMass = ChemicalUtils.Chemicals["O"].MonoMass;
11 static double massC_MonoMass = ChemicalUtils.Chemicals["C"].MonoMass;
12 static double massN_MonoMass = ChemicalUtils.Chemicals["N"].MonoMass;
13
14 static double massH_AvgMass = ChemicalUtils.Chemicals["H"].AvgMass;
15 static double massProton_AvgMass = ChemicalUtils.Chemicals["p"].AvgMass;
16 static double massO_AvgMass = ChemicalUtils.Chemicals["O"].AvgMass;
17 static double massC_AvgMass = ChemicalUtils.Chemicals["C"].AvgMass;
18 static double massN_AvgMass = ChemicalUtils.Chemicals["N"].AvgMass;
19
20 public static bool CalculateIons(out double[] outIonsNoNL,
21 out IonWithNL[] outIonsWithNL,
22 byte[] sequence,
23 double mass,
24 int charge,
25 Modification[] mods,
26 int maxNumberNeutralLoss,
27 int maxNumberNeutralLossModifications,
28 double lowerBound,
29 double upperBound,
30 bool mono,
31 string maxAllowedChargeState)
32 {
33 var setting = new InstrumentSetting() { UseBIon = true, UseYIon = true };
34
35 var AminoAcids = new AminoAcid[byte.MaxValue];
36 foreach (AminoAcid aa in ChemicalUtils.AminoAcids.Values)
37 {
38 AminoAcids[(byte) aa.OneLetterCode] = aa;
39 }
40
41 var MaxNrWaterLosses = 0;
42 var MaxNrAmmoniaLosses = 0;
43
44 outIonsNoNL = null;
45 outIonsWithNL = null;
46
47 int numIonsExpected = sequence.Length;
48 {
49 int numExpectedFactor = 0;
50 if (setting.UseAIon) ++numExpectedFactor;
51 if (setting.UseBIon) ++numExpectedFactor;
52 if (setting.UseCIon) ++numExpectedFactor;
53 if (setting.UseXIon) ++numExpectedFactor;
54 if (setting.UseYIon) ++numExpectedFactor;
55 if (setting.UseZIon) ++numExpectedFactor;
56 if (setting.UseZPlusHIon) ++numExpectedFactor;
57 if (setting.UseZPlusTwoHIon) ++numExpectedFactor;
58 // new ions
59 if (setting.UseAPlusHIon) ++numExpectedFactor;
60 if (setting.UseBPlusHIon) ++numExpectedFactor;
61 if (setting.UseCPlusHIon) ++numExpectedFactor;
62 if (setting.UseXPlusHIon) ++numExpectedFactor;
63 if (setting.UseYPlusHIon) ++numExpectedFactor;
64 if (setting.UseAMinusHIon) ++numExpectedFactor;
65 if (setting.UseBMinusHIon) ++numExpectedFactor;
66 if (setting.UseCMinusHIon) ++numExpectedFactor;
67 if (setting.UseXMinusHIon) ++numExpectedFactor;
68 if (setting.UseYMinusHIon) ++numExpectedFactor;
69 if (setting.UseZMinusHIon) ++numExpectedFactor;
70 if (numExpectedFactor < 2)
71 numExpectedFactor = 2;
72
73 numIonsExpected *= numExpectedFactor;
74 }
75
76 List<double> ionsNoNL = new List<double>(numIonsExpected); // ions without neutral Losses
77 List<IonWithNL> ionsWithNL = (setting.UseWaterLosses || setting.UseAmmoniaLosses) ? new List<IonWithNL>(numIonsExpected) : null; // ions without neutral Losses
78
79 double massH = massH_MonoMass;
80 double massProton = massProton_MonoMass;
81 double massO = massO_MonoMass;
82 double massC = massC_MonoMass;
83 double massN = massN_MonoMass;
84 if (!mono)
85 {
86 massH = massH_AvgMass;
87 massProton = massProton_AvgMass;
88 massO = massO_AvgMass;
89 massC = massC_AvgMass;
90 massN = massN_AvgMass;
91 }
92
93 try
94 {
95 //fwd: n-terminal ions
96 //rev: c-terminal ions
97 int possibleAmoniaLosses = 0;
98 int possibleWaterLosses = 0;
99 int possibleAmoniaLossesRev = 0;
100 int possibleWaterLossesRev = 0;
101 //N-Term
102 double lastMassFwd = massH + massProton;
103 double lastMassInternalFwd = mass - massH - massO - massH + massProton;
104 double lastMassInternalRev = mass - massH - massO - massH + massProton;
105
106 //N-Terminal modification
107 if (mods[0] != null)
108 {
109 lastMassFwd += mods[0].Mass(mono);
110 }
111 //C-Term
112 double lastMassRev = massH + massO + massProton;
113 //C-Terminal modification
114 if (mods[mods.Length - 1] != null)
115 {
116 lastMassRev += mods[mods.Length - 1].Mass(mono);
117 }
118 //possible neutral losses of specific modifications
119 List<double> fwdLosses = new List<double>();
120 List<double> revLosses = new List<double>();
121
122
123 #region ion calculation
124 int j = sequence.Length - 1;
125 for (int i = 0; i < sequence.Length - 1; ++i, --j)
126 {
127 byte aaFwd = sequence[i];
128 if (null == AminoAcids[aaFwd])
129 return false;
130 byte aaRev = sequence[j];
131 if (null == AminoAcids[aaRev])
132 return false;
133 AminoAcid currentAAFwd = AminoAcids[aaFwd];
134 lastMassFwd += currentAAFwd.Mass(mono);
135
136 //AA is modified
137 if (mods[i + 1] != null)
138 lastMassFwd += mods[i + 1].Mass(mono);
139
140 //Modification can have neutral loss
141 if (mods[i + 1] != null && mods[i + 1].NeutralLosses.Length > 0)
142 {
143 //HashSet<double> currentFwd = new HashSet<double>();
144 //add new neutral losses to existing losses
145 //neutral losses of modifications are either fixed or not;
146 //if not fixed, the unimod file contains two entries for neutral losses:
147 //1. the neutral loss mass; 2. the mass 0.0, to declare neutral loss is not fixed
148 //foreach (double lossMass in fwdLosses)
149 for (int m = 0; m < mods[i + 1].NeutralLosses.Length; ++m)
150 {
151 double newLoss = mods[i + 1].NeutralLosses[m];
152 int count = 0;
153 while (newLoss > 0 && fwdLosses.Contains(newLoss) && count < maxNumberNeutralLossModifications)
154 {
155 newLoss += newLoss;
156 ++count;
157 }
158 if (count < maxNumberNeutralLossModifications)
159 {
160 if (!fwdLosses.Contains(newLoss))
161 fwdLosses.Add(newLoss);
162 }
163 }
164 fwdLosses.Sort();
165 }
166 //same for reverse ions
167 AminoAcid currentAARev = AminoAcids[aaRev];
168 lastMassRev += currentAARev.Mass(mono);
169
170 if (mods[j + 1] != null)
171 {
172 lastMassRev += mods[j + 1].Mass(mono);
173 }
174 if (mods[j + 1] != null && mods[j + 1].NeutralLosses.Length > 0)
175 {
176 for (int m = 0; m < mods[j + 1].NeutralLosses.Length; ++m)
177 {
178 double newLoss = mods[j + 1].NeutralLosses[m];
179 int count = 0;
180 while (newLoss > 0 && revLosses.Contains(newLoss) && count < maxNumberNeutralLossModifications)
181 {
182 newLoss += newLoss;
183 ++count;
184 }
185 if (count < maxNumberNeutralLossModifications)
186 {
187 if (!revLosses.Contains(newLoss))
188 revLosses.Add(newLoss);
189 }
190 }
191 revLosses.Sort();
192 }
193
194 //if ion contains AA S,T,E or D water loss is possible
195 if ((aaFwd == 'S' || aaFwd == 'T' || aaFwd == 'E' || aaFwd == 'D'))
196 {
197 if (possibleWaterLosses < maxNumberNeutralLoss)
198 {
199 ++possibleWaterLosses;
200 }
201 ++MaxNrWaterLosses;
202 }
203 //if ion contains AA N,Q,R or K ammonia loss is possible
204 if ((aaFwd == 'N' || aaFwd == 'Q' || aaFwd == 'R' || aaFwd == 'K'))
205 {
206 if (possibleAmoniaLosses < maxNumberNeutralLoss)
207 {
208 ++possibleAmoniaLosses;
209 }
210 ++MaxNrAmmoniaLosses;
211 }
212 if (setting.UseImmoniumIons)
213 {
214 //calculate immonium ion
215 double massImmo = (double)(currentAAFwd.ImmoniumMass);
216 //if AA is modified, add modification
217 if (mods[i + 1] != null)
218 {
219 massImmo += mods[i + 1].Mass(mono);
220 }
221 if (InRange(massImmo, lowerBound, upperBound))
222 {
223 ionsNoNL.Add(massImmo);
224 }
225 //also immonium ion for last AA
226 if (j == sequence.Length - 1)
227 {
228 double massImmoRev = currentAARev.ImmoniumMass;
229 if (mods[j + 1] != null)
230 {
231 massImmoRev += mods[j + 1].Mass(mono);
232 }
233 if (InRange(massImmoRev, lowerBound, upperBound))
234 {
235 ionsNoNL.Add(massImmoRev);
236 }
237 }
238 }
239 if ((aaRev == 'S' || aaRev == 'T' || aaRev == 'E' || aaRev == 'D') && possibleWaterLossesRev < maxNumberNeutralLoss)
240 {
241 ++possibleWaterLossesRev;
242 }
243 if ((aaRev == 'N' || aaRev == 'Q' || aaRev == 'R' || aaRev == 'K') && possibleAmoniaLossesRev < maxNumberNeutralLoss)
244 ++possibleAmoniaLossesRev;
245
246 if (setting.UseInternalFragments)
247 {
248 if (i == 0)
249 {
250 lastMassInternalFwd -= lastMassFwd - lastMassRev;
251 if (mods[i + 1] != null)
252 {
253 lastMassInternalFwd -= mods[i + 1].Mass(mono);
254 }
255 if (mods[j + 1] != null)
256 {
257 lastMassInternalFwd -= mods[j + 1].Mass(mono);
258 }
259 if (InRange(lastMassInternalFwd, lowerBound, upperBound))
260 {
261 ionsNoNL.Add(lastMassInternalFwd);
262 }
263 CalculateAllInternalCombinations(lastMassInternalFwd, i, ionsNoNL, sequence, mods, lowerBound, upperBound, mono, AminoAcids);
264 }
265 else if (i > 0 && i < sequence.Length - 1)
266 {
267 lastMassInternalFwd -= currentAAFwd.Mass(mono);
268 if (mods[i + 1] != null)
269 {
270 lastMassInternalFwd -= mods[i + 1].Mass(mono);
271 }
272 if (InRange(lastMassInternalFwd, lowerBound, upperBound))
273 {
274 ionsNoNL.Add(lastMassInternalFwd);
275 }
276 CalculateAllInternalCombinations(lastMassInternalFwd, i, ionsNoNL, sequence, mods, lowerBound, upperBound, mono, AminoAcids);
277 }
278 }
279
280 if (setting.UseAIon)
281 {
282 //a ion
283 double aIon = lastMassFwd - massC - massH - massO;
284 CalculateIon(charge, ionsNoNL, ionsWithNL, possibleAmoniaLosses, possibleWaterLosses, massO, massH, massC,
285 massN, massProton, fwdLosses, setting, aIon, lowerBound, upperBound, maxAllowedChargeState);
286 }
287
288 if (setting.UseBIon)
289 {
290 //b ion
291 if (i > 0)
292 {
293 double bIon = lastMassFwd - massH;
294 CalculateIon(charge, ionsNoNL, ionsWithNL, possibleAmoniaLosses, possibleWaterLosses, massO, massH, massC,
295 massN, massProton, fwdLosses, setting, bIon, lowerBound, upperBound, maxAllowedChargeState);
296 }
297 }
298
299 if (setting.UseCIon)
300 {
301 //c
302 double cIon = lastMassFwd + massN + massH * 2;
303 CalculateIon(charge, ionsNoNL, ionsWithNL, possibleAmoniaLosses, possibleWaterLosses, massO, massH, massC,
304 massN, massProton, fwdLosses, setting, cIon, lowerBound, upperBound, maxAllowedChargeState);
305 }
306
307 if (setting.UseXIon)
308 {
309 //x
310 double xIon = lastMassRev + massC + massO - massH;
311 CalculateIon(charge, ionsNoNL, ionsWithNL, possibleAmoniaLossesRev, possibleWaterLossesRev, massO, massH, massC,
312 massN, massProton, revLosses, setting, xIon, lowerBound, upperBound, maxAllowedChargeState);
313 }
314
315 if (setting.UseYIon)
316 {
317 //y
318 double yIon = lastMassRev + massH;
319 CalculateIon(charge, ionsNoNL, ionsWithNL, possibleAmoniaLossesRev, possibleWaterLossesRev, massO, massH, massC,
320 massN, massProton, revLosses, setting, yIon, lowerBound, upperBound, maxAllowedChargeState);
321 }
322
323 if (setting.UseZIon)
324 {
325 //z
326 double zIon = lastMassRev - massN - massH * 2;
327 CalculateIon(charge, ionsNoNL, ionsWithNL, possibleAmoniaLossesRev, possibleWaterLossesRev, massO, massH, massC,
328 massN, massProton, revLosses, setting, zIon, lowerBound, upperBound, maxAllowedChargeState);
329 }
330 if (setting.UseZPlusHIon)
331 {
332 //z + 1
333 double zPlusHIon = lastMassRev - massN - massH;
334 CalculateIon(charge, ionsNoNL, ionsWithNL, possibleAmoniaLossesRev, possibleWaterLossesRev, massO, massH, massC,
335 massN, massProton, revLosses, setting, zPlusHIon, lowerBound, upperBound, maxAllowedChargeState);
336 }
337 if (setting.UseZPlusTwoHIon)
338 {
339 //z + 2
340 double zPlusTwoHIon = lastMassRev - massN;
341 CalculateIon(charge, ionsNoNL, ionsWithNL, possibleAmoniaLossesRev, possibleWaterLossesRev, massO, massH, massC,
342 massN, massProton, revLosses, setting, zPlusTwoHIon, lowerBound, upperBound, maxAllowedChargeState);
343 }
344 // new ions
345 if (setting.UseAPlusHIon)
346 {
347 //a + 1
348 double aIon = lastMassFwd - massC - massO;
349 CalculateIon(charge, ionsNoNL, ionsWithNL, possibleAmoniaLosses, possibleWaterLosses, massO,
350 massH, massC,
351 massN, massProton, fwdLosses, setting, aIon, lowerBound, upperBound, maxAllowedChargeState);
352 }
353 if (setting.UseAMinusHIon)
354 {
355 //a - 1
356 double aIon = lastMassFwd - massC - massO - 2 * massH;
357 CalculateIon(charge, ionsNoNL, ionsWithNL, possibleAmoniaLosses, possibleWaterLosses, massO,
358 massH, massC,
359 massN, massProton, fwdLosses, setting, aIon, lowerBound, upperBound, maxAllowedChargeState);
360 }
361 if (setting.UseBPlusHIon)
362 {
363 //b + 1
364 if (i > 0)
365 {
366 double bIon = lastMassFwd;
367 CalculateIon(charge, ionsNoNL, ionsWithNL, possibleAmoniaLosses, possibleWaterLosses, massO,
368 massH, massC,
369 massN, massProton, fwdLosses, setting, bIon, lowerBound, upperBound, maxAllowedChargeState);
370 }
371 }
372 if (setting.UseBMinusHIon)
373 {
374 //b - 1
375 if (i > 0)
376 {
377 double bIon = lastMassFwd - 2 * massH;
378 CalculateIon(charge, ionsNoNL, ionsWithNL, possibleAmoniaLosses, possibleWaterLosses, massO,
379 massH, massC,
380 massN, massProton, fwdLosses, setting, bIon, lowerBound, upperBound, maxAllowedChargeState);
381 }
382 }
383 if (setting.UseCPlusHIon)
384 {
385 //c +1
386 double cIon = lastMassFwd + massN + 3 * massH;
387 CalculateIon(charge, ionsNoNL, ionsWithNL, possibleAmoniaLosses, possibleWaterLosses, massO,
388 massH, massC,
389 massN, massProton, fwdLosses, setting, cIon, lowerBound, upperBound, maxAllowedChargeState);
390 }
391 if (setting.UseCMinusHIon)
392 {
393 //c -1
394 double cIon = lastMassFwd + massN + massH;
395 CalculateIon(charge, ionsNoNL, ionsWithNL, possibleAmoniaLosses, possibleWaterLosses, massO,
396 massH, massC,
397 massN, massProton, fwdLosses, setting, cIon, lowerBound, upperBound, maxAllowedChargeState);
398 }
399 if (setting.UseXPlusHIon)
400 {
401 //x +1
402 double xIon = lastMassRev + massC + massO;
403 CalculateIon(charge, ionsNoNL, ionsWithNL, possibleAmoniaLossesRev, possibleWaterLossesRev,
404 massO, massH, massC,
405 massN, massProton, revLosses, setting, xIon, lowerBound, upperBound, maxAllowedChargeState);
406 }
407 if (setting.UseXMinusHIon)
408 {
409 //x -1
410 double xIon = lastMassRev + massC + massO - 2 * massH;
411 CalculateIon(charge, ionsNoNL, ionsWithNL, possibleAmoniaLossesRev, possibleWaterLossesRev,
412 massO, massH, massC,
413 massN, massProton, revLosses, setting, xIon, lowerBound, upperBound, maxAllowedChargeState);
414 }
415
416 if (setting.UseYPlusHIon)
417 {
418 //y +1
419 double yIon = lastMassRev + 2 * massH;
420 CalculateIon(charge, ionsNoNL, ionsWithNL, possibleAmoniaLossesRev, possibleWaterLossesRev,
421 massO, massH, massC,
422 massN, massProton, revLosses, setting, yIon, lowerBound, upperBound, maxAllowedChargeState);
423 }
424 if (setting.UseYMinusHIon)
425 {
426 //y - 1
427 double yIon = lastMassRev;
428 CalculateIon(charge, ionsNoNL, ionsWithNL, possibleAmoniaLossesRev, possibleWaterLossesRev,
429 massO, massH, massC,
430 massN, massProton, revLosses, setting, yIon, lowerBound, upperBound, maxAllowedChargeState);
431 }
432
433 if (setting.UseZMinusHIon)
434 {
435 //z - 1
436 double zIon = lastMassRev - massN - massH * 3;
437 CalculateIon(charge, ionsNoNL, ionsWithNL, possibleAmoniaLossesRev, possibleWaterLossesRev,
438 massO, massH, massC,
439 massN, massProton, revLosses, setting, zIon, lowerBound, upperBound, maxAllowedChargeState);
440 }
441
442 }
443
444 #endregion
445 char lastAA = (char)sequence[sequence.Length - 1];
446 if ((lastAA == 'S' || lastAA == 'T' || lastAA == 'E' || lastAA == 'D'))
447 {
448 ++MaxNrWaterLosses;
449 }
450 if ((lastAA == 'N' || lastAA == 'Q' || lastAA == 'R' || lastAA == 'K'))
451 ++MaxNrAmmoniaLosses;
452
453 return GetUniqueIonsSorted(out outIonsNoNL, out outIonsWithNL, ionsNoNL, ionsWithNL);
454
455 }
456 catch (Exception e)
457 {
458 throw new Exception("Error in calculating ions for peptide " + Encoding.ASCII.GetString(sequence));
459 }
460
461 return false;
462 }
463
464 static bool InRange(double v, double min, double max)
465 {
466 return ((v >= min) && (v <= max));
467 }
468
469 static bool GetUniqueIonsSorted(out double[] outIonsNoNL, out IonWithNL[] outIonsWithNL, List<double> ionsNoNL, List<IonWithNL> ionsWithNL)
470 {
471 outIonsNoNL = null;
472 outIonsWithNL = null;
473
474 if (ionsNoNL.Count > 0)
475 {
476 double[] aIonsNoNL = new double[ionsNoNL.Count];
477 for (int i = 0; i < aIonsNoNL.Length; ++i)
478 {
479 aIonsNoNL[i] = IonMassStabilizer.Stabilize(ionsNoNL[i]);
480 }
481 Array.Sort(aIonsNoNL);
482 //Removing duplicates from array
483 int k1 = 0, k2 = 1;
484 for (; k2 < aIonsNoNL.Length; ++k2)
485 {
486 if (aIonsNoNL[k1] < aIonsNoNL[k2])
487 {
488 ++k1;
489 }
490 else
491 {
492 ++k2;
493 break;
494 }
495 }
496
497 for (; k2 < aIonsNoNL.Length; ++k2)
498 {
499 if (aIonsNoNL[k1] < aIonsNoNL[k2])
500 {
501 ++k1;
502 aIonsNoNL[k1] = aIonsNoNL[k2];
503 }
504 }
505
506 Array.Resize(ref aIonsNoNL, ++k1);
507 outIonsNoNL = aIonsNoNL;
508 }
509
510 if (null != ionsWithNL && ionsWithNL.Count > 0)
511 {
512 IonWithNL[] aIonsWithNL = ionsWithNL.ToArray();
513 FastSort(aIonsWithNL);
514 {
515 //Merging duplicates from array
516 int k1 = 0, k2 = 1;
517 for (; k2 < aIonsWithNL.Length; ++k2)
518 {
519 if (aIonsWithNL[k1].Mz < aIonsWithNL[k2].Mz)
520 {
521 ++k1;
522 }
523 else
524 {
525 aIonsWithNL[k1].AddRange(aIonsWithNL[k2]);
526 ++k2;
527 break;
528 }
529 }
530
531 for (; k2 < aIonsWithNL.Length; ++k2)
532 {
533 if (aIonsWithNL[k1].Mz < aIonsWithNL[k2].Mz)
534 {
535 ++k1;
536 aIonsWithNL[k1] = aIonsWithNL[k2];
537 }
538 else
539 {
540 aIonsWithNL[k1].AddRange(aIonsWithNL[k2]);
541 }
542 }
543
544 Array.Resize(ref aIonsWithNL, ++k1);
545 outIonsWithNL = aIonsWithNL;
546 }
547 }
548
549 if (null != outIonsNoNL || null != outIonsWithNL)
550 {
551 if (null == outIonsNoNL)
552 {
553 outIonsNoNL = Array.Empty<double>();
554 }
555 return true;
556 }
557 return false;
558 }
559
560 private static void doQuicksort(IonWithNL[] elements, int left, int right)
561 {
562 int i = left, j = right;
563 //middle pivot; Should avoid n^2 worst case for nearly sorted array
564 IonWithNL pivot = elements[left + (right - left) / 2];
565
566 while (i <= j)
567 {
568 while (elements[i].Mz < pivot.Mz)
569 {
570 i++;
571 }
572
573 while (elements[j].Mz > pivot.Mz)
574 {
575 j--;
576 }
577
578 if (i <= j)
579 {
580 // Swap
581 //IonWithNL tmp = elements[i];
582 //elements[i] = elements[j];
583 //elements[j] = tmp;
584
585 (elements[i], elements[j]) = (elements[j], elements[i]);
586
587 i++;
588 j--;
589 }
590 }
591
592 // Recursive calls
593 if (left < j)
594 {
595 doQuicksort(elements, left, j);
596 }
597
598 if (i < right)
599 {
600 doQuicksort(elements, i, right);
601 }
602 }
603
604 public static void FastSort(IonWithNL[] ions)
605 {
606 if (ions.Length > 1)
607 {
608 doQuicksort(ions, 0, ions.Length - 1);
609 }
610 }
611
612 public static void CalculateAllInternalCombinations(double lastMassInternal,
613 int i,
614 List<double> ionsNoNL,
615 byte[] seq,
616 Modification[] mods,
617 double lowerBound,
618 double upperBound,
619 bool mono,
620 AminoAcid[] aminoAcids)
621 {
622 double currentMass = lastMassInternal;
623 for (int k = seq.Length - 2; k > i + 2; --k)
624 {
625 currentMass -= aminoAcids[(char) seq[k]].Mass(mono);
626
627 if (mods[k + 1] != null)
628 {
629 currentMass -= mods[k + 1].Mass(mono);
630 }
631 if (InRange(currentMass, lowerBound, upperBound))
632 {
633 ionsNoNL.Add(currentMass);
634 }
635 }
636 }
637
638 public static void CalculateIon(int charge, List<double> ionsNoNL, List<IonWithNL> ionsWithNL, int possibleAmoniaLosses,
639 int possibleWaterLosses, double massO, double massH, double massC, double massN, double massProton,
640 List<double> losses, InstrumentSetting setting, double ion,
641 double lowerBound, double upperBound, string maxAllowedChargeState)
642 {
643 //add possible neutral losses of modifications
644 for (int i = 0; i < losses.Count; ++i)
645 {
646 double newMass = ion - losses[i];
647 if (InRange(newMass, lowerBound, upperBound))
648 {
649 //add water or ammonia losses
650 if ((setting.UseWaterLosses && possibleWaterLosses > 0) || (setting.UseAmmoniaLosses && possibleAmoniaLosses > 0))
651 {
652 IonWithNL ionWithNL = new IonWithNL(newMass);
653 ionsWithNL.Add(ionWithNL);
654 CalculatePossibleNeutralLosses(newMass, possibleWaterLosses, possibleAmoniaLosses,
655 1, setting.UseWaterLosses, setting.UseAmmoniaLosses, massO, massH, massC,
656 massN, ionWithNL, lowerBound, upperBound);
657 }
658 else
659 {
660 ionsNoNL.Add(newMass);
661 }
662 }
663 if (charge > 2)
664 {
665 CalculateHigherChargedIons(charge, ionsNoNL, ionsWithNL, possibleAmoniaLosses, possibleWaterLosses, massO, massH, massC, massN, massProton, setting, newMass, lowerBound, upperBound, maxAllowedChargeState);
666 }
667 }
668
669 if (losses.Count == 0)
670 {
671 if (InRange(ion, lowerBound, upperBound))
672 {
673 //add water or ammonia losses
674 if ((setting.UseWaterLosses && possibleWaterLosses > 0) || (setting.UseAmmoniaLosses && possibleAmoniaLosses > 0))
675 {
676 IonWithNL ionWithNL = new IonWithNL(ion);
677 ionsWithNL.Add(ionWithNL);
678 CalculatePossibleNeutralLosses(ion, possibleWaterLosses, possibleAmoniaLosses,
679 1, setting.UseWaterLosses, setting.UseAmmoniaLosses, massO, massH, massC,
680 massN, ionWithNL, lowerBound, upperBound);
681 }
682 else
683 {
684 ionsNoNL.Add(ion);
685 }
686 }
687 if (charge > 2)
688 {
689 CalculateHigherChargedIons(charge, ionsNoNL, ionsWithNL, possibleAmoniaLosses, possibleWaterLosses, massO, massH, massC, massN, massProton, setting, ion, lowerBound, upperBound, maxAllowedChargeState);
690 }
691 }
692 }
693
694 public static void CalculateHigherChargedIons(int charge, List<double> ionsNoNL, List<IonWithNL> ionsWithNL, int possibleAmoniaLosses,
695 int possibleWaterLosses, double massO, double massH, double massC, double massN, double massProton,
696 InstrumentSetting setting, double newMass, double lowerBound, double upperBound, string maxAllowedChargeState)
697 {
698 string allowedChargeStates = "+2";
699 switch (maxAllowedChargeState)
700 {
701 case "+2":
702 AddHigherChargedIons(ionsNoNL, ionsWithNL, possibleAmoniaLosses, possibleWaterLosses,
703 massO, massH, massC, massN, massProton, setting.UseWaterLosses, setting.UseAmmoniaLosses,
704 newMass, 1, lowerBound, upperBound);
705 break;
706 case "+3":
707 for (int i = 1; i < charge - 1 && i < 3; ++i)
708 {
709 AddHigherChargedIons(ionsNoNL, ionsWithNL, possibleAmoniaLosses, possibleWaterLosses,
710 massO, massH, massC, massN, massProton, setting.UseWaterLosses, setting.UseAmmoniaLosses,
711 newMass, i, lowerBound, upperBound);
712 }
713 break;
714 case "+4":
715 for (int i = 1; i < charge - 1 && i < 4; ++i)
716 {
717 AddHigherChargedIons(ionsNoNL, ionsWithNL, possibleAmoniaLosses, possibleWaterLosses,
718 massO, massH, massC, massN, massProton, setting.UseWaterLosses, setting.UseAmmoniaLosses,
719 newMass, i, lowerBound, upperBound);
720 }
721 break;
722 case "Precursor - 1":
723 for (int i = 1; i < charge - 1; ++i)
724 {
725 AddHigherChargedIons(ionsNoNL, ionsWithNL, possibleAmoniaLosses, possibleWaterLosses,
726 massO, massH, massC, massN, massProton, setting.UseWaterLosses, setting.UseAmmoniaLosses,
727 newMass, i, lowerBound, upperBound);
728 }
729 break;
730 }
731 }
732
733 public static void AddHigherChargedIons(List<double> ionsNoNL, List<IonWithNL> ionsWithNL, int possibleAmoniaLosses,
734 int possibleWaterLosses, double massO, double massH, double massC, double massN,
735 double massProton, bool water, bool ammonia, double ion, int currentCharge,
736 double lowerBound, double upperBound)
737 {
738 double chargedIon = ion + currentCharge * massProton;
739 ++currentCharge;
740
741 double myMass = chargedIon / currentCharge;
742 if (InRange(myMass, lowerBound, upperBound))
743 {
744 if ((water && possibleWaterLosses > 0) || (ammonia && possibleAmoniaLosses > 0))
745 {
746 IonWithNL ionWithNL = new IonWithNL(myMass);
747 ionsWithNL.Add(ionWithNL);
748 CalculatePossibleNeutralLosses(chargedIon, possibleWaterLosses, possibleAmoniaLosses,
749 currentCharge, water, ammonia, massO, massH,
750 massC, massN, ionWithNL, lowerBound, upperBound);
751 }
752 else
753 {
754 ionsNoNL.Add(myMass);
755 }
756 }
757 }
758
759 public static void CalculatePossibleNeutralLosses(double ionMass, int possibleWaterLosses,
760 int possibleAmoniaLosses, int charge, bool water, bool ammonia, double massO, double massH,
761 double massC, double massN, IonWithNL ionWithNL,
762 double lowerBound, double upperBound)
763 {
764 //handle neutral losses
765
766 double massWaterLoss = massH * 2 + massO;
767 double massAmoniaLoss = massH * 3 + massN;
768
769 int currentWaterLoss = 0;
770 while (water && currentWaterLoss < possibleWaterLosses)
771 {
772 ++currentWaterLoss;
773 double chargedIon = ionMass - currentWaterLoss * massWaterLoss;
774 double myMass = IonMassStabilizer.Stabilize(chargedIon / charge);
775 //if(InRange( myMass, lowerBound, upperBound ))
776 ionWithNL.Add(myMass);
777 }
778
779 int currentAmoniaLoss = 0;
780 while (ammonia && currentAmoniaLoss < possibleAmoniaLosses)
781 {
782 ++currentAmoniaLoss;
783 double chargedIon = ionMass - currentAmoniaLoss * massAmoniaLoss;
784 double myMass = IonMassStabilizer.Stabilize(chargedIon / charge);
785 //if(InRange( myMass, lowerBound, upperBound ))
786 ionWithNL.Add(myMass);
787 }
788 }
789 }
790
791 public class IonWithNL : List<double>
792 {
793 public double Mz { get; set; }
794
795
796 public IonWithNL(double baseMZ)
797 : base(2)
798 {
799 Mz = IonMassStabilizer.Stabilize(baseMZ);
800 }
801 }
802
803 internal static class IonMassStabilizer
804 {
805 const int SignificantBitsStabilizer = 47; // Approximately 14 decimal significant digits
806
807 const ulong RoundingAdditionStabilizer = unchecked(1uL << (52 - SignificantBitsStabilizer - 1));
808 const ulong ClearBitsStabilizerMask = unchecked(0xFFFFFFFFFFFFFFFFuL << (52 - SignificantBitsStabilizer));
809
810 /*
811 Implements super-fast base2 nearest-value rounding of double precision values
812 to specified number of significant 2-base digits
813 Utilizes specificity of IEEE 754 double-precision format
814 Works correctly in all cases except double.Max, NaN, +/-Inf etc in input
815 Specially tested correctness when 52bit mantissa is all 1 (0xFFFFFFFFFFFFF)
816 By benchmarks works 7-15 times faster than Math.Round()
817 */
818 static internal double Stabilize(double value)
819 {
820 #if !DISABLE_FP_STABILIZER
821 unchecked
822 {
823 ulong im = (RoundingAdditionStabilizer + (ulong)BitConverter.DoubleToInt64Bits(value)) & ClearBitsStabilizerMask;
824 return BitConverter.Int64BitsToDouble((long)im);
825 }
826 #else
827 return value;
828 #endif
829 }
830 }
831
832 public class Modification
833 {
834 public readonly string Title; //{ get; private set; }
835 public readonly string FullName; // { get; private set; }
836 private readonly double MonoMass; // { get; private set; }
837 private readonly double AvgMass; // { get; private set; }
838 public readonly char AA; // { get; private set; }
839 public double[] NeutralLosses { get; set; }
840 public bool NTerminal { get; set; }
841 public bool CTerminal { get; set; }
842 public bool ProteinTerminus { get; set; }
843 public int MaxOccurrence { get; set; }
844
845 public int ID { get; set; }
846
847 public double Mass(bool mono)
848 {
849 return mono ? MonoMass : AvgMass;
850 }
851
852 public bool Fixed { get; set; }
853 public bool SemiFixed { get; set; }
854
855 public Modification(string title, string name, double mono, double avg, char aa, bool fix, double[] neutralLosses,
856 bool nTerminal, bool cTerminal, int id, bool protein, int maxOccurrence, bool semiFixed = false)
857 {
858 Title = title;
859 FullName = name;
860 MonoMass = mono;
861 AvgMass = avg;
862 AA = aa;
863 Fixed = fix;
864 SemiFixed = semiFixed;
865 NeutralLosses = neutralLosses;
866 NTerminal = nTerminal;
867 CTerminal = cTerminal;
868 ID = id;
869 ProteinTerminus = protein;
870 MaxOccurrence = maxOccurrence;
871 }
872
874 {
875 Title = m.Title;
876 FullName = m.FullName;
877 MonoMass = m.MonoMass;
878 AvgMass = m.AvgMass;
879 AA = m.AA;
880 Fixed = m.Fixed;
885 ID = m.ID;
888 }
889
890 public Modification(Modification m, bool fix, bool nTerminal, bool cTerminal, bool protein, int maxOccurrence)
891 {
892 Title = m.Title;
893 FullName = m.FullName;
894 MonoMass = m.MonoMass;
895 AvgMass = m.AvgMass;
896 AA = m.AA;
897 Fixed = fix;
900 NTerminal = nTerminal;
901 CTerminal = cTerminal;
902 ID = m.ID;
903 ProteinTerminus = protein;
904 MaxOccurrence = maxOccurrence;
905 }
906
907 public override string ToString()
908 {
909 string name = Title + "(";
910 if (CTerminal)
911 {
912 name += "C-Term)";
913 }
914 else if (NTerminal)
915 {
916 name += "N-Term)";
917 }
918 else
919 {
920 name += AA + ")";
921 }
922 return name;
923 }
924
925 #region oldstuff
926 public static string GetSaveString(Modification modif)
927 {
928 StringBuilder builder = new StringBuilder();
929 builder.Append(modif.Title).Append("°");
930 builder.Append(modif.FullName).Append("°");
931 builder.Append(modif.MonoMass).Append("°");
932 builder.Append(modif.AvgMass).Append("°");
933 builder.Append(modif.AA).Append("°");
934 builder.Append(modif.Fixed).Append("°");
935 foreach (double nl in modif.NeutralLosses)
936 {
937 builder.Append(nl).Append(":");
938 }
939 builder.Append("°");
940 builder.Append(modif.NTerminal).Append("°");
941 builder.Append(modif.CTerminal).Append("°");
942 builder.Append(modif.ID).Append("°");
943 builder.Append(modif.ProteinTerminus);
944 return builder.ToString();
945 }
946
947 //public static Modification GetModifOfSaveString(string savedModif) {
948 // string[] parts = savedModif.Split('°');
949 // double monoMass = double.Parse(parts[2]);
950 // double avgMass = double.Parse(parts[3]);
951 // bool fixedModif = bool.Parse(parts[5]);
952 // string[] nls = parts[6].Split(new char[] {':'}, StringSplitOptions.RemoveEmptyEntries);
953 // double[] neutralLosses = new double[nls.Length];
954 // for (int i = 0; i < nls.Length; ++i) {
955 // double elem = double.Parse(nls[i]);
956 // neutralLosses[i] = elem;
957 // }
958 // bool nterm = bool.Parse(parts[7]);
959 // bool cterm = bool.Parse(parts[8]);
960 // int id = Int32.Parse(parts[9]);
961 // bool protein = bool.Parse(parts[10]);
962 // Modification m = new Modification(parts[0], parts[1], monoMass, avgMass, char.Parse(parts[4]), fixedModif, neutralLosses, nterm, cterm, id, protein);
963 // return m;
964 //}
965 #endregion
966 }
967
968 public class InstrumentSetting
969 {
971 {
972 Name = "";
973 UseAIon = false;
974 UseAmmoniaLosses = false;
975 UseBIon = false;
976 UseCIon = false;
977 UseImmoniumIons = false;
978 UseWaterLosses = false;
979 UseXIon = false;
980 UseYIon = false;
981 UseZIon = false;
982 UseZPlusHIon = false;
983 UseZPlusTwoHIon = false;
984 UseInternalFragments = false;
985 // new Ions
986 UseAMinusHIon = false;
987 UseBPlusHIon = false;
988 UseBMinusHIon = false;
989 UseCPlusHIon = false;
990 UseCMinusHIon = false;
991 UseXPlusHIon = false;
992 UseXMinusHIon = false;
993 UseYPlusHIon = false;
994 UseYMinusHIon = false;
995 UseZMinusHIon = false;
996 }
997
999 {
1001 {
1002 Name = this.Name,
1003 UseAIon = this.UseAIon,
1005 UseBIon = this.UseBIon,
1006 UseCIon = this.UseCIon,
1009 UseXIon = this.UseXIon,
1010 UseYIon = this.UseYIon,
1011 UseZIon = this.UseZIon,
1015 // new ions
1026 UseZMinusHIon = UseZMinusHIon
1027 };
1028 return instr;
1029 }
1030
1031 public string Name { get; set; }
1032 public bool UseAIon { get; set; }
1033 public bool UseBIon { get; set; }
1034 public bool UseYIon { get; set; }
1035 public bool UseWaterLosses { get; set; }
1036 public bool UseAmmoniaLosses { get; set; }
1037 public bool UseImmoniumIons { get; set; }
1038 public bool UseZIon { get; set; }
1039 public bool UseZPlusHIon { get; set; }
1040 public bool UseZPlusTwoHIon { get; set; }
1041 public bool UseXIon { get; set; }
1042 public bool UseCIon { get; set; }
1043 public bool UseInternalFragments { get; set; }
1044
1045 // new Ions
1046 public bool UseAPlusHIon { get; set; }
1047 public bool UseAMinusHIon { get; set; }
1048 public bool UseBPlusHIon { get; set; }
1049 public bool UseBMinusHIon { get; set; }
1050 public bool UseCPlusHIon { get; set; }
1051 public bool UseCMinusHIon { get; set; }
1052 public bool UseXPlusHIon { get; set; }
1053 public bool UseXMinusHIon { get; set; }
1054 public bool UseYPlusHIon { get; set; }
1055 public bool UseYMinusHIon { get; set; }
1056 public bool UseZMinusHIon { get; set; }
1057
1058 }
1059}
Modification(string title, string name, double mono, double avg, char aa, bool fix, double[] neutralLosses, bool nTerminal, bool cTerminal, int id, bool protein, int maxOccurrence, bool semiFixed=false)
static string GetSaveString(Modification modif)
Modification(Modification m, bool fix, bool nTerminal, bool cTerminal, bool protein, int maxOccurrence)