nmr-processing 8.0.0 → 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.
Files changed (57) hide show
  1. package/lib/index.d.ts +1 -1
  2. package/lib/index.js +1 -1
  3. package/lib/index.js.map +1 -1
  4. package/lib/peaks/NMRPeak1D.d.ts +0 -1
  5. package/lib/peaks/solventSuppression.d.ts +3 -0
  6. package/lib/peaks/solventSuppression.js +147 -0
  7. package/lib/peaks/solventSuppression.js.map +1 -0
  8. package/lib/peaks/util/jAnalyzer.d.ts +1 -1
  9. package/lib/ranges/rangesToXY.js +27 -3
  10. package/lib/ranges/rangesToXY.js.map +1 -1
  11. package/lib/signals/addDummySignals.d.ts +2 -0
  12. package/lib/signals/addDummySignals.js +56 -0
  13. package/lib/signals/addDummySignals.js.map +1 -0
  14. package/lib/signals/hackSignalsToXY.js +2 -48
  15. package/lib/signals/hackSignalsToXY.js.map +1 -1
  16. package/lib/signals/simulation/simulate1D.d.ts +1 -39
  17. package/lib/signals/simulation/simulate1D.js +13 -240
  18. package/lib/signals/simulation/simulate1D.js.map +1 -1
  19. package/lib/signals/simulation/simulateXYPeaks.d.ts +47 -0
  20. package/lib/signals/simulation/simulateXYPeaks.js +246 -0
  21. package/lib/signals/simulation/simulateXYPeaks.js.map +1 -0
  22. package/lib/utilities/getFrequency.d.ts +1 -1
  23. package/lib/utilities/getFrequency.js +4 -4
  24. package/lib/utilities/getFrequency.js.map +1 -1
  25. package/lib-esm/index.js +1 -1
  26. package/lib-esm/index.js.map +1 -1
  27. package/lib-esm/peaks/solventSuppression.js +140 -0
  28. package/lib-esm/peaks/solventSuppression.js.map +1 -0
  29. package/lib-esm/ranges/rangesToXY.js +27 -3
  30. package/lib-esm/ranges/rangesToXY.js.map +1 -1
  31. package/lib-esm/signals/addDummySignals.js +52 -0
  32. package/lib-esm/signals/addDummySignals.js.map +1 -0
  33. package/lib-esm/signals/hackSignalsToXY.js +2 -48
  34. package/lib-esm/signals/hackSignalsToXY.js.map +1 -1
  35. package/lib-esm/signals/simulation/simulate1D.js +14 -238
  36. package/lib-esm/signals/simulation/simulate1D.js.map +1 -1
  37. package/lib-esm/signals/simulation/simulateXYPeaks.js +239 -0
  38. package/lib-esm/signals/simulation/simulateXYPeaks.js.map +1 -0
  39. package/lib-esm/utilities/getFrequency.js +1 -1
  40. package/lib-esm/utilities/getFrequency.js.map +1 -1
  41. package/package.json +4 -2
  42. package/src/index.ts +1 -1
  43. package/src/peaks/NMRPeak1D.ts +0 -1
  44. package/src/peaks/solventSuppression.ts +186 -0
  45. package/src/peaks/util/jAnalyzer.ts +1 -1
  46. package/src/ranges/rangesToXY.ts +33 -4
  47. package/src/signals/addDummySignals.ts +77 -0
  48. package/src/signals/hackSignalsToXY.ts +2 -72
  49. package/src/signals/simulation/simulate1D.ts +14 -319
  50. package/src/signals/simulation/simulateXYPeaks.ts +332 -0
  51. package/src/utilities/getFrequency.ts +1 -1
  52. package/lib/constants/gyromagneticRatio.d.ts +0 -6
  53. package/lib/constants/gyromagneticRatio.js +0 -26
  54. package/lib/constants/gyromagneticRatio.js.map +0 -1
  55. package/lib-esm/constants/gyromagneticRatio.js +0 -23
  56. package/lib-esm/constants/gyromagneticRatio.js.map +0 -1
  57. package/src/constants/gyromagneticRatio.ts +0 -49
