Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file not shown.
Binary file not shown.
65 changes: 65 additions & 0 deletions src/Common/CommonStandard/DataStructure/ConcurrentLruCache.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using System.Collections.Generic;
using System.Threading;

namespace CompMs.Common.DataStructure;

public sealed class ConcurrentLruCache<TKey, TValue>
{
private readonly LruCache<TKey, TValue> _cache;
private readonly ReaderWriterLockSlim _locker;

public ConcurrentLruCache(int capacity, IEqualityComparer<TKey> comparer) {
_cache = new LruCache<TKey, TValue>(capacity, comparer);
_locker = new();
}

public ConcurrentLruCache(int capacity) {
_cache = new LruCache<TKey, TValue>(capacity);
_locker = new();
}

public TValue Get(TKey key) {
_locker.EnterReadLock();
try {
return _cache.Get(key);
}
finally {
_locker.ExitReadLock();
}
}
Comment on lines +21 to +29

public void Put(TKey key, TValue value) {
_locker.EnterWriteLock();
try {
_cache.Put(key, value);
}
finally {
_locker.ExitWriteLock();
}
}

public bool ContainsKey(TKey key) {
_locker.EnterReadLock();
try {
return _cache.ContainsKey(key);
}
finally {
_locker.ExitReadLock();
}
}

public bool TryGet(TKey key, out TValue value) {
_locker.EnterReadLock();
try {
if (_cache.ContainsKey(key)) {
value = _cache.Get(key);
return true;
}
value = default;
return false;
}
finally {
_locker.ExitReadLock();
}
}
Comment on lines +51 to +64
}
2 changes: 1 addition & 1 deletion src/MSDIAL5/MsdialCore/Enum/SupportFormat.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
using System.Text;

namespace CompMs.MsdialCore.Enum {
public enum SupportMsRawDataExtension { abf, ibf, cdf, mzml, wiff, raw, d, wiff2, qgd, lcd, lrp }
public enum SupportMsRawDataExtension { abf, ibf, cdf, mzml, wiff, raw, d, wiff2, qgd, lcd, lrp, imzml }
public enum MsdialDataStorageFormat { dcl, pai, arf, aef, msf, bfasta, bpep, spep, prf, rtc }
}
8 changes: 4 additions & 4 deletions src/MSDIAL5/MsdialCore/MsdialCore.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@
<PackageReference Include="Accord" Version="3.8.0" />
<PackageReference Include="Accord.Math" Version="3.8.0" />
<PackageReference Include="Accord.Statistics" Version="3.8.0" />
<PackageReference Include="RawDataHandler" Version="1.2.9225.202" Condition="'$(Configuration)'=='Release'" />
<PackageReference Include="RawDataHandler" Version="1.2.*-*" Condition="'$(Configuration)'=='Debug'" />
<PackageReference Include="RawDataHandler-Vendor-UnSupported" Version="1.2.*-*" Condition="'$(Configuration)'=='Debug vendor unsupported'" />
<PackageReference Include="RawDataHandler-Vendor-UnSupported" Version="1.2.*" Condition="'$(Configuration)'=='Release vendor unsupported'" />
<PackageReference Include="RawDataHandler" Version="1.3.9699.469" Condition="'$(Configuration)'=='Release'" />
<PackageReference Include="RawDataHandler" Version="1.3.*-*" Condition="'$(Configuration)'=='Debug'" />
<PackageReference Include="RawDataHandler-Vendor-UnSupported" Version="1.3.*-*" Condition="'$(Configuration)'=='Debug vendor unsupported'" />
<PackageReference Include="RawDataHandler-Vendor-UnSupported" Version="1.3.*" Condition="'$(Configuration)'=='Release vendor unsupported'" />
<PackageReference Include="System.Data.SQLite.Core" Version="1.0.112.2" />
<PackageReference Include="System.IO.Compression" Version="4.3.0" />
<PackageReference Include="System.Net.Http" Version="4.3.4" />
Expand Down
107 changes: 107 additions & 0 deletions src/MSDIAL5/MsdialDimsCore/Algorithm/DimsDataProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,82 @@ public int BinningMz(double mz) {
}
}

