diff --git a/package-lock.json b/package-lock.json index e8386855f..525b08a2c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -39,7 +39,7 @@ "ml-array-mean": "^2.0.0", "ml-array-median": "^2.0.0", "ml-conrec": "^6.0.0", - "ml-gsd": "^14.0.1", + "ml-gsd": "^14.1.0", "ml-signal-processing": "^2.2.1", "ml-spectra-processing": "^14.29.0", "ml-tree-similarity": "^2.2.0", @@ -3946,6 +3946,15 @@ "ml-spectra-processing": "^14.28.1" } }, + "node_modules/@zakodium/nmr-types/node_modules/ml-peak-shape-generator": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ml-peak-shape-generator/-/ml-peak-shape-generator-5.1.0.tgz", + "integrity": "sha512-W2Vx/+R65zyr/B13p5TLyqwjTgIzwR0xtjRyQ2ZSxDHdVaXxb6m+ajyyaHJqGG5mYzeqi97nGvpklhz0jrsbrg==", + "license": "MIT", + "dependencies": { + "cheminfo-types": "^1.15.0" + } + }, "node_modules/@zakodium/nmrium-core": { "version": "0.7.29", "resolved": "https://registry.npmjs.org/@zakodium/nmrium-core/-/nmrium-core-0.7.29.tgz", @@ -9323,6 +9332,15 @@ "ml-spectra-processing": "^14.28.1" } }, + "node_modules/ml-gsd/node_modules/ml-peak-shape-generator": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ml-peak-shape-generator/-/ml-peak-shape-generator-5.1.0.tgz", + "integrity": "sha512-W2Vx/+R65zyr/B13p5TLyqwjTgIzwR0xtjRyQ2ZSxDHdVaXxb6m+ajyyaHJqGG5mYzeqi97nGvpklhz0jrsbrg==", + "license": "MIT", + "dependencies": { + "cheminfo-types": "^1.15.0" + } + }, "node_modules/ml-hash-table": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/ml-hash-table/-/ml-hash-table-1.0.0.tgz", @@ -9529,6 +9547,15 @@ "ml-spectra-processing": "^14.2.0" } }, + "node_modules/ml-spectra-fitting/node_modules/ml-peak-shape-generator": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ml-peak-shape-generator/-/ml-peak-shape-generator-5.1.0.tgz", + "integrity": "sha512-W2Vx/+R65zyr/B13p5TLyqwjTgIzwR0xtjRyQ2ZSxDHdVaXxb6m+ajyyaHJqGG5mYzeqi97nGvpklhz0jrsbrg==", + "license": "MIT", + "dependencies": { + "cheminfo-types": "^1.15.0" + } + }, "node_modules/ml-spectra-processing": { "version": "14.29.0", "resolved": "https://registry.npmjs.org/ml-spectra-processing/-/ml-spectra-processing-14.29.0.tgz", @@ -9718,6 +9745,15 @@ "ml-matrix-convolution": "^2.0.0" } }, + "node_modules/nmr-processing/node_modules/ml-peak-shape-generator": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ml-peak-shape-generator/-/ml-peak-shape-generator-5.1.0.tgz", + "integrity": "sha512-W2Vx/+R65zyr/B13p5TLyqwjTgIzwR0xtjRyQ2ZSxDHdVaXxb6m+ajyyaHJqGG5mYzeqi97nGvpklhz0jrsbrg==", + "license": "MIT", + "dependencies": { + "cheminfo-types": "^1.15.0" + } + }, "node_modules/no-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", @@ -11849,6 +11885,15 @@ "ml-spectra-processing": "^14.28.1" } }, + "node_modules/spectrum-generator/node_modules/ml-peak-shape-generator": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ml-peak-shape-generator/-/ml-peak-shape-generator-5.1.0.tgz", + "integrity": "sha512-W2Vx/+R65zyr/B13p5TLyqwjTgIzwR0xtjRyQ2ZSxDHdVaXxb6m+ajyyaHJqGG5mYzeqi97nGvpklhz0jrsbrg==", + "license": "MIT", + "dependencies": { + "cheminfo-types": "^1.15.0" + } + }, "node_modules/stackback": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", diff --git a/package.json b/package.json index e17bd30f0..3b7c5ab8b 100644 --- a/package.json +++ b/package.json @@ -93,7 +93,7 @@ "ml-array-mean": "^2.0.0", "ml-array-median": "^2.0.0", "ml-conrec": "^6.0.0", - "ml-gsd": "^14.0.1", + "ml-gsd": "^14.1.0", "ml-signal-processing": "^2.2.1", "ml-spectra-processing": "^14.29.0", "ml-tree-similarity": "^2.2.0", diff --git a/src/component/1d/peaks/usePeakShapesPath.ts b/src/component/1d/peaks/usePeakShapesPath.ts index 303f81184..6c935f897 100644 --- a/src/component/1d/peaks/usePeakShapesPath.ts +++ b/src/component/1d/peaks/usePeakShapesPath.ts @@ -29,12 +29,13 @@ export function usePeakShapesPath(spectrum: Spectrum1D) { const frequency = spectrum.info.originFrequency; let pathSeries: DataXY | null = null; + switch (target) { case 'peakShape': { const { peak } = options; pathSeries = peakToXY(peak, { frequency, - nbPoints: 1024, + nbPoints: Math.ceil(width * 3), from: peak.x - (peak.width / frequency) * 9, to: peak.x + (peak.width / frequency) * 9, }); diff --git a/src/component/panels/PeaksPanel/PeaksPanel.tsx b/src/component/panels/PeaksPanel/PeaksPanel.tsx index a9161f523..995fd25d4 100644 --- a/src/component/panels/PeaksPanel/PeaksPanel.tsx +++ b/src/component/panels/PeaksPanel/PeaksPanel.tsx @@ -111,11 +111,11 @@ function PeaksPanelInner(props: PeaksPanelInnerProps) { const filterPeaks = peaks.values.filter((peak) => isInRange(peak.x, { from, to }), ); - if (filterPeaks.length <= 4) { + if (filterPeaks.length <= 15) { dispatch({ type: 'OPTIMIZE_PEAKS', payload: { peaks: filterPeaks } }); } else { toaster.show({ - message: 'optimization can be done on no more than 4 peaks', + message: 'optimization can be done on no more than 15 peaks', intent: 'danger', }); } diff --git a/src/component/reducer/actions/PeaksActions.ts b/src/component/reducer/actions/PeaksActions.ts index 970c94008..aa3419284 100644 --- a/src/component/reducer/actions/PeaksActions.ts +++ b/src/component/reducer/actions/PeaksActions.ts @@ -102,7 +102,7 @@ function handleAddPeak(draft: Draft, action: AddPeakAction) { x: candidatePeak.x, y: candidatePeak.y, width: 1, - shape: defaultPeakShape, + shape: { ...defaultPeakShape }, }; spectrum.peaks.values.push(...mapPeaks([peak], spectrum)); } @@ -131,7 +131,7 @@ function handleAddPeaks(draft: Draft, action: AddPeaksAction) { x: peak.x, y: peak.y, width: 1, - shape: defaultPeakShape, + shape: { ...defaultPeakShape }, }; spectrum.peaks.values.push(newPeak); } diff --git a/src/data/data1d/Spectrum1D/peaks/optimizePeaks.ts b/src/data/data1d/Spectrum1D/peaks/optimizePeaks.ts index 486d13586..057b512c1 100644 --- a/src/data/data1d/Spectrum1D/peaks/optimizePeaks.ts +++ b/src/data/data1d/Spectrum1D/peaks/optimizePeaks.ts @@ -1,7 +1,7 @@ import type { Peak1D } from '@zakodium/nmr-types'; import type { Spectrum1D } from '@zakodium/nmrium-core'; import { xFindClosestIndex } from 'ml-spectra-processing'; -import { mapPeaks, xyPeaksOptimization } from 'nmr-processing'; +import { mapPeaks, xyPeaksOptimizationByStages } from 'nmr-processing'; interface OptimizePeaksOptions { from: number; @@ -31,10 +31,116 @@ export function optimizePeaks( x = x.subarray(fromIndex, ToIndex); re = re.subarray(fromIndex, ToIndex); - const newPeaks = xyPeaksOptimization({ x, y: re }, peaks, { + const newPeaks = xyPeaksOptimizationByStages({ x, y: re }, peaks, { frequency, - groupingFactor: 3, + groupingFactor: 10, + stages: [ + { + optimization: { + kind: 'lm', + options: { maxIterations: 20, errorTolerance: 1e-3 }, + }, + parameters: { + fwhm: { + optimize: true, + min: (peak: any) => (peak.shape?.fwhm ?? 0) / 2, + max: (peak: any) => (peak.shape?.fwhm ?? 0) * 2, + }, + mu: { optimize: false }, + x: { optimize: false }, + y: { + optimize: true, + init: (peak: any) => peak.y * 0.8, + }, + }, + }, + { + optimization: { + kind: 'lm', + options: { maxIterations: 20, errorTolerance: 5e-4 }, + }, + parameters: { + fwhm: { + optimize: true, + min: (peak: any) => (peak.shape?.fwhm ?? 0) / 2, + max: (peak: any) => (peak.shape?.fwhm ?? 0) * 2, + }, + mu: { optimize: false }, + x: { optimize: false }, + y: { + optimize: true, + }, + }, + }, + { + optimization: { + kind: 'lm', + options: { maxIterations: 20, errorTolerance: 1e-5 }, + }, + parameters: { + fwhm: { + optimize: true, + min: (peak: any) => (peak.shape?.fwhm ?? 0) / 2, + max: (peak: any) => (peak.shape?.fwhm ?? 0) * 2, + }, + mu: { optimize: true }, + x: { optimize: false }, + y: { + optimize: true, + }, + }, + }, + { + optimization: { + kind: 'lm', + options: { maxIterations: 20, errorTolerance: 5e-4 }, + }, + parameters: { + fwhm: { + optimize: true, + min: (peak: any) => (peak.shape?.fwhm ?? 0) / 3, + max: (peak: any) => (peak.shape?.fwhm ?? 0) * 3, + }, + mu: { optimize: true }, + x: { optimize: true }, + y: { optimize: true }, + }, + }, + { + optimization: { + kind: 'lm', + options: { maxIterations: 20, errorTolerance: 1e-4 }, + }, + parameters: { + fwhm: { + optimize: true, + min: (peak: any) => (peak.shape?.fwhm ?? 0) / 2, + max: (peak: any) => (peak.shape?.fwhm ?? 0) * 2, + }, + mu: { optimize: false }, + x: { optimize: true }, + y: { optimize: true }, + }, + }, + { + optimization: { + kind: 'lm', + options: { maxIterations: 20, errorTolerance: 1e-8 }, + }, + parameters: { + fwhm: { + optimize: true, + min: (peak: any) => (peak.shape?.fwhm ?? 0) / 2, + max: (peak: any) => (peak.shape?.fwhm ?? 0) * 2, + }, + mu: { optimize: true }, + x: { optimize: true }, + y: { optimize: true }, + }, + }, + ], }); + return mapPeaks(spectrum.peaks.values.concat(newPeaks), spectrum, { checkIsExisting: false, });