@@ -0,0 +1,239 @@
1
+ import binarySearch from 'binary-search';
2
+ import { Matrix, EVD } from 'ml-matrix';
3
+ import { SparseMatrix } from 'ml-sparse-matrix';
4
+ import getPauliMatrix from './getPauliMatrix';
5
+ const smallValue = 1e-2;
6
+ export function simulateXYPeaks(
7
+ /**
8
+ * The SpinSystem object to be simulated
9
+ */
10
+ spinSystem, options = {}) {
11
+ let { lineWidth = 1, maxClusterSize = 8, frequency: frequencyMHz = 400, } = options;
12
+ const chemicalShifts = spinSystem.chemicalShifts.slice();
13
+ for (let i = 0; i < chemicalShifts.length; i++) {
14
+ chemicalShifts[i] = chemicalShifts[i] * frequencyMHz;
15
+ }
16
+ const multiplicity = spinSystem.multiplicity;
17
+ const xyPeaks = [];
18
+ for (const cluster of spinSystem.clusters) {
19
+ let clusterFake = cluster.map((cluster) => cluster < 0 ? -cluster - 1 : cluster);
20
+ let weight = 1;
21
+ let sumI = 0;
22
+ let frequencies = [];
23
+ let intensities = [];
24
+ if (cluster.length > maxClusterSize) {
25
+ // This is a single spin, but the cluster exceeds the maxClusterSize criteria
26
+ // we use the simple multiplicity algorithm
27
+ // Add the central peak. It will be split with every single J coupling.
28
+ let index = 0;
29
+ while (cluster[index++] < 0)
30
+ ;
31
+ index = cluster[index - 1];
32
+ frequencies.push(-chemicalShifts[index]);
33
+ for (let i = 0; i < cluster.length; i++) {
34
+ if (cluster[i] < 0) {
35
+ let jc = spinSystem.couplingConstants.get(index, clusterFake[i]) / 2;
36
+ let currentSize = frequencies.length;
37
+ for (let j = 0; j < currentSize; j++) {
38
+ frequencies.push(frequencies[j] + jc);
39
+ frequencies[j] -= jc;
40
+ }
41
+ }
42
+ }
43
+ frequencies.sort((a, b) => a - b);
44
+ sumI = frequencies.length;
45
+ weight = 1;
46
+ for (let i = 0; i < sumI; i++) {
47
+ intensities.push(1);
48
+ }
49
+ }
50
+ else {
51
+ const hamiltonian = getHamiltonian(chemicalShifts, spinSystem.couplingConstants, multiplicity, spinSystem.connectivity, clusterFake);
52
+ const hamSize = hamiltonian.rows;
53
+ // TODO: add support for sparse matrix in matrix types.
54
+ // @ts-expect-error sparse matrix not supported
55
+ const evd = new EVD(hamiltonian);
56
+ const V = evd.eigenvectorMatrix;
57
+ const diagB = evd.realEigenvalues;
58
+ const assignmentMatrix = new SparseMatrix(hamSize, hamSize);
59
+ const multLen = cluster.length;
60
+ weight = 0;
61
+ for (let n = 0; n < multLen; n++) {
62
+ const L = getPauliMatrix(multiplicity[clusterFake[n]]);
63
+ let temp = 1;
64
+ for (let j = 0; j < n; j++) {
65
+ temp *= multiplicity[clusterFake[j]];
66
+ }
67
+ const A = SparseMatrix.eye(temp);
68
+ temp = 1;
69
+ for (let j = n + 1; j < multLen; j++) {
70
+ temp *= multiplicity[clusterFake[j]];
71
+ }
72
+ const B = SparseMatrix.eye(temp);
73
+ const tempMat = A.kroneckerProduct(L.m).kroneckerProduct(B);
74
+ if (cluster[n] >= 0) {
75
+ assignmentMatrix.add(tempMat.mul(cluster[n] + 1));
76
+ weight++;
77
+ }
78
+ else {
79
+ assignmentMatrix.add(tempMat.mul(cluster[n]));
80
+ }
81
+ }
82
+ let rhoip = Matrix.zeros(hamSize, hamSize);
83
+ assignmentMatrix.forEachNonZero((i, j, v) => {
84
+ if (v > 0) {
85
+ for (let k = 0; k < V.columns; k++) {
86
+ let element = V.get(j, k);
87
+ if (element !== 0) {
88
+ rhoip.set(i, k, rhoip.get(i, k) + element);
89
+ }
90
+ }
91
+ }
92
+ return v;
93
+ });
94
+ let rhoip2 = rhoip.clone();
95
+ assignmentMatrix.forEachNonZero((i, j, v) => {
96
+ if (v < 0) {
97
+ for (let k = 0; k < V.columns; k++) {
98
+ let element = V.get(j, k);
99
+ if (element !== 0) {
100
+ rhoip2.set(i, k, rhoip2.get(i, k) + element);
101
+ }
102
+ }
103
+ }
104
+ return v;
105
+ });
106
+ const tV = V.transpose();
107
+ rhoip = tV.mmul(rhoip);
108
+ const sparseRhoip = new SparseMatrix(rhoip.to2DArray(), {
109
+ threshold: smallValue,
110
+ });
111
+ triuTimesAbs(sparseRhoip, smallValue);
112
+ rhoip2 = tV.mmul(rhoip2);
113
+ const sparseRhoip2 = new SparseMatrix(rhoip2.to2DArray(), {
114
+ threshold: smallValue,
115
+ });
116
+ sparseRhoip2.forEachNonZero((i, j, v) => {
117
+ return v;
118
+ });
119
+ triuTimesAbs(sparseRhoip2, smallValue);
120
+ sparseRhoip2.forEachNonZero((i, j, v) => {
121
+ let val = rhoip.get(i, j);
122
+ val = Math.min(Math.abs(val), Math.abs(v));
123
+ val *= val;
124
+ sumI += val;
125
+ let valFreq = diagB[i] - diagB[j];
126
+ let insertIn = binarySearch(frequencies, valFreq, (a, b) => a - b);
127
+ if (insertIn < 0) {
128
+ frequencies.splice(-1 - insertIn, 0, valFreq);
129
+ intensities.splice(-1 - insertIn, 0, val);
130
+ }
131
+ else {
132
+ intensities[insertIn] += val;
133
+ }
134
+ });
135
+ }
136
+ const numFreq = frequencies.length;
137
+ if (numFreq > 0) {
138
+ weight /= sumI;
139
+ const diff = lineWidth / 64;
140
+ let valFreq = frequencies[0];
141
+ let inte = intensities[0];
142
+ let count = 1;
143
+ for (let i = 1; i < numFreq; i++) {
144
+ if (Math.abs(frequencies[i] - valFreq / count) < diff) {
145
+ inte += intensities[i];
146
+ valFreq += frequencies[i];
147
+ count++;
148
+ }
149
+ else {
150
+ xyPeaks.push({
151
+ x: -valFreq / count / frequencyMHz,
152
+ y: inte * weight,
153
+ });
154
+ valFreq = frequencies[i];
155
+ inte = intensities[i];
156
+ count = 1;
157
+ }
158
+ }
159
+ xyPeaks.push({
160
+ x: -valFreq / count / frequencyMHz,
161
+ y: inte * weight,
162
+ });
163
+ }
164
+ }
165
+ return xyPeaks;
166
+ }
167
+ function triuTimesAbs(A, val) {
168
+ A.forEachNonZero((i, j, v) => {
169
+ if (i > j)
170
+ return 0;
171
+ if (Math.abs(v) <= val)
172
+ return 0;
173
+ return v;
174
+ });
175
+ }
176
+ /**
177
+ * Create a hamiltonian matrix for the given spinsystem
178
+ * @param {Array} chemicalShifts - An array containing the chemical shift in Hz
179
+ * @param {Array} couplingConstants - An array containing the coupling constants in Hz
180
+ * @param {Array} multiplicity - An array specifiying the multiplicities of each scalar coupling
181
+ * @param {Array} conMatrix - A one step connectivity matrix for the given spin system
182
+ * @param {Array} cluster - An binary array specifiying the spins to be considered for this hamiltonial
183
+ * @return {object}
184
+ */
185
+ function getHamiltonian(chemicalShifts, couplingConstants, multiplicity, conMatrix, cluster) {
186
+ let hamSize = 1;
187
+ for (const element of cluster) {
188
+ hamSize *= multiplicity[element];
189
+ }
190
+ const clusterHam = new SparseMatrix(hamSize, hamSize);
191
+ for (let pos = 0; pos < cluster.length; pos++) {
192
+ let n = cluster[pos];
193
+ const L = getPauliMatrix(multiplicity[n]);
194
+ let A1, B1;
195
+ let temp = 1;
196
+ for (let i = 0; i < pos; i++) {
197
+ temp *= multiplicity[cluster[i]];
198
+ }
199
+ A1 = SparseMatrix.eye(temp);
200
+ temp = 1;
201
+ for (let i = pos + 1; i < cluster.length; i++) {
202
+ temp *= multiplicity[cluster[i]];
203
+ }
204
+ B1 = SparseMatrix.eye(temp);
205
+ const alpha = chemicalShifts[n];
206
+ const kronProd = A1.kroneckerProduct(L.z).kroneckerProduct(B1);
207
+ clusterHam.add(kronProd.mul(alpha));
208
+ for (let pos2 = 0; pos2 < cluster.length; pos2++) {
209
+ const k = cluster[pos2];
210
+ if (conMatrix.get(n, k) === 1) {
211
+ const S = getPauliMatrix(multiplicity[k]);
212
+ let A2, B2;
213
+ let temp = 1;
214
+ for (let i = 0; i < pos2; i++) {
215
+ temp *= multiplicity[cluster[i]];
216
+ }
217
+ A2 = SparseMatrix.eye(temp);
218
+ temp = 1;
219
+ for (let i = pos2 + 1; i < cluster.length; i++) {
220
+ temp *= multiplicity[cluster[i]];
221
+ }
222
+ B2 = SparseMatrix.eye(temp);
223
+ const kron1 = A1.kroneckerProduct(L.x)
224
+ .kroneckerProduct(B1)
225
+ .mmul(A2.kroneckerProduct(S.x).kroneckerProduct(B2));
226
+ kron1.add(A1.kroneckerProduct(L.y)
227
+ .kroneckerProduct(B1)
228
+ .mul(-1)
229
+ .mmul(A2.kroneckerProduct(S.y).kroneckerProduct(B2)));
230
+ kron1.add(A1.kroneckerProduct(L.z)
231
+ .kroneckerProduct(B1)
232
+ .mmul(A2.kroneckerProduct(S.z).kroneckerProduct(B2)));
233
+ clusterHam.add(kron1.mul(couplingConstants.get(n, k) / 2));
234
+ }
235
+ }
236
+ }
237
+ return clusterHam;
238
+ }
239
+ //# sourceMappingURL=simulateXYPeaks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"simulateXYPeaks.js","sourceRoot":"","sources":["../../../src/signals/simulation/simulateXYPeaks.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { gyromagneticRatio } from '../constants/gyromagneticRatio';
1
+ import { gyromagneticRatio } from 'gyromagnetic-ratio';
2
2
  /**
3
3
  * calculate the frequency of a nucleus with respect to a reference nucleus
4
4
  */