public class DimsAccumulateDataProvider : BaseDataProvider
{
public DimsAccumulateDataProvider(IEnumerable<RawSpectrum> spectrums, double massTolerance, double timeBegin, double timeEnd)
: base(AccumulateRawSpectrums(
spectrums
.Where(spec => timeBegin <= spec.ScanStartTime && spec.ScanStartTime <= timeEnd)
.Select(spec => spec.ShallowCopy())
.ToArray(),
massTolerance)) {

}

public DimsAccumulateDataProvider(RawMeasurement rawObj, double massTolerance, double timeBegin, double timeEnd)
: this(rawObj.SpectrumList, massTolerance, timeBegin, timeEnd) {

}

public DimsAccumulateDataProvider(AnalysisFileBean file, double massTolerance, double timeBegin, double timeEnd, bool isGuiProcess = false, int retry = 5)
: this(LoadMeasurement(file, true, false, isGuiProcess, retry).SpectrumList, massTolerance, timeBegin, timeEnd) {

}


private static List<RawSpectrum> AccumulateRawSpectrums(RawSpectrum[] spectrums, double massTolerance) {
var targetmz4ppmcalc = 500.0;
var binning = new Binning(targetmz4ppmcalc, massTolerance);
var ms1Spectrums = spectrums.Where(spectrum => spectrum.MsLevel == 1).ToList();
var groups = ms1Spectrums.SelectMany(spectrum => spectrum.Spectrum)
.GroupBy(peak => binning.BinningMz(peak.Mz));
var massBins = new Dictionary<int, double[]>();
foreach (var group in groups) {
var peaks = group.ToList();
var accIntensity = peaks.Sum(peak => peak.Intensity);
var basepeak = peaks.Argmax(peak => peak.Intensity);
massBins[group.Key] = new double[] { basepeak.Mz, accIntensity, basepeak.Intensity };
}
var result = ms1Spectrums.First().ShallowCopy();
SpectrumParser.setSpectrumProperties(result, massBins, isSortMz: true);
var results = new[] { result }.Concat(spectrums.Where(spectrum => spectrum.MsLevel != 1)).ToList();
for (int i = 0; i < results.Count; i++) {
results[i].Index = i;
}
return results;
}
Comment on lines +178 to +198

class Binning
{
public double Pivot { get; }
public double Tolerance { get; }
public double Ppm { get; }
public double LogTolerance { get; }

public Binning(double pivot, double tolerance) {
Pivot = pivot;
Tolerance = tolerance;
Ppm = tolerance / Pivot * 1_000_000d;
LogTolerance = Math.Log(1 + Ppm / 1_000_000d);
}

/// <summary>
/// Calculates the bin index for a given m/z value.
/// </summary>
/// <param name="mz">The mass-to-charge (m/z) value.</param>
/// <returns>The bin index as an integer.</returns>
public int BinningMz(double mz) {
if (mz <= Pivot) {
return (int)(mz / Tolerance);
}
else {
int pivotbin = (int)(Pivot / Tolerance) + 1;
return pivotbin + (int)(Math.Log(mz / Pivot) / LogTolerance);
}
}
}
}

public class DimsBpiDataProviderFactory
: IDataProviderFactory<AnalysisFileBean>, IDataProviderFactory<RawMeasurement>
{
Expand Down Expand Up @@ -238,4 +314,35 @@ public IDataProvider Create(RawMeasurement source) {
return new DimsAverageDataProvider(source, massTolerance, timeBegin, timeEnd);
}
}

