mass-fragmentation
Advanced tools
Comparing version
163
lib/index.js
@@ -7,8 +7,3 @@ 'use strict'; | ||
var openchemlibUtils = require('openchemlib-utils'); | ||
var OCL = require('openchemlib'); | ||
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } | ||
var OCL__default = /*#__PURE__*/_interopDefaultLegacy(OCL); | ||
/** | ||
@@ -663,78 +658,43 @@ * The function performs the fragmentation of all single linear bonds | ||
function applyFragmentationReactions( | ||
ionizedFragments, | ||
reactions, | ||
maxDepth, | ||
) { | ||
const fragmentationReactions = reactions.filter( | ||
(reaction) => reaction.Label !== 'Ionization', | ||
); | ||
for (let ionizedMolecule of ionizedFragments.products) { | ||
if (ionizedMolecule.children?.length > 0) { | ||
for (let child of ionizedMolecule.children) { | ||
applyFragmentationReactions(child, fragmentationReactions, maxDepth); | ||
} | ||
} else { | ||
let moleculeToFragment = OCL__default["default"].Molecule.fromIDCode(ionizedMolecule.idCode); | ||
let fragmentation = openchemlibUtils.applyReactions( | ||
[moleculeToFragment], | ||
fragmentationReactions, | ||
{ | ||
maxDepth, | ||
}, | ||
); | ||
ionizedMolecule.children = fragmentation; | ||
} | ||
function getDatabase(databaseName) { | ||
if (databaseName === 'cid') { | ||
return cid; | ||
} | ||
} | ||
function applyIonizationReactions(molecule, reactions, ionizationLevel) { | ||
const ionizationReaction = reactions.filter( | ||
(reaction) => reaction.Label === 'Ionization', | ||
); | ||
let ionization = openchemlibUtils.applyReactions([molecule], ionizationReaction, { | ||
maxDepth: 1, | ||
}); | ||
function ionize(reactant, ionizationReactions, currentIonizationLevel) { | ||
for (let product of reactant.products) { | ||
if (currentIonizationLevel < ionizationLevel) { | ||
let currentMolecule = OCL__default["default"].Molecule.fromIDCode(product.idCode); | ||
let ionizedFragments = openchemlibUtils.applyReactions( | ||
[currentMolecule], | ||
ionizationReactions, | ||
{ | ||
maxDepth: 1, | ||
}, | ||
); | ||
product.children = ionizedFragments; | ||
currentIonizationLevel = currentIonizationLevel + 1; | ||
if (product.children.length > 0) { | ||
for (let child of product.children) { | ||
ionize(child, ionizationReactions, currentIonizationLevel); | ||
} | ||
} | ||
} | ||
} | ||
/** | ||
* @description Recursively insert monoisotopic mass information into the fragmentation trees, products and get array of monoisotopic masses | ||
* @param {Array} trees Fragmentation trees | ||
* @param {Array} products Fragmentation trees grouped by product idCode | ||
* @returns {object} Object with the following properties: | ||
* - masses: array of monoisotopic masses | ||
* - trees: array of fragmentation trees with monoisotopic mass | ||
* - products: array of trees grouped by product idCode with monoisotopic mass | ||
*/ | ||
function insertMfInfoFragments(trees, products) { | ||
let masses = {}; | ||
for (let tree of trees) { | ||
mfInfoFragments(tree, masses); | ||
} | ||
for (let ionizedFragment of ionization) { | ||
ionize(ionizedFragment, ionizationReaction, 1); | ||
for (let product of products) { | ||
const productMF = new mfParser.MF(product.mf); | ||
product.monoisotopicMass = | ||
Math.round( | ||
(productMF.getInfo().observedMonoisotopicMass ?? | ||
productMF.getInfo().monoisotopicMass) * 10000, | ||
) / 10000; | ||
} | ||
return ionization; | ||
} | ||
const masses = {}; | ||
function insertMfInfoFragments(fragmentation) { | ||
for (let fragment of fragmentation) { | ||
mfInfoFragments(fragment); | ||
} | ||
return { | ||
masses: Object.keys(masses).map(Number), | ||
tree: fragmentation, | ||
trees, | ||
products, | ||
}; | ||
} | ||
function mfInfoFragments(reaction) { | ||
/** | ||
* @description Recursively insert monoisotopic mass information into the fragmentation trees and get array of monoisotopic masses | ||
* @param {*} reaction Fragmentation trees | ||
* @param {*} masses Object with monoisotopic masses | ||
*/ | ||
function mfInfoFragments(reaction, masses) { | ||
if (reaction?.reactant) { | ||
@@ -752,7 +712,2 @@ const reactantMF = new mfParser.MF(reaction.reactant.mf); | ||
for (const product of reaction.products) { | ||
if (product.children?.length > 0) { | ||
for (const child of product.children) { | ||
mfInfoFragments(child); | ||
} | ||
} | ||
const productMF = new mfParser.MF(product.mf); | ||
@@ -765,2 +720,7 @@ product.monoisotopicMass = | ||
masses[product.monoisotopicMass] = true; | ||
if (product.children?.length > 0) { | ||
for (const child of product.children) { | ||
mfInfoFragments(child, masses); | ||
} | ||
} | ||
} | ||
@@ -770,31 +730,34 @@ } | ||
const databases = { | ||
cid, | ||
}; | ||
//ionizationLevel fix the maximum depth of the ionization reactions in the molecule | ||
/** | ||
* @description Fragment a molecule by applying reactions from a custom database of reactions | ||
* @param {import('openchemlib').Molecule} molecule - The OCL molecule to be fragmented | ||
* @param {Object} [options={}] | ||
* @param {string} [options.databaseName='cid'] - The database to be used | ||
* @param {string} [options.mode='positive'] - The mode to be used | ||
* @param {number} [options.maxDepth=5] - The maximum depth of the fragmentation tree | ||
* @returns {object} In-Silico fragmentation results with the following properties: | ||
* - masses: array of monoisotopic masses | ||
* - trees: array of fragmentation trees | ||
* - products: array of trees grouped by product idCode | ||
*/ | ||
function reactionFragmentation(molecule, options = {}) { | ||
let { | ||
database = 'cid', | ||
mode = 'positive', | ||
maxDepth = 0, | ||
ionizationLevel = 1, | ||
} = options; | ||
if (maxDepth === 0) { | ||
let mass = molecule.getMolecularFormula().absoluteWeight; | ||
maxDepth = Math.round(mass / 10); | ||
let { databaseName = 'cid', mode = 'positive', maxDepth = 5 } = options; | ||
let database = getDatabase(databaseName); | ||
if (!database) { | ||
throw new Error(`Database ${databaseName} not found`); | ||
} | ||
const reactions = databases[database][mode]; | ||
let ionizedFragments = applyIonizationReactions( | ||
molecule, | ||
reactions, | ||
ionizationLevel, | ||
const reactions = database[mode]; | ||
let fragments = openchemlibUtils.applyReactions([molecule], reactions, { | ||
maxDepth, | ||
}); | ||
let { masses, trees, products } = insertMfInfoFragments( | ||
fragments.trees, | ||
fragments.products, | ||
); | ||
for (let ionizedFragment of ionizedFragments) { | ||
applyFragmentationReactions(ionizedFragment, reactions, maxDepth); | ||
} | ||
let { masses, tree } = insertMfInfoFragments(ionizedFragments); | ||
return { | ||
masses, | ||
tree, | ||
trees, | ||
products, | ||
}; | ||
@@ -801,0 +764,0 @@ } |
{ | ||
"name": "mass-fragmentation", | ||
"version": "0.4.0", | ||
"version": "0.5.0", | ||
"description": "Code to fragment molecules", | ||
@@ -24,8 +24,9 @@ "main": "lib/index.js", | ||
"mf-parser": "^2.3.0", | ||
"openchemlib-utils": "^2.4.0" | ||
"openchemlib-utils": "^2.7.0" | ||
}, | ||
"devDependencies": { | ||
"openchemlib": "^8.2.0" | ||
"openchemlib": "^8.3.0", | ||
"react-tree-svg": "^0.1.0" | ||
}, | ||
"gitHead": "bc42b1937c4d78b61bf0548c5bf1397ab52d645a" | ||
"gitHead": "c697f5fab6e34146c16194f54cead9ddd1c8465f" | ||
} |
@@ -13,5 +13,19 @@ import OCL from 'openchemlib'; | ||
}; | ||
const result = reactionFragmentation(molecule, options); | ||
const { masses, trees, products } = reactionFragmentation( | ||
molecule, | ||
options, | ||
); | ||
expect(result).toMatchSnapshot(); | ||
expect(Object.keys(products[0])).toMatchInlineSnapshot(` | ||
[ | ||
"idCode", | ||
"mf", | ||
"trees", | ||
"reactions", | ||
"minSteps", | ||
"monoisotopicMass", | ||
] | ||
`); | ||
expect(masses).toHaveLength(17); | ||
expect(trees).toMatchSnapshot(); | ||
}); | ||
@@ -23,17 +37,155 @@ it('tropylium rearrangement: MDMA after Alpha cleavage', async () => { | ||
}; | ||
const result = reactionFragmentation(molecule, options); | ||
expect(result).toMatchSnapshot(); | ||
const { masses, trees, products } = reactionFragmentation( | ||
molecule, | ||
options, | ||
); | ||
expect(Object.keys(products[0])).toMatchInlineSnapshot(` | ||
[ | ||
"idCode", | ||
"mf", | ||
"trees", | ||
"reactions", | ||
"minSteps", | ||
"monoisotopicMass", | ||
] | ||
`); | ||
expect(masses).toHaveLength(7); | ||
expect(trees).toMatchSnapshot(); | ||
}); | ||
it('Full fragmentation: MDMA', async () => { | ||
const molecule = Molecule.fromSmiles('CNC(Cc1ccc2c(c1)OCO2)C'); | ||
const molecule = Molecule.fromSmiles('CC(CC1=CC2=C(C=C1)OCO2)NC'); | ||
const options = { | ||
ionizationLevel: 1, | ||
maxDepth: 5, | ||
}; | ||
let { masses, trees, products } = reactionFragmentation(molecule, options); | ||
expect(masses).toMatchInlineSnapshot(` | ||
[ | ||
193.1103, | ||
194.1176, | ||
31.0422, | ||
32.0495, | ||
163.0754, | ||
135.0441, | ||
68.0257, | ||
45.6862, | ||
30.047, | ||
28.0313, | ||
82.0413, | ||
55.03, | ||
97.5624, | ||
65.3774, | ||
65.0414, | ||
68.5296, | ||
97.0585, | ||
58.0651, | ||
136.0519, | ||
193.1097, | ||
135.0446, | ||
] | ||
`); | ||
expect(products[0]).toMatchInlineSnapshot(` | ||
{ | ||
"idCode": "dg~DBMBmeJYW]gJxZB@jj@@", | ||
"mf": "C11H16NO2(+)", | ||
"minSteps": 1, | ||
"monoisotopicMass": 194.1176, | ||
"reactions": [ | ||
"eM\`BN\`~b@!eM\`\`fN\`~c@#Q[ Q[#!R@AM?DquRo@ !R@AM?Dqtzo@", | ||
], | ||
"trees": [ | ||
{ | ||
"products": [ | ||
{ | ||
"children": [], | ||
"flag": true, | ||
"idCode": "dg~DBMBmeJYW]gJxZB@jj@@", | ||
"mf": "C11H16NO2(+)", | ||
"molfile": " | ||
Actelion Java MolfileCreator 1.0 | ||
const result = reactionFragmentation(molecule, options); | ||
14 15 0 0 0 0 0 0 0 0999 V2000 | ||
18.7073 -7.2671 -0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 | ||
19.5677 -7.7768 -0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 | ||
19.5565 -8.7767 -0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 | ||
18.6849 -9.2670 -0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 | ||
18.6737 -10.2669 -0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 | ||
17.8021 -10.7572 -0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 | ||
16.9417 -10.2475 -0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 | ||
16.9529 -9.2475 -0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 | ||
17.8245 -8.7573 -0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 | ||
16.1911 -10.9082 -0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 | ||
16.5876 -11.8263 -0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 | ||
17.5832 -11.7329 -0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 | ||
20.4444 -7.2836 -0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 | ||
21.3075 -7.7768 -0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 | ||
1 2 1 0 0 0 0 | ||
2 3 1 0 0 0 0 | ||
3 4 1 0 0 0 0 | ||
4 5 2 0 0 0 0 | ||
5 6 1 0 0 0 0 | ||
6 7 2 0 0 0 0 | ||
7 8 1 0 0 0 0 | ||
8 9 2 0 0 0 0 | ||
4 9 1 0 0 0 0 | ||
7 10 1 0 0 0 0 | ||
10 11 1 0 0 0 0 | ||
11 12 1 0 0 0 0 | ||
6 12 1 0 0 0 0 | ||
13 2 1 0 0 0 0 | ||
13 14 1 0 0 0 0 | ||
M CHG 1 13 1 | ||
M END | ||
", | ||
}, | ||
], | ||
"reactant": { | ||
"idCode": "dg~D@MBdie]v\\\\kahHBjh@@", | ||
"mf": "C11H15NO2", | ||
"molfile": " | ||
Actelion Java MolfileCreator 1.0 | ||
expect(result).toMatchSnapshot(); | ||
14 15 0 0 0 0 0 0 0 0999 V2000 | ||
1.7321 -1.5000 -0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 | ||
1.7321 -0.5000 -0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 | ||
2.5981 -0.0000 -0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 | ||
3.4641 -0.5000 -0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 | ||
4.3301 -0.0000 -0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 | ||
5.1962 -0.5000 -0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 | ||
5.1962 -1.5000 -0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 | ||
4.3301 -2.0000 -0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 | ||
3.4641 -1.5000 -0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 | ||
6.1472 -1.8090 -0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 | ||
6.7350 -1.0000 -0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 | ||
6.1472 -0.1910 -0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 | ||
0.8660 -0.0000 -0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 | ||
0.0000 -0.5000 -0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 | ||
1 2 1 0 0 0 0 | ||
2 3 1 0 0 0 0 | ||
3 4 1 0 0 0 0 | ||
4 5 2 0 0 0 0 | ||
5 6 1 0 0 0 0 | ||
6 7 2 0 0 0 0 | ||
7 8 1 0 0 0 0 | ||
8 9 2 0 0 0 0 | ||
4 9 1 0 0 0 0 | ||
7 10 1 0 0 0 0 | ||
10 11 1 0 0 0 0 | ||
11 12 1 0 0 0 0 | ||
6 12 1 0 0 0 0 | ||
2 13 1 0 0 0 0 | ||
13 14 1 0 0 0 0 | ||
M END | ||
", | ||
}, | ||
"reaction": { | ||
"Label": "Ionization", | ||
"rxnCode": "eM\`BN\`~b@!eM\`\`fN\`~c@#Q[ Q[#!R@AM?DquRo@ !R@AM?Dqtzo@", | ||
}, | ||
}, | ||
], | ||
} | ||
`); | ||
expect(trees).toMatchSnapshot(); | ||
}); | ||
}); |
@@ -1,36 +0,39 @@ | ||
import { cid } from './database/collisionInducedDissociation'; | ||
import { applyFragmentationReactions } from './utils/applyFragmentationReactions'; | ||
import { applyIonizationReactions } from './utils/applyIonizationReactions'; | ||
import { applyReactions } from 'openchemlib-utils'; | ||
import getDatabase from './database/getDatabase'; | ||
import { insertMfInfoFragments } from './utils/insertMfInfoFragments'; | ||
const databases = { | ||
cid, | ||
}; | ||
//ionizationLevel fix the maximum depth of the ionization reactions in the molecule | ||
/** | ||
* @description Fragment a molecule by applying reactions from a custom database of reactions | ||
* @param {import('openchemlib').Molecule} molecule - The OCL molecule to be fragmented | ||
* @param {Object} [options={}] | ||
* @param {string} [options.databaseName='cid'] - The database to be used | ||
* @param {string} [options.mode='positive'] - The mode to be used | ||
* @param {number} [options.maxDepth=5] - The maximum depth of the fragmentation tree | ||
* @returns {object} In-Silico fragmentation results with the following properties: | ||
* - masses: array of monoisotopic masses | ||
* - trees: array of fragmentation trees | ||
* - products: array of trees grouped by product idCode | ||
*/ | ||
export function reactionFragmentation(molecule, options = {}) { | ||
let { | ||
database = 'cid', | ||
mode = 'positive', | ||
maxDepth = 0, | ||
ionizationLevel = 1, | ||
} = options; | ||
if (maxDepth === 0) { | ||
let mass = molecule.getMolecularFormula().absoluteWeight; | ||
maxDepth = Math.round(mass / 10); | ||
let { databaseName = 'cid', mode = 'positive', maxDepth = 5 } = options; | ||
let database = getDatabase(databaseName); | ||
if (!database) { | ||
throw new Error(`Database ${databaseName} not found`); | ||
} | ||
const reactions = databases[database][mode]; | ||
let ionizedFragments = applyIonizationReactions( | ||
molecule, | ||
reactions, | ||
ionizationLevel, | ||
const reactions = database[mode]; | ||
let fragments = applyReactions([molecule], reactions, { | ||
maxDepth, | ||
}); | ||
let { masses, trees, products } = insertMfInfoFragments( | ||
fragments.trees, | ||
fragments.products, | ||
); | ||
for (let ionizedFragment of ionizedFragments) { | ||
applyFragmentationReactions(ionizedFragment, reactions, maxDepth); | ||
} | ||
let { masses, tree } = insertMfInfoFragments(ionizedFragments); | ||
return { | ||
masses, | ||
tree, | ||
trees, | ||
products, | ||
}; | ||
} |
import { MF } from 'mf-parser'; | ||
const masses = {}; | ||
export function insertMfInfoFragments(fragmentation) { | ||
for (let fragment of fragmentation) { | ||
mfInfoFragments(fragment); | ||
/** | ||
* @description Recursively insert monoisotopic mass information into the fragmentation trees, products and get array of monoisotopic masses | ||
* @param {Array} trees Fragmentation trees | ||
* @param {Array} products Fragmentation trees grouped by product idCode | ||
* @returns {object} Object with the following properties: | ||
* - masses: array of monoisotopic masses | ||
* - trees: array of fragmentation trees with monoisotopic mass | ||
* - products: array of trees grouped by product idCode with monoisotopic mass | ||
*/ | ||
export function insertMfInfoFragments(trees, products) { | ||
let masses = {}; | ||
for (let tree of trees) { | ||
mfInfoFragments(tree, masses); | ||
} | ||
for (let product of products) { | ||
const productMF = new MF(product.mf); | ||
product.monoisotopicMass = | ||
Math.round( | ||
(productMF.getInfo().observedMonoisotopicMass ?? | ||
productMF.getInfo().monoisotopicMass) * 10000, | ||
) / 10000; | ||
} | ||
return { | ||
masses: Object.keys(masses).map(Number), | ||
tree: fragmentation, | ||
trees, | ||
products, | ||
}; | ||
} | ||
function mfInfoFragments(reaction) { | ||
/** | ||
* @description Recursively insert monoisotopic mass information into the fragmentation trees and get array of monoisotopic masses | ||
* @param {*} reaction Fragmentation trees | ||
* @param {*} masses Object with monoisotopic masses | ||
*/ | ||
function mfInfoFragments(reaction, masses) { | ||
if (reaction?.reactant) { | ||
@@ -28,7 +50,2 @@ const reactantMF = new MF(reaction.reactant.mf); | ||
for (const product of reaction.products) { | ||
if (product.children?.length > 0) { | ||
for (const child of product.children) { | ||
mfInfoFragments(child); | ||
} | ||
} | ||
const productMF = new MF(product.mf); | ||
@@ -41,4 +58,9 @@ product.monoisotopicMass = | ||
masses[product.monoisotopicMass] = true; | ||
if (product.children?.length > 0) { | ||
for (const child of product.children) { | ||
mfInfoFragments(child, masses); | ||
} | ||
} | ||
} | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
1314030
24.58%3049
2.94%2
100%24
-7.69%Updated