/// <summary> /// Handle a single precursor element and child nodes /// Called by ReadPrecursorList (xml hierarchy) /// </summary> /// <param name="reader">XmlReader that is only valid for the scope of the single precursor element</param> /// <returns></returns> private Precursor ReadPrecursor(XmlReader reader) { reader.MoveToContent(); reader.ReadStartElement("precursor"); // Throws exception if we are not at the "precursor" tag. XmlReader innerReader; Precursor precursor = new Precursor(); while (reader.ReadState == ReadState.Interactive) { // Handle exiting out properly at EndElement tags if (reader.NodeType != XmlNodeType.Element) { reader.Read(); continue; } switch (reader.Name) { case "isolationWindow": // Schema requirements: zero to one instances of this element innerReader = reader.ReadSubtree(); innerReader.MoveToContent(); innerReader.ReadStartElement("isolationWindow"); // Throws exception if we are not at the "selectedIon" tag. while (innerReader.ReadState == ReadState.Interactive) { // Handle exiting out properly at EndElement tags if (innerReader.NodeType != XmlNodeType.Element) { innerReader.Read(); continue; } switch (innerReader.Name) { case "referenceableParamGroupRef": // Schema requirements: zero to many instances of this element innerReader.Skip(); break; case "cvParam": // Schema requirements: zero to many instances of this element /* MUST supply a *child* term of MS:1000792 (isolation window attribute) one or more times * e.g.: MS:1000827 (isolation window target m/z) * e.g.: MS:1000828 (isolation window lower offset) * e.g.: MS:1000829 (isolation window upper offset) */ switch (innerReader.GetAttribute("accession")) { case "MS:1000827": // name="isolation window target m/z" precursor.IsolationWindowTargetMz = Convert.ToDouble(innerReader.GetAttribute("value")); break; case "MS:1000828": // name="isolation window lower offset" precursor.IsolationWindowLowerOffset = Convert.ToDouble(innerReader.GetAttribute("value")); break; case "MS:1000829": // name="isolation window upper offset" precursor.IsolationWindowUpperOffset = Convert.ToDouble(innerReader.GetAttribute("value")); break; } innerReader.Read(); // Consume the cvParam element (no child nodes) break; case "userParam": // Schema requirements: zero to many instances of this element innerReader.Skip(); break; default: innerReader.Skip(); break; } } innerReader.Close(); reader.Read(); // "selectedIon" might not have child nodes // We will either consume the EndElement, or the same element that was passed to ReadSpectrum (in case of no child nodes) break; case "selectedIonList": // Schema requirements: zero to one instances of this element // mzML_1.0.0: one instance of this element precursor.Ions = ReadSelectedIonList(reader.ReadSubtree()); reader.ReadEndElement(); // "selectedIonList" must have child nodes break; case "activation": // Schema requirements: one instance of this element innerReader = reader.ReadSubtree(); innerReader.MoveToContent(); innerReader.ReadStartElement("activation"); // Throws exception if we are not at the "activation" tag. while (innerReader.ReadState == ReadState.Interactive) { // Handle exiting out properly at EndElement tags if (innerReader.NodeType != XmlNodeType.Element) { innerReader.Read(); continue; } switch (innerReader.Name) { case "referenceableParamGroupRef": // Schema requirements: zero to many instances of this element innerReader.Skip(); break; case "cvParam": // Schema requirements: zero to many instances of this element /* MAY supply a *child* term of MS:1000510 (precursor activation attribute) one or more times * e.g.: MS:1000045 (collision energy) * e.g.: MS:1000138 (percent collision energy) * e.g.: MS:1000245 (charge stripping) * e.g.: MS:1000412 (buffer gas) * e.g.: MS:1000419 (collision gas) * e.g.: MS:1000509 (activation energy) * e.g.: MS:1000869 (collision gas pressure) * MUST supply term MS:1000044 (dissociation method) or any of its children one or more times * e.g.: MS:1000133 (collision-induced dissociation) * e.g.: MS:1000134 (plasma desorption) * e.g.: MS:1000135 (post-source decay) * e.g.: MS:1000136 (surface-induced dissociation) * e.g.: MS:1000242 (blackbody infrared radiative dissociation) * e.g.: MS:1000250 (electron capture dissociation) * e.g.: MS:1000262 (infrared multiphoton dissociation) * e.g.: MS:1000282 (sustained off-resonance irradiation) * e.g.: MS:1000422 (high-energy collision-induced dissociation) * e.g.: MS:1000433 (low-energy collision-induced dissociation) * et al. * * e.g.: MS:1000133 "collision-induced dissociation" * e.g.: MS:1000134 "plasma desorption" * e.g.: MS:1000135 "post-source decay" * e.g.: MS:1000136 "surface-induced dissociation" * e.g.: MS:1000242 "blackbody infrared radiative dissociation" * e.g.: MS:1000250 "electron capture dissociation" * e.g.: MS:1000262 "infrared multiphoton dissociation" * e.g.: MS:1000282 "sustained off-resonance irradiation" * e.g.: MS:1000422 "beam-type collision-induced dissociation" * e.g.: MS:1000433 "low-energy collision-induced dissociation" * e.g.: MS:1000435 "photodissociation" * e.g.: MS:1000598 "electron transfer dissociation" * e.g.: MS:1000599 "pulsed q dissociation" * e.g.: MS:1001880 "in-source collision-induced dissociation" * e.g.: MS:1002000 "LIFT" * e.g.: MS:1002472 "trap-type collision-induced dissociation" */ switch (innerReader.GetAttribute("accession")) { case "MS:1000133": // name="collision-induced dissociation" precursor.Activation = ActivationMethod.CID; break; case "MS:1000598": // name="electron transfer dissociation" precursor.Activation = ActivationMethod.ETD; break; case "MS:1000422": // name="beam-type collision-induced dissociation", "high-energy collision-induced dissociation" precursor.Activation = ActivationMethod.HCD; break; case "MS:1000250": // name="electron capture dissociation" precursor.Activation = ActivationMethod.ECD; break; case "MS:1000599": // name="pulsed q dissociation" precursor.Activation = ActivationMethod.PQD; break; case "MS:1000045": // name="collision energy" //energy = Convert.ToDouble(innerReader.GetAttribute("value")); break; } innerReader.Read(); // Consume the cvParam element (no child nodes) break; case "userParam": // Schema requirements: zero to many instances of this element innerReader.Skip(); break; default: innerReader.Skip(); break; } } innerReader.Close(); reader.Read(); // "selectedIon" might not have child nodes // We will either consume the EndElement, or the same element that was passed to ReadSpectrum (in case of no child nodes) break; default: reader.Skip(); break; } } reader.Close(); return precursor; }
/// <summary> /// Handle a single spectrum element and child nodes /// Called by ReadSpectrumList (xml hierarchy) /// </summary> /// <param name="reader">XmlReader that is only valid for the scope of the single spectrum element</param> /// <param name="includePeaks">Whether to read binary data arrays</param> private Spectrum ReadSpectrum(XmlReader reader, bool includePeaks = true) { reader.MoveToContent(); string index = reader.GetAttribute("index"); //Console.WriteLine("Reading spectrum indexed by " + index); // This is correct for Thermo files converted by msConvert, but need to implement for others as well string spectrumId = reader.GetAttribute("id"); // Native ID in mzML_1.1.0; unique identifier in mzML_1.0.0, often same as nativeID string nativeId = spectrumId; if (_version == MzML_Version.mzML1_0_0) { nativeId = reader.GetAttribute("nativeID"); // Native ID in mzML_1.0.0 } int scanNum = -1; // If a random access reader, there is already a scan number stored, based on the order of the index. Use it instead. if (_randomAccess) { scanNum = (int) (_spectrumOffsets.NativeToIdMap[nativeId]); } else { scanNum = (int)(_artificialScanNum++); // Interpret the NativeID (if the format has an interpreter) and use it instead of the artificial number. // TODO: Better handling than the artificial ID for other nativeIDs (ones currently not supported) int num = 0; if (NativeIdConversion.TryGetScanNumberInt(nativeId, out num)) { scanNum = num; } } int defaultArraySize = Convert.ToInt32(reader.GetAttribute("defaultArrayLength")); reader.ReadStartElement("spectrum"); // Throws exception if we are not at the "spectrum" tag. bool is_ms_ms = false; int msLevel = 0; bool centroided = false; double tic = 0; List<Precursor> precursors = new List<Precursor>(); List<ScanData> scans = new List<ScanData>(); List<BinaryDataArray> bdas = new List<BinaryDataArray>(); while (reader.ReadState == ReadState.Interactive) { // Handle exiting out properly at EndElement tags if (reader.NodeType != XmlNodeType.Element) { reader.Read(); continue; } ////////////////////////////////////////////////////////////////////////////////////// /// /// MS1 Spectra: only need Spectrum data: scanNum, MSLevel, ElutionTime, mzArray, IntensityArray /// /// MS2 Spectra: use ProductSpectrum; adds ActivationMethod and IsolationWindow /// ////////////////////////////////////////////////////////////////////////////////////// switch (reader.Name) { case "referenceableParamGroupRef": // Schema requirements: zero to many instances of this element reader.Skip(); break; case "cvParam": // Schema requirements: zero to many instances of this element /* MAY supply a *child* term of MS:1000465 (scan polarity) only once * e.g.: MS:1000129 (negative scan) * e.g.: MS:1000130 (positive scan) * MUST supply a *child* term of MS:1000559 (spectrum type) only once * e.g.: MS:1000322 (charge inversion mass spectrum) * e.g.: MS:1000325 (constant neutral gain spectrum) * e.g.: MS:1000326 (constant neutral loss spectrum) * e.g.: MS:1000328 (e/2 mass spectrum) * e.g.: MS:1000341 (precursor ion spectrum) * e.g.: MS:1000579 (MS1 spectrum) * e.g.: MS:1000580 (MSn spectrum) * e.g.: MS:1000581 (CRM spectrum) * e.g.: MS:1000582 (SIM spectrum) * e.g.: MS:1000583 (SRM spectrum) * e.g.: MS:1000620 (PDA spectrum) * e.g.: MS:1000627 (selected ion current chromatogram) * e.g.: MS:1000789 (enhanced multiply charged spectrum) * e.g.: MS:1000790 (time-delayed fragmentation spectrum) * et al. * MUST supply term MS:1000525 (spectrum representation) or any of its children only once * e.g.: MS:1000127 (centroid spectrum) * e.g.: MS:1000128 (profile spectrum) * MAY supply a *child* term of MS:1000499 (spectrum attribute) one or more times * e.g.: MS:1000285 (total ion current) * e.g.: MS:1000497 (zoom scan) * e.g.: MS:1000504 (base peak m/z) * e.g.: MS:1000505 (base peak intensity) * e.g.: MS:1000511 (ms level) * e.g.: MS:1000527 (highest observed m/z) * e.g.: MS:1000528 (lowest observed m/z) * e.g.: MS:1000618 (highest observed wavelength) * e.g.: MS:1000619 (lowest observed wavelength) * e.g.: MS:1000796 (spectrum title) * et al. */ switch (reader.GetAttribute("accession")) { case "MS:1000127": // name="centroid spectrum" centroided = true; break; case "MS:1000128": // name="profile spectrum" centroided = false; break; case "MS:1000511": // name="ms level" msLevel = Convert.ToInt32(reader.GetAttribute("value")); break; case "MS:1000579": // name="MS1 spectrum" is_ms_ms = false; break; case "MS:1000580": // name="MSn spectrum" is_ms_ms = true; break; case "MS:1000285": // name="total ion current" tic = Convert.ToDouble(reader.GetAttribute("value")); break; } reader.Read(); // Consume the cvParam element (no child nodes) break; case "userParam": // Schema requirements: zero to many instances of this element reader.Skip(); break; case "spectrumDescription": // mzML_1.0.0 compatibility // Schema requirements: one instance of this element ReadSpectrumDescription(reader.ReadSubtree(), ref scans, ref precursors, out centroided); reader.ReadEndElement(); // "spectrumDescription" must have child nodes break; case "scanList": // Schema requirements: zero to one instances of this element scans.AddRange(ReadScanList(reader.ReadSubtree())); reader.ReadEndElement(); // "scanList" must have child nodes break; case "precursorList": // Schema requirements: zero to one instances of this element precursors.AddRange(ReadPrecursorList(reader.ReadSubtree())); reader.ReadEndElement(); // "precursorList" must have child nodes break; case "productList": // Schema requirements: zero to one instances of this element reader.Skip(); break; case "binaryDataArrayList": // Schema requirements: zero to one instances of this element if (includePeaks) { bdas.AddRange(ReadBinaryDataArrayList(reader.ReadSubtree(), defaultArraySize)); reader.ReadEndElement(); // "binaryDataArrayList" must have child nodes } else { reader.Skip(); } break; default: reader.Skip(); break; } } reader.Close(); // Process the spectrum data ScanData scan = new ScanData(); Spectrum spectrum; BinaryDataArray mzs = new BinaryDataArray(); BinaryDataArray intensities = new BinaryDataArray(); foreach (var bda in bdas) { if (bda.ArrayType == ArrayType.m_z_array) { mzs = bda; } else if (bda.ArrayType == ArrayType.intensity_array) { intensities = bda; } } if (!centroided && includePeaks) { // Centroid spectrum // ProteoWizard var centroider = new Centroider(mzs.Data, intensities.Data); double[] centroidedMzs, centroidedIntensities; centroider.GetCentroidedData(out centroidedMzs, out centroidedIntensities); mzs.Data = centroidedMzs; intensities.Data = centroidedIntensities; } if (scans.Count == 1) { scan = scans[0]; } else if (scans.Count > 1) { // TODO: Should do something else to appropriately handle combinations... scan = scans[0]; } if (is_ms_ms) { Precursor precursor = new Precursor(); if (precursors.Count == 1) { precursor = precursors[0]; } else if (precursors.Count > 1) { // TODO: Should do something else to appropriately handle multiple precursors... precursor = precursors[0]; } SelectedIon ion = new SelectedIon(); if (precursor.Ions.Count == 1) { ion = precursor.Ions[0]; } else if (precursor.Ions.Count > 1) { // TODO: Should do something else to appropriately handle multiple selected ions... ion = precursor.Ions[0]; } var pspectrum = new ProductSpectrum(mzs.Data, intensities.Data, scanNum); pspectrum.ActivationMethod = precursor.Activation; // Select mz value to use based on presence of a Thermo-specific user param. // The user param has a slightly higher precision, if that matters. double mz = scan.MonoisotopicMz == 0.0 ? ion.SelectedIonMz : scan.MonoisotopicMz; pspectrum.IsolationWindow = new IsolationWindow(precursor.IsolationWindowTargetMz, precursor.IsolationWindowLowerOffset, precursor.IsolationWindowUpperOffset, mz, ion.Charge); //pspectrum.IsolationWindow.OldCharge = ion.OldCharge; //pspectrum.IsolationWindow.SelectedIonMz = ion.SelectedIonMz; spectrum = pspectrum; } else { spectrum = new Spectrum(mzs.Data, intensities.Data, scanNum); } spectrum.MsLevel = msLevel; spectrum.ElutionTime = scan.StartTime; spectrum.NativeId = nativeId; spectrum.TotalIonCurrent = tic; return spectrum; }