nmr-processing 7.4.2 → 8.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/assignment/getAssignments.d.ts +6 -8
- package/lib/assignment/getAssignments.js +1 -1
- package/lib/assignment/getAssignments.js.map +1 -1
- package/lib/assignment/utils/getAssignment/buildAssignments.js +5 -0
- package/lib/assignment/utils/getAssignment/buildAssignments.js.map +1 -1
- package/lib/assignment/utils/getAssignment/checkIDs.js +5 -9
- package/lib/assignment/utils/getAssignment/checkIDs.js.map +1 -1
- package/lib/assignment/utils/getAssignment/formatData.js +30 -3
- package/lib/assignment/utils/getAssignment/formatData.js.map +1 -1
- package/lib/assignment/utils/getAssignment/partialScore.js +1 -1
- package/lib/assignment/utils/getAssignment/partialScore.js.map +1 -1
- package/lib/databases/getDatabase.d.ts +6 -1
- package/lib/databases/getDatabase.js +13 -3
- package/lib/databases/getDatabase.js.map +1 -1
- package/lib/index.d.ts +2 -2
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/peaks/NMRPeak1D.d.ts +0 -1
- package/lib/peaks/solventSuppression.d.ts +3 -0
- package/lib/peaks/solventSuppression.js +147 -0
- package/lib/peaks/solventSuppression.js.map +1 -0
- package/lib/peaks/util/jAnalyzer.d.ts +1 -1
- package/lib/prediction/predictAllSpectra.d.ts +22 -4
- package/lib/prediction/predictAllSpectra.js +102 -16
- package/lib/prediction/predictAllSpectra.js.map +1 -1
- package/lib/ranges/rangesToXY.js +27 -3
- package/lib/ranges/rangesToXY.js.map +1 -1
- package/lib/signals/addDummySignals.d.ts +2 -0
- package/lib/signals/addDummySignals.js +56 -0
- package/lib/signals/addDummySignals.js.map +1 -0
- package/lib/signals/hackSignalsToXY.js +2 -48
- package/lib/signals/hackSignalsToXY.js.map +1 -1
- package/lib/signals/simulation/simulate1D.d.ts +1 -39
- package/lib/signals/simulation/simulate1D.js +13 -240
- package/lib/signals/simulation/simulate1D.js.map +1 -1
- package/lib/signals/simulation/simulateXYPeaks.d.ts +47 -0
- package/lib/signals/simulation/simulateXYPeaks.js +246 -0
- package/lib/signals/simulation/simulateXYPeaks.js.map +1 -0
- package/lib/utilities/getFrequency.d.ts +1 -1
- package/lib/utilities/getFrequency.js +4 -4
- package/lib/utilities/getFrequency.js.map +1 -1
- package/lib/xyz/NMRSignal2D.d.ts +3 -3
- package/lib-esm/assignment/getAssignments.js +1 -1
- package/lib-esm/assignment/getAssignments.js.map +1 -1
- package/lib-esm/assignment/utils/getAssignment/buildAssignments.js +5 -0
- package/lib-esm/assignment/utils/getAssignment/buildAssignments.js.map +1 -1
- package/lib-esm/assignment/utils/getAssignment/checkIDs.js +5 -9
- package/lib-esm/assignment/utils/getAssignment/checkIDs.js.map +1 -1
- package/lib-esm/assignment/utils/getAssignment/formatData.js +30 -3
- package/lib-esm/assignment/utils/getAssignment/formatData.js.map +1 -1
- package/lib-esm/assignment/utils/getAssignment/partialScore.js +1 -1
- package/lib-esm/assignment/utils/getAssignment/partialScore.js.map +1 -1
- package/lib-esm/databases/getDatabase.js +13 -3
- package/lib-esm/databases/getDatabase.js.map +1 -1
- package/lib-esm/index.js +1 -1
- package/lib-esm/index.js.map +1 -1
- package/lib-esm/peaks/solventSuppression.js +140 -0
- package/lib-esm/peaks/solventSuppression.js.map +1 -0
- package/lib-esm/prediction/predictAllSpectra.js +102 -16
- package/lib-esm/prediction/predictAllSpectra.js.map +1 -1
- package/lib-esm/ranges/rangesToXY.js +27 -3
- package/lib-esm/ranges/rangesToXY.js.map +1 -1
- package/lib-esm/signals/addDummySignals.js +52 -0
- package/lib-esm/signals/addDummySignals.js.map +1 -0
- package/lib-esm/signals/hackSignalsToXY.js +2 -48
- package/lib-esm/signals/hackSignalsToXY.js.map +1 -1
- package/lib-esm/signals/simulation/simulate1D.js +14 -238
- package/lib-esm/signals/simulation/simulate1D.js.map +1 -1
- package/lib-esm/signals/simulation/simulateXYPeaks.js +239 -0
- package/lib-esm/signals/simulation/simulateXYPeaks.js.map +1 -0
- package/lib-esm/utilities/getFrequency.js +1 -1
- package/lib-esm/utilities/getFrequency.js.map +1 -1
- package/package.json +14 -12
- package/src/assignment/getAssignments.ts +8 -9
- package/src/assignment/utils/getAssignment/buildAssignments.ts +6 -0
- package/src/assignment/utils/getAssignment/checkIDs.ts +8 -8
- package/src/assignment/utils/getAssignment/formatData.ts +39 -3
- package/src/assignment/utils/getAssignment/partialScore.ts +2 -2
- package/src/databases/getDatabase.ts +19 -2
- package/src/index.ts +2 -2
- package/src/peaks/NMRPeak1D.ts +0 -1
- package/src/peaks/solventSuppression.ts +186 -0
- package/src/peaks/util/jAnalyzer.ts +1 -1
- package/src/prediction/predictAllSpectra.ts +151 -14
- package/src/ranges/rangesToXY.ts +33 -4
- package/src/signals/addDummySignals.ts +77 -0
- package/src/signals/hackSignalsToXY.ts +2 -72
- package/src/signals/simulation/simulate1D.ts +14 -319
- package/src/signals/simulation/simulateXYPeaks.ts +332 -0
- package/src/utilities/getFrequency.ts +1 -1
- package/src/xyz/NMRSignal2D.ts +3 -3
- package/lib/constants/gyromagneticRatio.d.ts +0 -6
- package/lib/constants/gyromagneticRatio.js +0 -26
- package/lib/constants/gyromagneticRatio.js.map +0 -1
- package/lib-esm/constants/gyromagneticRatio.js +0 -23
- package/lib-esm/constants/gyromagneticRatio.js.map +0 -1
- package/src/constants/gyromagneticRatio.ts +0 -49
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { PointXY } from 'cheminfo-types';
|
|
2
|
+
import linearSumAssignment from 'linear-sum-assignment';
|
|
3
|
+
import { Matrix } from 'ml-matrix';
|
|
4
|
+
import { gaussianFct } from 'ml-peak-shape-generator';
|
|
5
|
+
import { xFindClosestIndex, xMaxValue } from 'ml-spectra-processing';
|
|
6
|
+
|
|
7
|
+
import { NMRSignal1D } from '../signals/NMRSignal1D';
|
|
8
|
+
import { addDummySignals } from '../signals/addDummySignals';
|
|
9
|
+
import { signalsToSpinSystem } from '../signals/simulation/signalsToSpinSystem';
|
|
10
|
+
import { simulateXYPeaks } from '../signals/simulation/simulateXYPeaks';
|
|
11
|
+
import { splitSpinSystem } from '../signals/simulation/splitSpinSystem';
|
|
12
|
+
|
|
13
|
+
import { NMRPeak1D } from './NMRPeak1D';
|
|
14
|
+
|
|
15
|
+
export function solventSuppression(
|
|
16
|
+
peakList: NMRPeak1D[],
|
|
17
|
+
solvent: NMRSignal1D[],
|
|
18
|
+
options: any = {},
|
|
19
|
+
) {
|
|
20
|
+
const peaks = [...peakList];
|
|
21
|
+
sortAscending(peaks);
|
|
22
|
+
const xValues = peaks.map((peak) => peak.x);
|
|
23
|
+
|
|
24
|
+
const { markSolventPeaks = false } = options;
|
|
25
|
+
|
|
26
|
+
let solventSignals = solvent; //addDummySignals(solvent);
|
|
27
|
+
// solventSignals = solventSignals.slice(0, solventSignals.findIndex((signal) => signal.delta > 400));
|
|
28
|
+
|
|
29
|
+
for (const solventSignal of solventSignals) {
|
|
30
|
+
const solventXYPeaks = solventSignal.peaks
|
|
31
|
+
? solventSignal.peaks
|
|
32
|
+
: getSolventPeaks(solventSignal);
|
|
33
|
+
sortAscending(solventXYPeaks);
|
|
34
|
+
let upIndex = xFindClosestIndex(
|
|
35
|
+
xValues,
|
|
36
|
+
solventXYPeaks[solventXYPeaks.length - 1].x + 1,
|
|
37
|
+
);
|
|
38
|
+
let lowIndex = xFindClosestIndex(xValues, solventXYPeaks[0].x - 1);
|
|
39
|
+
if (upIndex === lowIndex) continue;
|
|
40
|
+
const nearPeaks = peaks.slice(lowIndex, upIndex + 1);
|
|
41
|
+
const amplitudeResiduals = [];
|
|
42
|
+
const deltaResiduals = [];
|
|
43
|
+
const positionResiduals = [];
|
|
44
|
+
for (let peak of nearPeaks) {
|
|
45
|
+
const shiftedSolventPeaks = getShiftedSolventPeaks(
|
|
46
|
+
peak,
|
|
47
|
+
solventSignal,
|
|
48
|
+
solventXYPeaks,
|
|
49
|
+
);
|
|
50
|
+
const closestPeaks = getClosestPeaks(shiftedSolventPeaks, nearPeaks);
|
|
51
|
+
let deltaResidual = 0;
|
|
52
|
+
let amplitudeResidual = 0;
|
|
53
|
+
let positionResidual = 0;
|
|
54
|
+
for (let i = 0; i < closestPeaks.length; i++) {
|
|
55
|
+
amplitudeResidual += Math.abs(
|
|
56
|
+
shiftedSolventPeaks[i].y - closestPeaks[i].y,
|
|
57
|
+
);
|
|
58
|
+
deltaResidual += Math.abs(shiftedSolventPeaks[i].x - closestPeaks[i].x);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (closestPeaks.length === 0) {
|
|
62
|
+
deltaResidual = Number.MAX_SAFE_INTEGER;
|
|
63
|
+
amplitudeResidual = Number.MAX_SAFE_INTEGER;
|
|
64
|
+
positionResidual = Number.MAX_SAFE_INTEGER;
|
|
65
|
+
} else {
|
|
66
|
+
positionResidual = gaussianFct(solventSignal.delta - peak.x, 0.5);
|
|
67
|
+
}
|
|
68
|
+
amplitudeResiduals.push(amplitudeResidual);
|
|
69
|
+
deltaResiduals.push(deltaResidual);
|
|
70
|
+
positionResiduals.push(positionResidual);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const maxAmplitude = xMaxValue(amplitudeResiduals);
|
|
74
|
+
const maxDelta = xMaxValue(deltaResiduals);
|
|
75
|
+
const maxPosition = xMaxValue(positionResiduals);
|
|
76
|
+
|
|
77
|
+
let minIndex = -1;
|
|
78
|
+
let minScore = Number.MAX_SAFE_INTEGER;
|
|
79
|
+
for (let i = 0; i < deltaResiduals.length; i++) {
|
|
80
|
+
const value =
|
|
81
|
+
(amplitudeResiduals[i] / maxAmplitude +
|
|
82
|
+
deltaResiduals[i] / maxDelta +
|
|
83
|
+
(1 - positionResiduals[i]) / maxPosition) /
|
|
84
|
+
3;
|
|
85
|
+
if (minScore > value) {
|
|
86
|
+
minIndex = i;
|
|
87
|
+
minScore = value;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const shiftedSolventPeaks = getShiftedSolventPeaks(
|
|
91
|
+
nearPeaks[minIndex],
|
|
92
|
+
solventSignal,
|
|
93
|
+
solventXYPeaks,
|
|
94
|
+
);
|
|
95
|
+
const diff = getDiffMatrix(shiftedSolventPeaks, nearPeaks);
|
|
96
|
+
const { rowAssignments, gain } = linearSumAssignment(diff, {
|
|
97
|
+
maximaze: false,
|
|
98
|
+
});
|
|
99
|
+
if (gain === -1) return peaks;
|
|
100
|
+
|
|
101
|
+
if (markSolventPeaks) {
|
|
102
|
+
for (let index of rowAssignments) {
|
|
103
|
+
peaks[index + lowIndex].kind = 'solvent';
|
|
104
|
+
}
|
|
105
|
+
} else {
|
|
106
|
+
rowAssignments.sort((a, b) => b - a);
|
|
107
|
+
for (let index of rowAssignments) {
|
|
108
|
+
peaks.splice(index + lowIndex, 1);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return peaks;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function getSolventPeaks(
|
|
117
|
+
signal: NMRSignal1D,
|
|
118
|
+
options: { frequency?: number; maxClusterSize?: number } = {},
|
|
119
|
+
) {
|
|
120
|
+
let signals = addDummySignals([signal]);
|
|
121
|
+
let spinSystem = signalsToSpinSystem(signals);
|
|
122
|
+
|
|
123
|
+
const { frequency = 400, maxClusterSize = 8 } = options;
|
|
124
|
+
|
|
125
|
+
spinSystem.clusters = splitSpinSystem(spinSystem, {
|
|
126
|
+
frequency,
|
|
127
|
+
maxClusterSize,
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
const peaks = simulateXYPeaks(spinSystem);
|
|
131
|
+
return peaks.filter((peak) => peak.x < 1000);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function getShiftedSolventPeaks(
|
|
135
|
+
peak: PointXY,
|
|
136
|
+
solventSignal: any,
|
|
137
|
+
solventXYPeaks: PointXY[],
|
|
138
|
+
) {
|
|
139
|
+
const shiftedSolventPeaks: PointXY[] = JSON.parse(
|
|
140
|
+
JSON.stringify(solventXYPeaks),
|
|
141
|
+
);
|
|
142
|
+
// shift x values of solventPeaks to center it to the current peak.
|
|
143
|
+
const deltaPPM = peak.x - solventSignal.delta;
|
|
144
|
+
const maxIntensity = shiftedSolventPeaks.reduce(
|
|
145
|
+
(max, current) => (current.y > max ? current.y : max),
|
|
146
|
+
shiftedSolventPeaks[0].y,
|
|
147
|
+
);
|
|
148
|
+
for (let shiftedSolventPeak of shiftedSolventPeaks) {
|
|
149
|
+
shiftedSolventPeak.x += deltaPPM;
|
|
150
|
+
shiftedSolventPeak.y /= maxIntensity;
|
|
151
|
+
}
|
|
152
|
+
return shiftedSolventPeaks;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function getClosestPeaks(shiftedSolventPeaks: PointXY[], nearPeaks: PointXY[]) {
|
|
156
|
+
const diff = getDiffMatrix(shiftedSolventPeaks, nearPeaks);
|
|
157
|
+
const { rowAssignments, gain } = linearSumAssignment(diff, {
|
|
158
|
+
maximaze: false,
|
|
159
|
+
});
|
|
160
|
+
if (gain === -1) return [];
|
|
161
|
+
|
|
162
|
+
const assignmentPeaks = [];
|
|
163
|
+
let maxValue = Number.MIN_SAFE_INTEGER;
|
|
164
|
+
for (let index of rowAssignments) {
|
|
165
|
+
if (maxValue < nearPeaks[index].y) maxValue = nearPeaks[index].y;
|
|
166
|
+
assignmentPeaks.push({ ...nearPeaks[index] });
|
|
167
|
+
}
|
|
168
|
+
assignmentPeaks.forEach((peak, i, arr) => (arr[i].y /= maxValue));
|
|
169
|
+
return assignmentPeaks;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function sortAscending(peaks: PointXY[]) {
|
|
173
|
+
return peaks[0].x > peaks[1].x ? peaks.sort((a, b) => a.x - b.x) : peaks;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function getDiffMatrix(rows: PointXY[], columns: PointXY[]) {
|
|
177
|
+
const nbColumns = columns.length;
|
|
178
|
+
const nbRows = rows.length;
|
|
179
|
+
const diff = new Matrix(nbRows, nbColumns);
|
|
180
|
+
for (let r = 0; r < nbRows; r++) {
|
|
181
|
+
for (let c = 0; c < nbColumns; c++) {
|
|
182
|
+
diff.set(r, c, Math.abs(rows[r].x - columns[c].x));
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return diff;
|
|
186
|
+
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import { FromTo, PointXY } from 'cheminfo-types';
|
|
1
2
|
import type { Molecule } from 'openchemlib';
|
|
2
3
|
|
|
4
|
+
import { signals2DToZ } from '../signals/signals2DToZ';
|
|
3
5
|
import { OptionsSignalsToXY, signalsToXY } from '../signals/signalsToXY';
|
|
6
|
+
import { getFrequency } from '../utilities/getFrequency';
|
|
4
7
|
|
|
5
8
|
import { PredictAllOptions, predictAll } from './predictAll';
|
|
6
9
|
/**
|
|
@@ -10,39 +13,173 @@ import { PredictAllOptions, predictAll } from './predictAll';
|
|
|
10
13
|
* @param options
|
|
11
14
|
*/
|
|
12
15
|
|
|
16
|
+
interface OneDOptions {
|
|
17
|
+
proton: FromTo;
|
|
18
|
+
carbon: FromTo;
|
|
19
|
+
nbPoints: number;
|
|
20
|
+
lineWidth: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface TwoDOptions {
|
|
24
|
+
from: PointXY;
|
|
25
|
+
to: PointXY;
|
|
26
|
+
nbPoints: PointXY;
|
|
27
|
+
}
|
|
28
|
+
interface SimulationOptions {
|
|
29
|
+
frequency: number;
|
|
30
|
+
oneD?: Partial<OneDOptions>;
|
|
31
|
+
twoD?: Partial<TwoDOptions>;
|
|
32
|
+
}
|
|
33
|
+
interface PredictAllSpectraOptions {
|
|
34
|
+
prediction?: PredictAllOptions;
|
|
35
|
+
simulation?: SimulationOptions;
|
|
36
|
+
}
|
|
37
|
+
|
|
13
38
|
export async function predictAllSpectra(
|
|
14
39
|
molecule: Molecule,
|
|
15
|
-
options: {
|
|
16
|
-
prediction?: PredictAllOptions;
|
|
17
|
-
simulation?: OptionsSignalsToXY;
|
|
18
|
-
} = {},
|
|
40
|
+
options: PredictAllSpectraOptions = {},
|
|
19
41
|
) {
|
|
20
|
-
const
|
|
42
|
+
const {
|
|
43
|
+
simulation: simulationOptions = { oneD: {}, twoD: {} } as SimulationOptions,
|
|
44
|
+
prediction: predictionOptions = {},
|
|
45
|
+
} = options;
|
|
46
|
+
|
|
47
|
+
const predictions = await predictAll(molecule, predictionOptions);
|
|
21
48
|
const spectra: any[] = [];
|
|
22
49
|
const result = { spectra, molecules: [{ molfile: predictions.molfile }] };
|
|
23
|
-
|
|
50
|
+
|
|
51
|
+
const oneDOptions: OneDOptions = {
|
|
52
|
+
...{
|
|
53
|
+
proton: { from: 0, to: 14 },
|
|
54
|
+
carbon: { from: 0, to: 200 },
|
|
55
|
+
nbPoints: 4098,
|
|
56
|
+
lineWidth: 0.03,
|
|
57
|
+
},
|
|
58
|
+
...(simulationOptions.oneD || {}),
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const twoDOptions: TwoDOptions = {
|
|
62
|
+
...{
|
|
63
|
+
from: { x: oneDOptions.proton.from, y: oneDOptions.carbon.from },
|
|
64
|
+
to: { x: oneDOptions.proton.to, y: oneDOptions.carbon.to },
|
|
65
|
+
nbPoints: { x: 1024, y: 1024 },
|
|
66
|
+
},
|
|
67
|
+
...(simulationOptions.twoD || {}),
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
for (const experiment in predictions) {
|
|
71
|
+
switch (experiment) {
|
|
72
|
+
case 'carbon':
|
|
73
|
+
case 'proton': {
|
|
74
|
+
spectra.push(
|
|
75
|
+
get1DSpectrum(predictions[experiment], {
|
|
76
|
+
nbPoints: oneDOptions.nbPoints,
|
|
77
|
+
lineWidth: oneDOptions.lineWidth,
|
|
78
|
+
...oneDOptions[experiment],
|
|
79
|
+
experiment,
|
|
80
|
+
frequency: simulationOptions.frequency,
|
|
81
|
+
}),
|
|
82
|
+
);
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
case 'cosy':
|
|
86
|
+
case 'hsqc':
|
|
87
|
+
case 'hmbc': {
|
|
88
|
+
spectra.push(
|
|
89
|
+
get2DSpectrum(predictions[experiment], {
|
|
90
|
+
...twoDOptions,
|
|
91
|
+
experiment,
|
|
92
|
+
frequency: simulationOptions.frequency,
|
|
93
|
+
}),
|
|
94
|
+
);
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
default:
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
24
101
|
|
|
25
102
|
return result;
|
|
26
103
|
}
|
|
27
104
|
|
|
28
|
-
|
|
29
|
-
|
|
105
|
+
type Get2DSpectrumOptions = TwoDOptions & {
|
|
106
|
+
experiment: string;
|
|
107
|
+
frequency: number;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
function get2DSpectrum(prediction: any, options: Get2DSpectrumOptions) {
|
|
111
|
+
const { signals, zones, nuclei } = prediction;
|
|
112
|
+
const { frequency: baseFrequency, experiment } = options;
|
|
113
|
+
|
|
114
|
+
const width = get2DWidth(nuclei);
|
|
115
|
+
const frequency = calculateFrequency(nuclei, baseFrequency);
|
|
116
|
+
|
|
117
|
+
const spectrumData = signals2DToZ(signals, {
|
|
118
|
+
...options,
|
|
119
|
+
width,
|
|
120
|
+
factor: 3,
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
const spectrum = {
|
|
124
|
+
data: { ...spectrumData, noise: 0.01 },
|
|
125
|
+
info: {
|
|
126
|
+
nucleus: nuclei,
|
|
127
|
+
originFrequency: frequency,
|
|
128
|
+
baseFrequency: frequency,
|
|
129
|
+
pulseSequence: experiment,
|
|
130
|
+
experiment: '2d',
|
|
131
|
+
},
|
|
132
|
+
zones: { values: zones },
|
|
133
|
+
};
|
|
134
|
+
return spectrum;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function get2DWidth(nucleus: string[]) {
|
|
138
|
+
return nucleus[0] === nucleus[1] ? 0.02 : { x: 0.02, y: 0.2133 };
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
type Get1DSpectrumOptions = OptionsSignalsToXY & { experiment: string };
|
|
142
|
+
|
|
143
|
+
function get1DSpectrum(prediction: any, options: Get1DSpectrumOptions) {
|
|
144
|
+
const { frequency = 400, experiment } = options;
|
|
145
|
+
const { signals, nucleus } = prediction;
|
|
146
|
+
const realFrequency = calculateFrequency(nucleus, frequency) as number;
|
|
147
|
+
const { x, y } = signalsToXY(signals, {
|
|
148
|
+
...options,
|
|
149
|
+
frequency: realFrequency,
|
|
150
|
+
});
|
|
30
151
|
const spectrum = {
|
|
31
|
-
id: '',
|
|
32
152
|
// todo Array.from is temporary for the developement
|
|
33
153
|
data: { x: Array.from(x), re: Array.from(y), im: null },
|
|
34
154
|
info: {
|
|
35
155
|
nucleus: prediction.nucleus,
|
|
36
156
|
dimension: 1,
|
|
37
|
-
originFrequency:
|
|
38
|
-
baseFrequency:
|
|
157
|
+
originFrequency: realFrequency,
|
|
158
|
+
baseFrequency: realFrequency,
|
|
39
159
|
pulseSequence: 'prediction',
|
|
40
|
-
experiment
|
|
160
|
+
experiment,
|
|
41
161
|
isFt: true,
|
|
42
162
|
},
|
|
43
|
-
|
|
44
|
-
//ranges: { values: prediction.ranges },
|
|
163
|
+
ranges: { values: prediction.ranges },
|
|
45
164
|
};
|
|
46
165
|
|
|
47
166
|
return spectrum;
|
|
48
167
|
}
|
|
168
|
+
|
|
169
|
+
function calculateFrequency(
|
|
170
|
+
nucleus: string | string[],
|
|
171
|
+
frequency: number,
|
|
172
|
+
): number | string {
|
|
173
|
+
if (typeof nucleus === 'string') {
|
|
174
|
+
return getFrequency(nucleus, { nucleus: '1H', frequency });
|
|
175
|
+
} else {
|
|
176
|
+
if (nucleus[0] === nucleus[1]) {
|
|
177
|
+
return `${frequency},${frequency}`;
|
|
178
|
+
} else {
|
|
179
|
+
return `${frequency},${getFrequency(nucleus[1], {
|
|
180
|
+
nucleus: nucleus[0],
|
|
181
|
+
frequency,
|
|
182
|
+
})}`;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
package/src/ranges/rangesToXY.ts
CHANGED
|
@@ -49,13 +49,21 @@ function checkForSignals(
|
|
|
49
49
|
if (!range.signals) throw new Error('range has not signals');
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
|
+
|
|
53
|
+
const defaultFromTo = (nucleus = '') => {
|
|
54
|
+
switch (nucleus.toUpperCase()) {
|
|
55
|
+
case '13C':
|
|
56
|
+
return { from: -5, to: 206 };
|
|
57
|
+
default:
|
|
58
|
+
return { from: -0.5, to: 10.5 };
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
52
62
|
export function rangesToXY(ranges: NMRRange[], options: any = {}) {
|
|
53
63
|
checkForSignals(ranges);
|
|
54
64
|
let {
|
|
55
65
|
frequency = 400,
|
|
56
66
|
lineWidth = 1,
|
|
57
|
-
from = 0,
|
|
58
|
-
to = 10,
|
|
59
67
|
nbPoints = 16 * 1024,
|
|
60
68
|
shape = { kind: 'gaussian' },
|
|
61
69
|
} = options;
|
|
@@ -66,9 +74,10 @@ export function rangesToXY(ranges: NMRRange[], options: any = {}) {
|
|
|
66
74
|
}
|
|
67
75
|
};
|
|
68
76
|
|
|
77
|
+
const { from, to } = getFromTo(ranges, options);
|
|
69
78
|
const spectrumOptions = {
|
|
70
|
-
from,
|
|
71
79
|
to,
|
|
80
|
+
from,
|
|
72
81
|
nbPoints,
|
|
73
82
|
shape,
|
|
74
83
|
lineWidth,
|
|
@@ -139,7 +148,7 @@ function peaksOfMultiplet(delta: number, options: any) {
|
|
|
139
148
|
const {
|
|
140
149
|
frequency,
|
|
141
150
|
lineWidth,
|
|
142
|
-
intensities = [1, 2, 5, 4, 5,
|
|
151
|
+
intensities = [1, 2, 5, 4, 5, 7, 5, 4, 5, 2, 1],
|
|
143
152
|
} = options;
|
|
144
153
|
|
|
145
154
|
const lineWidthPpm = lineWidth / frequency;
|
|
@@ -182,3 +191,23 @@ function normalizeSpectrum(
|
|
|
182
191
|
}
|
|
183
192
|
}
|
|
184
193
|
}
|
|
194
|
+
|
|
195
|
+
function getFromTo(ranges: NMRRange[], options: any) {
|
|
196
|
+
const { from: defaultFrom, to: defaultTo } = defaultFromTo(options.nucleus);
|
|
197
|
+
|
|
198
|
+
let rangesFrom = Number.MAX_SAFE_INTEGER;
|
|
199
|
+
let rangesTo = Number.MIN_SAFE_INTEGER;
|
|
200
|
+
for (const range of ranges) {
|
|
201
|
+
for (const signal of range.signals || []) {
|
|
202
|
+
if (rangesFrom > signal.delta) rangesFrom = signal.delta;
|
|
203
|
+
if (rangesTo < signal.delta) rangesTo = signal.delta;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const {
|
|
208
|
+
from = Math.min(rangesFrom - 0.5, defaultFrom),
|
|
209
|
+
to = Math.max(rangesTo + 0.5, defaultTo),
|
|
210
|
+
} = options;
|
|
211
|
+
|
|
212
|
+
return { from, to };
|
|
213
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { couplingPatterns } from '../constants/couplingPatterns';
|
|
2
|
+
|
|
3
|
+
import { NMRSignal1D } from './NMRSignal1D';
|
|
4
|
+
import type { Jcoupling } from './jcoupling';
|
|
5
|
+
|
|
6
|
+
export function addDummySignals(signals: NMRSignal1D[]) {
|
|
7
|
+
let newSignals: NMRSignal1D[] = JSON.parse(JSON.stringify(signals));
|
|
8
|
+
|
|
9
|
+
signals.forEach((signal, s) => {
|
|
10
|
+
const { js: jCouplings = [], atoms: signalAssignment = [s] } = signal;
|
|
11
|
+
|
|
12
|
+
let { newCouplings, tempSignals } = checkCouplings(
|
|
13
|
+
jCouplings,
|
|
14
|
+
newSignals,
|
|
15
|
+
signalAssignment,
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
if (tempSignals.length > 0) newSignals.push(...tempSignals);
|
|
19
|
+
|
|
20
|
+
newSignals[s].js = newCouplings;
|
|
21
|
+
newSignals[s].atoms = signalAssignment;
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
return newSignals.sort((a, b) => a.delta - b.delta);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function checkCouplings(
|
|
28
|
+
jCouplings: Jcoupling[],
|
|
29
|
+
signals: NMRSignal1D[],
|
|
30
|
+
signalAssignment: number[],
|
|
31
|
+
) {
|
|
32
|
+
let newSignalAssignment = signals.length - 1;
|
|
33
|
+
let tempSignals: NMRSignal1D[] = [];
|
|
34
|
+
const newCouplings = jCouplings.reduce<Jcoupling[]>(
|
|
35
|
+
(newCouplings, jCoupling) => {
|
|
36
|
+
const { atoms = [], multiplicity, coupling } = jCoupling;
|
|
37
|
+
if (atoms.length === 0) {
|
|
38
|
+
if (coupling && multiplicity) {
|
|
39
|
+
let tempCouplings: Jcoupling[] = [];
|
|
40
|
+
const nbLinks = couplingPatterns.indexOf(multiplicity);
|
|
41
|
+
for (let i = 0; i < nbLinks; i++) {
|
|
42
|
+
newSignalAssignment++;
|
|
43
|
+
tempCouplings.push({
|
|
44
|
+
coupling,
|
|
45
|
+
atoms: [newSignalAssignment],
|
|
46
|
+
});
|
|
47
|
+
tempSignals.push(
|
|
48
|
+
formatSignal(coupling, [newSignalAssignment], signalAssignment),
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
} else {
|
|
52
|
+
newCouplings.push(jCoupling);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return newCouplings;
|
|
56
|
+
},
|
|
57
|
+
[],
|
|
58
|
+
);
|
|
59
|
+
return { newCouplings, tempSignals };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function formatSignal(
|
|
63
|
+
coupling: number,
|
|
64
|
+
newSignalAssignment: number[],
|
|
65
|
+
signalAssignment: number[],
|
|
66
|
+
) {
|
|
67
|
+
return {
|
|
68
|
+
delta: 100000,
|
|
69
|
+
atoms: newSignalAssignment,
|
|
70
|
+
js: [
|
|
71
|
+
{
|
|
72
|
+
coupling,
|
|
73
|
+
atoms: signalAssignment,
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
};
|
|
77
|
+
}
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import { couplingPatterns } from '../constants/couplingPatterns';
|
|
2
|
-
|
|
3
1
|
import type { NMRSignal1D } from './NMRSignal1D';
|
|
4
|
-
import
|
|
2
|
+
import { addDummySignals } from './addDummySignals';
|
|
5
3
|
import { OptionsSignalsToXY, signalsToXY } from './signalsToXY';
|
|
6
4
|
|
|
7
5
|
/**
|
|
@@ -13,74 +11,6 @@ export function hackSignalsToXY(
|
|
|
13
11
|
signals: NMRSignal1D[],
|
|
14
12
|
options: OptionsSignalsToXY = {},
|
|
15
13
|
) {
|
|
16
|
-
let newSignals =
|
|
17
|
-
|
|
18
|
-
signals.forEach((signal, s) => {
|
|
19
|
-
const { js: jCouplings = [], atoms: signalAssignment = [s] } = signal;
|
|
20
|
-
|
|
21
|
-
let { newCouplings, tempSignals } = checkCouplings(
|
|
22
|
-
jCouplings,
|
|
23
|
-
newSignals,
|
|
24
|
-
signalAssignment,
|
|
25
|
-
);
|
|
26
|
-
|
|
27
|
-
if (tempSignals.length > 0) newSignals.push(...tempSignals);
|
|
28
|
-
|
|
29
|
-
newSignals[s].js = newCouplings;
|
|
30
|
-
newSignals[s].atoms = signalAssignment;
|
|
31
|
-
});
|
|
32
|
-
|
|
14
|
+
let newSignals = addDummySignals(signals);
|
|
33
15
|
return signalsToXY(newSignals, options);
|
|
34
16
|
}
|
|
35
|
-
|
|
36
|
-
function checkCouplings(
|
|
37
|
-
jCouplings: Jcoupling[],
|
|
38
|
-
signals: NMRSignal1D[],
|
|
39
|
-
signalAssignment: number[],
|
|
40
|
-
) {
|
|
41
|
-
let newSignalAssignment = signals.length - 1;
|
|
42
|
-
let tempSignals: NMRSignal1D[] = [];
|
|
43
|
-
const newCouplings = jCouplings.reduce<Jcoupling[]>(
|
|
44
|
-
(newCouplings, jCoupling) => {
|
|
45
|
-
const { atoms = [], multiplicity, coupling } = jCoupling;
|
|
46
|
-
if (atoms.length === 0) {
|
|
47
|
-
if (coupling && multiplicity) {
|
|
48
|
-
let tempCouplings: Jcoupling[] = [];
|
|
49
|
-
const nbLinks = couplingPatterns.indexOf(multiplicity);
|
|
50
|
-
for (let i = 0; i < nbLinks; i++) {
|
|
51
|
-
newSignalAssignment++;
|
|
52
|
-
tempCouplings.push({
|
|
53
|
-
coupling,
|
|
54
|
-
atoms: [newSignalAssignment],
|
|
55
|
-
});
|
|
56
|
-
tempSignals.push(
|
|
57
|
-
formatSignal(coupling, [newSignalAssignment], signalAssignment),
|
|
58
|
-
);
|
|
59
|
-
}
|
|
60
|
-
} else {
|
|
61
|
-
newCouplings.push(jCoupling);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
return newCouplings;
|
|
65
|
-
},
|
|
66
|
-
[],
|
|
67
|
-
);
|
|
68
|
-
return { newCouplings, tempSignals };
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function formatSignal(
|
|
72
|
-
coupling: number,
|
|
73
|
-
newSignalAssignment: number[],
|
|
74
|
-
signalAssignment: number[],
|
|
75
|
-
) {
|
|
76
|
-
return {
|
|
77
|
-
delta: 100000,
|
|
78
|
-
atoms: newSignalAssignment,
|
|
79
|
-
js: [
|
|
80
|
-
{
|
|
81
|
-
coupling,
|
|
82
|
-
atoms: signalAssignment,
|
|
83
|
-
},
|
|
84
|
-
],
|
|
85
|
-
};
|
|
86
|
-
}
|