private SpectrumPeaksInfo.MI[] ReadSpectrum(BiblioSpectrumInfo info) { const int lenPair = sizeof(float) + sizeof(float); byte[] peaks = new byte[info.NumPeaks * lenPair]; lock (ReadStream) { Stream fs = ReadStream.Stream; // Seek to stored location fs.Seek(info.Location, SeekOrigin.Begin); // Single read to get all the peaks if (fs.Read(peaks, 0, peaks.Length) < peaks.Length) { throw new IOException(Resources.BiblioSpecLibrary_ReadSpectrum_Failure_trying_to_read_peaks); } } // Build the list var arrayMI = new SpectrumPeaksInfo.MI[info.NumPeaks]; for (int i = 0, iNext = 0; i < peaks.Length; i += lenPair) { arrayMI[iNext].Intensity = BitConverter.ToSingle(peaks, i + sizeof(Single)); arrayMI[iNext++].Mz = BitConverter.ToSingle(peaks, i); } return(arrayMI); }
protected override SpectrumPeaksInfo.MI[] ReadSpectrum(XHunterSpectrumInfo info) { const int lenPair = sizeof(byte) + sizeof(float); byte[] peaks = new byte[info.NumPeaks * lenPair]; lock (ReadStream) { try { Stream fs = ReadStream.Stream; // Seek to stored location fs.Seek(info.Location, SeekOrigin.Begin); // Single read to get all the peaks if (fs.Read(peaks, 0, peaks.Length) < peaks.Length) { throw new IOException(Resources.XHunterLibrary_ReadSpectrum_Failure_trying_to_read_peaks); } } catch (Exception) { // If an exception is thrown, close the stream in case the failure is something // like a network failure that can be remedied by re-opening the stream. ReadStream.CloseStream(); throw; } } // Build the list var arrayMI = new SpectrumPeaksInfo.MI[info.NumPeaks]; // Read intensities for (int i = 0; i < info.NumPeaks; i++) { arrayMI[i].Intensity = peaks[i]; } // Read m/z values for (int i = info.NumPeaks, iNext = 0; i < peaks.Length; i += sizeof(float)) { arrayMI[iNext++].Mz = BitConverter.ToSingle(peaks, i); } return(arrayMI); }
private void PeakAnnotationsTest() { // Verify an operation that could break if SpectrumPeaksInfo.MI changes from struct to class var existingAnnotations = new List <SpectrumPeakAnnotation> { SpectrumPeakAnnotation.Create(new CustomIon(new CustomMolecule("C12H5N6", "foo"), Adduct.M_MINUS), "commentFoo") }; var existing = new SpectrumPeaksInfo.MI { Intensity = 111, Mz = 222, Annotations = existingAnnotations }; var combined = new List <SpectrumPeakAnnotation>(); foreach (var spectrumPeakAnnotation in existing.Annotations) { combined.Add(spectrumPeakAnnotation); } combined.Add(SpectrumPeakAnnotation.Create(new CustomIon(new CustomMolecule("C12H5N7", "bar"), Adduct.M_MINUS), "commentBar")); var updated = existing.ChangeAnnotations(combined); Assume.IsTrue(!Equals(existing, updated)); // This may fail if SpectrumPeaksInfo.MI changes from struct to class }
private string NoteIfAnnotationMzDisagrees(LibKey key, SpectrumPeaksInfo.MI peak) { foreach (var peakAnnotation in peak.GetAnnotationsEnumerator()) { var charge = peakAnnotation.Ion.Adduct; var monoisotopicMass = charge.MassFromMz(peak.Mz, MassType.Monoisotopic); var averageMass = charge.MassFromMz(peak.Mz, MassType.Average); if (!(peakAnnotation.Ion.MonoisotopicMass.Equals(monoisotopicMass, Settings.TransitionSettings.Instrument.MzMatchTolerance) || peakAnnotation.Ion.AverageMass.Equals(averageMass, Settings.TransitionSettings.Instrument.MzMatchTolerance))) { return(string.Format( @"annotated observed ({0}) and theoretical ({1}) masses differ for peak {2} of library entry {3} by more than the current instrument mz match tolerance of {4}", peak.Mz, peakAnnotation.Ion.MonoisotopicMassMz, peakAnnotation, key, Settings.TransitionSettings.Instrument.MzMatchTolerance)); } } return(null); }
public PrositMS2Spectrum(SrmSettings settings, PrositIntensityModel.PeptidePrecursorNCE peptidePrecursorNCE, int precursorIndex, PrositIntensityModel.PrositIntensityOutput prositIntensityOutput, IsotopeLabelType labelTypeOverride = null) { PeptidePrecursorNCE = peptidePrecursorNCE; Settings = settings; var precursor = peptidePrecursorNCE.NodeGroup; var peptide = peptidePrecursorNCE.NodePep; var calc = settings.GetFragmentCalc(IsotopeLabelType.light, peptide.ExplicitMods); var ionTable = calc.GetFragmentIonMasses(peptide.Target); // TODO: get mods and pass them as explicit mods above? var ions = ionTable.GetLength(1); var mis = new List <SpectrumPeaksInfo.MI>(ions * PrositConstants.IONS_PER_RESIDUE); for (int i = 0; i < ions; ++i) { var intensities = prositIntensityOutput.OutputRows[precursorIndex].Intensities[i].Intensities .Select(ReLu).ToArray(); var yMIs = CalcMIs(ionTable[IonType.y, ions - i - 1], intensities, 0); var bMIs = CalcMIs(ionTable[IonType.b, i], intensities, PrositConstants.IONS_PER_RESIDUE / 2); mis.AddRange(yMIs); mis.AddRange(bMIs); } var maxIntensity = mis.Max(mi => mi.Intensity); // Max Norm for (int i = 0; i < mis.Count; ++i) { mis[i] = new SpectrumPeaksInfo.MI { Mz = mis[i].Mz, Intensity = mis[i].Intensity / maxIntensity } } ; SpectrumPeaks = new SpectrumPeaksInfo(mis.ToArray()); }
/// <summary> /// Test only method for creating a <see cref="XHunterLibrary"/> file /// from another loaded <see cref="Library"/>. Should this move into test project? /// </summary> /// <param name="streamManager">Provides access to the file system</param> /// <param name="path">Path to write to</param> /// <param name="library">The loaded library to use as a data source</param> /// <param name="lowIntensity">True to use 20 lowest intensity peaks for bad spectrum</param> public static void Write(IStreamManager streamManager, string path, Library library, bool lowIntensity) { using (FileSaver fs = new FileSaver(path, streamManager)) using (Stream outStream = streamManager.CreateStream(fs.SafeName, FileMode.Create, true)) { outStream.Write(BitConverter.GetBytes(0), 0, sizeof(int)); outStream.Write(BitConverter.GetBytes(library.SpectrumCount), 0, sizeof(int)); byte[] header = new byte[256 - 8]; const string headerText = @"HLF v=2 s=test.hlf d=2009.02.04"; Encoding.UTF8.GetBytes(headerText, 0, headerText.Length, header, 0); outStream.Write(header, 0, header.Length); SequenceMassCalc calc = new SequenceMassCalc(MassType.Monoisotopic); byte[] seqBuffer = new byte[1024]; foreach (var key in library.Keys) { SpectrumPeaksInfo peaksInfo; if (!library.TryLoadSpectrum(key, out peaksInfo)) { continue; } // Fake X! Hunter filtering by choosing just the to 20 peaks SpectrumPeaksInfo.MI[] peaks = peaksInfo.Peaks.ToArray(); // Sort by intensity if (lowIntensity) { Array.Sort(peaks, (p1, p2) => Comparer.Default.Compare(p1.Intensity, p2.Intensity)); // ascending } else { Array.Sort(peaks, (p1, p2) => Comparer.Default.Compare(p2.Intensity, p1.Intensity)); // descending } float maxI = peaks.Length == 0 ? 0 : peaks[0].Intensity; // Take 20 most intense peaks SpectrumPeaksInfo.MI[] peaksFiltered = new SpectrumPeaksInfo.MI[Math.Min(20, peaks.Length)]; Array.Copy(peaks, peaksFiltered, peaksFiltered.Length); // Resort by m/z (ineffient, but this is test code) Array.Sort(peaksFiltered, (p1, p2) => Comparer.Default.Compare(p1.Mz, p2.Mz)); double totalI = 0; byte[] peakBytes = new byte[(sizeof(byte) + sizeof(float)) * peaksFiltered.Length]; for (int i = 0; i < peaksFiltered.Length; i++) { var mi = peaksFiltered[i]; // Calculate the X! Hunter processed intensity value float intensity = 100f * mi.Intensity / maxI; totalI += intensity; // Fill the peaks buffer peakBytes[i] = (byte)(int)intensity; Array.Copy(BitConverter.GetBytes((float)mi.Mz), 0, peakBytes, peaksFiltered.Length + i * 4, sizeof(float)); } var sequence = key.Target.ToString(); // Only works for unmodified sequence Debug.Assert(!key.IsModified); double precursorMH = calc.GetPrecursorMass(sequence); outStream.Write(BitConverter.GetBytes(precursorMH), 0, sizeof(double)); outStream.Write(BitConverter.GetBytes(key.Charge), 0, sizeof(int)); // Value rounded for consistent serialization round-tripping float i2 = (float)Math.Round(Math.Sqrt(totalI), 4); outStream.Write(BitConverter.GetBytes(i2), 0, sizeof(float)); outStream.Write(BitConverter.GetBytes(0.0001f), 0, sizeof(float)); outStream.Write(BitConverter.GetBytes(sequence.Length), 0, sizeof(int)); // Sequence Encoding.UTF8.GetBytes(sequence, 0, sequence.Length, seqBuffer, 0); outStream.Write(seqBuffer, 0, sequence.Length); // Peaks outStream.Write(BitConverter.GetBytes(peaksFiltered.Length), 0, sizeof(int)); outStream.Write(peakBytes, 0, peakBytes.Length); // Modifications outStream.Write(BitConverter.GetBytes(0), 0, sizeof(int)); // Homologs outStream.Write(BitConverter.GetBytes(0), 0, sizeof(int)); } streamManager.Finish(outStream); fs.Commit(); } }
public RankedMI(SpectrumPeaksInfo.MI mi, int indexMz) { _mi = mi; IndexMz = indexMz; }
private LibraryRankedSpectrumInfo(SpectrumPeaksInfo info, IsotopeLabelType labelType, TransitionGroupDocNode groupDocNode, SrmSettings settings, Target lookupSequence, ExplicitMods lookupMods, IEnumerable <Adduct> charges, IEnumerable <IonType> types, IEnumerable <Adduct> rankCharges, IEnumerable <IonType> rankTypes, double?score, bool useFilter, bool matchAll, int minPeaks) { LabelType = labelType; // Avoid ReSharper multiple enumeration warning var rankChargesArray = rankCharges.ToArray(); var rankTypesArray = rankTypes.ToArray(); TransitionGroup group = groupDocNode.TransitionGroup; bool isProteomic = group.IsProteomic; if (score == null && groupDocNode.HasLibInfo && groupDocNode.LibInfo is BiblioSpecSpectrumHeaderInfo libInfo) { Score = libInfo.Score; } else { Score = score; } if (!useFilter) { if (charges == null) { charges = GetRanked(rankChargesArray, isProteomic ? Transition.DEFAULT_PEPTIDE_CHARGES : Transition.DEFAULT_MOLECULE_CHARGES); } if (types == null) { types = GetRanked(rankTypesArray, isProteomic ? Transition.PEPTIDE_ION_TYPES : Transition.MOLECULE_ION_TYPES); } matchAll = true; } bool limitRanks = groupDocNode.IsCustomIon && // For small molecules, cap the number of ranked ions displayed if we don't have any peak metadata groupDocNode.Transitions.Any(t => string.IsNullOrEmpty(t.FragmentIonName)); RankParams rp = new RankParams { sequence = lookupSequence, precursorAdduct = group.PrecursorAdduct, adducts = charges ?? rankCharges, types = types ?? rankTypes, matchAll = matchAll, rankCharges = rankChargesArray.Select(a => Math.Abs(a.AdductCharge)).ToArray(), rankTypes = rankTypesArray, // Precursor isotopes will not be included in MS/MS, if they will be filtered // from MS1 excludePrecursorIsotopes = settings.TransitionSettings.FullScan.IsEnabledMs, tranSettings = settings.TransitionSettings, rankLimit = limitRanks ? settings.TransitionSettings.Libraries.IonCount : (int?)null }; // Get necessary mass calculators and masses var calcMatchPre = settings.GetPrecursorCalc(labelType, lookupMods); var calcMatch = isProteomic ? settings.GetFragmentCalc(labelType, lookupMods) : settings.GetDefaultFragmentCalc(); var calcPredict = isProteomic ? settings.GetFragmentCalc(group.LabelType, lookupMods) : calcMatch; if (isProteomic && rp.sequence.IsProteomic) { rp.precursorMz = SequenceMassCalc.GetMZ(calcMatchPre.GetPrecursorMass(rp.sequence), rp.precursorAdduct); rp.massPreMatch = calcMatch.GetPrecursorFragmentMass(rp.sequence); rp.massesMatch = calcMatch.GetFragmentIonMasses(rp.sequence); rp.knownFragments = null; } else if (!isProteomic && !rp.sequence.IsProteomic) { string isotopicForumla; rp.precursorMz = SequenceMassCalc.GetMZ(calcMatchPre.GetPrecursorMass(rp.sequence.Molecule, null, rp.precursorAdduct, out isotopicForumla), rp.precursorAdduct); rp.massPreMatch = calcMatch.GetPrecursorFragmentMass(rp.sequence); // rp.massesMatch = calcMatch.GetFragmentIonMasses(rp.molecule); CONSIDER, for some molecule types someday? // For small molecules we can't predict fragmentation, so just use those we have // Older Resharper code inspection implementations insist on warning here // Resharper disable PossibleMultipleEnumeration var existing = groupDocNode.Transitions.Where(tran => tran.Transition.IsNonPrecursorNonReporterCustomIon()).Select(t => t.Transition.CustomIon.GetMass(MassType.Monoisotopic)).ToArray(); rp.massesMatch = new IonTable <TypedMass>(IonType.custom, existing.Length); for (var i = 0; i < existing.Length; i++) { rp.massesMatch[IonType.custom, i] = existing[i]; } // Resharper restore PossibleMultipleEnumeration rp.knownFragments = groupDocNode.Transitions.Where(tran => tran.Transition.IsNonPrecursorNonReporterCustomIon()).Select(t => new KnownFragment { Adduct = t.Transition.Adduct, Name = t.GetFragmentIonName(CultureInfo.CurrentCulture, settings.TransitionSettings.Libraries.IonMatchTolerance), Mz = t.Mz }).ToList(); } else { rp.precursorMz = 0.0; rp.massPreMatch = TypedMass.ZERO_MONO_MASSH; rp.massesMatch = IonTable <TypedMass> .EMPTY; rp.knownFragments = null; } rp.massPrePredict = rp.massPreMatch; rp.massesPredict = rp.massesMatch; if (!ReferenceEquals(calcPredict, calcMatch)) { rp.massPrePredict = calcPredict.GetPrecursorFragmentMass(rp.sequence); if (rp.sequence.IsProteomic) // CONSIDER - eventually we may be able to predict fragments for small molecules? { rp.massesPredict = calcPredict.GetFragmentIonMasses(rp.sequence); } } // Get values of interest from the settings. var tranSettings = settings.TransitionSettings; var predict = tranSettings.Prediction; var filter = tranSettings.Filter; var libraries = tranSettings.Libraries; var instrument = tranSettings.Instrument; // Get potential losses to all fragments in this peptide rp.massType = predict.FragmentMassType; rp.potentialLosses = TransitionGroup.CalcPotentialLosses(rp.sequence, settings.PeptideSettings.Modifications, lookupMods, rp.massType); // Create arrays because ReadOnlyCollection enumerators are too slow // In some cases these collections must be enumerated for every ion // allowed in the library specturm. rp.startFinder = filter.FragmentRangeFirst; rp.endFinder = filter.FragmentRangeLast; // Get library settings Tolerance = libraries.IonMatchTolerance; rp.tolerance = Tolerance; rp.pick = tranSettings.Libraries.Pick; int ionMatchCount = libraries.IonCount; // If no library filtering will happen, return all rankings for view in the UI if (!useFilter || rp.pick == TransitionLibraryPick.none) { if (rp.pick == TransitionLibraryPick.none) { rp.pick = TransitionLibraryPick.all; } ionMatchCount = -1; } // Get instrument settings rp.minMz = instrument.MinMz; rp.maxMz = instrument.MaxMz; // Get the library spectrum mass-intensity pairs IList <SpectrumPeaksInfo.MI> listMI = info.Peaks; // Because sorting and matching observed ions with predicted // ions appear as bottlenecks in a profiler, a minimum number // of peaks may be supplied to allow the use of a 2-phase linear // filter that can significantly reduce the number of peaks // needing the O(n*log(n)) sorting and the O(n*m) matching. int len = listMI.Count; float intensityCutoff = 0; if (minPeaks != -1) { // Start searching for good cut-off at mean intensity. double totalIntensity = info.Intensities.Sum(); FindIntensityCutoff(listMI, 0, (float)(totalIntensity / len) * 2, minPeaks, 1, ref intensityCutoff, ref len); } // Create filtered peak array storing original index for m/z ordering // to avoid needing to sort to return to this order. RankedMI[] arrayRMI = new RankedMI[len]; // Detect when m/z values are out of order, and use the expensive sort // by m/z to correct this. double lastMz = double.MinValue; bool sortMz = false; for (int i = 0, j = 0, lenOrig = listMI.Count; i < lenOrig; i++) { SpectrumPeaksInfo.MI mi = listMI[i]; if (mi.Intensity >= intensityCutoff || intensityCutoff == 0) { arrayRMI[j] = new RankedMI(mi, j); j++; } if (ionMatchCount == -1) { if (mi.Mz < lastMz) { sortMz = true; } lastMz = mi.Mz; } } // The one expensive sort is used to determine rank order // by intensity, or m/z in case of a tie. Array.Sort(arrayRMI, OrderIntensityDesc); RankedMI[] arrayResult = new RankedMI[ionMatchCount != -1 ? ionMatchCount : arrayRMI.Length]; foreach (RankedMI rmi in arrayRMI) { rmi.CalculateRank(rp); // If not filtering for only the highest ionMatchCount ranks if (ionMatchCount == -1) { // Put the ranked record back where it started in the // m/z ordering to avoid a second sort. arrayResult[rmi.IndexMz] = rmi; } // Otherwise, if this ion was ranked, add it to the result array else if (rmi.Rank > 0) { int countRanks = rmi.Rank; arrayResult[countRanks - 1] = rmi; // And stop when the array is full if (countRanks == ionMatchCount) { break; } } } // Is this a theoretical library with no intensity variation? If so it can't be ranked. // If it has any interesting peak annotations, pass those through if (rp.Ranked == 0 && arrayRMI.All(rmi => rmi.Intensity == arrayRMI[0].Intensity)) { // Only do this if we have been asked to limit the ions matched, and there are any annotations if (ionMatchCount != -1 && arrayRMI.Any(rmi => rmi.HasAnnotations)) { // Pass through anything with an annotation as being of probable interest arrayResult = arrayRMI.Where(rmi => rmi.HasAnnotations).ToArray(); ionMatchCount = -1; } } // If not enough ranked ions were found, fill the rest of the results array if (ionMatchCount != -1) { for (int i = rp.Ranked; i < ionMatchCount; i++) { arrayResult[i] = RankedMI.EMPTY; } } // If all ions are to be included, and some were found out of order, then // the expensive full sort by m/z is necesary. else if (sortMz) { Array.Sort(arrayResult, OrderMz); } _spectrum = MakeReadOnly(arrayResult); }
private TransitionDocNode TransitionFromPeakAndAnnotations(LibKey key, TransitionGroupDocNode nodeGroup, Adduct fragmentCharge, SpectrumPeaksInfo.MI peak, int?rank) { var charge = fragmentCharge; var monoisotopicMass = charge.MassFromMz(peak.Mz, MassType.Monoisotopic); var averageMass = charge.MassFromMz(peak.Mz, MassType.Average); // Caution here - library peak (observed) mz may not exactly match (theoretical) mz of the annotation // In the case of multiple annotations, produce single transition for display in library explorer var annotations = peak.GetAnnotationsEnumerator().ToArray(); var spectrumPeakAnnotationIon = peak.AnnotationsAggregateDescriptionIon; var molecule = spectrumPeakAnnotationIon.Adduct.IsEmpty ? new CustomMolecule(monoisotopicMass, averageMass) : spectrumPeakAnnotationIon; var note = (annotations.Length > 1) ? TextUtil.LineSeparate(annotations.Select(a => a.ToString())) : null; var noteIfAnnotationMzDisagrees = NoteIfAnnotationMzDisagrees(key, peak); if (noteIfAnnotationMzDisagrees != null) { if (note == null) { note = noteIfAnnotationMzDisagrees; } else { note = TextUtil.LineSeparate(note, noteIfAnnotationMzDisagrees); } } var transition = new Transition(nodeGroup.TransitionGroup, spectrumPeakAnnotationIon.Adduct.IsEmpty ? charge : spectrumPeakAnnotationIon.Adduct, 0, molecule); return(new TransitionDocNode(transition, Annotations.EMPTY.ChangeNote(note), null, monoisotopicMass, rank.HasValue ? new TransitionDocNode.TransitionQuantInfo(null, new TransitionLibInfo(rank.Value, peak.Intensity), true) : TransitionDocNode.TransitionQuantInfo.DEFAULT, ExplicitTransitionValues.EMPTY, null)); }
private LibraryRankedSpectrumInfo(SpectrumPeaksInfo info, IsotopeLabelType labelType, TransitionGroup group, SrmSettings settings, string lookupSequence, ExplicitMods lookupMods, IEnumerable <int> charges, IEnumerable <IonType> types, IEnumerable <int> rankCharges, IEnumerable <IonType> rankTypes, bool useFilter, bool matchAll, int minPeaks) { LabelType = labelType; // Avoid ReSharper multiple enumeration warning var rankChargesArray = rankCharges.ToArray(); var rankTypesArray = rankTypes.ToArray(); if (!useFilter) { if (charges == null) { charges = GetRanked(rankChargesArray, Transition.ALL_CHARGES); } if (types == null) { types = GetRanked(rankTypesArray, Transition.ALL_TYPES); } matchAll = true; } RankParams rp = new RankParams { sequence = lookupSequence, precursorCharge = group.PrecursorCharge, charges = charges ?? rankCharges, types = types ?? rankTypes, matchAll = matchAll, rankCharges = rankChargesArray, rankTypes = rankTypesArray, // Precursor isotopes will not be included in MS/MS, if they will be filtered // from MS1 excludePrecursorIsotopes = settings.TransitionSettings.FullScan.IsEnabledMs, tranSettings = settings.TransitionSettings }; // Get necessary mass calculators and masses var calcMatchPre = settings.GetPrecursorCalc(labelType, lookupMods); var calcMatch = settings.GetFragmentCalc(labelType, lookupMods); var calcPredict = settings.GetFragmentCalc(group.LabelType, lookupMods); if (!string.IsNullOrEmpty(rp.sequence)) { rp.precursorMz = SequenceMassCalc.GetMZ(calcMatchPre.GetPrecursorMass(rp.sequence), rp.precursorCharge); rp.massPreMatch = calcMatch.GetPrecursorFragmentMass(rp.sequence); rp.massesMatch = calcMatch.GetFragmentIonMasses(rp.sequence); } else { rp.precursorMz = 0.0; rp.massPreMatch = 0.0; rp.massesMatch = new double[0, 0]; } rp.massPrePredict = rp.massPreMatch; rp.massesPredict = rp.massesMatch; if (!ReferenceEquals(calcPredict, calcMatch) && !string.IsNullOrEmpty(rp.sequence)) { rp.massPrePredict = calcPredict.GetPrecursorFragmentMass(rp.sequence); rp.massesPredict = calcPredict.GetFragmentIonMasses(rp.sequence); } // Get values of interest from the settings. var tranSettings = settings.TransitionSettings; var predict = tranSettings.Prediction; var filter = tranSettings.Filter; var libraries = tranSettings.Libraries; var instrument = tranSettings.Instrument; // Get potential losses to all fragments in this peptide rp.massType = predict.FragmentMassType; rp.potentialLosses = TransitionGroup.CalcPotentialLosses(rp.sequence, settings.PeptideSettings.Modifications, lookupMods, rp.massType); // Create arrays because ReadOnlyCollection enumerators are too slow // In some cases these collections must be enumerated for every ion // allowed in the library specturm. rp.startFinder = filter.FragmentRangeFirst; rp.endFinder = filter.FragmentRangeLast; // Get library settings Tolerance = libraries.IonMatchTolerance; rp.tolerance = Tolerance; rp.pick = tranSettings.Libraries.Pick; int ionMatchCount = libraries.IonCount; // If no library filtering will happen, return all rankings for view in the UI if (!useFilter || rp.pick == TransitionLibraryPick.none) { if (rp.pick == TransitionLibraryPick.none) { rp.pick = TransitionLibraryPick.all; } ionMatchCount = -1; } // Get instrument settings rp.minMz = instrument.MinMz; rp.maxMz = instrument.MaxMz; // Get the library spectrum mass-intensity pairs IList <SpectrumPeaksInfo.MI> listMI = info.Peaks; // Because sorting and matching observed ions with predicted // ions appear as bottlenecks in a profiler, a minimum number // of peaks may be supplied to allow the use of a 2-phase linear // filter that can significantly reduce the number of peaks // needing the O(n*log(n)) sorting and the O(n*m) matching. int len = listMI.Count; float intensityCutoff = 0; if (minPeaks != -1) { // Start searching for good cut-off at mean intensity. double totalIntensity = info.Intensities.Sum(); FindIntensityCutoff(listMI, 0, (float)(totalIntensity / len) * 2, minPeaks, 1, ref intensityCutoff, ref len); } // Create filtered peak array storing original index for m/z ordering // to avoid needing to sort to return to this order. RankedMI[] arrayRMI = new RankedMI[len]; // Detect when m/z values are out of order, and use the expensive sort // by m/z to correct this. double lastMz = double.MinValue; bool sortMz = false; for (int i = 0, j = 0, lenOrig = listMI.Count; i < lenOrig; i++) { SpectrumPeaksInfo.MI mi = listMI[i]; if (mi.Intensity >= intensityCutoff || intensityCutoff == 0) { arrayRMI[j] = new RankedMI(mi, j); j++; } if (ionMatchCount == -1) { if (mi.Mz < lastMz) { sortMz = true; } lastMz = mi.Mz; } } // The one expensive sort is used to determine rank order // by intensity. Array.Sort(arrayRMI, OrderIntensityDesc); RankedMI[] arrayResult = new RankedMI[ionMatchCount != -1 ? ionMatchCount : arrayRMI.Length]; foreach (RankedMI rmi in arrayRMI) { rmi.CalculateRank(rp); // If not filtering for only the highest ionMatchCount ranks if (ionMatchCount == -1) { // Put the ranked record back where it started in the // m/z ordering to avoid a second sort. arrayResult[rmi.IndexMz] = rmi; } // Otherwise, if this ion was ranked, add it to the result array else if (rmi.Rank > 0) { int countRanks = rmi.Rank; arrayResult[countRanks - 1] = rmi; // And stop when the array is full if (countRanks == ionMatchCount) { break; } } } // If not enough ranked ions were found, fill the rest of the results array if (ionMatchCount != -1) { for (int i = rp.Ranked; i < ionMatchCount; i++) { arrayResult[i] = RankedMI.EMPTY; } } // If all ions are to be included, and some were found out of order, then // the expensive full sort by m/z is necesary. else if (sortMz) { Array.Sort(arrayResult, OrderMz); } _spectrum = MakeReadOnly(arrayResult); }
private SpectrumPeaksInfo.MI[] ReadSpectrum(BiblioSpectrumInfo info) { const int lenPair = sizeof(float) + sizeof(float); byte[] peaks = new byte[info.NumPeaks * lenPair]; lock (ReadStream) { Stream fs = ReadStream.Stream; // Seek to stored location fs.Seek(info.Location, SeekOrigin.Begin); // Single read to get all the peaks if (fs.Read(peaks, 0, peaks.Length) < peaks.Length) throw new IOException(Resources.BiblioSpecLibrary_ReadSpectrum_Failure_trying_to_read_peaks); } // Build the list var arrayMI = new SpectrumPeaksInfo.MI[info.NumPeaks]; for (int i = 0, iNext = 0; i < peaks.Length; i += lenPair) { arrayMI[iNext].Intensity = BitConverter.ToSingle(peaks, i + sizeof (Single)); arrayMI[iNext++].Mz = BitConverter.ToSingle(peaks, i); } return arrayMI; }
public LibraryRankedSpectrumInfo RankSpectrum(SpectrumPeaksInfo info, int minPeaks, double?score) { var ionsToReturn = FragmentFilterObj.FragmentMatchCount; RankingState rankingState = new RankingState() { matchAll = MatchAll, }; // Get the library spectrum mass-intensity pairs IList <SpectrumPeaksInfo.MI> listMI = info.Peaks; // Because sorting and matching observed ions with predicted // ions appear as bottlenecks in a profiler, a minimum number // of peaks may be supplied to allow the use of a 2-phase linear // filter that can significantly reduce the number of peaks // needing the O(n*log(n)) sorting and the O(n*m) matching. int len = listMI.Count; float intensityCutoff = 0; if (minPeaks != -1) { // Start searching for good cut-off at mean intensity. double totalIntensity = info.Intensities.Sum(); FindIntensityCutoff(listMI, 0, (float)(totalIntensity / len) * 2, minPeaks, 1, ref intensityCutoff, ref len); } // Create filtered peak array storing original index for m/z ordering // to avoid needing to sort to return to this order. RankedMI[] arrayRMI = new RankedMI[len]; // Detect when m/z values are out of order, and use the expensive sort // by m/z to correct this. double lastMz = double.MinValue; bool sortMz = false; for (int i = 0, j = 0, lenOrig = listMI.Count; i < lenOrig; i++) { SpectrumPeaksInfo.MI mi = listMI[i]; if (mi.Intensity >= intensityCutoff || intensityCutoff == 0) { arrayRMI[j] = new RankedMI(mi, j); j++; } if (!ionsToReturn.HasValue) { if (mi.Mz < lastMz) { sortMz = true; } lastMz = mi.Mz; } } // The one expensive sort is used to determine rank order // by intensity, or m/z in case of a tie. Array.Sort(arrayRMI, OrderIntensityDesc); RankedMI[] arrayResult = new RankedMI[ionsToReturn.HasValue ? ionsToReturn.Value : arrayRMI.Length]; foreach (RankedMI rmi in arrayRMI) { var rankedRmi = CalculateRank(rankingState, rmi); // If not filtering for only the highest ionMatchCount ranks if (!ionsToReturn.HasValue) { // Put the ranked record back where it started in the // m/z ordering to avoid a second sort. arrayResult[rmi.IndexMz] = rankedRmi; } // Otherwise, if this ion was ranked, add it to the result array else if (rankedRmi.Rank > 0) { int countRanks = rankedRmi.Rank; arrayResult[countRanks - 1] = rankedRmi; // And stop when the array is full if (countRanks == ionsToReturn.Value) { break; } } } // Is this a theoretical library with no intensity variation? If so it can't be ranked. // If it has any interesting peak annotations, pass those through if (rankingState.Ranked == 0 && arrayRMI.All(rmi => rmi.Intensity == arrayRMI[0].Intensity)) { // Only do this if we have been asked to limit the ions matched, and there are any annotations if (ionsToReturn.HasValue && arrayRMI.Any(rmi => rmi.HasAnnotations)) { // Pass through anything with an annotation as being of probable interest arrayResult = arrayRMI.Where(rmi => rmi.HasAnnotations).ToArray(); ionsToReturn = null; } } // If not enough ranked ions were found, fill the rest of the results array if (ionsToReturn.HasValue) { for (int i = rankingState.Ranked; i < ionsToReturn.Value; i++) { arrayResult[i] = RankedMI.EMPTY; } } // If all ions are to be included, and some were found out of order, then // the expensive full sort by m/z is necessary. else if (sortMz) { Array.Sort(arrayResult, OrderMz); } double?spectrumScore; if (score == null && GroupDocNode.HasLibInfo && GroupDocNode.LibInfo is BiblioSpecSpectrumHeaderInfo libInfo) { spectrumScore = libInfo.Score; } else { spectrumScore = score; } return(new LibraryRankedSpectrumInfo(PredictLabelType, Libraries.IonMatchTolerance, arrayResult, spectrumScore)); }