@@ -1 +1 @@
1
- {"version":3,"file":"getFrequency.js","sourceRoot":"","sources":["../../src/utilities/getFrequency.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAU,MAAM,gCAAgC,CAAC;AAgB3E;;GAEG;AACH,MAAM,UAAU,YAAY;AAC1B;;GAEG;AACH,OAAgB,EAChB,mBAAwC;IAExC,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,mBAAmB,CAAC;IAE/D,cAAc,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;IAEpC,OAAO,CACL,CAAC,SAAS,GAAG,iBAAiB,CAAC,OAAiB,CAAC,CAAC;QAClD,iBAAiB,CAAC,UAAoB,CAAC,CACxC,CAAC;AACJ,CAAC;AAED,SAAS,cAAc,CAAC,GAAG,OAAkB;IAC3C,IAAI,MAAM,GAAa,EAAE,CAAC;IAC1B,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE;QACvB,IAAI,CAAC,CAAC,CAAC,IAAI,iBAAiB,CAAC,EAAE;YAC7B,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;SAChB;KACF;IACD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE;QACvB,MAAM,IAAI,KAAK,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC;KAC3D;AACH,CAAC"}
1
+ {"version":3,"file":"getFrequency.js","sourceRoot":"","sources":["../../src/utilities/getFrequency.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAU,MAAM,oBAAoB,CAAC;AAgB/D;;GAEG;AACH,MAAM,UAAU,YAAY;AAC1B;;GAEG;AACH,OAAgB,EAChB,mBAAwC;IAExC,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,mBAAmB,CAAC;IAE/D,cAAc,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;IAEpC,OAAO,CACL,CAAC,SAAS,GAAG,iBAAiB,CAAC,OAAiB,CAAC,CAAC;QAClD,iBAAiB,CAAC,UAAoB,CAAC,CACxC,CAAC;AACJ,CAAC;AAED,SAAS,cAAc,CAAC,GAAG,OAAkB;IAC3C,IAAI,MAAM,GAAa,EAAE,CAAC;IAC1B,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE;QACvB,IAAI,CAAC,CAAC,CAAC,IAAI,iBAAiB,CAAC,EAAE;YAC7B,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;SAChB;KACF;IACD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE;QACvB,MAAM,IAAI,KAAK,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC;KAC3D;AACH,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nmr-processing",
3
- "version": "8.0.0",
3
+ "version": "8.1.0",
4
4
  "description": "Pure functions allowing to process NMR spectra.",
5
5
  "main": "./lib/index.js",
6
6
  "module": "./lib-esm/index.js",
@@ -58,7 +58,9 @@
58
58
  "binary-search": "^1.3.6",
59
59
  "cross-fetch": "^3.1.5",
60
60
  "form-data": "^4.0.0",
61
+ "gyromagnetic-ratio": "^1.0.0",
61
62
  "is-any-array": "^2.0.0",
63
+ "linear-sum-assignment": "^1.0.3",
62
64
  "ml-array-mean": "^1.1.6",
63
65
  "ml-array-rescale": "^1.3.7",
64
66
  "ml-array-sequential-fill": "^1.1.8",
@@ -76,6 +78,6 @@
76
78
  "ml-tree-set": "^0.1.1",
77
79
  "nmr-correlation": "^2.2.5",
78
80
  "openchemlib-utils": "^1.11.0",
79
- "spectrum-generator": "^8.0.1"
81
+ "spectrum-generator": "^8.0.2"
80
82
  }
81
83
  }
package/src/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- export * from './constants/gyromagneticRatio';
1
+ export * from 'gyromagnetic-ratio';
2
2
  export * from './constants/impurities';
3
3
  export * from './constants/couplingPatterns';
4
4
 
@@ -3,6 +3,5 @@ import type { Shape1D } from 'ml-peak-shape-generator';
3
3
 
4
4
  export interface NMRPeak1D extends PeakXYWidth {
5
5
  kind?: string;
6
- fwhm?: number;
7
6
  shape?: Shape1D;
8
7
  }
@@ -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
+ }
@@ -29,7 +29,7 @@ interface IntergralData {
29
29
  to: number;
30
30
  }
31
31
 
32
- export type Peak1DIntern = Omit<NMRPeak1D, 'y' | 'shape' | 'fwhm'> & {
32
+ export type Peak1DIntern = Omit<NMRPeak1D, 'y' | 'shape'> & {
33
33
  intensity: number;
34
34
  };
35
35
 
@@ -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, 3, 4, 2, 1],
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
+ }