public class DimsAccumulateDataProviderFactory
: IDataProviderFactory<AnalysisFileBean>, IDataProviderFactory<RawMeasurement>
{
private readonly double massTolerance;
private readonly double timeBegin;
private readonly double timeEnd;
private readonly int retry;
private readonly bool isGuiProcess;

public DimsAccumulateDataProviderFactory(
double massTolerance,
double timeBegin = double.MinValue,
double timeEnd = double.MaxValue,
int retry = 5,
bool isGuiProcess = false) {
this.massTolerance = massTolerance;
this.timeBegin = timeBegin;
this.timeEnd = timeEnd;
this.retry = retry;
this.isGuiProcess = isGuiProcess;
}

public IDataProvider Create(AnalysisFileBean source) {
return new DimsAccumulateDataProvider(source, massTolerance, timeBegin, timeEnd, isGuiProcess, retry);
}

public IDataProvider Create(RawMeasurement source) {
return new DimsAccumulateDataProvider(source, massTolerance, timeBegin, timeEnd);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ namespace CompMs.MsdialDimsCore.Parameter
[Union(0, typeof(DimsBpiDataProviderFactoryParameter))]
[Union(1, typeof(DimsTicDataProviderFactoryParameter))]
[Union(2, typeof(DimsAverageDataProviderFactoryParameter))]
[Union(3, typeof(DimsAccumulateDataProviderFactoryParameter))]
public interface IDimsDataProviderFactoryParameter
{
IDataProviderFactory<AnalysisFileBean> Create(int retry, bool isGuiProcess);
Expand Down Expand Up @@ -84,4 +85,29 @@ public IDataProviderFactory<RawMeasurement> Create() {
return new DimsAverageDataProviderFactory(MassTolerance, TimeBegin, TimeEnd);
}
}

[MessagePackObject]
public class DimsAccumulateDataProviderFactoryParameter: IDimsDataProviderFactoryParameter
{
public DimsAccumulateDataProviderFactoryParameter(double timeBegin, double timeEnd, double massTolerance) {
TimeBegin = timeBegin;
TimeEnd = timeEnd;
MassTolerance = massTolerance;
}

[Key(nameof(TimeBegin))]
public double TimeBegin { get; }
[Key(nameof(TimeEnd))]
public double TimeEnd { get; }
[Key(nameof(MassTolerance))]
public double MassTolerance { get; }

public IDataProviderFactory<AnalysisFileBean> Create(int retry, bool isGuiProcess) {
return new DimsAccumulateDataProviderFactory(MassTolerance, TimeBegin, TimeEnd, retry, isGuiProcess);
}

public IDataProviderFactory<RawMeasurement> Create() {
return new DimsAccumulateDataProviderFactory(MassTolerance, TimeBegin, TimeEnd);
}
}
}
2 changes: 1 addition & 1 deletion src/MSDIAL5/MsdialDimsCore/ProcessFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public async Task RunAsync(AnalysisFileBean file, ProcessOption option, IProgres
// faeture detections
Console.WriteLine("Peak picking started");
var ms1Spectrum = provider.LoadMs1Spectrums().Argmax(spec => spec.Spectrum.Length);
var chromPeaks = DataAccess.ConvertRawPeakElementToChromatogramPeakList(ms1Spectrum.Spectrum);
var chromPeaks = DataAccess.ConvertRawPeakElementToChromatogramPeakList(ms1Spectrum.Spectrum, param.MassRangeBegin, param.MassRangeEnd);
var sChromPeaks = new Chromatogram(chromPeaks, ChromXType.Mz, ChromXUnit.Mz).ChromatogramSmoothing(param.SmoothingMethod, param.SmoothingLevel).AsPeakArray();

var peakPickResults = PeakDetection.PeakDetectionVS1(sChromPeaks, param.MinimumDatapoints, param.MinimumAmplitude);
Expand Down
40 changes: 37 additions & 3 deletions src/MSDIAL5/MsdialGuiApp/Model/Chart/BitmapImageModel.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using CompMs.CommonMVVM;
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Media;
using System.Windows.Media.Imaging;

Expand All @@ -9,8 +11,9 @@ internal sealed class BitmapImageModel : BindableBase
{
public static readonly int ImageMargin = 1;

private readonly SemaphoreSlim _semaphoreSlim = new(1, 1);

private Func<BitmapSource>? _bitmapSourceFactory;
private BitmapSource? _bitmapSource;

public BitmapImageModel(BitmapSource bitmapSource, string title) {
_bitmapSource = bitmapSource;
Expand All @@ -22,8 +25,39 @@ public BitmapImageModel(Func<BitmapSource> bitmapSourceFactory, string title) {
Title = title;
}

public string Title { get; }
public BitmapSource BitmapSource => _bitmapSource ?? _bitmapSourceFactory!.Invoke();
public string Title { get; internal set; }

public BitmapSource? BitmapSource {
get => _bitmapSource;
private set => SetProperty(ref _bitmapSource, value);
}
private BitmapSource? _bitmapSource;

public bool LoadingBitmap {
get => _loadingBitmap;
private set => SetProperty(ref _loadingBitmap, value);
}
private bool _loadingBitmap = false;

public async Task EnsureBitmapSourceAsync() {
if (_bitmapSource is not null || LoadingBitmap) {
return;
}

await _semaphoreSlim.WaitAsync().ConfigureAwait(false);

try {
if (_bitmapSource is not null || LoadingBitmap) {
return;
}
LoadingBitmap = true;
BitmapSource = await Task.Run(() => _bitmapSourceFactory!.Invoke()).ConfigureAwait(false);
LoadingBitmap = false;
}
finally {
_semaphoreSlim.Release();
}
}
Comment on lines +42 to +60

public BitmapImageModel WithPalette(BitmapPalette palette) {
if (_bitmapSource is null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ public bool UseAverageMs1 {
}
private bool useAverageMs1 = false;

public bool UseAccumulateMs1 {
get => useAccumulateMs1;
set => SetProperty(ref useAccumulateMs1, value);
}
private bool useAccumulateMs1 = false;

public double TimeBegin {
get => timeBegin;
set => SetProperty(ref timeBegin, value);
Expand Down Expand Up @@ -65,6 +71,9 @@ public IDimsDataProviderFactoryParameter CreateDataProviderFactoryParameter() {
else if (UseAverageMs1) {
return new DimsAverageDataProviderFactoryParameter(TimeBegin, TimeEnd, MassTolerance);
}
else if (UseAccumulateMs1) {
return new DimsAccumulateDataProviderFactoryParameter(TimeBegin, TimeEnd, MassTolerance);
}
throw new NotSupportedException();
}

Expand Down Expand Up @@ -92,6 +101,13 @@ private void PrepareProviderFactoryParameter(IDimsDataProviderFactoryParameter f
TimeEnd = averageParameter.TimeEnd;
MassTolerance = averageParameter.MassTolerance;
break;
case DimsAccumulateDataProviderFactoryParameter accumulateParameter:
UseMs1WithHighestTic = false;
UseAccumulateMs1 = true;
TimeBegin = accumulateParameter.TimeBegin;
TimeEnd = accumulateParameter.TimeEnd;
MassTolerance = accumulateParameter.MassTolerance;
break;
Comment on lines +104 to +110
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@

internal interface IWholeImageResultModel
{
public IntensityImageModel? SelectedPeakIntensities { get; }
public IntensityImagePlaceholderModel IntensityImagePlaceholder { get; }
}
4 changes: 2 additions & 2 deletions src/MSDIAL5/MsdialGuiApp/Model/Imaging/ImagingRoiModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ public void Select() {
IsSelected = true;
}

public async Task SavePositionsAsync(CancellationToken token = default) {
using var stream = File.Open($"{Id}.csv", FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
public async Task SavePositionsAsync(string directoryPath, CancellationToken token = default) {
using var stream = File.Open(Path.Combine(directoryPath, $"{Id}.csv"), FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
var header = string.Join(",", ["XIndex", "YIndex", "Pos"]);
var encoded = UTF8Encoding.Default.GetBytes(header + "\n");
await stream.WriteAsync(encoded, 0, encoded.Length).ConfigureAwait(false);
Comment on lines +53 to 57
Expand Down
6 changes: 3 additions & 3 deletions src/MSDIAL5/MsdialGuiApp/Model/Imaging/IntensityImageModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ internal sealed class IntensityImageModel : BindableBase
{
private readonly MaldiFrames _frameInfos;
private readonly RawIntensityOnPixelsLoader _intensitiesLoader;
private readonly int _peakIndex;
internal readonly int _peakIndex;

public IntensityImageModel(MaldiFrames frameInfos, ChromatogramPeakFeatureModel peak, MaldiFrameLaserInfo laserInfo, RawIntensityOnPixelsLoader intensitiesLoader, int peakIndex) {
_frameInfos = frameInfos;
Expand All @@ -42,7 +42,7 @@ public BitmapImageModel BitmapImageModel {

var factory = () => {
var image = new byte[width * stride * height];
var pixels = _intensitiesLoader.Load(_peakIndex);
var pixels = _intensitiesLoader.LoadAsync(_peakIndex).Result;
var features = pixels.PixelPeakFeaturesList[0];
var sorted = features.IntensityArray.OrderBy(x => x).ToArray();
var length = sorted.Length;
Expand All @@ -64,7 +64,7 @@ public async Task SaveAsync(Stream stream, bool skipUnknownPeaks = true, System.
if (skipUnknownPeaks && string.IsNullOrEmpty(Peak.Name)) {
return;
}
var pixels = _intensitiesLoader.Load(_peakIndex);
var pixels = await _intensitiesLoader.LoadAsync(_peakIndex, token).ConfigureAwait(false);
var row = string.Format("{0},{1},{2},{3},", Peak.MasterPeakID, Peak.Name, Mz.Value, Drift.Value) + string.Join(",", pixels.PixelPeakFeaturesList[0].IntensityArray);
var encoded = UTF8Encoding.Default.GetBytes(row + "\n");
await stream.WriteAsync(encoded, 0, encoded.Length, token).ConfigureAwait(false);
Expand Down
Loading
Loading