From 0d606190216646afeaaa685740f773b4f47c3847 Mon Sep 17 00:00:00 2001 From: "mikiko.takahashi" Date: Fri, 13 Feb 2026 17:04:26 +0900 Subject: [PATCH 1/8] Add sdf format exporter --- src/Common/CommonStandard/Enum/CommonEnums.cs | 2 +- .../MsdialCore/Export/AlignmentSdfExporter.cs | 28 ++++ .../MsdialCore/Export/AnalysisSdfExporter.cs | 37 +++++ .../MsdialCore/Export/SpectraExport.cs | 155 +++++++++++++++++- .../Model/Dims/DimsMethodModel.cs | 1 + .../Model/Gcms/GcmsMethodModel.cs | 1 + .../ImagingImms/ImagingImmsMethodModel.cs | 10 ++ .../Model/Imms/ImmsMethodModel.cs | 10 ++ .../Model/Lcimms/LcimmsMethodModel.cs | 11 ++ .../Model/Lcms/LcmsMethodModel.cs | 11 ++ .../ViewModel/Dims/DimsAlignmentViewModel.cs | 2 +- .../ViewModel/Dims/DimsAnalysisViewModel.cs | 2 +- .../ViewModel/Dims/DimsMethodViewModel.cs | 9 + .../ViewModel/Imms/ImmsAlignmentViewModel.cs | 2 +- .../ViewModel/Lcms/LcmsAlignmentViewModel.cs | 2 +- 15 files changed, 277 insertions(+), 6 deletions(-) create mode 100644 src/MSDIAL5/MsdialCore/Export/AlignmentSdfExporter.cs create mode 100644 src/MSDIAL5/MsdialCore/Export/AnalysisSdfExporter.cs diff --git a/src/Common/CommonStandard/Enum/CommonEnums.cs b/src/Common/CommonStandard/Enum/CommonEnums.cs index 9ecfec21e..1e4720d51 100644 --- a/src/Common/CommonStandard/Enum/CommonEnums.cs +++ b/src/Common/CommonStandard/Enum/CommonEnums.cs @@ -39,7 +39,7 @@ public enum RiCompoundType { Alkanes, Fames } public enum AlignmentIndexType { RT, RI } public enum TargetOmics { Metabolomics, Lipidomics, Proteomics } public enum Ionization { ESI, EI } - public enum ExportSpectraFileFormat { mgf, msp, txt, mat, ms } + public enum ExportSpectraFileFormat { mgf, msp, txt, mat, ms, sdf } public enum ExportspectraType { profile, centroid, deconvoluted } public enum IonAbundanceUnit { Intensity, Height, Area, pmol, fmol, ng, pg, diff --git a/src/MSDIAL5/MsdialCore/Export/AlignmentSdfExporter.cs b/src/MSDIAL5/MsdialCore/Export/AlignmentSdfExporter.cs new file mode 100644 index 000000000..2d01211fd --- /dev/null +++ b/src/MSDIAL5/MsdialCore/Export/AlignmentSdfExporter.cs @@ -0,0 +1,28 @@ +using CompMs.MsdialCore.DataObj; +using CompMs.MsdialCore.MSDec; +using System.IO; + +namespace CompMs.MsdialCore.Export; + +public sealed class AlignmentSdfExporter : IAlignmentSpectraExporter +{ + private readonly bool _exportNoMs2Molecule; + private readonly bool _set2dCoordinates; + public AlignmentSdfExporter(bool exportNoMs2Molecule, bool set2dCoordinates) + { + _exportNoMs2Molecule = exportNoMs2Molecule; + _set2dCoordinates = set2dCoordinates; + } + public AlignmentSdfExporter() : this(exportNoMs2Molecule: true, set2dCoordinates: true) { } + + void IAlignmentSpectraExporter.Export(Stream stream, AlignmentSpotProperty spot, MSDecResult msdecResult) + { + SpectraExport.SaveSpectraTableAsSdfFormat( + stream, + spot, + msdecResult.Spectrum, + _exportNoMs2Molecule, + _set2dCoordinates + ); + } +} diff --git a/src/MSDIAL5/MsdialCore/Export/AnalysisSdfExporter.cs b/src/MSDIAL5/MsdialCore/Export/AnalysisSdfExporter.cs new file mode 100644 index 000000000..55327eb2a --- /dev/null +++ b/src/MSDIAL5/MsdialCore/Export/AnalysisSdfExporter.cs @@ -0,0 +1,37 @@ +using CompMs.MsdialCore.DataObj; +using CompMs.MsdialCore.Parser; +using System; +using System.IO; + +namespace CompMs.MsdialCore.Export +{ + public sealed class AnalysisSdfExporter : IAnalysisExporter + { + private readonly Func> _loaderFactory; + + public AnalysisSdfExporter(Func> loaderFuctory) { + _loaderFactory = loaderFuctory ?? throw new ArgumentNullException(nameof(loaderFuctory)); + } + private readonly bool _exportNoMs2Molecule; + private readonly bool _set2dCoordinates; + public AnalysisSdfExporter(bool exportNoMs2Molecule, bool set2dCoordinates) + { + _exportNoMs2Molecule = exportNoMs2Molecule; + _set2dCoordinates = set2dCoordinates; + } + public AnalysisSdfExporter() : this(exportNoMs2Molecule: true, set2dCoordinates: true) { } + + void IAnalysisExporter.Export(Stream stream, AnalysisFileBean analysisFile, ChromatogramPeakFeatureCollection peakFeatureCollection, ExportStyle exportStyle) { + var loader = _loaderFactory(analysisFile); + foreach (var peak in peakFeatureCollection.Items) { + SpectraExport.SaveSpectraTableAsSdfFormat( + stream, + peak, + loader.Load(peak).Spectrum, + _exportNoMs2Molecule, + _set2dCoordinates + ); + } + } + } +} diff --git a/src/MSDIAL5/MsdialCore/Export/SpectraExport.cs b/src/MSDIAL5/MsdialCore/Export/SpectraExport.cs index 5592a2e73..dc5542c73 100644 --- a/src/MSDIAL5/MsdialCore/Export/SpectraExport.cs +++ b/src/MSDIAL5/MsdialCore/Export/SpectraExport.cs @@ -14,6 +14,10 @@ using System.IO; using System.Linq; using System.Text; +using NCDK; +using NCDK.Smiles; +using NCDK.Layout; +using NCDK.IO; namespace CompMs.MsdialCore.Export { @@ -34,6 +38,9 @@ public static void SaveSpectraTable( case ExportSpectraFileFormat.mgf: SaveSpectraTableAsMgfFormat(exportStream, chromPeakFeature, scan.Spectrum); break; + case ExportSpectraFileFormat.sdf: + SaveSpectraTableAsSdfFormat(exportStream, chromPeakFeature, scan.Spectrum); + break; case ExportSpectraFileFormat.mat: SaveSpectraTableAsMatFormat(exportStream, chromPeakFeature, scan.Spectrum, spectrumList, mapper, parameter); break; @@ -53,7 +60,8 @@ public static void SaveSpectraTable( IMSScanProperty scan, DataBaseMapper mapper, ParameterBase parameter, - AlignmentSpotProperty isotopeTrackedLastSpot = null) { + AlignmentSpotProperty isotopeTrackedLastSpot = null) + { switch (spectraFormat) { case ExportSpectraFileFormat.msp: SaveSpectraTableAsNistFormat(exportStream, spotProperty, scan.Spectrum, mapper, parameter); @@ -61,6 +69,9 @@ public static void SaveSpectraTable( case ExportSpectraFileFormat.mgf: SaveSpectraTableAsMgfFormat(exportStream, spotProperty, scan.Spectrum); break; + case ExportSpectraFileFormat.sdf: + SaveSpectraTableAsSdfFormat(exportStream, spotProperty, scan.Spectrum); + break; case ExportSpectraFileFormat.mat: SaveSpectraTableAsMatFormat(exportStream, spotProperty, scan.Spectrum, mapper, parameter, isotopeTrackedLastSpot); break; @@ -308,6 +319,148 @@ private static void WriteChromXFieldAsMGF( } #endregion + #region sdf + public static void SaveSpectraTableAsSdfFormat( + Stream stream, + AlignmentSpotProperty spotProperty, + IEnumerable spectrum, + bool exportNoMs2Molecule = false, + bool Set2dCoordinates = false + ) + { + if (!exportNoMs2Molecule && !spotProperty.IsMsmsAssigned) + { + return; + } + using (StreamWriter sw = new StreamWriter(stream, Encoding.ASCII, 4096, true)) + { + if(spotProperty.IsMsmsAssigned) + { + MolBlockFromSmiles(sw, spotProperty.SMILES, Set2dCoordinates); + } + else + { + EmptyMolBlock(sw); + } + WriteChromPeakFeatureInfoAsSdf(sw, spotProperty, spectrum); + sw.WriteLine("$$$$"); + sw.WriteLine(); + } + } + public static void SaveSpectraTableAsSdfFormat( + Stream stream, + ChromatogramPeakFeature chromPeakFeature, + IEnumerable spectrum, + bool exportNoMs2Molecule = false, + bool Set2dCoordinates = false + ) + { + using (StreamWriter sw = new StreamWriter(stream, Encoding.ASCII, 4096, true)) + { + if (chromPeakFeature.SMILES != null) + { + MolBlockFromSmiles(sw, chromPeakFeature.SMILES, Set2dCoordinates); + } + else + { + EmptyMolBlock(sw); + } + WriteChromPeakFeatureInfoAsSdf(sw, chromPeakFeature, spectrum); + sw.WriteLine("$$$$"); + sw.WriteLine(); + } + } + private static void WriteSdfDataItem(StreamWriter sw, string fieldName, string value) + { + sw.WriteLine("> <" + fieldName + ">"); + sw.WriteLine(value ?? string.Empty); + //sw.WriteLine(); + } + private static void EmptyMolBlock(StreamWriter sw) + { + sw.WriteLine(""); + sw.WriteLine(" MS-DIAL"); + sw.WriteLine(); + sw.WriteLine(" 0 0 0 0 0 0 999 V2000"); + sw.WriteLine("M END"); + + } + private static void MolBlockFromSmiles(StreamWriter sw, string smiles, bool Set2dCoordinates) + { + var sp = new SmilesParser(); + IAtomContainer mol = sp.ParseSmiles(smiles); + var sdg = new StructureDiagramGenerator + { + Molecule = mol + }; + if(Set2dCoordinates) + { + sdg.GenerateCoordinates(); + } + mol = sdg.Molecule; + using var w = new MDLV2000Writer(sw); + w.Write(mol); + } + private static void WriteChromPeakFeatureInfoAsSdf( + StreamWriter sw, + AlignmentSpotProperty spotProperty, + IEnumerable spectrum) + { + var sb = new StringBuilder(); + WriteSdfDataItem(sw, "NAME", string.IsNullOrWhiteSpace(spotProperty.Name)? "Unknown": spotProperty.Name); + WriteSdfDataItem(sw, "SCANS", spotProperty.MasterAlignmentID.ToString()); + WriteSdfDataItem(sw, "PRECURSOR_MZ", Math.Round(spotProperty.MassCenter,5).ToString()); + WriteSdfDataItem(sw, "ION_MODE", spotProperty.IonMode.ToString()); + + if (spotProperty.IsMsmsAssigned) + { + if (spotProperty.AdductType != null) WriteSdfDataItem(sw, "PRECURSOR_TYPE", spotProperty.AdductType.AdductIonName); + if (!string.IsNullOrWhiteSpace(spotProperty.Formula.FormulaString)) WriteSdfDataItem(sw, "FORMULA", spotProperty.Formula.FormulaString); + if (!string.IsNullOrWhiteSpace(spotProperty.InChIKey)) WriteSdfDataItem(sw, "FORMULA", spotProperty.InChIKey); + if (!string.IsNullOrWhiteSpace(spotProperty.SMILES)) WriteSdfDataItem(sw, "FORMULA", spotProperty.SMILES); + WriteSdfDataItem(sw, "MS_LEVEL", "MS2"); + var peaks = spectrum.Where(spec => spec.Intensity > 0).ToList(); + WriteSdfDataItem(sw, "NUM_PEAKS", peaks.Count.ToString()); + var peaksText = string.Join( + "\n", + spectrum.Select(p => + $"{Math.Round(p.Mass, 5)} {Math.Round(p.Intensity,0)}" + ) + ); + WriteSdfDataItem(sw, "MASS_SPECTRAL_PEAKS", peaksText); + } + } + private static void WriteChromPeakFeatureInfoAsSdf( + StreamWriter sw, + ChromatogramPeakFeature spotProperty, + IEnumerable spectrum) + { + var sb = new StringBuilder(); + WriteSdfDataItem(sw, "NAME", string.IsNullOrWhiteSpace(spotProperty.Name) ? "Unknown" : spotProperty.Name); + WriteSdfDataItem(sw, "SCANS", spotProperty.PeakID.ToString()); + WriteSdfDataItem(sw, "PRECURSOR_MZ", Math.Round(spotProperty.PrecursorMz, 5).ToString()); + WriteSdfDataItem(sw, "ION_MODE", spotProperty.IonMode.ToString()); + + if (spotProperty.IsMsmsContained) + { + if (spotProperty.AdductType != null) WriteSdfDataItem(sw, "PRECURSOR_TYPE", spotProperty.AdductType.AdductIonName); + if (!string.IsNullOrWhiteSpace(spotProperty.Formula.FormulaString)) WriteSdfDataItem(sw, "FORMULA", spotProperty.Formula.FormulaString); + if (!string.IsNullOrWhiteSpace(spotProperty.InChIKey)) WriteSdfDataItem(sw, "FORMULA", spotProperty.InChIKey); + if (!string.IsNullOrWhiteSpace(spotProperty.SMILES)) WriteSdfDataItem(sw, "FORMULA", spotProperty.SMILES); + WriteSdfDataItem(sw, "MS_LEVEL", "MS2"); + var peaks = spectrum.Where(spec => spec.Intensity > 0).ToList(); + WriteSdfDataItem(sw, "NUM_PEAKS", peaks.Count.ToString()); + var peaksText = string.Join( + "\n", + spectrum.Select(p => + $"{Math.Round(p.Mass, 5)} {Math.Round(p.Intensity, 0)}" + ) + ); + WriteSdfDataItem(sw, "MASS_SPECTRAL_PEAKS", peaksText); + } + } + #endregion + #region mat private static void SaveSpectraTableAsMatFormat( Stream stream, diff --git a/src/MSDIAL5/MsdialGuiApp/Model/Dims/DimsMethodModel.cs b/src/MSDIAL5/MsdialGuiApp/Model/Dims/DimsMethodModel.cs index fa87a97a6..95b05a1dd 100644 --- a/src/MSDIAL5/MsdialGuiApp/Model/Dims/DimsMethodModel.cs +++ b/src/MSDIAL5/MsdialGuiApp/Model/Dims/DimsMethodModel.cs @@ -99,6 +99,7 @@ public DimsMethodModel( peakSpotSupplyer, new AlignmentSpectraExportFormat("Msp", "msp", new AlignmentMspExporter(storage.DataBaseMapper, storage.Parameter)), new AlignmentSpectraExportFormat("Mgf", "mgf", new AlignmentMgfExporter()), + new AlignmentSpectraExportFormat("Sdf", "sdf", new AlignmentSdfExporter()), new AlignmentSpectraExportFormat("Mat", "mat", new AlignmentMatExporter(storage.DataBaseMapper, storage.Parameter))); var gnps = new AlignmentGnpsExportModel("GNPS", quantTypes, new GnpsMetadataAccessor(storage.DataBaseMapper, storage.Parameter), peakMeta.GetAccessor(), fileMeta.GetAccessor(), analysisFileBeanModelCollection); var spectraAndReference = new AlignmentMatchedSpectraExportModel(peakSpotSupplyer, storage.DataBaseMapper, analysisFileBeanModelCollection.IncludedAnalysisFiles, CompoundSearcherCollection.BuildSearchers(storage.DataBases, storage.DataBaseMapper)); diff --git a/src/MSDIAL5/MsdialGuiApp/Model/Gcms/GcmsMethodModel.cs b/src/MSDIAL5/MsdialGuiApp/Model/Gcms/GcmsMethodModel.cs index be89e2a3c..382df99ff 100644 --- a/src/MSDIAL5/MsdialGuiApp/Model/Gcms/GcmsMethodModel.cs +++ b/src/MSDIAL5/MsdialGuiApp/Model/Gcms/GcmsMethodModel.cs @@ -112,6 +112,7 @@ public GcmsMethodModel(AnalysisFileBeanModelCollection analysisFileBeanModelColl peakSpotSupplyer, new AlignmentSpectraExportFormat("Msp", "msp", new AlignmentMspExporter(storage.DataBaseMapper, storage.Parameter)), new AlignmentSpectraExportFormat("Mgf", "mgf", new AlignmentMgfExporter()), + new AlignmentSpectraExportFormat("Sdf", "sdf", new AlignmentSdfExporter()), new AlignmentSpectraExportFormat("Mat", "mat", new AlignmentMatExporter(storage.DataBaseMapper, storage.Parameter))); var gnps = new AlignmentGnpsExportModel("GNPS", quantTypes, new GnpsMetadataAccessor(storage.DataBaseMapper, storage.Parameter), peakMeta.GetAccessor(), fileMeta.GetAccessor(), analysisFileBeanModelCollection); var exportGroups = new List { peakGroup, spectraGroup, gnps, }; diff --git a/src/MSDIAL5/MsdialGuiApp/Model/ImagingImms/ImagingImmsMethodModel.cs b/src/MSDIAL5/MsdialGuiApp/Model/ImagingImms/ImagingImmsMethodModel.cs index f879ec605..6ddff5703 100644 --- a/src/MSDIAL5/MsdialGuiApp/Model/ImagingImms/ImagingImmsMethodModel.cs +++ b/src/MSDIAL5/MsdialGuiApp/Model/ImagingImms/ImagingImmsMethodModel.cs @@ -15,6 +15,7 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.ComponentModel; using System.Diagnostics; using System.Linq; using System.Threading; @@ -150,6 +151,15 @@ public AnalysisResultExportModel CreateExportAnalysisModel() { FileSuffix = "mgf", Label = "MASCOT format (*.mgf)" }, + new SpectraTypeSelectableMsdialAnalysisExportModel(new Dictionary> { + [ExportspectraType.deconvoluted] = new AnalysisSdfExporter(file => new MSDecLoader(file.DeconvolutionFilePath, file.DeconvolutionFilePathList)), + [ExportspectraType.centroid] = new AnalysisSdfExporter(file => new CentroidMsScanPropertyLoader(_storage.Parameter.ProviderFactoryParameter.Create().Create(file.LoadRawMeasurement(true, true, 5, 5000)), _storage.Parameter.MS2DataType)), + }) + { + FilePrefix = "Sdf", + FileSuffix = "sdf", + Label = "MDL SDfile (*.sdf)" + }, new MsdialAnalysisMassBankRecordExportModel(_storage.Parameter.ProjectParam, StudyContext), }; diff --git a/src/MSDIAL5/MsdialGuiApp/Model/Imms/ImmsMethodModel.cs b/src/MSDIAL5/MsdialGuiApp/Model/Imms/ImmsMethodModel.cs index 9056c73ae..844ae0aad 100644 --- a/src/MSDIAL5/MsdialGuiApp/Model/Imms/ImmsMethodModel.cs +++ b/src/MSDIAL5/MsdialGuiApp/Model/Imms/ImmsMethodModel.cs @@ -104,6 +104,7 @@ public ImmsMethodModel(AnalysisFileBeanModelCollection analysisFileBeanModelColl peakSpotSupplyer, new AlignmentSpectraExportFormat("Msp", "msp", new AlignmentMspExporter(storage.DataBaseMapper, storage.Parameter)), new AlignmentSpectraExportFormat("Mgf", "mgf", new AlignmentMgfExporter()), + new AlignmentSpectraExportFormat("Sdf", "sdf", new AlignmentSdfExporter()), new AlignmentSpectraExportFormat("Mat", "mat", new AlignmentMatExporter(storage.DataBaseMapper, storage.Parameter))); var gnps = new AlignmentGnpsExportModel("GNPS", quantTypes, new GnpsMetadataAccessor(storage.DataBaseMapper, storage.Parameter), peakMeta.GetAccessor(), fileMeta.GetAccessor(), analysisFileBeanModelCollection); var spectraAndReference = new AlignmentMatchedSpectraExportModel(peakSpotSupplyer, storage.DataBaseMapper, analysisFileBeanModelCollection.IncludedAnalysisFiles, CompoundSearcherCollection.BuildSearchers(storage.DataBases, storage.DataBaseMapper)); @@ -317,6 +318,15 @@ public AnalysisResultExportModel CreateExportAnalysisResult() { FileSuffix = "mgf", Label = "MASCOT format (*.mgf)" }, + new SpectraTypeSelectableMsdialAnalysisExportModel(new Dictionary> { + [ExportspectraType.deconvoluted] = new AnalysisSdfExporter(file => new MSDecLoader(file.DeconvolutionFilePath, file.DeconvolutionFilePathList)), + [ExportspectraType.centroid] = new AnalysisSdfExporter(file => new CentroidMsScanPropertyLoader(ProviderFactory.Create(file), _storage.Parameter.MS2DataType)), + }) + { + FilePrefix = "Sdf", + FileSuffix = "sdf", + Label = "MDL SDfile (*.sdf)" + }, new MsdialAnalysisMassBankRecordExportModel(_storage.Parameter.ProjectParam, StudyContext), }; return new AnalysisResultExportModel(AnalysisFileModelCollection, _storage.Parameter.ProjectParam.ProjectFolderPath, _broker, models); diff --git a/src/MSDIAL5/MsdialGuiApp/Model/Lcimms/LcimmsMethodModel.cs b/src/MSDIAL5/MsdialGuiApp/Model/Lcimms/LcimmsMethodModel.cs index e5d3cbfd3..6744f814f 100644 --- a/src/MSDIAL5/MsdialGuiApp/Model/Lcimms/LcimmsMethodModel.cs +++ b/src/MSDIAL5/MsdialGuiApp/Model/Lcimms/LcimmsMethodModel.cs @@ -26,6 +26,7 @@ using Reactive.Bindings.Notifiers; using System; using System.Collections.Generic; +using System.ComponentModel; using System.Diagnostics; using System.Linq; using System.Reactive.Linq; @@ -100,6 +101,7 @@ public LcimmsMethodModel(AnalysisFileBeanModelCollection analysisFileBeanModelCo peakSpotSupplyer, new AlignmentSpectraExportFormat("Msp", "msp", new AlignmentMspExporter(storage.DataBaseMapper, storage.Parameter)), new AlignmentSpectraExportFormat("Mgf", "mgf", new AlignmentMgfExporter()), + new AlignmentSpectraExportFormat("Sdf", "sdf", new AlignmentSdfExporter()), new AlignmentSpectraExportFormat("Mat", "mat", new AlignmentMatExporter(storage.DataBaseMapper, storage.Parameter))); var gnps = new AlignmentGnpsExportModel("GNPS", quantTypes, new GnpsMetadataAccessor(storage.DataBaseMapper, storage.Parameter), peakMeta.GetAccessor(), fileMeta.GetAccessor(), analysisFileBeanModelCollection); var spectraAndReference = new AlignmentMatchedSpectraExportModel(peakSpotSupplyer, storage.DataBaseMapper, analysisFileBeanModelCollection.IncludedAnalysisFiles, CompoundSearcherCollection.BuildSearchers(storage.DataBases, storage.DataBaseMapper)); @@ -319,6 +321,15 @@ static RawMeasurement map(AnalysisFileBean file) { FileSuffix = "mgf", Label = "MASCOT format (*.mgf)" }, + new SpectraTypeSelectableMsdialAnalysisExportModel(new Dictionary> { + [ExportspectraType.deconvoluted] = new AnalysisSdfExporter(file => new MSDecLoader(file.DeconvolutionFilePath, file.DeconvolutionFilePathList)), + [ExportspectraType.centroid] = new AnalysisSdfExporter(file => new CentroidMsScanPropertyLoader(factory.Create(file), Storage.Parameter.MS2DataType)), + }) + { + FilePrefix = "Sdf", + FileSuffix = "sdf", + Label = "MDL SDfile (*.sdf)" + }, new MsdialAnalysisMassBankRecordExportModel(Storage.Parameter.ProjectParam, _studyContext), }; return new AnalysisResultExportModel(AnalysisFileModelCollection, Storage.Parameter.ProjectParam.ProjectFolderPath, _broker, models); diff --git a/src/MSDIAL5/MsdialGuiApp/Model/Lcms/LcmsMethodModel.cs b/src/MSDIAL5/MsdialGuiApp/Model/Lcms/LcmsMethodModel.cs index 4f4d8d2bd..5393dcadc 100644 --- a/src/MSDIAL5/MsdialGuiApp/Model/Lcms/LcmsMethodModel.cs +++ b/src/MSDIAL5/MsdialGuiApp/Model/Lcms/LcmsMethodModel.cs @@ -22,6 +22,7 @@ using Reactive.Bindings.Notifiers; using System; using System.Collections.Generic; +using System.ComponentModel; using System.Diagnostics; using System.Linq; using System.Reactive.Linq; @@ -110,6 +111,7 @@ public LcmsMethodModel( peakSpotSupplyer, new AlignmentSpectraExportFormat("Msp", "msp", new AlignmentMspExporter(storage.DataBaseMapper, storage.Parameter)), new AlignmentSpectraExportFormat("Mgf", "mgf", new AlignmentMgfExporter()), + new AlignmentSpectraExportFormat("Sdf", "sdf", new AlignmentSdfExporter()), new AlignmentSpectraExportFormat("Mat", "mat", new AlignmentMatExporter(storage.DataBaseMapper, storage.Parameter))); var gnps = new AlignmentGnpsExportModel("GNPS", quantTypes, new GnpsMetadataAccessor(storage.DataBaseMapper, storage.Parameter), peakMeta.GetAccessor(), fileMeta.GetAccessor(), analysisFileBeanModelCollection); var massBank = new AlignmentResultMassBankRecordExportModel(peakSpotSupplyer, storage.Parameter.ProjectParam, studyContext); @@ -412,6 +414,15 @@ public AnalysisResultExportModel ExportAnalysis() { FileSuffix = "mgf", Label = "MASCOT format (*.mgf)" }, + new SpectraTypeSelectableMsdialAnalysisExportModel(new Dictionary> { + [ExportspectraType.deconvoluted] = new AnalysisSdfExporter(file => new MSDecLoader(file.DeconvolutionFilePath, file.DeconvolutionFilePathList)), + [ExportspectraType.centroid] = new AnalysisSdfExporter(file => new CentroidMsScanPropertyLoader(_providerFactory.Create(file), _storage.Parameter.MS2DataType)), + }) + { + FilePrefix = "Sdf", + FileSuffix = "sdf", + Label = "MDL SDfile (*.sdf)" + }, new MsdialAnalysisMassBankRecordExportModel(_storage.Parameter.ProjectParam, _studyContext), }; return new AnalysisResultExportModel(AnalysisFileModelCollection, _storage.Parameter.ProjectParam.ProjectFolderPath, _broker, models); diff --git a/src/MSDIAL5/MsdialGuiApp/ViewModel/Dims/DimsAlignmentViewModel.cs b/src/MSDIAL5/MsdialGuiApp/ViewModel/Dims/DimsAlignmentViewModel.cs index 19b0ef76a..d55a1c0c0 100644 --- a/src/MSDIAL5/MsdialGuiApp/ViewModel/Dims/DimsAlignmentViewModel.cs +++ b/src/MSDIAL5/MsdialGuiApp/ViewModel/Dims/DimsAlignmentViewModel.cs @@ -156,7 +156,7 @@ private void SaveSpectra() var request = new SaveFileNameRequest(_model.SaveSpectra) { Title = "Save spectra", - Filter = "NIST format(*.msp)|*.msp|MassBank format(*.txt)|*.txt;|MASCOT format(*.mgf)|*.mgf|MSFINDER format(*.mat)|*.mat;|SIRIUS format(*.ms)|*.ms", + Filter = "NIST format(*.msp)|*.msp|MassBank format(*.txt)|*.txt;|MASCOT format(*.mgf)|*.mgf|MDF SDfile(*.sdf)|*.sdf|MSFINDER format(*.mat)|*.mat;|SIRIUS format(*.ms)|*.ms", RestoreDirectory = true, AddExtension = true, }; diff --git a/src/MSDIAL5/MsdialGuiApp/ViewModel/Dims/DimsAnalysisViewModel.cs b/src/MSDIAL5/MsdialGuiApp/ViewModel/Dims/DimsAnalysisViewModel.cs index 5289f2154..832dea07b 100644 --- a/src/MSDIAL5/MsdialGuiApp/ViewModel/Dims/DimsAnalysisViewModel.cs +++ b/src/MSDIAL5/MsdialGuiApp/ViewModel/Dims/DimsAnalysisViewModel.cs @@ -161,7 +161,7 @@ private void SaveSpectra() var request = new SaveFileNameRequest(_model.SaveSpectra) { Title = "Save spectra", - Filter = "NIST format(*.msp)|*.msp|MassBank format(*.txt)|*.txt;|MASCOT format(*.mgf)|*.mgf|MSFINDER format(*.mat)|*.mat;|SIRIUS format(*.ms)|*.ms", + Filter = "NIST format(*.msp)|*.msp|MassBank format(*.txt)|*.txt;|MASCOT format(*.mgf)|*.mgf|MDF SDfile(*.sdf)|*.sdf|MSFINDER format(*.mat)|*.mat;|SIRIUS format(*.ms)|*.ms", RestoreDirectory = true, AddExtension = true, }; diff --git a/src/MSDIAL5/MsdialGuiApp/ViewModel/Dims/DimsMethodViewModel.cs b/src/MSDIAL5/MsdialGuiApp/ViewModel/Dims/DimsMethodViewModel.cs index a31460a01..dffaa7a34 100644 --- a/src/MSDIAL5/MsdialGuiApp/ViewModel/Dims/DimsMethodViewModel.cs +++ b/src/MSDIAL5/MsdialGuiApp/ViewModel/Dims/DimsMethodViewModel.cs @@ -119,6 +119,15 @@ private void ExportAnalysis() { FileSuffix = "mgf", Label = "MASCOT format (*.mgf)" }, + new SpectraTypeSelectableMsdialAnalysisExportModel(new Dictionary> { + [ExportspectraType.deconvoluted] = new AnalysisMgfExporter(file => new MSDecLoader(file.DeconvolutionFilePath, file.DeconvolutionFilePathList)), + [ExportspectraType.centroid] = new AnalysisSdfExporter(file => new CentroidMsScanPropertyLoader(_model.ProviderFactory.Create(file), container.Parameter.MS2DataType)) + }) + { + FilePrefix = "Sdf", + FileSuffix = "sdf", + Label = "MDL SDfile (*.sdf)" + }, new MsdialAnalysisMassBankRecordExportModel(container.Parameter.ProjectParam, _model.StudyContext), }; var model = new AnalysisResultExportModel(_model.AnalysisFileModelCollection, _model.Storage.Parameter.ProjectParam.ProjectFolderPath, _broker, models); diff --git a/src/MSDIAL5/MsdialGuiApp/ViewModel/Imms/ImmsAlignmentViewModel.cs b/src/MSDIAL5/MsdialGuiApp/ViewModel/Imms/ImmsAlignmentViewModel.cs index 0bcc70dad..d9b0b0e47 100644 --- a/src/MSDIAL5/MsdialGuiApp/ViewModel/Imms/ImmsAlignmentViewModel.cs +++ b/src/MSDIAL5/MsdialGuiApp/ViewModel/Imms/ImmsAlignmentViewModel.cs @@ -187,7 +187,7 @@ private void SaveSpectra() { var request = new SaveFileNameRequest(_model.SaveSpectra) { Title = "Save spectra", - Filter = "NIST format(*.msp)|*.msp|MassBank format(*.txt)|*.txt;|MASCOT format(*.mgf)|*.mgf|MSFINDER format(*.mat)|*.mat;|SIRIUS format(*.ms)|*.ms", + Filter = "NIST format(*.msp)|*.msp|MassBank format(*.txt)|*.txt;|MASCOT format(*.mgf)|*.mgf|MDF SDfile(*.sdf)|*.sdf|MSFINDER format(*.mat)|*.mat;|SIRIUS format(*.ms)|*.ms", RestoreDirectory = true, AddExtension = true, }; diff --git a/src/MSDIAL5/MsdialGuiApp/ViewModel/Lcms/LcmsAlignmentViewModel.cs b/src/MSDIAL5/MsdialGuiApp/ViewModel/Lcms/LcmsAlignmentViewModel.cs index 5365e1c87..02a3415b6 100644 --- a/src/MSDIAL5/MsdialGuiApp/ViewModel/Lcms/LcmsAlignmentViewModel.cs +++ b/src/MSDIAL5/MsdialGuiApp/ViewModel/Lcms/LcmsAlignmentViewModel.cs @@ -219,7 +219,7 @@ private void SaveSpectra() { var request = new SaveFileNameRequest(_model.SaveSpectra) { Title = "Save spectra", - Filter = "NIST format(*.msp)|*.msp|MassBank format(*.txt)|*.txt;|MASCOT format(*.mgf)|*.mgf|MSFINDER format(*.mat)|*.mat;|SIRIUS format(*.ms)|*.ms", + Filter = "NIST format(*.msp)|*.msp|MassBank format(*.txt)|*.txt;|MASCOT format(*.mgf)|*.mgf|MDL SDfile(*.sdf)|*.sdf|MSFINDER format(*.mat)|*.mat;|SIRIUS format(*.ms)|*.ms", RestoreDirectory = true, AddExtension = true, }; From e043a194627057467510555b17cf4d1da1fd25f8 Mon Sep 17 00:00:00 2001 From: "mikiko.takahashi" Date: Fri, 13 Feb 2026 17:09:43 +0900 Subject: [PATCH 2/8] fix tag line("_" change to " ") --- .../MsdialCore/Export/SpectraExport.cs | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/MSDIAL5/MsdialCore/Export/SpectraExport.cs b/src/MSDIAL5/MsdialCore/Export/SpectraExport.cs index dc5542c73..89bb5508f 100644 --- a/src/MSDIAL5/MsdialCore/Export/SpectraExport.cs +++ b/src/MSDIAL5/MsdialCore/Export/SpectraExport.cs @@ -409,25 +409,25 @@ private static void WriteChromPeakFeatureInfoAsSdf( var sb = new StringBuilder(); WriteSdfDataItem(sw, "NAME", string.IsNullOrWhiteSpace(spotProperty.Name)? "Unknown": spotProperty.Name); WriteSdfDataItem(sw, "SCANS", spotProperty.MasterAlignmentID.ToString()); - WriteSdfDataItem(sw, "PRECURSOR_MZ", Math.Round(spotProperty.MassCenter,5).ToString()); - WriteSdfDataItem(sw, "ION_MODE", spotProperty.IonMode.ToString()); + WriteSdfDataItem(sw, "PRECURSOR MZ", Math.Round(spotProperty.MassCenter,5).ToString()); + WriteSdfDataItem(sw, "ION MODE", spotProperty.IonMode.ToString()); if (spotProperty.IsMsmsAssigned) { - if (spotProperty.AdductType != null) WriteSdfDataItem(sw, "PRECURSOR_TYPE", spotProperty.AdductType.AdductIonName); + if (spotProperty.AdductType != null) WriteSdfDataItem(sw, "PRECURSOR TYPE", spotProperty.AdductType.AdductIonName); if (!string.IsNullOrWhiteSpace(spotProperty.Formula.FormulaString)) WriteSdfDataItem(sw, "FORMULA", spotProperty.Formula.FormulaString); if (!string.IsNullOrWhiteSpace(spotProperty.InChIKey)) WriteSdfDataItem(sw, "FORMULA", spotProperty.InChIKey); if (!string.IsNullOrWhiteSpace(spotProperty.SMILES)) WriteSdfDataItem(sw, "FORMULA", spotProperty.SMILES); - WriteSdfDataItem(sw, "MS_LEVEL", "MS2"); + WriteSdfDataItem(sw, "MS LEVEL", "MS2"); var peaks = spectrum.Where(spec => spec.Intensity > 0).ToList(); - WriteSdfDataItem(sw, "NUM_PEAKS", peaks.Count.ToString()); + WriteSdfDataItem(sw, "NUM PEAKS", peaks.Count.ToString()); var peaksText = string.Join( "\n", spectrum.Select(p => $"{Math.Round(p.Mass, 5)} {Math.Round(p.Intensity,0)}" ) ); - WriteSdfDataItem(sw, "MASS_SPECTRAL_PEAKS", peaksText); + WriteSdfDataItem(sw, "MASS SPECTRAL PEAKS", peaksText); } } private static void WriteChromPeakFeatureInfoAsSdf( @@ -438,25 +438,25 @@ private static void WriteChromPeakFeatureInfoAsSdf( var sb = new StringBuilder(); WriteSdfDataItem(sw, "NAME", string.IsNullOrWhiteSpace(spotProperty.Name) ? "Unknown" : spotProperty.Name); WriteSdfDataItem(sw, "SCANS", spotProperty.PeakID.ToString()); - WriteSdfDataItem(sw, "PRECURSOR_MZ", Math.Round(spotProperty.PrecursorMz, 5).ToString()); - WriteSdfDataItem(sw, "ION_MODE", spotProperty.IonMode.ToString()); + WriteSdfDataItem(sw, "PRECURSOR MZ", Math.Round(spotProperty.PrecursorMz, 5).ToString()); + WriteSdfDataItem(sw, "ION MODE", spotProperty.IonMode.ToString()); if (spotProperty.IsMsmsContained) { - if (spotProperty.AdductType != null) WriteSdfDataItem(sw, "PRECURSOR_TYPE", spotProperty.AdductType.AdductIonName); + if (spotProperty.AdductType != null) WriteSdfDataItem(sw, "PRECURSOR TYPE", spotProperty.AdductType.AdductIonName); if (!string.IsNullOrWhiteSpace(spotProperty.Formula.FormulaString)) WriteSdfDataItem(sw, "FORMULA", spotProperty.Formula.FormulaString); if (!string.IsNullOrWhiteSpace(spotProperty.InChIKey)) WriteSdfDataItem(sw, "FORMULA", spotProperty.InChIKey); if (!string.IsNullOrWhiteSpace(spotProperty.SMILES)) WriteSdfDataItem(sw, "FORMULA", spotProperty.SMILES); - WriteSdfDataItem(sw, "MS_LEVEL", "MS2"); + WriteSdfDataItem(sw, "MS LEVEL", "MS2"); var peaks = spectrum.Where(spec => spec.Intensity > 0).ToList(); - WriteSdfDataItem(sw, "NUM_PEAKS", peaks.Count.ToString()); + WriteSdfDataItem(sw, "NUM PEAKS", peaks.Count.ToString()); var peaksText = string.Join( "\n", spectrum.Select(p => $"{Math.Round(p.Mass, 5)} {Math.Round(p.Intensity, 0)}" ) ); - WriteSdfDataItem(sw, "MASS_SPECTRAL_PEAKS", peaksText); + WriteSdfDataItem(sw, "MASS SPECTRAL PEAKS", peaksText); } } #endregion From 8cff4a16869dedfb673dd4d696d6d44016655a7b Mon Sep 17 00:00:00 2001 From: "mikiko.takahashi" Date: Tue, 17 Feb 2026 13:36:53 +0900 Subject: [PATCH 3/8] minor --- src/MSDIAL5/MsdialCore/Export/SpectraExport.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/MSDIAL5/MsdialCore/Export/SpectraExport.cs b/src/MSDIAL5/MsdialCore/Export/SpectraExport.cs index 89bb5508f..ea5199542 100644 --- a/src/MSDIAL5/MsdialCore/Export/SpectraExport.cs +++ b/src/MSDIAL5/MsdialCore/Export/SpectraExport.cs @@ -39,7 +39,7 @@ public static void SaveSpectraTable( SaveSpectraTableAsMgfFormat(exportStream, chromPeakFeature, scan.Spectrum); break; case ExportSpectraFileFormat.sdf: - SaveSpectraTableAsSdfFormat(exportStream, chromPeakFeature, scan.Spectrum); + SaveSpectraTableAsSdfFormat(exportStream, chromPeakFeature, scan.Spectrum, true, false); break; case ExportSpectraFileFormat.mat: SaveSpectraTableAsMatFormat(exportStream, chromPeakFeature, scan.Spectrum, spectrumList, mapper, parameter); @@ -324,7 +324,7 @@ public static void SaveSpectraTableAsSdfFormat( Stream stream, AlignmentSpotProperty spotProperty, IEnumerable spectrum, - bool exportNoMs2Molecule = false, + bool exportNoMs2Molecule = true, bool Set2dCoordinates = false ) { @@ -351,13 +351,17 @@ public static void SaveSpectraTableAsSdfFormat( Stream stream, ChromatogramPeakFeature chromPeakFeature, IEnumerable spectrum, - bool exportNoMs2Molecule = false, + bool exportNoMs2Molecule = true, bool Set2dCoordinates = false ) { + if (!exportNoMs2Molecule && !chromPeakFeature.IsMsmsContained) + { + return; + } using (StreamWriter sw = new StreamWriter(stream, Encoding.ASCII, 4096, true)) { - if (chromPeakFeature.SMILES != null) + if (chromPeakFeature.IsMsmsContained) { MolBlockFromSmiles(sw, chromPeakFeature.SMILES, Set2dCoordinates); } From b098b31e52ebc5514e9e8b26289dcc1b27a07faf Mon Sep 17 00:00:00 2001 From: "mikiko.takahashi" Date: Tue, 17 Feb 2026 15:42:37 +0900 Subject: [PATCH 4/8] change to use stringBuilder --- .../MsdialCore/Export/AnalysisSdfExporter.cs | 10 +- .../MsdialCore/Export/SpectraExport.cs | 147 +++++++++--------- 2 files changed, 76 insertions(+), 81 deletions(-) diff --git a/src/MSDIAL5/MsdialCore/Export/AnalysisSdfExporter.cs b/src/MSDIAL5/MsdialCore/Export/AnalysisSdfExporter.cs index 55327eb2a..53d8020c9 100644 --- a/src/MSDIAL5/MsdialCore/Export/AnalysisSdfExporter.cs +++ b/src/MSDIAL5/MsdialCore/Export/AnalysisSdfExporter.cs @@ -12,14 +12,8 @@ public sealed class AnalysisSdfExporter : IAnalysisExporter> loaderFuctory) { _loaderFactory = loaderFuctory ?? throw new ArgumentNullException(nameof(loaderFuctory)); } - private readonly bool _exportNoMs2Molecule; - private readonly bool _set2dCoordinates; - public AnalysisSdfExporter(bool exportNoMs2Molecule, bool set2dCoordinates) - { - _exportNoMs2Molecule = exportNoMs2Molecule; - _set2dCoordinates = set2dCoordinates; - } - public AnalysisSdfExporter() : this(exportNoMs2Molecule: true, set2dCoordinates: true) { } + private readonly bool _exportNoMs2Molecule = true; + private readonly bool _set2dCoordinates = true; void IAnalysisExporter.Export(Stream stream, AnalysisFileBean analysisFile, ChromatogramPeakFeatureCollection peakFeatureCollection, ExportStyle exportStyle) { var loader = _loaderFactory(analysisFile); diff --git a/src/MSDIAL5/MsdialCore/Export/SpectraExport.cs b/src/MSDIAL5/MsdialCore/Export/SpectraExport.cs index ea5199542..932e4ff49 100644 --- a/src/MSDIAL5/MsdialCore/Export/SpectraExport.cs +++ b/src/MSDIAL5/MsdialCore/Export/SpectraExport.cs @@ -9,15 +9,16 @@ using CompMs.MsdialCore.DataObj; using CompMs.MsdialCore.Parameter; using CompMs.MsdialCore.Utility; +using NCDK; +using NCDK.IO; +using NCDK.Layout; +using NCDK.Smiles; using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Text; -using NCDK; -using NCDK.Smiles; -using NCDK.Layout; -using NCDK.IO; namespace CompMs.MsdialCore.Export { @@ -39,7 +40,7 @@ public static void SaveSpectraTable( SaveSpectraTableAsMgfFormat(exportStream, chromPeakFeature, scan.Spectrum); break; case ExportSpectraFileFormat.sdf: - SaveSpectraTableAsSdfFormat(exportStream, chromPeakFeature, scan.Spectrum, true, false); + SaveSpectraTableAsSdfFormat(exportStream, chromPeakFeature, scan.Spectrum, true, true); break; case ExportSpectraFileFormat.mat: SaveSpectraTableAsMatFormat(exportStream, chromPeakFeature, scan.Spectrum, spectrumList, mapper, parameter); @@ -70,7 +71,7 @@ public static void SaveSpectraTable( SaveSpectraTableAsMgfFormat(exportStream, spotProperty, scan.Spectrum); break; case ExportSpectraFileFormat.sdf: - SaveSpectraTableAsSdfFormat(exportStream, spotProperty, scan.Spectrum); + SaveSpectraTableAsSdfFormat(exportStream, spotProperty, scan.Spectrum, true, true); break; case ExportSpectraFileFormat.mat: SaveSpectraTableAsMatFormat(exportStream, spotProperty, scan.Spectrum, mapper, parameter, isotopeTrackedLastSpot); @@ -324,72 +325,71 @@ public static void SaveSpectraTableAsSdfFormat( Stream stream, AlignmentSpotProperty spotProperty, IEnumerable spectrum, - bool exportNoMs2Molecule = true, - bool Set2dCoordinates = false + bool exportNoMs2Molecule, + bool Set2dCoordinates ) { if (!exportNoMs2Molecule && !spotProperty.IsMsmsAssigned) { return; } - using (StreamWriter sw = new StreamWriter(stream, Encoding.ASCII, 4096, true)) + var sb = new StringBuilder(8 * 1024); + if(spotProperty.IsMsmsAssigned) { - if(spotProperty.IsMsmsAssigned) - { - MolBlockFromSmiles(sw, spotProperty.SMILES, Set2dCoordinates); - } - else - { - EmptyMolBlock(sw); - } - WriteChromPeakFeatureInfoAsSdf(sw, spotProperty, spectrum); - sw.WriteLine("$$$$"); - sw.WriteLine(); + MolBlockFromSmiles(sb, spotProperty.SMILES, Set2dCoordinates); } + else + { + EmptyMolBlock(sb); + } + WriteChromPeakFeatureInfoAsSdf(sb, spotProperty, spectrum); + sb.AppendLine("$$$$"); + sb.AppendLine(); + var bytes = Encoding.ASCII.GetBytes(sb.ToString()); + stream.Write(bytes, 0, bytes.Length); } public static void SaveSpectraTableAsSdfFormat( Stream stream, ChromatogramPeakFeature chromPeakFeature, IEnumerable spectrum, - bool exportNoMs2Molecule = true, - bool Set2dCoordinates = false + bool exportNoMs2Molecule, + bool Set2dCoordinates ) { if (!exportNoMs2Molecule && !chromPeakFeature.IsMsmsContained) { return; } - using (StreamWriter sw = new StreamWriter(stream, Encoding.ASCII, 4096, true)) + var sb = new StringBuilder(8 * 1024); + if (chromPeakFeature.IsMsmsContained) { - if (chromPeakFeature.IsMsmsContained) - { - MolBlockFromSmiles(sw, chromPeakFeature.SMILES, Set2dCoordinates); - } - else - { - EmptyMolBlock(sw); - } - WriteChromPeakFeatureInfoAsSdf(sw, chromPeakFeature, spectrum); - sw.WriteLine("$$$$"); - sw.WriteLine(); + MolBlockFromSmiles(sb, chromPeakFeature.SMILES, Set2dCoordinates); + } + else + { + EmptyMolBlock(sb); } + WriteChromPeakFeatureInfoAsSdf(sb, chromPeakFeature, spectrum); + sb.AppendLine("$$$$"); + sb.AppendLine(); + var bytes = Encoding.ASCII.GetBytes(sb.ToString()); + stream.Write(bytes, 0, bytes.Length); } - private static void WriteSdfDataItem(StreamWriter sw, string fieldName, string value) + private static void WriteSdfDataItem(StringBuilder sb, string fieldName, string value) { - sw.WriteLine("> <" + fieldName + ">"); - sw.WriteLine(value ?? string.Empty); - //sw.WriteLine(); + sb.AppendLine("> <" + fieldName + ">"); + sb.AppendLine(value ?? string.Empty); } - private static void EmptyMolBlock(StreamWriter sw) + private static void EmptyMolBlock(StringBuilder sb) { - sw.WriteLine(""); - sw.WriteLine(" MS-DIAL"); - sw.WriteLine(); - sw.WriteLine(" 0 0 0 0 0 0 999 V2000"); - sw.WriteLine("M END"); + sb.AppendLine(""); + sb.AppendLine(" MS-DIAL"); + sb.AppendLine(); + sb.AppendLine(" 0 0 0 0 0 0 999 V2000"); + sb.AppendLine("M END"); } - private static void MolBlockFromSmiles(StreamWriter sw, string smiles, bool Set2dCoordinates) + private static void MolBlockFromSmiles(StringBuilder sb, string smiles, bool Set2dCoordinates) { var sp = new SmilesParser(); IAtomContainer mol = sp.ParseSmiles(smiles); @@ -402,65 +402,66 @@ private static void MolBlockFromSmiles(StreamWriter sw, string smiles, bool Set2 sdg.GenerateCoordinates(); } mol = sdg.Molecule; - using var w = new MDLV2000Writer(sw); - w.Write(mol); + using var tw = new StringWriter(sb, CultureInfo.InvariantCulture); + using (var w = new MDLV2000Writer(tw)) + { + w.Write(mol); + }; } private static void WriteChromPeakFeatureInfoAsSdf( - StreamWriter sw, + StringBuilder sb, AlignmentSpotProperty spotProperty, IEnumerable spectrum) { - var sb = new StringBuilder(); - WriteSdfDataItem(sw, "NAME", string.IsNullOrWhiteSpace(spotProperty.Name)? "Unknown": spotProperty.Name); - WriteSdfDataItem(sw, "SCANS", spotProperty.MasterAlignmentID.ToString()); - WriteSdfDataItem(sw, "PRECURSOR MZ", Math.Round(spotProperty.MassCenter,5).ToString()); - WriteSdfDataItem(sw, "ION MODE", spotProperty.IonMode.ToString()); + WriteSdfDataItem(sb, "NAME", string.IsNullOrWhiteSpace(spotProperty.Name)? "Unknown": spotProperty.Name); + WriteSdfDataItem(sb, "SCANS", spotProperty.MasterAlignmentID.ToString()); + WriteSdfDataItem(sb, "PRECURSOR MZ", Math.Round(spotProperty.MassCenter,5).ToString()); + WriteSdfDataItem(sb, "ION MODE", spotProperty.IonMode.ToString()); if (spotProperty.IsMsmsAssigned) { - if (spotProperty.AdductType != null) WriteSdfDataItem(sw, "PRECURSOR TYPE", spotProperty.AdductType.AdductIonName); - if (!string.IsNullOrWhiteSpace(spotProperty.Formula.FormulaString)) WriteSdfDataItem(sw, "FORMULA", spotProperty.Formula.FormulaString); - if (!string.IsNullOrWhiteSpace(spotProperty.InChIKey)) WriteSdfDataItem(sw, "FORMULA", spotProperty.InChIKey); - if (!string.IsNullOrWhiteSpace(spotProperty.SMILES)) WriteSdfDataItem(sw, "FORMULA", spotProperty.SMILES); - WriteSdfDataItem(sw, "MS LEVEL", "MS2"); + if (spotProperty.AdductType != null) WriteSdfDataItem(sb, "PRECURSOR TYPE", spotProperty.AdductType.AdductIonName); + if (!string.IsNullOrWhiteSpace(spotProperty.Formula.FormulaString)) WriteSdfDataItem(sb, "FORMULA", spotProperty.Formula.FormulaString); + if (!string.IsNullOrWhiteSpace(spotProperty.InChIKey)) WriteSdfDataItem(sb, "FORMULA", spotProperty.InChIKey); + if (!string.IsNullOrWhiteSpace(spotProperty.SMILES)) WriteSdfDataItem(sb, "FORMULA", spotProperty.SMILES); + WriteSdfDataItem(sb, "MS LEVEL", "MS2"); var peaks = spectrum.Where(spec => spec.Intensity > 0).ToList(); - WriteSdfDataItem(sw, "NUM PEAKS", peaks.Count.ToString()); + WriteSdfDataItem(sb, "NUM PEAKS", peaks.Count.ToString()); var peaksText = string.Join( "\n", spectrum.Select(p => $"{Math.Round(p.Mass, 5)} {Math.Round(p.Intensity,0)}" ) ); - WriteSdfDataItem(sw, "MASS SPECTRAL PEAKS", peaksText); + WriteSdfDataItem(sb, "MASS SPECTRAL PEAKS", peaksText); } } private static void WriteChromPeakFeatureInfoAsSdf( - StreamWriter sw, + StringBuilder sb, ChromatogramPeakFeature spotProperty, IEnumerable spectrum) { - var sb = new StringBuilder(); - WriteSdfDataItem(sw, "NAME", string.IsNullOrWhiteSpace(spotProperty.Name) ? "Unknown" : spotProperty.Name); - WriteSdfDataItem(sw, "SCANS", spotProperty.PeakID.ToString()); - WriteSdfDataItem(sw, "PRECURSOR MZ", Math.Round(spotProperty.PrecursorMz, 5).ToString()); - WriteSdfDataItem(sw, "ION MODE", spotProperty.IonMode.ToString()); + WriteSdfDataItem(sb, "NAME", string.IsNullOrWhiteSpace(spotProperty.Name) ? "Unknown" : spotProperty.Name); + WriteSdfDataItem(sb, "SCANS", spotProperty.PeakID.ToString()); + WriteSdfDataItem(sb, "PRECURSOR MZ", Math.Round(spotProperty.PrecursorMz, 5).ToString()); + WriteSdfDataItem(sb, "ION MODE", spotProperty.IonMode.ToString()); if (spotProperty.IsMsmsContained) { - if (spotProperty.AdductType != null) WriteSdfDataItem(sw, "PRECURSOR TYPE", spotProperty.AdductType.AdductIonName); - if (!string.IsNullOrWhiteSpace(spotProperty.Formula.FormulaString)) WriteSdfDataItem(sw, "FORMULA", spotProperty.Formula.FormulaString); - if (!string.IsNullOrWhiteSpace(spotProperty.InChIKey)) WriteSdfDataItem(sw, "FORMULA", spotProperty.InChIKey); - if (!string.IsNullOrWhiteSpace(spotProperty.SMILES)) WriteSdfDataItem(sw, "FORMULA", spotProperty.SMILES); - WriteSdfDataItem(sw, "MS LEVEL", "MS2"); + if (spotProperty.AdductType != null) WriteSdfDataItem(sb, "PRECURSOR TYPE", spotProperty.AdductType.AdductIonName); + if (!string.IsNullOrWhiteSpace(spotProperty.Formula.FormulaString)) WriteSdfDataItem(sb, "FORMULA", spotProperty.Formula.FormulaString); + if (!string.IsNullOrWhiteSpace(spotProperty.InChIKey)) WriteSdfDataItem(sb, "FORMULA", spotProperty.InChIKey); + if (!string.IsNullOrWhiteSpace(spotProperty.SMILES)) WriteSdfDataItem(sb, "FORMULA", spotProperty.SMILES); + WriteSdfDataItem(sb, "MS LEVEL", "MS2"); var peaks = spectrum.Where(spec => spec.Intensity > 0).ToList(); - WriteSdfDataItem(sw, "NUM PEAKS", peaks.Count.ToString()); + WriteSdfDataItem(sb, "NUM PEAKS", peaks.Count.ToString()); var peaksText = string.Join( "\n", spectrum.Select(p => $"{Math.Round(p.Mass, 5)} {Math.Round(p.Intensity, 0)}" ) ); - WriteSdfDataItem(sw, "MASS SPECTRAL PEAKS", peaksText); + WriteSdfDataItem(sb, "MASS SPECTRAL PEAKS", peaksText); } } #endregion From 925fbd2b6b77153ba9559105fadef1d6f8e347f1 Mon Sep 17 00:00:00 2001 From: "mikiko.takahashi" Date: Mon, 2 Mar 2026 11:51:14 +0900 Subject: [PATCH 5/8] Fix Sdf export item --- src/MSDIAL5/MsdialCore/Export/SpectraExport.cs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/MSDIAL5/MsdialCore/Export/SpectraExport.cs b/src/MSDIAL5/MsdialCore/Export/SpectraExport.cs index 932e4ff49..47e4e5f46 100644 --- a/src/MSDIAL5/MsdialCore/Export/SpectraExport.cs +++ b/src/MSDIAL5/MsdialCore/Export/SpectraExport.cs @@ -415,15 +415,18 @@ private static void WriteChromPeakFeatureInfoAsSdf( { WriteSdfDataItem(sb, "NAME", string.IsNullOrWhiteSpace(spotProperty.Name)? "Unknown": spotProperty.Name); WriteSdfDataItem(sb, "SCANS", spotProperty.MasterAlignmentID.ToString()); - WriteSdfDataItem(sb, "PRECURSOR MZ", Math.Round(spotProperty.MassCenter,5).ToString()); + WriteSdfDataItem(sb, "PRECURSOR M/Z", Math.Round(spotProperty.MassCenter, 5).ToString()); WriteSdfDataItem(sb, "ION MODE", spotProperty.IonMode.ToString()); if (spotProperty.IsMsmsAssigned) { if (spotProperty.AdductType != null) WriteSdfDataItem(sb, "PRECURSOR TYPE", spotProperty.AdductType.AdductIonName); if (!string.IsNullOrWhiteSpace(spotProperty.Formula.FormulaString)) WriteSdfDataItem(sb, "FORMULA", spotProperty.Formula.FormulaString); - if (!string.IsNullOrWhiteSpace(spotProperty.InChIKey)) WriteSdfDataItem(sb, "FORMULA", spotProperty.InChIKey); - if (!string.IsNullOrWhiteSpace(spotProperty.SMILES)) WriteSdfDataItem(sb, "FORMULA", spotProperty.SMILES); + if (!string.IsNullOrWhiteSpace(spotProperty.Formula.Mass.ToString())) WriteSdfDataItem(sb, "EXACT MASS", Math.Round(spotProperty.Formula.Mass, 5).ToString()); + if (!string.IsNullOrWhiteSpace(spotProperty.InChIKey)) WriteSdfDataItem(sb, "INCHIKEY", spotProperty.InChIKey); + if (!string.IsNullOrWhiteSpace(spotProperty.SMILES)) WriteSdfDataItem(sb, "SMILES", spotProperty.SMILES); + if (!string.IsNullOrWhiteSpace(spotProperty.TimesCenter.RT.Value.ToString())) WriteSdfDataItem(sb, "RETENTION TIME", spotProperty.TimesCenter.RT.Value.ToString()); + if (!string.IsNullOrWhiteSpace(spotProperty.Ontology)) WriteSdfDataItem(sb, "ONTOLOGY", spotProperty.Ontology); WriteSdfDataItem(sb, "MS LEVEL", "MS2"); var peaks = spectrum.Where(spec => spec.Intensity > 0).ToList(); WriteSdfDataItem(sb, "NUM PEAKS", peaks.Count.ToString()); @@ -443,15 +446,18 @@ private static void WriteChromPeakFeatureInfoAsSdf( { WriteSdfDataItem(sb, "NAME", string.IsNullOrWhiteSpace(spotProperty.Name) ? "Unknown" : spotProperty.Name); WriteSdfDataItem(sb, "SCANS", spotProperty.PeakID.ToString()); - WriteSdfDataItem(sb, "PRECURSOR MZ", Math.Round(spotProperty.PrecursorMz, 5).ToString()); + WriteSdfDataItem(sb, "PRECURSOR M/Z", Math.Round(spotProperty.PrecursorMz, 5).ToString()); WriteSdfDataItem(sb, "ION MODE", spotProperty.IonMode.ToString()); if (spotProperty.IsMsmsContained) { if (spotProperty.AdductType != null) WriteSdfDataItem(sb, "PRECURSOR TYPE", spotProperty.AdductType.AdductIonName); if (!string.IsNullOrWhiteSpace(spotProperty.Formula.FormulaString)) WriteSdfDataItem(sb, "FORMULA", spotProperty.Formula.FormulaString); - if (!string.IsNullOrWhiteSpace(spotProperty.InChIKey)) WriteSdfDataItem(sb, "FORMULA", spotProperty.InChIKey); - if (!string.IsNullOrWhiteSpace(spotProperty.SMILES)) WriteSdfDataItem(sb, "FORMULA", spotProperty.SMILES); + if (!string.IsNullOrWhiteSpace(spotProperty.Formula.Mass.ToString())) WriteSdfDataItem(sb, "EXACT MASS", Math.Round(spotProperty.Formula.Mass, 5).ToString()); + if (!string.IsNullOrWhiteSpace(spotProperty.InChIKey)) WriteSdfDataItem(sb, "INCHIKEY", spotProperty.InChIKey); + if (!string.IsNullOrWhiteSpace(spotProperty.SMILES)) WriteSdfDataItem(sb, "SMILES", spotProperty.SMILES); + if (!string.IsNullOrWhiteSpace(spotProperty.ChromXs.RT.Value.ToString())) WriteSdfDataItem(sb, "RETENTION TIME", spotProperty.ChromXs.RT.Value.ToString()); + if (!string.IsNullOrWhiteSpace(spotProperty.Ontology)) WriteSdfDataItem(sb, "ONTOLOGY", spotProperty.Ontology); WriteSdfDataItem(sb, "MS LEVEL", "MS2"); var peaks = spectrum.Where(spec => spec.Intensity > 0).ToList(); WriteSdfDataItem(sb, "NUM PEAKS", peaks.Count.ToString()); From 882c54031e37f936a916a88443e1840dcdedbc0a Mon Sep 17 00:00:00 2001 From: "mikiko.takahashi" Date: Mon, 2 Mar 2026 15:20:16 +0900 Subject: [PATCH 6/8] change decimal --- src/MSDIAL5/MsdialCore/Export/SpectraExport.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/MSDIAL5/MsdialCore/Export/SpectraExport.cs b/src/MSDIAL5/MsdialCore/Export/SpectraExport.cs index 47e4e5f46..950439f35 100644 --- a/src/MSDIAL5/MsdialCore/Export/SpectraExport.cs +++ b/src/MSDIAL5/MsdialCore/Export/SpectraExport.cs @@ -425,7 +425,7 @@ private static void WriteChromPeakFeatureInfoAsSdf( if (!string.IsNullOrWhiteSpace(spotProperty.Formula.Mass.ToString())) WriteSdfDataItem(sb, "EXACT MASS", Math.Round(spotProperty.Formula.Mass, 5).ToString()); if (!string.IsNullOrWhiteSpace(spotProperty.InChIKey)) WriteSdfDataItem(sb, "INCHIKEY", spotProperty.InChIKey); if (!string.IsNullOrWhiteSpace(spotProperty.SMILES)) WriteSdfDataItem(sb, "SMILES", spotProperty.SMILES); - if (!string.IsNullOrWhiteSpace(spotProperty.TimesCenter.RT.Value.ToString())) WriteSdfDataItem(sb, "RETENTION TIME", spotProperty.TimesCenter.RT.Value.ToString()); + if (!string.IsNullOrWhiteSpace(spotProperty.TimesCenter.RT.Value.ToString())) WriteSdfDataItem(sb, "RETENTION TIME", Math.Round(spotProperty.TimesCenter.RT.Value, 3).ToString()); if (!string.IsNullOrWhiteSpace(spotProperty.Ontology)) WriteSdfDataItem(sb, "ONTOLOGY", spotProperty.Ontology); WriteSdfDataItem(sb, "MS LEVEL", "MS2"); var peaks = spectrum.Where(spec => spec.Intensity > 0).ToList(); @@ -433,7 +433,7 @@ private static void WriteChromPeakFeatureInfoAsSdf( var peaksText = string.Join( "\n", spectrum.Select(p => - $"{Math.Round(p.Mass, 5)} {Math.Round(p.Intensity,0)}" + $"{Math.Round(p.Mass, 4)} {Math.Round(p.Intensity,0)}" ) ); WriteSdfDataItem(sb, "MASS SPECTRAL PEAKS", peaksText); From 7b28f8e18beaa7a27fa3daa445b91eb28f64ae89 Mon Sep 17 00:00:00 2001 From: mikikot113 <122430993+mikikot113@users.noreply.github.com> Date: Tue, 10 Mar 2026 10:50:42 +0900 Subject: [PATCH 7/8] Update src/MSDIAL5/MsdialCore/Export/SpectraExport.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/MSDIAL5/MsdialCore/Export/SpectraExport.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/MSDIAL5/MsdialCore/Export/SpectraExport.cs b/src/MSDIAL5/MsdialCore/Export/SpectraExport.cs index 950439f35..c7e6655a8 100644 --- a/src/MSDIAL5/MsdialCore/Export/SpectraExport.cs +++ b/src/MSDIAL5/MsdialCore/Export/SpectraExport.cs @@ -425,7 +425,8 @@ private static void WriteChromPeakFeatureInfoAsSdf( if (!string.IsNullOrWhiteSpace(spotProperty.Formula.Mass.ToString())) WriteSdfDataItem(sb, "EXACT MASS", Math.Round(spotProperty.Formula.Mass, 5).ToString()); if (!string.IsNullOrWhiteSpace(spotProperty.InChIKey)) WriteSdfDataItem(sb, "INCHIKEY", spotProperty.InChIKey); if (!string.IsNullOrWhiteSpace(spotProperty.SMILES)) WriteSdfDataItem(sb, "SMILES", spotProperty.SMILES); - if (!string.IsNullOrWhiteSpace(spotProperty.TimesCenter.RT.Value.ToString())) WriteSdfDataItem(sb, "RETENTION TIME", Math.Round(spotProperty.TimesCenter.RT.Value, 3).ToString()); + if (spotProperty.TimesCenter != null && spotProperty.TimesCenter.RT != null && spotProperty.TimesCenter.RT.Value >= 0) + WriteSdfDataItem(sb, "RETENTION TIME", Math.Round(spotProperty.TimesCenter.RT.Value, 3).ToString()); if (!string.IsNullOrWhiteSpace(spotProperty.Ontology)) WriteSdfDataItem(sb, "ONTOLOGY", spotProperty.Ontology); WriteSdfDataItem(sb, "MS LEVEL", "MS2"); var peaks = spectrum.Where(spec => spec.Intensity > 0).ToList(); From 2e466b9499360dfc3e493f069da4a95219e32182 Mon Sep 17 00:00:00 2001 From: mikikot113 <122430993+mikikot113@users.noreply.github.com> Date: Tue, 10 Mar 2026 10:52:12 +0900 Subject: [PATCH 8/8] Update src/MSDIAL5/MsdialCore/Export/SpectraExport.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/MSDIAL5/MsdialCore/Export/SpectraExport.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MSDIAL5/MsdialCore/Export/SpectraExport.cs b/src/MSDIAL5/MsdialCore/Export/SpectraExport.cs index c7e6655a8..c4d2f5587 100644 --- a/src/MSDIAL5/MsdialCore/Export/SpectraExport.cs +++ b/src/MSDIAL5/MsdialCore/Export/SpectraExport.cs @@ -422,7 +422,7 @@ private static void WriteChromPeakFeatureInfoAsSdf( { if (spotProperty.AdductType != null) WriteSdfDataItem(sb, "PRECURSOR TYPE", spotProperty.AdductType.AdductIonName); if (!string.IsNullOrWhiteSpace(spotProperty.Formula.FormulaString)) WriteSdfDataItem(sb, "FORMULA", spotProperty.Formula.FormulaString); - if (!string.IsNullOrWhiteSpace(spotProperty.Formula.Mass.ToString())) WriteSdfDataItem(sb, "EXACT MASS", Math.Round(spotProperty.Formula.Mass, 5).ToString()); + if (spotProperty.Formula.Mass > 0d) WriteSdfDataItem(sb, "EXACT MASS", Math.Round(spotProperty.Formula.Mass, 5).ToString()); if (!string.IsNullOrWhiteSpace(spotProperty.InChIKey)) WriteSdfDataItem(sb, "INCHIKEY", spotProperty.InChIKey); if (!string.IsNullOrWhiteSpace(spotProperty.SMILES)) WriteSdfDataItem(sb, "SMILES", spotProperty.SMILES); if (spotProperty.TimesCenter != null && spotProperty.TimesCenter.RT != null && spotProperty.TimesCenter.RT.Value >= 0)