Comparing version 0.8.0 to 0.9.1
{ | ||
"name": "mf-finder", | ||
"version": "0.8.0", | ||
"version": "0.9.1", | ||
"description": "Find a molecular formula from a monoisotopic mass", | ||
@@ -21,8 +21,8 @@ "main": "src/index.js", | ||
"dependencies": { | ||
"atom-sorter": "^0.8.0", | ||
"chemical-elements": "^0.7.0", | ||
"mf-parser": "^0.8.0", | ||
"mf-utilities": "^0.8.0", | ||
"atom-sorter": "^0.9.1", | ||
"chemical-elements": "^0.9.1", | ||
"mf-parser": "^0.9.1", | ||
"mf-utilities": "^0.9.1", | ||
"molecular-formula": "^1.1.0" | ||
} | ||
} |
@@ -8,10 +8,17 @@ 'use strict'; | ||
let possibilities = preprocessRanges([ | ||
{mf: 'C', min: 0, max: 2}, {mf: 'H+', min: 0, max: 2}, | ||
{mf: 'O', min: 0, max: 0}, {mf: 'Cl', min: 0, max: 3}, | ||
{mf: 'Me', min: 0, max: 1}, {mf: 'Ca++', min: 0, max: 1} | ||
{ mf: 'C', min: 0, max: 2 }, | ||
{ mf: 'H+', min: 0, max: 2 }, | ||
{ mf: 'O', min: 0, max: 0 }, | ||
{ mf: 'Cl', min: 0, max: 3 }, | ||
{ mf: 'Me', min: 0, max: 1 }, | ||
{ mf: 'Ca++', min: 0, max: 1 } | ||
]); | ||
expect(Array.isArray(possibilities)).toBeTruthy(); | ||
expect(Array.isArray(possibilities)).toBe(true); | ||
expect(possibilities).toHaveLength(5); | ||
expect(possibilities[1]) | ||
.toMatchObject({mf: 'H+', isGroup: true, charge: 1, unsaturation: -2}); | ||
expect(possibilities[1]).toMatchObject({ | ||
mf: 'H+', | ||
isGroup: true, | ||
charge: 1, | ||
unsaturation: -2 | ||
}); | ||
}); | ||
@@ -21,7 +28,9 @@ | ||
let possibilities = preprocessRanges([ | ||
{mf: 'H', min: 0, max: 2}, {mf: 'H+', min: -2, max: 2}, | ||
{mf: 'Cl(-)', min: 0, max: 2}, {mf: '(C-1H)2', min: 0, max: 2} | ||
{ mf: 'H', min: 0, max: 2 }, | ||
{ mf: 'H+', min: -2, max: 2 }, | ||
{ mf: 'Cl(-)', min: 0, max: 2 }, | ||
{ mf: '(C-1H)2', min: 0, max: 2 } | ||
]); | ||
expect(Array.isArray(possibilities)).toBeTruthy(); | ||
expect(Array.isArray(possibilities)).toBe(true); | ||
expect(possibilities[0]).toMatchObject({ | ||
@@ -37,4 +46,4 @@ minCharge: -4, | ||
let possibilities = preprocessRanges('C1-10H1-10ClBr2N0'); | ||
expect(Array.isArray(possibilities)).toBeTruthy(); | ||
expect(possibilities.length).toBe(4); | ||
expect(Array.isArray(possibilities)).toBe(true); | ||
expect(possibilities).toHaveLength(4); | ||
expect(possibilities[0]).toMatchObject({ | ||
@@ -41,0 +50,0 @@ minCharge: 0, |
@@ -7,108 +7,116 @@ 'use strict'; | ||
describe('TargetMassCache', () => { | ||
it('the result with one atom', () => { | ||
let possibilities = preprocessRanges([{ mf: 'C', min: 0, max: 2 }]); | ||
let cache = new TargetMassCache(100, possibilities, { allowNeutral: true }); | ||
expect(cache.minCharge).toBe(0); | ||
expect(cache.maxCharge).toBe(0); | ||
expect(cache.getMinMass(0)).toBe(99.99); | ||
expect(cache.getMaxMass(0)).toBe(100.01); | ||
}); | ||
it('the result with one atom', () => { | ||
let possibilities = preprocessRanges( | ||
[ | ||
{ mf: 'C', min: 0, max: 2 }, | ||
] | ||
); | ||
let cache = new TargetMassCache(100, possibilities, { allowNeutral: true }); | ||
expect(cache.minCharge).toBe(0); | ||
expect(cache.maxCharge).toBe(0); | ||
expect(cache.getMinMass(0)).toBe(99.99); | ||
expect(cache.getMaxMass(0)).toBe(100.01); | ||
it('check the result with charge', () => { | ||
let possibilities = preprocessRanges([ | ||
{ mf: 'C', min: 0, max: 2 }, | ||
{ mf: 'H+', min: 0, max: 2 }, | ||
{ mf: 'O', min: 0, max: 0 } | ||
]); | ||
let cache = new TargetMassCache(100, possibilities); | ||
expect(cache).toMatchObject({ | ||
minCharge: 0, | ||
maxCharge: 2 | ||
}); | ||
}); | ||
it('check the result with charge', () => { | ||
let possibilities = preprocessRanges( | ||
[ | ||
{ mf: 'C', min: 0, max: 2 }, | ||
{ mf: 'H+', min: 0, max: 2 }, | ||
{ mf: 'O', min: 0, max: 0 }, | ||
] | ||
); | ||
let cache = new TargetMassCache(100, possibilities); | ||
expect(cache).toMatchObject({ | ||
minCharge: 0, | ||
maxCharge: 2, | ||
}); | ||
it('check the result with min, max charge', () => { | ||
let possibilities = preprocessRanges([{ mf: 'C+', min: -10, max: 5 }]); | ||
let cache = new TargetMassCache(100, possibilities, { | ||
minCharge: -1, | ||
maxCharge: 2 | ||
}); | ||
expect(cache.minCharge).toBe(-1); | ||
expect(cache.maxCharge).toBe(2); | ||
}); | ||
it('check the result with min, max charge', () => { | ||
let possibilities = preprocessRanges( | ||
[ | ||
{ mf: 'C+', min: -10, max: 5 }, | ||
] | ||
); | ||
let cache = new TargetMassCache(100, possibilities, { minCharge: -1, maxCharge: 2 }); | ||
expect(cache.minCharge).toBe(-1); | ||
expect(cache.maxCharge).toBe(2); | ||
it('check the result with neutral', () => { | ||
let possibilities = preprocessRanges([{ mf: 'C', min: 0, max: 2 }]); | ||
let cache = new TargetMassCache(100, possibilities, { | ||
minCharge: -1, | ||
maxCharge: 2, | ||
precision: 1000, | ||
allowNeutral: true | ||
}); | ||
it('check the result with neutral', () => { | ||
let possibilities = preprocessRanges( | ||
[ | ||
{ mf: 'C', min: 0, max: 2 }, | ||
] | ||
); | ||
let cache = new TargetMassCache(100, possibilities, { minCharge: -1, maxCharge: 2, precision: 1000, allowNeutral: true }); | ||
expect(cache).toEqual({ | ||
minCharge: 0, | ||
maxCharge: 0, | ||
data: [{ charge: 0, minMass: 99.9, maxMass: 100.1 }] | ||
}); | ||
expect(cache).toMatchObject({ | ||
minCharge: 0, | ||
maxCharge: 0, | ||
data: [{ charge: 0, minMass: 99.9, maxMass: 100.1 }] | ||
}); | ||
}); | ||
it('check the result with simple charge', () => { | ||
let possibilities = preprocessRanges( | ||
[ | ||
{ mf: 'C+', min: 0, max: 2 }, | ||
] | ||
); | ||
let cache = new TargetMassCache(100, possibilities, { minCharge: -1, maxCharge: 2, precision: 1000 }); | ||
expect(cache).toEqual({ | ||
minCharge: 0, | ||
maxCharge: 2, | ||
data: [ | ||
{ charge: 0, minMass: Number.MAX_SAFE_INTEGER, maxMass: Number.MIN_SAFE_INTEGER }, | ||
{ charge: 1, minMass: 99.90054857990907, maxMass: 100.10054857990906 }, | ||
{ charge: 2, minMass: 199.80109715981814, maxMass: 200.20109715981812 } | ||
] | ||
}); | ||
it('check the result with simple charge', () => { | ||
let possibilities = preprocessRanges([{ mf: 'C+', min: 0, max: 2 }]); | ||
let cache = new TargetMassCache(100, possibilities, { | ||
minCharge: -1, | ||
maxCharge: 2, | ||
precision: 1000 | ||
}); | ||
expect(cache).toMatchObject({ | ||
minCharge: 0, | ||
maxCharge: 2, | ||
data: [ | ||
{ | ||
charge: 0, | ||
minMass: Number.MAX_SAFE_INTEGER, | ||
maxMass: Number.MIN_SAFE_INTEGER | ||
}, | ||
{ charge: 1, minMass: 99.90054857990907, maxMass: 100.10054857990906 }, | ||
{ charge: 2, minMass: 199.80109715981814, maxMass: 200.20109715981812 } | ||
] | ||
}); | ||
}); | ||
it('check the result positive and negative charge', () => { | ||
let possibilities = preprocessRanges( | ||
[ | ||
{ mf: 'C+', min: -1, max: 1 }, | ||
] | ||
); | ||
let cache = new TargetMassCache(100, possibilities, { minCharge: -1, maxCharge: 1, allowNeutral: false, precision: 1e6 }); | ||
expect(cache).toEqual({ | ||
minCharge: -1, | ||
maxCharge: 1, | ||
data: [ | ||
{ charge: -1, minMass: -0.00054857990907, maxMass: 199.99945142009094 }, | ||
{ charge: 0, minMass: Number.MAX_SAFE_INTEGER, maxMass: Number.MIN_SAFE_INTEGER }, | ||
{ charge: 1, minMass: 0.00054857990907, maxMass: 200.00054857990906 } | ||
] | ||
}); | ||
it('check the result positive and negative charge', () => { | ||
let possibilities = preprocessRanges([{ mf: 'C+', min: -1, max: 1 }]); | ||
let cache = new TargetMassCache(100, possibilities, { | ||
minCharge: -1, | ||
maxCharge: 1, | ||
allowNeutral: false, | ||
precision: 1e6 | ||
}); | ||
expect(cache).toMatchObject({ | ||
minCharge: -1, | ||
maxCharge: 1, | ||
data: [ | ||
{ charge: -1, minMass: -0.00054857990907, maxMass: 199.99945142009094 }, | ||
{ | ||
charge: 0, | ||
minMass: Number.MAX_SAFE_INTEGER, | ||
maxMass: Number.MIN_SAFE_INTEGER | ||
}, | ||
{ charge: 1, minMass: 0.00054857990907, maxMass: 200.00054857990906 } | ||
] | ||
}); | ||
}); | ||
it('check the result with extra default charge', () => { | ||
let possibilities = preprocessRanges( | ||
[ | ||
{ mf: 'C+', min: -1, max: 1 }, | ||
] | ||
); | ||
let cache = new TargetMassCache(100, possibilities, { charge: 1, minCharge: -1, maxCharge: 1, allowNeutral: false, precision: 1e6 }); | ||
expect(cache).toEqual({ | ||
minCharge: 0, | ||
maxCharge: 1, | ||
data: [ | ||
{ charge: 0, minMass: Number.MAX_SAFE_INTEGER, maxMass: Number.MIN_SAFE_INTEGER }, | ||
{ charge: 1, minMass: 0.00054857990907, maxMass: 200.00054857990906 } | ||
] | ||
}); | ||
it('check the result with extra default charge', () => { | ||
let possibilities = preprocessRanges([{ mf: 'C+', min: -1, max: 1 }]); | ||
let cache = new TargetMassCache(100, possibilities, { | ||
charge: 1, | ||
minCharge: -1, | ||
maxCharge: 1, | ||
allowNeutral: false, | ||
precision: 1e6 | ||
}); | ||
expect(cache).toMatchObject({ | ||
minCharge: 0, | ||
maxCharge: 1, | ||
data: [ | ||
{ | ||
charge: 0, | ||
minMass: Number.MAX_SAFE_INTEGER, | ||
maxMass: Number.MIN_SAFE_INTEGER | ||
}, | ||
{ charge: 1, minMass: 0.00054857990907, maxMass: 200.00054857990906 } | ||
] | ||
}); | ||
}); | ||
}); |
471
src/index.js
'use strict'; | ||
const atomSorter = require('atom-sorter'); | ||
const preprocessRanges = require('./preprocessRanges'); | ||
const preprocessIonizations = require('mf-utilities/src/preprocessIonizations'); | ||
const getMsInfo = require('mf-utilities/src/getMsInfo'); | ||
const preprocessRanges = require('./preprocessRanges'); | ||
const TargetMassCache = require('./TargetMassCache'); | ||
@@ -14,3 +15,3 @@ | ||
* {string} [options.ionizations=''] - comma separated list of ionizations | ||
* * @return {} | ||
* @return {} | ||
*/ | ||
@@ -21,260 +22,252 @@ | ||
module.exports = function findMF(targetMass, options = {}) { | ||
const { | ||
minCharge = Number.MIN_SAFE_INTEGER, | ||
maxCharge = Number.MAX_SAFE_INTEGER, | ||
unsaturation = {}, | ||
maxIterations = 1e8, | ||
allowNeutral = true, // if there is no msem we use em ! | ||
ranges = [ | ||
{ mf: 'C', min: 0, max: 100 }, | ||
{ mf: 'H', min: 0, max: 100 }, | ||
{ mf: 'O', min: 0, max: 100 }, | ||
{ mf: 'N', min: 0, max: 100 } | ||
] | ||
} = options; | ||
const { | ||
minCharge = Number.MIN_SAFE_INTEGER, | ||
maxCharge = Number.MAX_SAFE_INTEGER, | ||
unsaturation = {}, | ||
maxIterations = 1e8, | ||
allowNeutral = true, // if there is no msem we use em ! | ||
ranges = [ | ||
{ mf: 'C', min: 0, max: 100 }, | ||
{ mf: 'H', min: 0, max: 100 }, | ||
{ mf: 'O', min: 0, max: 100 }, | ||
{ mf: 'N', min: 0, max: 100 } | ||
] | ||
} = options; | ||
let filterUnsaturation = unsaturation ? true : false; | ||
// we calculate not the real unsaturation but the one before dividing by 2 + 1 | ||
let fakeMinUnsaturation = | ||
unsaturation.min === undefined | ||
? Number.MIN_SAFE_INTEGER | ||
: (unsaturation.min - 1) * 2; | ||
let fakeMaxUnsaturation = | ||
unsaturation.max === undefined | ||
? Number.MAX_SAFE_INTEGER | ||
: (unsaturation.max - 1) * 2; | ||
let filterUnsaturation = unsaturation ? true : false; | ||
// we calculate not the real unsaturation but the one before dividing by 2 + 1 | ||
let fakeMinUnsaturation = | ||
unsaturation.min === undefined | ||
? Number.MIN_SAFE_INTEGER | ||
: (unsaturation.min - 1) * 2; | ||
let fakeMaxUnsaturation = | ||
unsaturation.max === undefined | ||
? Number.MAX_SAFE_INTEGER | ||
: (unsaturation.max - 1) * 2; | ||
let filterCharge = | ||
minCharge !== Number.MIN_SAFE_INTEGER || | ||
maxCharge !== Number.MAX_SAFE_INTEGER; | ||
let filterCharge = | ||
minCharge !== Number.MIN_SAFE_INTEGER || | ||
maxCharge !== Number.MAX_SAFE_INTEGER; | ||
let result = { | ||
mfs: [], | ||
info: { | ||
numberMFEvaluated: 0, | ||
numberResults: 0 | ||
} | ||
let result = { | ||
mfs: [], | ||
info: { | ||
numberMFEvaluated: 0, | ||
numberResults: 0 | ||
} | ||
}; | ||
let orderMapping = []; // used to sort the atoms | ||
// we need to make the processing for all the ionizations | ||
let ionizations = preprocessIonizations(options.ionizations); | ||
for (let ionization of ionizations) { | ||
let currentIonization = { | ||
currentMonoisotopicMass: ionization.em, | ||
currentCharge: ionization.charge, | ||
currentUnsaturation: 0 // we don't take into account the unsaturation of the ionization agent | ||
}; | ||
let orderMapping = []; // used to sort the atoms | ||
// if (DEBUG) console.log('new ionization', ionization.mf, ionization.em, ionization.charge); | ||
// ionization em and charge will be used to set the first atom value | ||
let possibilities = preprocessRanges(ranges); | ||
orderMapping = getOrderMapping(possibilities); | ||
// we need to make the processing for all the ionizations | ||
let ionizations = preprocessIonizations(options.ionizations); | ||
for (let ionization of ionizations) { | ||
let currentIonization = { | ||
currentMonoisotopicMass: ionization.em, | ||
currentCharge: ionization.charge, | ||
currentUnsaturation: 0 // we don't take into account the unsaturation of the ionization agent | ||
}; | ||
// if (DEBUG) console.log('new ionization', ionization.mf, ionization.em, ionization.charge); | ||
// ionization em and charge will be used to set the first atom value | ||
let possibilities = preprocessRanges(ranges); | ||
orderMapping = getOrderMapping(possibilities); | ||
if (possibilities.length === 0) return { mfs: [] }; | ||
targetMassCache = new TargetMassCache( | ||
targetMass, | ||
possibilities, | ||
Object.assign({}, options, { charge: ionization.charge }) | ||
); | ||
if (possibilities.length === 0) return { mfs: [] }; | ||
targetMassCache = new TargetMassCache( | ||
targetMass, | ||
possibilities, | ||
Object.assign({}, options, { charge: ionization.charge }) | ||
); | ||
let theEnd = false; | ||
let maxPosition = possibilities.length; | ||
let lastPosition = possibilities.length - 1; | ||
let currentPosition = 0; | ||
let currentAtom; | ||
let previousAtom; | ||
let lastPossibility = possibilities[lastPosition]; | ||
let theEnd = false; | ||
let maxPosition = possibilities.length; | ||
let lastPosition = possibilities.length - 1; | ||
let currentPosition = 0; | ||
let currentAtom; | ||
let previousAtom; | ||
let lastPossibility = possibilities[lastPosition]; | ||
initializePossibilities(possibilities, currentIonization); | ||
initializePossibilities(possibilities, currentIonization); | ||
// if (DEBUG) console.log('possibilities', possibilities.map((a) => `${a.mf + a.originalMinCount}-${a.originalMaxCount}`)); | ||
// if (DEBUG) console.log('possibilities', possibilities.map((a) => `${a.mf + a.originalMinCount}-${a.originalMaxCount}`)); | ||
let isValid = false; // designed so that the first time it is not a valid solution | ||
while (!theEnd) { | ||
if (result.info.numberMFEvaluated++ > maxIterations) { | ||
throw Error( | ||
`Iteration number is over the current maximum of: ${maxIterations}` | ||
); | ||
} | ||
if (filterUnsaturation) { | ||
let unsaturationValue = lastPossibility.currentUnsaturation; | ||
let isOdd = Math.abs(unsaturationValue % 2); | ||
if ( | ||
(unsaturation.onlyInteger && isOdd === 1) || | ||
(unsaturation.onlyNonInteger && isOdd === 0) || | ||
fakeMinUnsaturation > unsaturationValue || | ||
fakeMaxUnsaturation < unsaturationValue | ||
) { | ||
isValid = false; | ||
} | ||
} | ||
if ( | ||
filterCharge && | ||
(lastPossibility.currentCharge < minCharge || | ||
lastPossibility.currentCharge > maxCharge) | ||
) { | ||
isValid = false; | ||
} | ||
let isValid = false; // designed so that the first time it is not a valid solution | ||
while (!theEnd) { | ||
if (result.info.numberMFEvaluated++ > maxIterations) { | ||
throw Error( | ||
`Iteration number is over the current maximum of: ${maxIterations}` | ||
); | ||
} | ||
if (filterUnsaturation) { | ||
let unsaturationValue = lastPossibility.currentUnsaturation; | ||
let isOdd = Math.abs(unsaturationValue % 2); | ||
if ( | ||
(unsaturation.onlyInteger && isOdd === 1) || | ||
(unsaturation.onlyNonInteger && isOdd === 0) || | ||
fakeMinUnsaturation > unsaturationValue || | ||
fakeMaxUnsaturation < unsaturationValue | ||
) { | ||
isValid = false; | ||
} | ||
} | ||
if ( | ||
filterCharge && | ||
(lastPossibility.currentCharge < minCharge || | ||
lastPossibility.currentCharge > maxCharge) | ||
) { | ||
isValid = false; | ||
} | ||
if (isValid) { | ||
let minMass = targetMassCache.getMinMass(lastPossibility.currentCharge); | ||
let maxMass = targetMassCache.getMaxMass(lastPossibility.currentCharge); | ||
if ( | ||
lastPossibility.currentMonoisotopicMass < minMass || | ||
lastPossibility.currentMonoisotopicMass > maxMass | ||
) { | ||
isValid = false; | ||
} | ||
} | ||
if (isValid) { | ||
result.mfs.push( | ||
getResult( | ||
possibilities, | ||
targetMass, | ||
allowNeutral, | ||
ionization, | ||
orderMapping | ||
) | ||
); | ||
result.info.numberResults++; | ||
} | ||
if (isValid) { | ||
let minMass = targetMassCache.getMinMass( | ||
lastPossibility.currentCharge | ||
); | ||
let maxMass = targetMassCache.getMaxMass( | ||
lastPossibility.currentCharge | ||
); | ||
if ( | ||
lastPossibility.currentMonoisotopicMass < minMass || | ||
lastPossibility.currentMonoisotopicMass > maxMass | ||
) { | ||
isValid = false; | ||
} | ||
} | ||
if (isValid) { | ||
result.mfs.push( | ||
getResult( | ||
possibilities, | ||
targetMass, | ||
allowNeutral, | ||
ionization, | ||
orderMapping | ||
) | ||
); | ||
result.info.numberResults++; | ||
} | ||
isValid = true; | ||
// we need to setup all the arrays if possible | ||
while (currentPosition < maxPosition && currentPosition >= 0) { | ||
currentAtom = possibilities[currentPosition]; | ||
previousAtom = | ||
currentPosition === 0 | ||
? currentIonization | ||
: possibilities[currentPosition - 1]; | ||
if (currentAtom.currentCount < currentAtom.currentMaxCount) { | ||
currentAtom.currentCount++; | ||
updateCurrentAtom(currentAtom, previousAtom); | ||
if (currentPosition < lastPosition) { | ||
currentPosition++; | ||
setCurrentMinMax( | ||
possibilities[currentPosition], | ||
possibilities[currentPosition - 1] | ||
); | ||
} else { | ||
break; | ||
} | ||
} else { | ||
currentPosition--; | ||
} | ||
} | ||
isValid = true; | ||
// we need to setup all the arrays if possible | ||
while (currentPosition < maxPosition && currentPosition >= 0) { | ||
currentAtom = possibilities[currentPosition]; | ||
previousAtom = | ||
currentPosition === 0 | ||
? currentIonization | ||
: possibilities[currentPosition - 1]; | ||
if (currentAtom.currentCount < currentAtom.currentMaxCount) { | ||
currentAtom.currentCount++; | ||
updateCurrentAtom(currentAtom, previousAtom); | ||
if (currentPosition < lastPosition) { | ||
currentPosition++; | ||
setCurrentMinMax( | ||
possibilities[currentPosition], | ||
possibilities[currentPosition - 1] | ||
); | ||
} else { | ||
break; | ||
} | ||
} else { | ||
currentPosition--; | ||
} | ||
} | ||
if (currentPosition < 0) { | ||
theEnd = true; | ||
} | ||
} | ||
if (currentPosition < 0) { | ||
theEnd = true; | ||
} | ||
} | ||
} | ||
result.mfs.sort((a, b) => a.ms.ppm - b.ms.ppm); | ||
return result; | ||
result.mfs.sort((a, b) => a.ms.ppm - b.ms.ppm); | ||
return result; | ||
}; | ||
function updateCurrentAtom(currentAtom, previousAtom) { | ||
currentAtom.currentMonoisotopicMass = | ||
previousAtom.currentMonoisotopicMass + | ||
currentAtom.em * currentAtom.currentCount; | ||
currentAtom.currentCharge = | ||
previousAtom.currentCharge + | ||
currentAtom.charge * currentAtom.currentCount; | ||
currentAtom.currentUnsaturation = | ||
previousAtom.currentUnsaturation + | ||
currentAtom.unsaturation * currentAtom.currentCount; | ||
currentAtom.currentMonoisotopicMass = | ||
previousAtom.currentMonoisotopicMass + | ||
currentAtom.em * currentAtom.currentCount; | ||
currentAtom.currentCharge = | ||
previousAtom.currentCharge + currentAtom.charge * currentAtom.currentCount; | ||
currentAtom.currentUnsaturation = | ||
previousAtom.currentUnsaturation + | ||
currentAtom.unsaturation * currentAtom.currentCount; | ||
} | ||
function getResult( | ||
possibilities, | ||
targetMass, | ||
allowNeutralMolecules, | ||
ionization, | ||
orderMapping | ||
possibilities, | ||
targetMass, | ||
allowNeutralMolecules, | ||
ionization, | ||
orderMapping | ||
) { | ||
let lastPossibility = possibilities[possibilities.length - 1]; | ||
let lastPossibility = possibilities[possibilities.length - 1]; | ||
let result = { | ||
em: lastPossibility.currentMonoisotopicMass - ionization.em, | ||
unsaturation: lastPossibility.currentUnsaturation, | ||
mf: '', | ||
charge: lastPossibility.currentCharge - ionization.charge, | ||
ionization | ||
}; | ||
let result = { | ||
em: lastPossibility.currentMonoisotopicMass - ionization.em, | ||
unsaturation: lastPossibility.currentUnsaturation, | ||
mf: '', | ||
charge: lastPossibility.currentCharge - ionization.charge, | ||
ionization | ||
}; | ||
// we check that the first time we meet the ionization group it does not end | ||
// in the final result | ||
// we check that the first time we meet the ionization group it does not end | ||
// in the final result | ||
for (let i = 0; i < possibilities.length; i++) { | ||
let possibility = possibilities[orderMapping[i]]; | ||
if (possibility.currentCount !== 0) { | ||
if (possibility.isGroup) { | ||
result.mf += `(${possibility.mf})`; | ||
if (possibility.currentCount !== 1) { | ||
result.mf += possibility.currentCount; | ||
} | ||
} else { | ||
result.mf += possibility.mf; | ||
if (possibility.currentCount !== 1) { | ||
result.mf += possibility.currentCount; | ||
} | ||
} | ||
for (let i = 0; i < possibilities.length; i++) { | ||
let possibility = possibilities[orderMapping[i]]; | ||
if (possibility.currentCount !== 0) { | ||
if (possibility.isGroup) { | ||
result.mf += `(${possibility.mf})`; | ||
if (possibility.currentCount !== 1) { | ||
result.mf += possibility.currentCount; | ||
} | ||
} else { | ||
result.mf += possibility.mf; | ||
if (possibility.currentCount !== 1) { | ||
result.mf += possibility.currentCount; | ||
} | ||
} | ||
} | ||
result.unsaturation = | ||
(result.unsaturation + Math.abs(result.charge)) / 2 + 1; | ||
result.ms = getMsInfo(result, { targetMass, allowNeutralMolecules }); | ||
return result; | ||
} | ||
result.unsaturation = (result.unsaturation + Math.abs(result.charge)) / 2 + 1; | ||
result.ms = getMsInfo(result, { targetMass, allowNeutralMolecules }).ms; | ||
return result; | ||
} | ||
function setCurrentMinMax(currentAtom, previousAtom) { | ||
// the current min max can only be optimize if the charge will not change anymore | ||
if (currentAtom.innerCharge === true || currentAtom.charge !== 0) { | ||
currentAtom.currentMinCount = currentAtom.originalMinCount; | ||
currentAtom.currentMaxCount = currentAtom.originalMaxCount; | ||
currentAtom.currentCount = currentAtom.currentMinCount - 1; | ||
} else { | ||
// no more change of charge, we can optimize | ||
let currentMass = | ||
previousAtom !== undefined | ||
? previousAtom.currentMonoisotopicMass | ||
: 0; | ||
let currentCharge = | ||
previousAtom !== undefined ? previousAtom.currentCharge : 0; | ||
currentAtom.currentMinCount = Math.max( | ||
Math.floor( | ||
(targetMassCache.getMinMass(currentCharge) - | ||
currentMass - | ||
currentAtom.maxInnerMass) / | ||
currentAtom.em | ||
), | ||
currentAtom.originalMinCount | ||
); | ||
currentAtom.currentMaxCount = Math.min( | ||
Math.floor( | ||
(targetMassCache.getMaxMass(currentCharge) - | ||
currentMass - | ||
currentAtom.minInnerMass) / | ||
currentAtom.em | ||
), | ||
currentAtom.originalMaxCount | ||
); | ||
currentAtom.currentCount = currentAtom.currentMinCount - 1; | ||
} | ||
// the current min max can only be optimize if the charge will not change anymore | ||
if (currentAtom.innerCharge === true || currentAtom.charge !== 0) { | ||
currentAtom.currentMinCount = currentAtom.originalMinCount; | ||
currentAtom.currentMaxCount = currentAtom.originalMaxCount; | ||
currentAtom.currentCount = currentAtom.currentMinCount - 1; | ||
} else { | ||
// no more change of charge, we can optimize | ||
let currentMass = | ||
previousAtom !== undefined ? previousAtom.currentMonoisotopicMass : 0; | ||
let currentCharge = | ||
previousAtom !== undefined ? previousAtom.currentCharge : 0; | ||
currentAtom.currentMinCount = Math.max( | ||
Math.floor( | ||
(targetMassCache.getMinMass(currentCharge) - | ||
currentMass - | ||
currentAtom.maxInnerMass) / | ||
currentAtom.em | ||
), | ||
currentAtom.originalMinCount | ||
); | ||
currentAtom.currentMaxCount = Math.min( | ||
Math.floor( | ||
(targetMassCache.getMaxMass(currentCharge) - | ||
currentMass - | ||
currentAtom.minInnerMass) / | ||
currentAtom.em | ||
), | ||
currentAtom.originalMaxCount | ||
); | ||
currentAtom.currentCount = currentAtom.currentMinCount - 1; | ||
} | ||
} | ||
function initializePossibilities(possibilities, currentIonization) { | ||
for (let i = 0; i < possibilities.length; i++) { | ||
if (i === 0) { | ||
updateCurrentAtom(possibilities[i], currentIonization); | ||
setCurrentMinMax(possibilities[i], currentIonization); | ||
} else { | ||
updateCurrentAtom(possibilities[i], possibilities[i - 1]); | ||
} | ||
for (let i = 0; i < possibilities.length; i++) { | ||
if (i === 0) { | ||
updateCurrentAtom(possibilities[i], currentIonization); | ||
setCurrentMinMax(possibilities[i], currentIonization); | ||
} else { | ||
updateCurrentAtom(possibilities[i], possibilities[i - 1]); | ||
} | ||
} | ||
} | ||
@@ -284,17 +277,17 @@ | ||
function possibilitiesToString(possibilities) { | ||
return possibilities.map((a) => [ | ||
`mf:${a.mf}`, | ||
`current:${a.currentCount}`, | ||
`min:${a.currentMinCount}`, | ||
`max:${a.currentMaxCount}`, | ||
`charge:${a.currentCharge}` | ||
]); | ||
return possibilities.map((a) => [ | ||
`mf:${a.mf}`, | ||
`current:${a.currentCount}`, | ||
`min:${a.currentMinCount}`, | ||
`max:${a.currentMaxCount}`, | ||
`charge:${a.currentCharge}` | ||
]); | ||
} | ||
function getOrderMapping(possibilities) { | ||
let mapping = possibilities.map((p, i) => ({ atom: p.mf, index: i })); | ||
mapping.sort((a, b) => { | ||
return atomSorter(a.atom, b.atom); | ||
}); | ||
return mapping.map((a) => a.index); | ||
let mapping = possibilities.map((p, i) => ({ atom: p.mf, index: i })); | ||
mapping.sort((a, b) => { | ||
return atomSorter(a.atom, b.atom); | ||
}); | ||
return mapping.map((a) => a.index); | ||
} |
@@ -77,3 +77,3 @@ 'use strict'; | ||
possibilities = possibilities.filter( | ||
r => r.originalMinCount !== 0 || r.originalMaxCount !== 0 | ||
(r) => r.originalMinCount !== 0 || r.originalMaxCount !== 0 | ||
); | ||
@@ -80,0 +80,0 @@ // we will sort the way we analyse the data |
@@ -10,45 +10,45 @@ 'use strict'; | ||
let TargetMassCache = function TargetMassCache(targetMass, possibilities, options = {}) { | ||
const { | ||
allowNeutral = false, // msem because em in this case ! | ||
minCharge = Number.MIN_SAFE_INTEGER, | ||
maxCharge = Number.MAX_SAFE_INTEGER, | ||
charge = 0, | ||
precision = 100 | ||
} = options; | ||
if (!possibilities || possibilities.length === 0) return {}; | ||
const { | ||
allowNeutral = false, // msem because em in this case ! | ||
minCharge = Number.MIN_SAFE_INTEGER, | ||
maxCharge = Number.MAX_SAFE_INTEGER, | ||
charge = 0, | ||
precision = 100 | ||
} = options; | ||
if (!possibilities || possibilities.length === 0) return {}; | ||
let firstPossibility = possibilities[0]; | ||
let currentMinCharge = Math.max(minCharge, firstPossibility.minCharge + charge); | ||
let currentMaxCharge = Math.min(maxCharge, firstPossibility.maxCharge + charge); | ||
let firstPossibility = possibilities[0]; | ||
let currentMinCharge = Math.max(minCharge, firstPossibility.minCharge + charge); | ||
let currentMaxCharge = Math.min(maxCharge, firstPossibility.maxCharge + charge); | ||
this.minCharge = currentMinCharge; | ||
this.maxCharge = currentMaxCharge; | ||
this.minCharge = currentMinCharge; | ||
this.maxCharge = currentMaxCharge; | ||
let size = this.maxCharge - this.minCharge + 1; | ||
this.data = []; | ||
let minMass = 0; | ||
let maxMass = 0; | ||
let range = targetMass * precision / 1e6; | ||
for (let i = 0; i < size; i++) { | ||
let currentCharge = i + this.minCharge; | ||
if (currentCharge === 0) { | ||
if (allowNeutral) { | ||
minMass = targetMass - range; | ||
maxMass = targetMass + range; | ||
} else { | ||
minMass = Number.MAX_SAFE_INTEGER; | ||
maxMass = Number.MIN_SAFE_INTEGER; | ||
} | ||
} else { | ||
minMass = (targetMass - range) * Math.abs(currentCharge) + ELECTRON_MASS * currentCharge; | ||
maxMass = (targetMass + range) * Math.abs(currentCharge) + ELECTRON_MASS * currentCharge; | ||
} | ||
let size = this.maxCharge - this.minCharge + 1; | ||
this.data = []; | ||
let minMass = 0; | ||
let maxMass = 0; | ||
let range = targetMass * precision / 1e6; | ||
for (let i = 0; i < size; i++) { | ||
let currentCharge = i + this.minCharge; | ||
if (currentCharge === 0) { | ||
if (allowNeutral) { | ||
minMass = targetMass - range; | ||
maxMass = targetMass + range; | ||
} else { | ||
minMass = Number.MAX_SAFE_INTEGER; | ||
maxMass = Number.MIN_SAFE_INTEGER; | ||
} | ||
} else { | ||
minMass = (targetMass - range) * Math.abs(currentCharge) + ELECTRON_MASS * currentCharge; | ||
maxMass = (targetMass + range) * Math.abs(currentCharge) + ELECTRON_MASS * currentCharge; | ||
} | ||
this.data.push({ | ||
charge: currentCharge, | ||
minMass, | ||
maxMass | ||
}); | ||
} | ||
this.data.push({ | ||
charge: currentCharge, | ||
minMass, | ||
maxMass | ||
}); | ||
} | ||
}; | ||
@@ -59,7 +59,7 @@ | ||
TargetMassCache.prototype.getMinMass = function (charge) { | ||
return (this.data[charge - this.minCharge]) ? this.data[charge - this.minCharge].minMass : Number.MAX_SAFE_INTEGER; | ||
return (this.data[charge - this.minCharge]) ? this.data[charge - this.minCharge].minMass : Number.MAX_SAFE_INTEGER; | ||
}; | ||
TargetMassCache.prototype.getMaxMass = function (charge) { | ||
return (this.data[charge - this.minCharge]) ? this.data[charge - this.minCharge].maxMass : Number.MIN_SAFE_INTEGER; | ||
return (this.data[charge - this.minCharge]) ? this.data[charge - this.minCharge].maxMass : Number.MIN_SAFE_INTEGER; | ||
}; |
904
31852
+ Addedatom-sorter@0.9.1(transitive)
+ Addedchemical-elements@0.9.1(transitive)
+ Addedchemical-groups@0.9.1(transitive)
+ Addedmf-parser@0.9.1(transitive)
+ Addedmf-utilities@0.9.1(transitive)
- Removedatom-sorter@0.8.0(transitive)
- Removedchemical-elements@0.7.0(transitive)
- Removedchemical-groups@0.8.0(transitive)
- Removedmf-parser@0.8.0(transitive)
- Removedmf-utilities@0.8.0(transitive)
Updatedatom-sorter@^0.9.1
Updatedchemical-elements@^0.9.1
Updatedmf-parser@^0.9.1
Updatedmf-utilities@^0.9.1