diff --git a/.github/workflows/nodejs-ts.yml b/.github/workflows/nodejs-ts.yml index 3bbf906..d847288 100644 --- a/.github/workflows/nodejs-ts.yml +++ b/.github/workflows/nodejs-ts.yml @@ -11,5 +11,5 @@ jobs: # Documentation: https://github.com/zakodium/workflows#nodejs-ci uses: zakodium/workflows/.github/workflows/nodejs.yml@nodejs-v1 with: - node-version-matrix: '[12, 14, 16, 18]' + node-version-matrix: '[14, 16, 18]' lint-check-types: true diff --git a/package.json b/package.json index bbd10ea..bf9fb7c 100644 --- a/package.json +++ b/package.json @@ -82,7 +82,7 @@ "cheminfo-types": "^1.1.0", "ml-peak-shape-generator": "^4.1.1", "ml-savitzky-golay-generalized": "4.0.1", - "ml-spectra-fitting": "4.0.2", + "ml-spectra-fitting": "4.1.0", "ml-spectra-processing": "^11.6.0" } } diff --git a/src/GSDPeak.ts b/src/GSDPeak.ts index ec7ce95..2235eec 100644 --- a/src/GSDPeak.ts +++ b/src/GSDPeak.ts @@ -1,3 +1,5 @@ +import { Shape1D } from 'ml-peak-shape-generator'; + export interface GSDPeak { x: number; y: number; @@ -14,6 +16,9 @@ export interface GSDPeak { * This allows to determine if a peak is soft or not */ ddY: number; + + shape: Shape1D; + inflectionPoints: { from: { x: number; index: number }; to: { x: number; index: number }; diff --git a/src/__tests__/gaussian-smooth.test.ts b/src/__tests__/gaussian-smooth.test.ts index 13d9fe9..31a4123 100644 --- a/src/__tests__/gaussian-smooth.test.ts +++ b/src/__tests__/gaussian-smooth.test.ts @@ -38,6 +38,10 @@ describe('gaussian simulated peaks', () => { ddY: -259.83290100626783, width: 0.08, index: 25, + shape: { + fwhm: 0.09419280180123805, + kind: 'gaussian', + }, inflectionPoints: { from: { x: -0.54, index: 23 }, to: { x: -0.46, index: 27 }, @@ -49,6 +53,10 @@ describe('gaussian simulated peaks', () => { ddY: -259.83290100626783, width: 0.08, index: 75, + shape: { + fwhm: 0.09419280180123805, + kind: 'gaussian', + }, inflectionPoints: { from: { x: 0.46, index: 73 }, to: { x: 0.54, index: 77 }, diff --git a/src/__tests__/gaussian.test.ts b/src/__tests__/gaussian.test.ts index 382d060..86e7901 100644 --- a/src/__tests__/gaussian.test.ts +++ b/src/__tests__/gaussian.test.ts @@ -38,6 +38,10 @@ describe('smooth:false option', () => { ddY: -259.83290100626783, width: 0.08, index: 25, + shape: { + fwhm: 0.09419280180123805, + kind: 'gaussian', + }, inflectionPoints: { from: { index: 23, x: -0.54 }, to: { index: 27, x: -0.46 }, @@ -49,6 +53,10 @@ describe('smooth:false option', () => { ddY: -259.83290100626783, width: 0.08, index: 75, + shape: { + fwhm: 0.09419280180123805, + kind: 'gaussian', + }, inflectionPoints: { from: { index: 73, x: 0.46 }, to: { index: 77, x: 0.54 }, @@ -68,6 +76,10 @@ describe('smooth:false option', () => { ddY: -259.83290100626783, width: 0.08, index: 25, + shape: { + fwhm: 0.09419280180123805, + kind: 'gaussian', + }, inflectionPoints: { from: { index: 23, x: -0.54 }, to: { index: 27, x: -0.46 }, @@ -79,6 +91,10 @@ describe('smooth:false option', () => { ddY: -259.83290100626783, width: 0.08, index: 75, + shape: { + fwhm: 0.09419280180123805, + kind: 'gaussian', + }, inflectionPoints: { from: { index: 73, x: 0.46 }, to: { index: 77, x: 0.54 }, @@ -101,6 +117,10 @@ describe('smooth:false option', () => { ddY: 259.83290100626783, width: 0.08, index: 25, + shape: { + fwhm: 0.09419280180123805, + kind: 'gaussian', + }, inflectionPoints: { from: { index: 23, x: -0.54 }, to: { index: 27, x: -0.46 }, @@ -112,6 +132,10 @@ describe('smooth:false option', () => { ddY: 259.83290100626783, width: 0.08, index: 75, + shape: { + fwhm: 0.09419280180123805, + kind: 'gaussian', + }, inflectionPoints: { from: { index: 73, x: 0.46 }, to: { index: 77, x: 0.54 }, @@ -135,6 +159,10 @@ describe('smooth:false option', () => { ddY: 259.83290100626783, width: 0.08, index: 25, + shape: { + fwhm: 0.09419280180123805, + kind: 'gaussian', + }, inflectionPoints: { from: { index: 23, x: -0.54 }, to: { index: 27, x: -0.46 }, @@ -146,6 +174,10 @@ describe('smooth:false option', () => { ddY: 259.83290100626783, width: 0.08, index: 75, + shape: { + fwhm: 0.09419280180123805, + kind: 'gaussian', + }, inflectionPoints: { from: { index: 73, x: 0.46 }, to: { index: 77, x: 0.54 }, diff --git a/src/__tests__/power.test.ts b/src/__tests__/power.test.ts index 610d7ed..0ce2f4f 100644 --- a/src/__tests__/power.test.ts +++ b/src/__tests__/power.test.ts @@ -56,6 +56,10 @@ describe('power', () => { width: 0.3, index: 100, ddY: -33832.03463203485, + shape: { + fwhm: 0.35322300675464324, + kind: 'gaussian', + }, inflectionPoints: { from: { x: 4.85, index: 97 }, to: { x: 5.15, index: 103 }, @@ -78,6 +82,10 @@ describe('power', () => { width: 0.05, index: 89, ddY: 800, + shape: { + fwhm: 0.05887050112577352, + kind: 'gaussian', + }, inflectionPoints: { from: { x: 4.4, index: 88 }, to: { x: 4.45, index: 89 }, @@ -89,6 +97,10 @@ describe('power', () => { width: 0.2, index: 100, ddY: -44914.28571428623, + shape: { + fwhm: 0.2354820045030941, + kind: 'gaussian', + }, inflectionPoints: { from: { x: 4.9, index: 98 }, to: { x: 5.1, index: 102 }, @@ -100,6 +112,10 @@ describe('power', () => { width: 0.05, index: 111, ddY: 800, + shape: { + fwhm: 0.05887050112577352, + kind: 'gaussian', + }, inflectionPoints: { from: { x: 5.55, index: 111 }, to: { x: 5.6, index: 112 }, diff --git a/src/__tests__/ubiquitin.test.ts b/src/__tests__/ubiquitin.test.ts index c762b2c..9a555ab 100644 --- a/src/__tests__/ubiquitin.test.ts +++ b/src/__tests__/ubiquitin.test.ts @@ -28,6 +28,10 @@ describe('Global spectra deconvolution ubiquitin', () => { ddY: -15468134.039875854, width: 0.002420000000000755, index: 11, + shape: { + fwhm: 0.0028493322544883375, + kind: 'gaussian', + }, inflectionPoints: { from: { x: 200.054133, index: 9 }, to: { x: 200.056553, index: 13 }, diff --git a/src/gsd.ts b/src/gsd.ts index ba60486..b8c265e 100644 --- a/src/gsd.ts +++ b/src/gsd.ts @@ -1,4 +1,5 @@ import type { DataXY } from 'cheminfo-types'; +import { Shape1D } from 'ml-peak-shape-generator'; import { sgg, SGGOptions } from 'ml-savitzky-golay-generalized'; import { xIsEquallySpaced, @@ -9,6 +10,7 @@ import { } from 'ml-spectra-processing'; import { GSDPeak } from './GSDPeak'; +import { appendShapeAndFWHM } from './utils/appendShapeAndFWHM'; import { optimizeTop } from './utils/optimizeTop'; export interface GSDOptions { @@ -44,6 +46,11 @@ export interface GSDOptions { * @default false */ realTopDetection?: boolean; + /** + * Shape used to add FWHM property in the peaks + * @default {kind:'gaussian'} + */ + shape?: Shape1D; } /** @@ -60,6 +67,7 @@ export function gsd(data: DataXY, options: GSDOptions = {}): GSDPeak[] { windowSize: 9, polynomial: 3, }, + shape, noiseLevel, smoothY = false, maxCriteria = true, @@ -239,14 +247,14 @@ export function gsd(data: DataXY, options: GSDOptions = {}): GSDPeak[] { peaks.push({ x: deltaX, y: yData[minddYIndex], - width: width, + width, index: minddYIndex, ddY: ddY[minddYIndex], inflectionPoints: { from: intervalL[possible], to: intervalR[possible], }, - }); + } as GSDPeak); } } } @@ -266,5 +274,5 @@ export function gsd(data: DataXY, options: GSDOptions = {}): GSDPeak[] { return a.x - b.x; }); - return peaks; + return appendShapeAndFWHM(peaks, { shape }); } diff --git a/src/post/__tests__/optimizePeaksWithLogs.test.ts b/src/post/__tests__/optimizePeaksWithLogs.test.ts index 40e8e8e..a178a93 100644 --- a/src/post/__tests__/optimizePeaksWithLogs.test.ts +++ b/src/post/__tests__/optimizePeaksWithLogs.test.ts @@ -30,7 +30,7 @@ describe('optimizePeaksWithLogs', () => { expect(result.logs).toMatchObject([ { iterations: 100, - error: 0.000017852930772995625, + error: 0.000017852931247867768, parameters: { kind: 'lm', options: { timeout: 10 } }, message: 'optimization successful', groupSize: 1, diff --git a/src/post/broadenPeaks.ts b/src/post/broadenPeaks.ts index 9b91c23..c992c6a 100644 --- a/src/post/broadenPeaks.ts +++ b/src/post/broadenPeaks.ts @@ -10,7 +10,7 @@ import { GSDPeak } from '../GSDPeak'; */ export function broadenPeaks( - peakList: GSDPeak[], + peakList: Omit[], options: { /** * @default 2 diff --git a/src/post/optimizePeaks.ts b/src/post/optimizePeaks.ts index 78543b2..c04c8c2 100644 --- a/src/post/optimizePeaks.ts +++ b/src/post/optimizePeaks.ts @@ -1,4 +1,5 @@ import type { DataXY, PeakXYWidth } from 'cheminfo-types'; +import { FromTo } from 'cheminfo-types'; import { Shape1D } from 'ml-peak-shape-generator'; import type { OptimizationOptions } from 'ml-spectra-fitting'; @@ -7,6 +8,14 @@ import { GSDPeakOptimized } from '../GSDPeakOptimized'; import { optimizePeaksWithLogs } from './optimizePeaksWithLogs'; export interface OptimizePeaksOptions { + /** + * baseline + */ + baseline?: number; + /** + * range to apply the optimization + */ + fromTo?: Partial; /** * Shape to use for optimization * @default {kind:'gaussian'} diff --git a/src/post/optimizePeaksWithLogs.ts b/src/post/optimizePeaksWithLogs.ts index 9a9268e..0d745a9 100644 --- a/src/post/optimizePeaksWithLogs.ts +++ b/src/post/optimizePeaksWithLogs.ts @@ -21,6 +21,8 @@ export function optimizePeaksWithLogs( options: OptimizePeaksOptions = {}, ): { logs: any[]; optimizedPeaks: GSDPeakOptimized[] } { const { + fromTo = {}, + baseline, shape = { kind: 'gaussian' }, groupingFactor = 1, factorLimits = 2, @@ -40,7 +42,6 @@ export function optimizePeaksWithLogs( let groups = groupPeaks(peakList, { factor: groupingFactor }); let logs: any[] = []; let results: GSDPeakOptimized[] = []; - groups.forEach((peakGroup) => { const start = Date.now(); // In order to make optimization we will add fwhm and shape on all the peaks @@ -49,8 +50,11 @@ export function optimizePeaksWithLogs( const firstPeak = peaks[0]; const lastPeak = peaks[peaks.length - 1]; - const from = firstPeak.x - firstPeak.width * factorLimits; - const to = lastPeak.x + lastPeak.width * factorLimits; + const { + from = firstPeak.x - firstPeak.width * factorLimits, + to = lastPeak.x + lastPeak.width * factorLimits, + } = fromTo; + const { fromIndex, toIndex } = xGetFromToIndex(data.x, { from, to }); const x = @@ -76,6 +80,7 @@ export function optimizePeaksWithLogs( peaks: optimizedPeaks, } = optimize({ x, y }, peaks, { shape, + baseline, optimization, });