mass-fragmentation
Advanced tools
Comparing version 1.0.2 to 1.0.3
595
lib/index.js
@@ -8,2 +8,5 @@ 'use strict'; | ||
var OCL = require('openchemlib'); | ||
var fs = require('fs'); | ||
var path = require('path'); | ||
var reactTreeSvg = require('react-tree-svg'); | ||
@@ -335,345 +338,27 @@ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } | ||
const cid = { | ||
positive: [ | ||
{ | ||
Label: 'Alpha cleavage', | ||
rxnCode: | ||
'gCa@@duPGtF@!fI@Gu` eM@bXzB#qbq a` qQp#!RtEJHC^h@GPhB !RGUi| !R@FN?Dquz_@', | ||
}, | ||
{ | ||
Label: 'Alpha cleavage', | ||
rxnCode: | ||
'gJPI@DBTijhCzB@!gK`I@DJTcj`OhH@#qbqh qfUH#!Rmwwp_[]|_g|]lcp !R?`BE@[_|b@H]vgp', | ||
}, | ||
{ | ||
Label: 'Alpha cleavage', | ||
rxnCode: | ||
'gJPa@eJSKTA}E@!eMH`eIxH eFHBLGtP#qbqh qbH qu#!Rw`BH?J_|bMt]|hH !Rwpq?Dquz?@ !R?`@]vgp', | ||
}, | ||
{ | ||
Label: 'Alpha cleavage', | ||
rxnCode: | ||
'gJQIL@`ReMS@_PP!eMH`eIxH eFB@HcA}D@#qbqh qJh Ql#!Rm?wpwXE?y{p]lmp !Rwxq?Dquz?@ !R@NL]vgp', | ||
}, | ||
{ | ||
Label: 'Alpha cleavage', | ||
rxnCode: | ||
'gJXA@IRij`OhH@!gKhAAirPFhB#qbuH qfjH#!RM{upoPD@Mwp]lgp !R@BL?x~WAwrp]Vgp', | ||
}, | ||
{ | ||
Label: 'Alpha cleavage', | ||
rxnCode: | ||
'gJYADEJSKTA}E`!eMH`eIxH eF`BLGtX#qbqh qbH qu#!RO`BH?C_|bGt]|hH !Rwtq?Dquz_@ !R?`@]vgp', | ||
}, | ||
{ | ||
Label: 'Alpha cleavage', | ||
rxnCode: | ||
'gC`AAJTu@P!eF@bXpP eF@HpP#qTq qa Q\\#!R@AM?DpAlGTJB !R@DQCrip !R_sL}Nmp', | ||
}, | ||
{ | ||
Label: 'Alpha cleavage', | ||
rxnCode: | ||
'gChA@IRfhCzC@!eF`BLD eF@bXpP#qbq Ql qQ#!R{|L@p~~lG[K| !R@DL]Vgp !R_xp]ngp', | ||
}, | ||
{ | ||
Label: 'Aromatic Indicutive Cleavage', | ||
rxnCode: | ||
'gC`AAJTu@]SP!eF@bXpP eF@HhP#qrL QX qq#!Rgzp@kI}cG]K| !R@BL]vgp !R@BL]~kp', | ||
}, | ||
{ | ||
Label: 'Benzyl carbocation Resonance', | ||
rxnCode: | ||
'gOpAGJUKutkPD!gOpAMJUNusMHD#qkOfP qi{tP#!Rm?w~@Hc}mpJw@ox@GWHB !R?`BH?[_}mpJw@k_}G]k|', | ||
}, | ||
{ | ||
Label: 'Benzyl carbocation Resonance', | ||
rxnCode: | ||
'gOpAMJUNusMHD!gOpAGJUKutkPD#qkyfP qkOfP#!Rb@Jw@oy?bOvH?_y?G_H| !Rb@K~_{\\BbOvH?_y?GXY|', | ||
}, | ||
{ | ||
Label: 'Benzyl carbocation Resonance', | ||
rxnCode: | ||
'gOpAMJUNusMHD!gOpAMJUKua@@D#qi{tP qkOfP#!R?`BH?[_}mpJw@k_}GWK| !Rm?w~@Hc}mpJw@ox@G]k|', | ||
}, | ||
{ | ||
Label: 'Dioxolane elimination (aromatic)', | ||
rxnCode: | ||
'detDB@iiRfUwJFZB@H@H!eFHBJD daDHB@RTfUVyiZPB#IaLhfGed Ql IZdDezP#!RXJhcFCx@NALlFC`Lp@[@FZJpGWK| !R@FL]vkp !R@CzbuzJppAg@Ac`LNAL]aop', | ||
}, | ||
{ | ||
Label: 'Displacement Reaction a.', | ||
rxnCode: | ||
'daxHbN@HzTiuZjl@vaHpdLE\\QFHIG~p`!gKPI@DBTi\\Zj@` eFHBND#q{ENT q~Yh Qd#!Rm?vH?[\\Bm?vw@ox@?`@]ThH !R?g|_Fa}eTv\\]vcp !R@AL]Agp', | ||
}, | ||
{ | ||
Label: 'Displacement Reaction a.', | ||
rxnCode: | ||
'dazHBF@IRUUzj`CZNcGPpUqDX`d_{G@!gKXA@IReqjhB eFHBND#q]|VH qOtP q]#!RO`@~@N\\Byswg@n\\}y?p]XbH !RcLbLipBe@Lh]Ngp !R_pp]~op', | ||
}, | ||
{ | ||
Label: 'Displacement Reaction b.', | ||
rxnCode: | ||
'gJXA@IRUjPH!eM@fXvB eF`BLGtP#qlVH qrH ql#!R@FL@[@CC@Fp]bgp !RGtL@PqwZO@ !R@DL]~gp', | ||
}, | ||
{ | ||
Label: 'Displacement Reaction b.', | ||
rxnCode: | ||
'gFpa@eJSJZuLA}E@!gBQA@IReZf@~`` eFHBND#qYuT quT qY#!RE?rQ@G\\M[xYsAAwR`` !R@Cx~Gsx_GUj| !R_pp]nop', | ||
}, | ||
{ | ||
Label: 'Displacement Reaction b.', | ||
rxnCode: | ||
'gFpa@eJSJZuRA|eE`!gCaA@IRUdCzB@ eFHBND#qYuT qre qY#!RM?sQ@D}MW|YKAAwb`` !R_rq?Dw|lGYk| !R_rp]~gp', | ||
}, | ||
{ | ||
Label: 'Displacement Reaction b.', | ||
rxnCode: | ||
'gFpa@eJSJZuTA|eE`!gJQA@IRUzPH fI@Gu`#qYuT qYlP Qp#!RM?sQ@D}MW|YKAAvR`` !R_`A~@M\\Buwt]fdH !RGWi|', | ||
}, | ||
{ | ||
Label: 'Displacement Reaction b.', | ||
rxnCode: | ||
'gFqIL@`ReLfts@_HQX@!gC`I@DBTeY@~`` eFHBND#qrlK quY Qd#!Rsg}_qdc}Stw}mquRw@ !R_rq?Dw|lGYk| !R_rp]~gp', | ||
}, | ||
{ | ||
Label: 'Displacement Reaction b.', | ||
rxnCode: | ||
'gFqIL@`ReLftt`_PP!gBPI@DBTiVi`OhH@ eFHBND#qrlK qqk Qd#!Rsg}_qdc}Stu}mqvrw@ !R@Gy~Owx?GUj| !R_rp]ngp', | ||
}, | ||
{ | ||
Label: 'Displacement Reaction b.', | ||
rxnCode: | ||
'gFqIL@`ReLftu@_HQX@!gJQA@IRUzPH fH`PA}X#qrlK qbKh Qp#!RSg}_Q`a}Sxu}mqtrw@ !Ro`B~@I_|e{t]fdH !RGWi|', | ||
}, | ||
{ | ||
Label: 'Displacement Reaction b.', | ||
rxnCode: | ||
'gFyADEJSJZuTA|ee`!gJQA@IRUzPH fJ@Gup#qYuT qYlP Qp#!R]?pq@B?MOrXkAAvR`` !R?`C~@K\\Bm?t]fdH !RG_i|', | ||
}, | ||
{ | ||
Label: 'Displacement Reaction b.', | ||
rxnCode: | ||
'gOpiEPDBTfVKZj`OdHl@!gGQIJ@`Rdj|tHgzV@ fI@Gu`#qbqkx qbOn qX#!RU?qQ@@~MGtXYGplDG^K| !Rog~~_{B}lCrpoQtzg@ !RGPY|', | ||
}, | ||
{ | ||
Label: 'Displacement Reaction b.', | ||
rxnCode: | ||
'gOqa@jIRYXmjj@~Pbp!gGPaAQJRksPb_iX@ fI@Gu`#qYuW` qYlV qx#!RU?qQ@@~MGtXYGplDG^K| !Rog~~_{B}lCrpoQtzg@ !RGPY|', | ||
}, | ||
{ | ||
Label: 'Grob-Wharton', | ||
rxnCode: | ||
'gNpaAQJSOMU@_HQX@!gCa@@duPGtD@ eMH`eIXOh`#qNWeX qNW qlX#!Rb@JH_Wx@b@I~@Ha}GWJB !Ru?sW_Wx@GYjB !R_rq?Dqwz_@', | ||
}, | ||
{ | ||
Label: 'Grob-Wharton', | ||
rxnCode: | ||
'gNxaLJIRU[jXCzZ@!eMH`eIxH eF`BJGtP eFHBND#q{iV` Qv qM q{#!Rm?vw?[\\|m?v~_{\\|GXJ\\ !R@Kx@oauZ_@ !R@BL]^gp !R_tp]Agp', | ||
}, | ||
{ | ||
Label: 'Inductive cleavage', | ||
rxnCode: | ||
'gChA@IRVhCTB!eF``fJD eF@Hp_za@#qbq Qd qq#!R@AL@[@@SGPi| !R@AL]Vgp !R@AL]^kp', | ||
}, | ||
{ | ||
Label: 'Inductive cleavages assisted by β-hydrogen removal', | ||
rxnCode: | ||
'daxHB@zTeVVjh@~ah@!gGQA@IRUVi@~`` eF@HhP#IaLJnXP qbqk IxP#!RtEsPO]A\\tCwPWCy?tCt]Bkp !R@DL@[@AC@Fp@Pquz_@ !R@DL]akp', | ||
}, | ||
{ | ||
Label: 'Inductive cleavages assisted by β-hydrogen removal', | ||
rxnCode: | ||
'gFqAAYRe|Zj`OiH@!gGQA@IRezY@~``#qbqk qbqk#!R_O@T~pAYDEKp@quJ_@ !R@Fq?[@@S_qM?[AwZ_@', | ||
}, | ||
{ | ||
Label: 'Oxonium Resonance', | ||
rxnCode: 'eFH`fJGtP!eFHbfLGtP#QX QX#!R@Fp]rgp !R@FL]Nkp', | ||
}, | ||
{ | ||
Label: 'Remote Hydrogen rearrangement', | ||
rxnCode: | ||
'gCa@@dmPGrDJ@!fI@Gu` eM@HvB#qbq a` qrH#!R@AL@[@@SGPi| !RGUi| !R_qL@Dquzo@', | ||
}, | ||
{ | ||
Label: 'Remote Hydrogen rearrangement', | ||
rxnCode: | ||
'gCa@@eMPD!eFHBLGtP eF@HhP#qTq Q qa#!R@NL?xpAlG_K !R@FL]Ngp !R@FL]Akp', | ||
}, | ||
{ | ||
Label: 'Remote Hydrogen rearrangement', | ||
rxnCode: | ||
'dcN@B@J\\bRbRaWCUUUL@_ITC@!gFpAEJTjZuRA@ eM`AIhH eM@HvB#IO^adkBFU qb^i IO^ IeQ#!Rm?u~_?C}|Gq~_?A}|Gu~_?A||Gwp_Atr{@ !R_g~w_K_}mwvw_AvZo@ !R@Fp@XqtF_@ !ROvL@XquFo@', | ||
}, | ||
{ | ||
Label: 'Resonance', | ||
rxnCode: 'eMB`HIRVB!eMB`HiRZB#Qg Qg#!R@Fp@HquJ_@ !R@Fp@Hqtz?@', | ||
}, | ||
{ | ||
Label: 'Resonance', | ||
rxnCode: 'eM``eIXH!eM`beIhH#Qg Qg#!R@Fp@XquJ_@ !R@Fp@Xqtz?@', | ||
}, | ||
{ | ||
Label: 'Retro-Diels–Alder ', | ||
rxnCode: | ||
'daD@@DiURijj`B!gC`@Die@` gC`@DiZDC@#IZhILxP q]a ISND#!RmwvH__y??g~H_Xa}?g|]lnp !R@Fq?[@@SGWk| !Rb@K~_xc}GUj|', | ||
}, | ||
{ | ||
Label: 'Retro-Diels–Alder ', | ||
rxnCode: | ||
'daDH@@RfVijZj@H!gGQ@@dsMLA@ eF@HhP#IhfpXS` IhlEN qc#!RbGw~@H`B?g~H@ox@bGt]Bgp !RmpJw?_x@?g~w@atZ?@ !R@AL]~kp', | ||
}, | ||
{ | ||
Label: 'Retro-Diels–Alder ', | ||
rxnCode: | ||
'dclD@@QIeUeieujZij@H!eFHBJD deTH@@RUf\\YYif`aF#ItYyRkFMH IeP It^JlMd`#!R?g~H?[_}?g?~_{_}m?w~_xc}?g~w?QuRK@ !R_vp]~gp !RmpK~@K\\Bm?w~@Oy?m?rw@ox@G^hb', | ||
}, | ||
{ | ||
Label: 'Retro-Diels–Alder ', | ||
rxnCode: | ||
'dieH@DxLbbbQ[fjZ`B!gGY@BDeUYaLp eM`BM``#IgBRnaa IgDaa qMx#!RmpJw__x@mpK~_{\\B?`Bw@atb@` !Rb@JH@hc}b@K~@Atz`` !R_qL@DqtF_@', | ||
}, | ||
{ | ||
Label: 'Retro-Diels–Alder ', | ||
rxnCode: | ||
'dif@@@Ri^zZjfh@`!gGP@DjZY`H eM`BM``#IZhddp^ Ipdza IZI#!R?g~H@k\\BbGvH_X`B?g~H_Qurg@ !Rm?w~@Hc}mpK~_qvZo@ !R_qL@Dqwz_@', | ||
}, | ||
{ | ||
Label: 'Retro-Diels–Alder ', | ||
rxnCode: | ||
'dclDB@{iRYeevz]ZA`b@B!gGPa@QJRmsPbX gFp@DiTt@@B#IwbFleVPx IwaeS qb^i#!RbCvH?Ky_bCv~@H`}oe~H?Hc|oe~~@Aurw@ !RbKvHoX`Bo`BHoQtz_@ !Rog~wOK_}m?vwOAwzO@', | ||
}, | ||
{ | ||
Label: 'Retro-Diels–Alder ', | ||
rxnCode: | ||
'dmLDB@{irJJIQEneV`hB@B!eF@HhP didDB@hIRVY~G``@@`#IwlDdeZa` IFP IwlIEjF#!Rg`BH?H_|g`BHGYx@a?rH?Hc|bAt]\\bH !R_uP]Fop !RmuvwWYy?mrr`g[^]muvwKAtzg@', | ||
}, | ||
{ | ||
Label: 'Retro-Diels–Alder ', | ||
rxnCode: | ||
'fdeA`D@\\brTfYY_m]gIZ|EhJB@@h@@~aF@!didHB@BTfUvfZVV@OhB@ didD@@iIYgxUie`B#iup`jXdibCPcXKoI iSxKhvMHP IwbkEYH#!RbOrHW]x@bOs^[xc|w`BHWXa]w`C^[xc|ipK^@Mx@i?s^@AuRx` !Ri?rgW]x@i?rg?Mx@i?rg?Aujp` !RmsvwO]y?mqrPw[_]msvwGAwzG@', | ||
}, | ||
{ | ||
Label: 'Retro-heteroene (Claisen rearrangement)', | ||
rxnCode: | ||
'daxH@@Re^jf`B!daxH@@RUiZjPB#IphiFG` IrDZax`#!RbOrW?Hb}e{vH?Hb}o`@]|hH !R@BL@[@@c@FqlWrM_@BL]~cp', | ||
}, | ||
{ | ||
Label: 'Retro-heteroene (McLafferty-type rearrangement)', | ||
rxnCode: | ||
'daxH@@RUiZjPB!gJP@DjYdB eMHAIdOh`#IaLJfxP IJfxP Qg#!R@FL@[@Ac@FqlOvL?@FL]Bcp !R|Grw_Gy?|Ot]vkp !R@Fp@XqtF_@', | ||
}, | ||
{ | ||
Label: 'Tropylium rearragement', | ||
rxnCode: | ||
'gOpAMJUKua@@D!gOpAGJTju`@@D#qgVMX qYoTp#!Rg`BGgXb]a?rG?H^]GPhB !RN@swnix@C[AkCDJpGSkB', | ||
}, | ||
{ | ||
Label: 'Tropylium resonance', | ||
rxnCode: | ||
'gOpAGJTju`@@D!gOpAGJTju`@@D#IaLkit I{MbTh#!Rh@VUDFcl?H`N@HCfGXiB !R[X~@abJwFEL@F[y_GSi\\', | ||
}, | ||
{ | ||
Label: 'Tropylium resonance', | ||
rxnCode: | ||
'gOpAGJTju`@@D!gOpAGJTju`@@D#q]yfP qgSuH#!R[X?@qaJONGO@v_y?GWK\\ !RQps~_}?YQwMmc}?FG[i|', | ||
}, | ||
{ | ||
Label: 'Tropylium resonance', | ||
rxnCode: | ||
'gOpAGJTju`@@D!gOpAGJTju`@@D#qrLoX qMsW`#!RN@s_qox@[[AGCAJpGWKB !R[[C@vQJpN@s@qox@G_iB', | ||
}, | ||
{ | ||
Label: 'Tropylium resonance', | ||
rxnCode: | ||
'gOpAGJTju`@@D!gOpAGJTju`@@D#qMsW` q{eQp#!R[[C@vQJpN@s@qox@GPkB !Rw}dx\\vvO?g|Rct]sGP[\\', | ||
}, | ||
{ | ||
Label: 'Tropylium resonance', | ||
rxnCode: | ||
'gOpAGJTju`@@D!gOpAGJTju`@@D#q{eQp qTwKh#!Rw}dx\\vvO?g|Rct]sG]K\\ !R?`AG\\t\\Lw|[_vVvpGSiB', | ||
}, | ||
{ | ||
Label: 'Tropylium resonance', | ||
rxnCode: | ||
'gOpAGJTju`@@D!gOpAGJTju`@@D#qTwKh qqUcx#!R?`AG\\t\\Lw|[_vVvpG]JB !Rw|XxCFvp?`@RlD\\LG[kB', | ||
}, | ||
{ | ||
Label: 'Tropylium resonance ', | ||
rxnCode: | ||
'gOpAGJTju`@@D!gOpAGJTju`@@D#qgSuH qrLoX#!RQps~_}?YQwMmc}?FGUH| !RN@s_qox@[[AGCAJpGXXb', | ||
}, | ||
], | ||
negative: [], | ||
}; | ||
const ei = { | ||
positive: [], | ||
negative: [], | ||
}; | ||
const esi = { | ||
positive: [ | ||
{ | ||
Label: 'Ionization', | ||
rxnCode: 'eF`BLGtX!eF``fLGt\\#Qd Qd#!R@FL]Rgp !R@FL]vgp', | ||
}, | ||
{ | ||
Label: 'Ionization', | ||
rxnCode: 'eM`BN`~b@!eM``fN`~c@#Q[ Q[#!R@AM?DquRo@ !R@AM?Dqtzo@', | ||
}, | ||
{ | ||
Label: 'Ionization', | ||
rxnCode: 'gCh@AGj@`!gChA@Icu@_PP#qTq qTq#!RbOvw?_x@GYK| !RbOvw?_x@GWk|', | ||
}, | ||
{ | ||
Label: 'Ionization', | ||
rxnCode: | ||
'gCa@@dmPFtwhHcMCAlp!eMH`eIhOhp#Qv@ Qv#!R@AL@[@@SGSH| !R@Fp@Dqwz``', | ||
}, | ||
{ | ||
Label: 'Ionization', | ||
rxnCode: 'eFB@HcA}D@!eFB`HIcA}F@#QX QX#!R_vp]bgp !R_vp]vgp', | ||
}, | ||
{ | ||
Label: 'Ionization', | ||
rxnCode: 'eMB@HchH!eMB`HIchOh`#Q[ Q[#!R@AM?Dqtbo@ !R@AM?Dquz@`', | ||
}, | ||
{ | ||
Label: 'Ionization', | ||
rxnCode: 'eMHAIXMicLLG~r!eFH`fJGtP#QX QX#!R@AL@[AtbO@ !R@AL]nkp', | ||
}, | ||
{ | ||
Label: 'Ionization', | ||
rxnCode: 'eMHBN``!eMH`fN`~b@#Qg Qg#!R@FL?XqtJ_@ !R@FL?XqwZ_@', | ||
}, | ||
{ | ||
Label: 'Ionization', | ||
rxnCode: 'eFB@HcA}D@!eFB`HIcA}F@#QX QX#!R_vp]bgp !R_vp]vgp', | ||
}, | ||
{ | ||
Label: 'Ionization', | ||
rxnCode: 'eFHBLGtP!eFH`fLGtX#QX QX#!R@AL]Pmp !R@AL]^gp', | ||
}, | ||
{ | ||
Label: 'Ionization', | ||
rxnCode: 'eM`BN`~b@!eM``fN`~bOza@#Qg Qg#!R@Fq?[AuJ?@ !R@FM?Dqvz_@', | ||
}, | ||
], | ||
negative: [], | ||
}; | ||
/** | ||
* @description get fragmentation databases based on the technique used | ||
* @param {string} databaseName "cid", "ei" or "esi" | ||
* @returns {object} object containing the fragmentation database | ||
* @description Get the default databases of reactions for positive and negative mode | ||
* @param {Object} options - Options for database selection | ||
* @param {string} [options.mode='positive'] - The mode to be used | ||
* @param {string} [options.kind='ionization'] - The kind of database to be used | ||
* @param {string} dwarEntry - The dwar entry to be used | ||
* @returns | ||
*/ | ||
function getDatabase(databaseName) { | ||
switch (databaseName) { | ||
case 'cid': | ||
return cid; | ||
case 'ei': | ||
return ei; | ||
default: | ||
return esi; | ||
function getDatabases(options = {}, dwarEntry = '') { | ||
const { mode = 'positive', kind = 'ionization' } = options; | ||
if (!dwarEntry) { | ||
dwarEntry = fs.readFileSync( | ||
path.join(__dirname, './ReactionMassFragmentation.dwar'), | ||
'utf8', | ||
); | ||
} | ||
const data = openchemlibUtils.parseDwar(dwarEntry).data; | ||
const database = data.filter( | ||
(entry) => | ||
// @ts-ignore | ||
entry.kind === kind && (entry.mode === mode || entry?.mode === 'both'), | ||
); | ||
return database; | ||
} | ||
@@ -684,9 +369,6 @@ | ||
* @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 | ||
* - products: array of trees grouped by product idCode | ||
* @returns {Array} array of mz values | ||
*/ | ||
function getMasses(trees, products) { | ||
function getMasses(trees) { | ||
let masses = {}; | ||
@@ -696,18 +378,14 @@ for (const tree of trees) { | ||
} | ||
return { | ||
masses: Object.keys(masses).map(Number), | ||
trees, | ||
products, | ||
}; | ||
return Object.keys(masses).map(Number); | ||
} | ||
function getMassesFromTree(currentBranch, masses) { | ||
for (const product of currentBranch.products) { | ||
if (Math.abs(product.charge) > 0) { | ||
masses[product.mz] = true; | ||
for (const molecule of currentBranch.molecules) { | ||
if (Math.abs(molecule.info.charge) > 0) { | ||
masses[molecule.info.mz] = true; | ||
} | ||
if (product.children.length > 0) { | ||
for (const child of product.children) { | ||
getMassesFromTree(child, masses); | ||
} | ||
} | ||
if (currentBranch.children && currentBranch.children.length > 0) { | ||
for (const child of currentBranch.children) { | ||
getMassesFromTree(child, masses); | ||
} | ||
@@ -719,125 +397,132 @@ } | ||
* @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 {import('openchemlib').Molecule} oclMolecule - 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 | ||
* @param {boolean} [options.getProductsTrees=false] - If true, the products trees are returned else products is an empty array | ||
* @param {number} [options.maxDepth=5] - The maximum depth of the overall fragmentation tree | ||
* @param {number} [options.limitReactions=200] - The maximum number of reactions to be applied | ||
* @param {string} [options.dwarEntry] - The dwar entry to be used, if not provided, the default one will be used | ||
* @param {number} [options.maxIonizationDepth=1] - The maximum depth of the ionization tree | ||
* @param {Object} [options.customDatabase={}] - A custom database of reactions | ||
* @param {Array} [options.customDatabase.positive] - A custom database of reactions for positive mode | ||
* @param {Array} [options.customDatabase.negative] - A custom database of reactions for negative mode | ||
* @param {Object} [options.customDatabase.ionization] - A custom database ionization reactions | ||
* @param {Array} [options.customDatabase.ionization.positive] - A custom database of ionization reactions for positive mode | ||
* @param {Array} [options.customDatabase.ionization.negative] - A custom database of ionization reactions for negative mode | ||
* @param {number} [options.minIonizationDepth=1] - The minimum depth of the ionization tree | ||
* @param {number} [options.minReactionDepth=0] - The minimum depth of the reaction tree | ||
* @param {number} [options.maxReactionDepth=3] - The maximum depth of the reaction 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 | ||
* - validNodes: nodes without dead branches | ||
*/ | ||
function reactionFragmentation(molecule, options = {}) { | ||
let { | ||
databaseName = 'cid', | ||
function reactionFragmentation(oclMolecule, options = {}) { | ||
const { | ||
mode = 'positive', | ||
dwarEntry = '', | ||
maxDepth = 5, | ||
limitReactions = 200, | ||
minIonizationDepth = 1, | ||
maxIonizationDepth = 1, | ||
getProductsTrees = false, | ||
customDatabase = {}, | ||
limitReactions = 200, | ||
minReactionDepth = 0, | ||
maxReactionDepth = 3, | ||
} = options; | ||
let database; | ||
let IonizationDb; | ||
if (customDatabase[mode] && customDatabase[mode].length > 0) { | ||
database = customDatabase; | ||
} else { | ||
database = getDatabase(databaseName); | ||
} | ||
if (!database) { | ||
throw new Error(`Database ${databaseName} not found`); | ||
} | ||
if (databaseName === 'cid') { | ||
if ( | ||
customDatabase.ionization && | ||
customDatabase.ionization[mode].length > 0 | ||
) { | ||
IonizationDb = customDatabase.ionization[mode]; | ||
} else { | ||
IonizationDb = getDatabase('')[mode]; | ||
} | ||
} | ||
let results = {}; | ||
const reactions = database[mode]; | ||
if (IonizationDb) { | ||
let ionizationFragments = { | ||
trees: [], | ||
products: [], | ||
}; | ||
for ( | ||
let currentMaxIonizationDepth = 1; | ||
currentMaxIonizationDepth <= maxIonizationDepth; | ||
currentMaxIonizationDepth++ | ||
) { | ||
let ionizationLevelResult = openchemlibUtils.applyReactions([molecule], IonizationDb, { | ||
maxDepth: currentMaxIonizationDepth, | ||
limitReactions, | ||
}); | ||
// add array to ionizationfragments.trees | ||
// @ts-ignore | ||
ionizationFragments.trees.push(...ionizationLevelResult.trees); | ||
// @ts-ignore | ||
ionizationFragments.products.push(...ionizationLevelResult.products); | ||
} | ||
for (let tree of ionizationFragments.trees) { | ||
getMoleculesToFragment(tree, reactions, maxDepth, limitReactions); | ||
} | ||
if (getProductsTrees) { | ||
const reactions = new openchemlibUtils.Reactions(OCL__default["default"], { | ||
moleculeInfoCallback: (molecule) => { | ||
// @ts-ignore | ||
ionizationFragments.products = openchemlibUtils.groupTreesByProducts( | ||
ionizationFragments.trees, | ||
); | ||
} | ||
results = ionizationFragments; | ||
} else { | ||
results = openchemlibUtils.applyReactions([molecule], reactions, { | ||
maxDepth, | ||
limitReactions, | ||
}); | ||
} | ||
const mf = openchemlibUtils.getMF(molecule).mf; | ||
const mfInfo = new mfParser.MF(mf).getInfo(); | ||
return { | ||
mf, | ||
mw: mfInfo.mass, | ||
em: mfInfo.monoisotopicMass, | ||
mz: mfInfo.observedMonoisotopicMass, | ||
charge: mfInfo.charge, | ||
}; | ||
}, | ||
maxDepth, | ||
limitReactions, | ||
skipProcessed: true, | ||
}); | ||
let { masses, trees, products } = getMasses(results.trees, results.products); | ||
reactions.appendHead([oclMolecule]); | ||
reactions.applyOneReactantReactions( | ||
getDatabases({ kind: 'ionization', mode }, dwarEntry), | ||
{ | ||
min: minIonizationDepth, | ||
max: maxIonizationDepth, | ||
}, | ||
); | ||
reactions.applyOneReactantReactions( | ||
getDatabases({ kind: 'reaction', mode }, dwarEntry), | ||
{ | ||
min: minReactionDepth, | ||
max: maxReactionDepth, | ||
}, | ||
); | ||
const trees = reactions.trees; | ||
const validNodes = reactions.getValidNodes(); | ||
const masses = getMasses(trees); | ||
return { | ||
trees, | ||
validNodes, | ||
masses, | ||
trees, | ||
products, | ||
}; | ||
} | ||
function getMoleculesToFragment(tree, reactions, maxDepth, limitReactions) { | ||
for (let product of tree.products) { | ||
if (product.children.length === 0) { | ||
if (product.charge !== 0) { | ||
let molecule = OCL__default["default"].Molecule.fromIDCode(product.idCode); | ||
function getFragmentationSVG(trees, options = {}) { | ||
const { OCL, accuracy, masses = [] } = options; | ||
let fragments = openchemlibUtils.applyReactions([molecule], reactions, { | ||
maxDepth, | ||
limitReactions, | ||
getProductsTrees: true, | ||
}); | ||
// @ts-ignore | ||
product.children = fragments.trees; | ||
} | ||
} else { | ||
for (let child of product.children) { | ||
getMoleculesToFragment(child, reactions, maxDepth, limitReactions); | ||
} | ||
const rendererOptions = { | ||
nodeRenderer: reactTreeSvg.moleculeRenderer, | ||
arrowRendererOptions: { | ||
getLabel: (node) => { | ||
return node?.reaction?.labreel; | ||
}, | ||
labelPosition: 'center', | ||
}, | ||
nodeRendererOptions: { | ||
getTopLabel: (node) => { | ||
const mz = node?.molecules[0]?.info?.mz; | ||
if (mz === undefined) return; | ||
return `${mz.toFixed(4)} m/z`; | ||
}, | ||
getBoxStyle: (node) => { | ||
for (const molecule of node.molecules) { | ||
if (isInRange(masses, molecule?.info?.mz, accuracy)) { | ||
return { | ||
fillOpacity: 0.2, | ||
fill: 'red', | ||
}; | ||
} | ||
} | ||
}, | ||
getMolecules: (node) => { | ||
return node.molecules.map((molecule) => | ||
OCL.Molecule.fromIDCode(molecule.idCode), | ||
); | ||
}, | ||
}, | ||
positionOptions: { | ||
spacingHorizontal: 150, | ||
}, | ||
}; | ||
return reactTreeSvg.render(trees, rendererOptions); | ||
} | ||
function isInRange(masses, mass, accuracy) { | ||
if (!mass || !masses) { | ||
return false; | ||
} | ||
const massAccuracy = (accuracy * mass) / 1e6; | ||
for (const value of masses) { | ||
if (Math.abs(value - mass) <= massAccuracy) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
exports.cid = cid; | ||
exports.ei = ei; | ||
exports.esi = esi; | ||
exports.fragment = fragment; | ||
exports.getDatabases = getDatabases; | ||
exports.getFragmentationSVG = getFragmentationSVG; | ||
exports.reactionFragmentation = reactionFragmentation; |
{ | ||
"name": "mass-fragmentation", | ||
"version": "1.0.2", | ||
"version": "1.0.3", | ||
"description": "Code to fragment molecules", | ||
@@ -24,3 +24,4 @@ "main": "lib/index.js", | ||
"mf-parser": "^3.0.1", | ||
"openchemlib-utils": "5.2.0" | ||
"openchemlib-utils": "^5.3.0", | ||
"react-tree-svg": "^0.6.1" | ||
}, | ||
@@ -30,3 +31,3 @@ "devDependencies": { | ||
}, | ||
"gitHead": "5c97fecfd23faf2cc4120ce5e22f0c85b8e734b1" | ||
"gitHead": "de5e985142af9c9a5a8a7dc1b2cbb4ddf12981e1" | ||
} |
@@ -8,305 +8,9 @@ import OCL from 'openchemlib'; | ||
describe('ReactionFragmentation', async () => { | ||
it('Alpha cleavage: MDMAH+', async () => { | ||
const molecule = Molecule.fromSmiles('C[NH2+]C(C)Cc2ccc1OCOc1c2'); | ||
const options = { | ||
maxDepth: 20, | ||
maxIonizationDepth: 2, | ||
getProductsTrees: true, | ||
}; | ||
const { masses, trees, products } = reactionFragmentation( | ||
molecule, | ||
options, | ||
); | ||
expect(Object.keys(products[0])).toMatchInlineSnapshot(` | ||
[ | ||
"idCode", | ||
"mf", | ||
"em", | ||
"mz", | ||
"charge", | ||
"trees", | ||
"reactions", | ||
"minSteps", | ||
] | ||
`); | ||
expect(masses).toHaveLength(6); | ||
it('Alpha cleavage: MDMA', async () => { | ||
const molecule = Molecule.fromSmiles('CNC(Cc1ccc2c(c1)OCO2)C'); | ||
const { trees, validNodes, masses } = reactionFragmentation(molecule); | ||
expect(validNodes).toHaveLength(274); | ||
expect(masses).toHaveLength(52); | ||
expect(trees).toMatchSnapshot(); | ||
}); | ||
it('tropylium rearrangement: MDMA after Alpha cleavage', async () => { | ||
const molecule = Molecule.fromSmiles('[CH2+]c2ccc1OCOc1c2'); | ||
const options = { | ||
maxDepth: 5, | ||
maxIonizationDepth: 2, | ||
getProductsTrees: true, | ||
}; | ||
const { masses, trees, products } = reactionFragmentation( | ||
molecule, | ||
options, | ||
); | ||
expect(Object.keys(products[0])).toMatchInlineSnapshot(` | ||
[ | ||
"idCode", | ||
"mf", | ||
"em", | ||
"mz", | ||
"charge", | ||
"trees", | ||
"reactions", | ||
"minSteps", | ||
] | ||
`); | ||
expect(masses).toHaveLength(2); | ||
expect(trees).toMatchSnapshot(); | ||
}); | ||
it('Full fragmentation: MDMA', async () => { | ||
const molecule = Molecule.fromSmiles('CC(CC1=CC2=C(C=C1)OCO2)NC'); | ||
const options = { | ||
maxDepth: 5, | ||
getProductsTrees: true, | ||
limitReactions: 500, | ||
}; | ||
let { masses, trees, products } = reactionFragmentation(molecule, options); | ||
expect(masses).toMatchInlineSnapshot(` | ||
[ | ||
194.11810400000002, | ||
163.075905, | ||
135.04460500000002, | ||
105.03404, | ||
193.11027900000002, | ||
58.065674, | ||
] | ||
`); | ||
expect(products[0]).toMatchInlineSnapshot(` | ||
{ | ||
"charge": 1, | ||
"em": 194.11810400000002, | ||
"idCode": "dg~DBMBmeJYW]gJxZB@jj@@", | ||
"mf": "C11H16NO2(+)", | ||
"minSteps": 1, | ||
"mz": 194.11810400000002, | ||
"reactions": [ | ||
"eM\`BN\`~b@!eM\`\`fN\`~c@#Q[ Q[#!R@AM?DquRo@ !R@AM?Dqtzo@", | ||
], | ||
"trees": [ | ||
{ | ||
"products": [ | ||
{ | ||
"charge": 1, | ||
"children": [], | ||
"em": 194.11810400000002, | ||
"flag": true, | ||
"idCode": "dg~DBMBmeJYW]gJxZB@jj@@", | ||
"mf": "C11H16NO2(+)", | ||
"molfile": " | ||
Actelion Java MolfileCreator 1.0 | ||
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 | ||
", | ||
"mz": 194.11810400000002, | ||
}, | ||
], | ||
"reactant": { | ||
"charge": 0, | ||
"em": 193.11027900000002, | ||
"idCode": "dg~D@MBdie]v\\\\kahHBjh@@", | ||
"mf": "C11H15NO2", | ||
"molfile": " | ||
Actelion Java MolfileCreator 1.0 | ||
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 | ||
", | ||
"mz": 193.11027900000002, | ||
}, | ||
"reaction": { | ||
"Label": "Ionization", | ||
"rxnCode": "eM\`BN\`~b@!eM\`\`fN\`~c@#Q[ Q[#!R@AM?DquRo@ !R@AM?Dqtzo@", | ||
}, | ||
}, | ||
], | ||
} | ||
`); | ||
expect(trees).toMatchSnapshot(); | ||
}); | ||
it('Full fragmentation: MDMA with custom database', async () => { | ||
const molecule = Molecule.fromSmiles('CC(CC1=CC2=C(C=C1)OCO2)NC'); | ||
const customDatabase = { | ||
positive: [ | ||
{ | ||
Label: 'Alpha cleavage', | ||
rxnCode: | ||
'gCa@@duPGtF@!fI@Gu` eM@bXzB#qbq a` qQp#!RtEJHC^h@GPhB !RGUi| !R@FN?Dquz_@', | ||
}, | ||
{ | ||
Label: 'Alpha cleavage', | ||
rxnCode: | ||
'gJPI@DBTijhCzB@!gK`I@DJTcj`OhH@#qbqh qfUH#!Rmwwp_[]|_g|]lcp !R?`BE@[_|b@H]vgp', | ||
}, | ||
{ | ||
Label: 'Alpha cleavage', | ||
rxnCode: | ||
'gJPa@eJSKTA}E@!eMH`eIxH eFHBLGtP#qbqh qbH qu#!Rw`BH?J_|bMt]|hH !Rwpq?Dquz?@ !R?`@]vgp', | ||
}, | ||
{ | ||
Label: 'Alpha cleavage', | ||
rxnCode: | ||
'gJQIL@`ReMS@_PP!eMH`eIxH eFB@HcA}D@#qbqh qJh Ql#!Rm?wpwXE?y{p]lmp !Rwxq?Dquz?@ !R@NL]vgp', | ||
}, | ||
{ | ||
Label: 'Alpha cleavage', | ||
rxnCode: | ||
'gJXA@IRij`OhH@!gKhAAirPFhB#qbuH qfjH#!RM{upoPD@Mwp]lgp !R@BL?x~WAwrp]Vgp', | ||
}, | ||
{ | ||
Label: 'Alpha cleavage', | ||
rxnCode: | ||
'gJYADEJSKTA}E`!eMH`eIxH eF`BLGtX#qbqh qbH qu#!RO`BH?C_|bGt]|hH !Rwtq?Dquz_@ !R?`@]vgp', | ||
}, | ||
{ | ||
Label: 'Alpha cleavage', | ||
rxnCode: | ||
'gC`AAJTu@P!eF@bXpP eF@HpP#qTq qa Q\\#!R@AM?DpAlGTJB !R@DQCrip !R_sL}Nmp', | ||
}, | ||
{ | ||
Label: 'Alpha cleavage', | ||
rxnCode: | ||
'gChA@IRfhCzC@!eF`BLD eF@bXpP#qbq Ql qQ#!R{|L@p~~lG[K| !R@DL]Vgp !R_xp]ngp', | ||
}, | ||
{ | ||
Label: 'Aromatic Indicutive Cleavage', | ||
rxnCode: | ||
'gC`AAJTu@]SP!eF@bXpP eF@HhP#qrL QX qq#!Rgzp@kI}cG]K| !R@BL]vgp !R@BL]~kp', | ||
}, | ||
], | ||
ionization: { | ||
positive: [ | ||
{ | ||
Label: 'Ionization', | ||
rxnCode: 'eF`BLGtX!eF``fLGt\\#Qd Qd#!R@FL]Rgp !R@FL]vgp', | ||
}, | ||
{ | ||
Label: 'Ionization', | ||
rxnCode: 'eM`BN`~b@!eM``fN`~c@#Q[ Q[#!R@AM?DquRo@ !R@AM?Dqtzo@', | ||
}, | ||
{ | ||
Label: 'Ionization', | ||
rxnCode: | ||
'gCh@AGj@`!gChA@Icu@_PP#qTq qTq#!RbOvw?_x@GYK| !RbOvw?_x@GWk|', | ||
}, | ||
{ | ||
Label: 'Ionization', | ||
rxnCode: | ||
'gCa@@dmPFtwhHcMCAlp!eMH`eIhOhp#Qv@ Qv#!R@AL@[@@SGSH| !R@Fp@Dqwz``', | ||
}, | ||
{ | ||
Label: 'Ionization', | ||
rxnCode: 'eFB@HcA}D@!eFB`HIcA}F@#QX QX#!R_vp]bgp !R_vp]vgp', | ||
}, | ||
{ | ||
Label: 'Ionization', | ||
rxnCode: 'eMB@HchH!eMB`HIchOh`#Q[ Q[#!R@AM?Dqtbo@ !R@AM?Dquz@`', | ||
}, | ||
{ | ||
Label: 'Ionization', | ||
rxnCode: 'eMHAIXMicLLG~r!eFH`fJGtP#QX QX#!R@AL@[AtbO@ !R@AL]nkp', | ||
}, | ||
{ | ||
Label: 'Ionization', | ||
rxnCode: 'eMHBN``!eMH`fN`~b@#Qg Qg#!R@FL?XqtJ_@ !R@FL?XqwZ_@', | ||
}, | ||
{ | ||
Label: 'Ionization', | ||
rxnCode: 'eFB@HcA}D@!eFB`HIcA}F@#QX QX#!R_vp]bgp !R_vp]vgp', | ||
}, | ||
{ | ||
Label: 'Ionization', | ||
rxnCode: 'eFHBLGtP!eFH`fLGtX#QX QX#!R@AL]Pmp !R@AL]^gp', | ||
}, | ||
{ | ||
Label: 'Ionization', | ||
rxnCode: 'eM`BN`~b@!eM``fN`~bOza@#Qg Qg#!R@Fq?[AuJ?@ !R@FM?Dqvz_@', | ||
}, | ||
], | ||
}, | ||
}; | ||
const options = { | ||
maxDepth: 5, | ||
customDatabase, | ||
getProductsTrees: true, | ||
}; | ||
let { masses, trees, products } = reactionFragmentation(molecule, options); | ||
expect(masses).toMatchInlineSnapshot(` | ||
[ | ||
194.11810400000002, | ||
163.075905, | ||
135.04460500000002, | ||
193.11027900000002, | ||
] | ||
`); | ||
expect(products[0].mf).toMatchInlineSnapshot('"C11H16NO2(+)"'); | ||
expect(trees).toMatchSnapshot(); | ||
}); | ||
}); |
export * from './fragment.js'; | ||
export * from './reactionFragmentation.js'; | ||
export * from './database/getDatabase.js'; | ||
export * from './getFragmentationSVG.js'; | ||
export * from './database/getDatabases.js'; |
@@ -0,125 +1,79 @@ | ||
import { MF } from 'mf-parser'; | ||
import OCL from 'openchemlib'; | ||
import { applyReactions, groupTreesByProducts } from 'openchemlib-utils'; | ||
import { Reactions, getMF } from 'openchemlib-utils'; | ||
import getDatabase from './database/getDatabase'; | ||
import { getDatabases } from './database/getDatabases'; | ||
import { getMasses } from './utils/getMasses'; | ||
/** | ||
* @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 {import('openchemlib').Molecule} oclMolecule - 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 | ||
* @param {boolean} [options.getProductsTrees=false] - If true, the products trees are returned else products is an empty array | ||
* @param {number} [options.maxDepth=5] - The maximum depth of the overall fragmentation tree | ||
* @param {number} [options.limitReactions=200] - The maximum number of reactions to be applied | ||
* @param {string} [options.dwarEntry] - The dwar entry to be used, if not provided, the default one will be used | ||
* @param {number} [options.maxIonizationDepth=1] - The maximum depth of the ionization tree | ||
* @param {Object} [options.customDatabase={}] - A custom database of reactions | ||
* @param {Array} [options.customDatabase.positive] - A custom database of reactions for positive mode | ||
* @param {Array} [options.customDatabase.negative] - A custom database of reactions for negative mode | ||
* @param {Object} [options.customDatabase.ionization] - A custom database ionization reactions | ||
* @param {Array} [options.customDatabase.ionization.positive] - A custom database of ionization reactions for positive mode | ||
* @param {Array} [options.customDatabase.ionization.negative] - A custom database of ionization reactions for negative mode | ||
* @param {number} [options.minIonizationDepth=1] - The minimum depth of the ionization tree | ||
* @param {number} [options.minReactionDepth=0] - The minimum depth of the reaction tree | ||
* @param {number} [options.maxReactionDepth=3] - The maximum depth of the reaction 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 | ||
* - validNodes: nodes without dead branches | ||
*/ | ||
export function reactionFragmentation(molecule, options = {}) { | ||
let { | ||
databaseName = 'cid', | ||
export function reactionFragmentation(oclMolecule, options = {}) { | ||
const { | ||
mode = 'positive', | ||
dwarEntry = '', | ||
maxDepth = 5, | ||
limitReactions = 200, | ||
minIonizationDepth = 1, | ||
maxIonizationDepth = 1, | ||
getProductsTrees = false, | ||
customDatabase = {}, | ||
limitReactions = 200, | ||
minReactionDepth = 0, | ||
maxReactionDepth = 3, | ||
} = options; | ||
let database; | ||
let IonizationDb; | ||
if (customDatabase[mode] && customDatabase[mode].length > 0) { | ||
database = customDatabase; | ||
} else { | ||
database = getDatabase(databaseName); | ||
} | ||
if (!database) { | ||
throw new Error(`Database ${databaseName} not found`); | ||
} | ||
if (databaseName === 'cid') { | ||
if ( | ||
customDatabase.ionization && | ||
customDatabase.ionization[mode].length > 0 | ||
) { | ||
IonizationDb = customDatabase.ionization[mode]; | ||
} else { | ||
IonizationDb = getDatabase('')[mode]; | ||
} | ||
} | ||
let results = {}; | ||
const reactions = database[mode]; | ||
if (IonizationDb) { | ||
let ionizationFragments = { | ||
trees: [], | ||
products: [], | ||
}; | ||
for ( | ||
let currentMaxIonizationDepth = 1; | ||
currentMaxIonizationDepth <= maxIonizationDepth; | ||
currentMaxIonizationDepth++ | ||
) { | ||
let ionizationLevelResult = applyReactions([molecule], IonizationDb, { | ||
maxDepth: currentMaxIonizationDepth, | ||
limitReactions, | ||
}); | ||
// add array to ionizationfragments.trees | ||
// @ts-ignore | ||
ionizationFragments.trees.push(...ionizationLevelResult.trees); | ||
// @ts-ignore | ||
ionizationFragments.products.push(...ionizationLevelResult.products); | ||
} | ||
for (let tree of ionizationFragments.trees) { | ||
getMoleculesToFragment(tree, reactions, maxDepth, limitReactions); | ||
} | ||
if (getProductsTrees) { | ||
const reactions = new Reactions(OCL, { | ||
moleculeInfoCallback: (molecule) => { | ||
// @ts-ignore | ||
ionizationFragments.products = groupTreesByProducts( | ||
ionizationFragments.trees, | ||
); | ||
} | ||
results = ionizationFragments; | ||
} else { | ||
results = applyReactions([molecule], reactions, { | ||
maxDepth, | ||
limitReactions, | ||
}); | ||
} | ||
const mf = getMF(molecule).mf; | ||
const mfInfo = new MF(mf).getInfo(); | ||
return { | ||
mf, | ||
mw: mfInfo.mass, | ||
em: mfInfo.monoisotopicMass, | ||
mz: mfInfo.observedMonoisotopicMass, | ||
charge: mfInfo.charge, | ||
}; | ||
}, | ||
maxDepth, | ||
limitReactions, | ||
skipProcessed: true, | ||
}); | ||
let { masses, trees, products } = getMasses(results.trees, results.products); | ||
reactions.appendHead([oclMolecule]); | ||
reactions.applyOneReactantReactions( | ||
getDatabases({ kind: 'ionization', mode }, dwarEntry), | ||
{ | ||
min: minIonizationDepth, | ||
max: maxIonizationDepth, | ||
}, | ||
); | ||
reactions.applyOneReactantReactions( | ||
getDatabases({ kind: 'reaction', mode }, dwarEntry), | ||
{ | ||
min: minReactionDepth, | ||
max: maxReactionDepth, | ||
}, | ||
); | ||
const trees = reactions.trees; | ||
const validNodes = reactions.getValidNodes(); | ||
const masses = getMasses(trees); | ||
return { | ||
trees, | ||
validNodes, | ||
masses, | ||
trees, | ||
products, | ||
}; | ||
} | ||
function getMoleculesToFragment(tree, reactions, maxDepth, limitReactions) { | ||
for (let product of tree.products) { | ||
if (product.children.length === 0) { | ||
if (product.charge !== 0) { | ||
let molecule = OCL.Molecule.fromIDCode(product.idCode); | ||
let fragments = applyReactions([molecule], reactions, { | ||
maxDepth, | ||
limitReactions, | ||
getProductsTrees: true, | ||
}); | ||
// @ts-ignore | ||
product.children = fragments.trees; | ||
} | ||
} else { | ||
for (let child of product.children) { | ||
getMoleculesToFragment(child, reactions, maxDepth, limitReactions); | ||
} | ||
} | ||
} | ||
} |
/** | ||
* @description get array of mz from fragmentation trees | ||
* @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 | ||
* - products: array of trees grouped by product idCode | ||
* @returns {Array} array of mz values | ||
*/ | ||
export function getMasses(trees, products) { | ||
export function getMasses(trees) { | ||
let masses = {}; | ||
@@ -15,20 +12,16 @@ for (const tree of trees) { | ||
} | ||
return { | ||
masses: Object.keys(masses).map(Number), | ||
trees, | ||
products, | ||
}; | ||
return Object.keys(masses).map(Number); | ||
} | ||
function getMassesFromTree(currentBranch, masses) { | ||
for (const product of currentBranch.products) { | ||
if (Math.abs(product.charge) > 0) { | ||
masses[product.mz] = true; | ||
for (const molecule of currentBranch.molecules) { | ||
if (Math.abs(molecule.info.charge) > 0) { | ||
masses[molecule.info.mz] = true; | ||
} | ||
if (product.children.length > 0) { | ||
for (const child of product.children) { | ||
getMassesFromTree(child, masses); | ||
} | ||
} | ||
if (currentBranch.children && currentBranch.children.length > 0) { | ||
for (const child of currentBranch.children) { | ||
getMassesFromTree(child, masses); | ||
} | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
867724
27
3
2419
3
+ Addedreact-tree-svg@^0.6.1
+ Added@eslint-community/eslint-utils@4.4.1(transitive)
+ Added@eslint-community/regexpp@4.12.1(transitive)
+ Added@eslint/eslintrc@2.1.4(transitive)
+ Added@eslint/js@8.57.1(transitive)
+ Added@humanwhocodes/config-array@0.13.0(transitive)
+ Added@humanwhocodes/module-importer@1.0.1(transitive)
+ Added@humanwhocodes/object-schema@2.0.3(transitive)
+ Added@lukeed/csprng@1.1.0(transitive)
+ Added@lukeed/uuid@2.0.1(transitive)
+ Added@nodelib/fs.scandir@2.1.5(transitive)
+ Added@nodelib/fs.stat@2.0.5(transitive)
+ Added@nodelib/fs.walk@1.2.8(transitive)
+ Added@ungap/structured-clone@1.3.0(transitive)
+ Addedacorn@8.14.0(transitive)
+ Addedacorn-jsx@5.3.2(transitive)
+ Addedajv@6.12.6(transitive)
+ Addedansi-regex@5.0.1(transitive)
+ Addedansi-styles@4.3.0(transitive)
+ Addedargparse@2.0.1(transitive)
+ Addedbalanced-match@1.0.2(transitive)
+ Addedbrace-expansion@1.1.11(transitive)
+ Addedcallsites@3.1.0(transitive)
+ Addedchalk@4.1.2(transitive)
+ Addedcolor-convert@2.0.1(transitive)
+ Addedcolor-name@1.1.4(transitive)
+ Addedconcat-map@0.0.1(transitive)
+ Addedcross-spawn@7.0.6(transitive)
+ Addeddebug@4.4.0(transitive)
+ Addeddeep-is@0.1.4(transitive)
+ Addeddoctrine@3.0.0(transitive)
+ Addedescape-string-regexp@4.0.0(transitive)
+ Addedeslint@8.57.1(transitive)
+ Addedeslint-scope@7.2.2(transitive)
+ Addedeslint-visitor-keys@3.4.3(transitive)
+ Addedespree@9.6.1(transitive)
+ Addedesquery@1.6.0(transitive)
+ Addedesrecurse@4.3.0(transitive)
+ Addedestraverse@5.3.0(transitive)
+ Addedesutils@2.0.3(transitive)
+ Addedfast-deep-equal@3.1.3(transitive)
+ Addedfast-json-stable-stringify@2.1.0(transitive)
+ Addedfast-levenshtein@2.0.6(transitive)
+ Addedfastq@1.18.0(transitive)
+ Addedfile-entry-cache@6.0.1(transitive)
+ Addedfind-up@5.0.0(transitive)
+ Addedflat-cache@3.2.0(transitive)
+ Addedflatted@3.3.2(transitive)
+ Addedfs.realpath@1.0.0(transitive)
+ Addedglob@7.2.3(transitive)
+ Addedglob-parent@6.0.2(transitive)
+ Addedglobals@13.24.0(transitive)
+ Addedgraphemer@1.4.0(transitive)
+ Addedhas-flag@4.0.0(transitive)
+ Addedignore@5.3.2(transitive)
+ Addedimport-fresh@3.3.0(transitive)
+ Addedimurmurhash@0.1.4(transitive)
+ Addedinflight@1.0.6(transitive)
+ Addedinherits@2.0.4(transitive)
+ Addedis-extglob@2.1.1(transitive)
+ Addedis-glob@4.0.3(transitive)
+ Addedis-path-inside@3.0.3(transitive)
+ Addedisexe@2.0.0(transitive)
+ Addedjs-tokens@4.0.0(transitive)
+ Addedjs-yaml@4.1.0(transitive)
+ Addedjson-buffer@3.0.1(transitive)
+ Addedjson-schema-traverse@0.4.1(transitive)
+ Addedjson-stable-stringify-without-jsonify@1.0.1(transitive)
+ Addedkeyv@4.5.4(transitive)
+ Addedlevn@0.4.1(transitive)
+ Addedlocate-path@6.0.0(transitive)
+ Addedlodash.merge@4.6.2(transitive)
+ Addedloose-envify@1.4.0(transitive)
+ Addedminimatch@3.1.2(transitive)
+ Addedms@2.1.3(transitive)
+ Addednatural-compare@1.4.0(transitive)
+ Addednumeral@2.0.6(transitive)
+ Addedonce@1.4.0(transitive)
+ Addedopenchemlib-utils@5.21.1(transitive)
+ Addedoptionator@0.9.4(transitive)
+ Addedp-limit@3.1.0(transitive)
+ Addedp-locate@5.0.0(transitive)
+ Addedparent-module@1.0.1(transitive)
+ Addedpath-exists@4.0.0(transitive)
+ Addedpath-is-absolute@1.0.1(transitive)
+ Addedpath-key@3.1.1(transitive)
+ Addedprelude-ls@1.2.1(transitive)
+ Addedpunycode@2.3.1(transitive)
+ Addedqueue-microtask@1.2.3(transitive)
+ Addedreact@18.3.1(transitive)
+ Addedreact-dom@18.3.1(transitive)
+ Addedreact-tree-svg@0.6.1(transitive)
+ Addedresolve-from@4.0.0(transitive)
+ Addedreusify@1.0.4(transitive)
+ Addedrimraf@3.0.2(transitive)
+ Addedrun-parallel@1.2.0(transitive)
+ Addedscheduler@0.23.2(transitive)
+ Addedshebang-command@2.0.0(transitive)
+ Addedshebang-regex@3.0.0(transitive)
+ Addedstrip-ansi@6.0.1(transitive)
+ Addedstrip-json-comments@3.1.1(transitive)
+ Addedsupports-color@7.2.0(transitive)
+ Addedtext-table@0.2.0(transitive)
+ Addedtype-check@0.4.0(transitive)
+ Addedtype-fest@0.20.2(transitive)
+ Addeduri-js@4.4.1(transitive)
+ Addedwhich@2.0.2(transitive)
+ Addedword-wrap@1.2.5(transitive)
+ Addedwrappy@1.0.2(transitive)
+ Addedyocto-queue@0.1.0(transitive)
- Removedopenchemlib-utils@5.2.0(transitive)
Updatedopenchemlib-utils@^5.3.0