forked from enviPath/enviPy
Current Dev State
This commit is contained in:
488
static/js/ketcher2/script/chem/molfile/molfile.js
Normal file
488
static/js/ketcher2/script/chem/molfile/molfile.js
Normal file
@ -0,0 +1,488 @@
|
||||
/****************************************************************************
|
||||
* Copyright 2017 EPAM Systems
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
***************************************************************************/
|
||||
|
||||
var element = require('./../element');
|
||||
|
||||
var common = require('./common');
|
||||
var utils = require('./utils');
|
||||
|
||||
function Molfile(v3000) {
|
||||
/* reader */
|
||||
/* saver */
|
||||
this.molecule = null;
|
||||
this.molfile = null;
|
||||
this.v3000 = v3000 || false;
|
||||
}
|
||||
|
||||
Molfile.prototype.parseCTFile = function (molfileLines) {
|
||||
var ret = null;
|
||||
if (molfileLines[0].search('\\$RXN') == 0)
|
||||
ret = common.parseRxn(molfileLines);
|
||||
else
|
||||
ret = common.parseMol(molfileLines);
|
||||
ret.initHalfBonds();
|
||||
ret.initNeighbors();
|
||||
ret.markFragments();
|
||||
return ret;
|
||||
};
|
||||
|
||||
Molfile.prototype.prepareSGroups = function (skipErrors, preserveIndigoDesc) {
|
||||
var mol = this.molecule;
|
||||
var toRemove = [];
|
||||
var errors = 0;
|
||||
|
||||
this.molecule.sGroupForest.getSGroupsBFS().reverse().forEach(function (id) {
|
||||
var sgroup = mol.sgroups.get(id);
|
||||
var errorIgnore = false;
|
||||
|
||||
try {
|
||||
common.prepareForSaving[sgroup.type](sgroup, mol);
|
||||
} catch (ex) {
|
||||
if (!skipErrors || typeof (ex.id) != 'number')
|
||||
throw ex;
|
||||
errorIgnore = true;
|
||||
}
|
||||
/* eslint-disable no-mixed-operators*/
|
||||
if (errorIgnore ||
|
||||
!preserveIndigoDesc && /^INDIGO_.+_DESC$/i.test(sgroup.data.fieldName)) {
|
||||
/* eslint-enable no-mixed-operators*/
|
||||
errors += errorIgnore;
|
||||
toRemove.push(sgroup.id);
|
||||
}
|
||||
}, this);
|
||||
if (errors)
|
||||
throw new Error('WARNING: ' + errors + ' invalid S-groups were detected. They will be omitted.');
|
||||
|
||||
for (var i = 0; i < toRemove.length; ++i)
|
||||
mol.sGroupDelete(toRemove[i]);
|
||||
return mol;
|
||||
};
|
||||
|
||||
Molfile.prototype.getCTab = function (molecule, rgroups) {
|
||||
/* saver */
|
||||
this.molecule = molecule.clone();
|
||||
this.molfile = '';
|
||||
this.writeCTab2000(rgroups);
|
||||
return this.molfile;
|
||||
};
|
||||
|
||||
Molfile.prototype.saveMolecule = function (molecule, skipSGroupErrors, norgroups, preserveIndigoDesc) { // eslint-disable-line max-statements
|
||||
/* saver */
|
||||
this.reaction = molecule.rxnArrows.count() > 0;
|
||||
if (molecule.rxnArrows.count() > 1)
|
||||
throw new Error('Reaction may not contain more than one arrow');
|
||||
this.molfile = '' + molecule.name;
|
||||
if (this.reaction) {
|
||||
if (molecule.rgroups.count() > 0)
|
||||
throw new Error('Unable to save the structure - reactions with r-groups are not supported at the moment');
|
||||
var components = molecule.getComponents();
|
||||
|
||||
var reactants = components.reactants;
|
||||
var products = components.products;
|
||||
var all = reactants.concat(products);
|
||||
this.molfile = '$RXN\n\n\n\n' +
|
||||
utils.paddedNum(reactants.length, 3) +
|
||||
utils.paddedNum(products.length, 3) +
|
||||
utils.paddedNum(0, 3) + '\n';
|
||||
for (var i = 0; i < all.length; ++i) {
|
||||
var saver = new Molfile(false);
|
||||
var submol = molecule.clone(all[i], null, true);
|
||||
var molfile = saver.saveMolecule(submol, false, true);
|
||||
this.molfile += '$MOL\n' + molfile;
|
||||
}
|
||||
return this.molfile;
|
||||
}
|
||||
|
||||
if (molecule.rgroups.count() > 0) {
|
||||
if (norgroups) {
|
||||
molecule = molecule.getScaffold();
|
||||
} else {
|
||||
var scaffold = new Molfile(false).getCTab(molecule.getScaffold(), molecule.rgroups);
|
||||
this.molfile = '$MDL REV 1\n$MOL\n$HDR\n\n\n\n$END HDR\n';
|
||||
this.molfile += '$CTAB\n' + scaffold + '$END CTAB\n';
|
||||
|
||||
molecule.rgroups.each(function (rgid, rg) {
|
||||
this.molfile += '$RGP\n';
|
||||
this.writePaddedNumber(rgid, 3);
|
||||
this.molfile += '\n';
|
||||
rg.frags.each(function (fnum, fid) {
|
||||
var group = new Molfile(false).getCTab(molecule.getFragment(fid));
|
||||
this.molfile += '$CTAB\n' + group + '$END CTAB\n';
|
||||
}, this);
|
||||
this.molfile += '$END RGP\n';
|
||||
}, this);
|
||||
this.molfile += '$END MOL\n';
|
||||
|
||||
return this.molfile;
|
||||
}
|
||||
}
|
||||
|
||||
this.molecule = molecule.clone();
|
||||
|
||||
this.prepareSGroups(skipSGroupErrors, preserveIndigoDesc);
|
||||
|
||||
this.writeHeader();
|
||||
|
||||
// TODO: saving to V3000
|
||||
this.writeCTab2000();
|
||||
|
||||
return this.molfile;
|
||||
};
|
||||
|
||||
Molfile.prototype.writeHeader = function () {
|
||||
/* saver */
|
||||
|
||||
var date = new Date();
|
||||
|
||||
this.writeCR(); // TODO: write structure name
|
||||
this.writeWhiteSpace(2);
|
||||
this.write('Ketcher');
|
||||
this.writeWhiteSpace();
|
||||
this.writeCR(((date.getMonth() + 1) + '').padStart(2) + (date.getDate() + '').padStart(2) + ((date.getFullYear() % 100) + '').padStart(2) +
|
||||
(date.getHours() + '').padStart(2) + (date.getMinutes() + '').padStart(2) + '2D 1 1.00000 0.00000 0');
|
||||
this.writeCR();
|
||||
};
|
||||
|
||||
Molfile.prototype.write = function (str) {
|
||||
/* saver */
|
||||
this.molfile += str;
|
||||
};
|
||||
|
||||
Molfile.prototype.writeCR = function (str) {
|
||||
/* saver */
|
||||
if (arguments.length == 0)
|
||||
str = '';
|
||||
|
||||
this.molfile += str + '\n';
|
||||
};
|
||||
|
||||
Molfile.prototype.writeWhiteSpace = function (length) {
|
||||
/* saver */
|
||||
|
||||
if (arguments.length == 0)
|
||||
length = 1;
|
||||
|
||||
this.write(' '.repeat(Math.max(length, 0)));
|
||||
};
|
||||
|
||||
Molfile.prototype.writePadded = function (str, width) {
|
||||
/* saver */
|
||||
this.write(str);
|
||||
this.writeWhiteSpace(width - str.length);
|
||||
};
|
||||
|
||||
Molfile.prototype.writePaddedNumber = function (number, width) {
|
||||
/* saver */
|
||||
|
||||
var str = (number - 0).toString();
|
||||
|
||||
this.writeWhiteSpace(width - str.length);
|
||||
this.write(str);
|
||||
};
|
||||
|
||||
Molfile.prototype.writePaddedFloat = function (number, width, precision) {
|
||||
/* saver */
|
||||
|
||||
this.write(utils.paddedNum(number, width, precision));
|
||||
};
|
||||
|
||||
Molfile.prototype.writeCTab2000Header = function () {
|
||||
/* saver */
|
||||
|
||||
this.writePaddedNumber(this.molecule.atoms.count(), 3);
|
||||
this.writePaddedNumber(this.molecule.bonds.count(), 3);
|
||||
|
||||
this.writePaddedNumber(0, 3);
|
||||
this.writeWhiteSpace(3);
|
||||
this.writePaddedNumber(this.molecule.isChiral ? 1 : 0, 3);
|
||||
this.writePaddedNumber(0, 3);
|
||||
this.writeWhiteSpace(12);
|
||||
this.writePaddedNumber(999, 3);
|
||||
this.writeCR(' V2000');
|
||||
};
|
||||
|
||||
Molfile.prototype.writeCTab2000 = function (rgroups) { // eslint-disable-line max-statements
|
||||
/* saver */
|
||||
this.writeCTab2000Header();
|
||||
|
||||
this.mapping = {};
|
||||
var i = 1;
|
||||
|
||||
/* eslint-disable camelcase*/
|
||||
var atomList_list = [];
|
||||
var atomProps_list = [];
|
||||
/* eslint-enable camel-case*/
|
||||
this.molecule.atoms.each(function (id, atom) { // eslint-disable-line max-statements
|
||||
this.writePaddedFloat(atom.pp.x, 10, 4);
|
||||
this.writePaddedFloat(-atom.pp.y, 10, 4);
|
||||
this.writePaddedFloat(atom.pp.z, 10, 4);
|
||||
this.writeWhiteSpace();
|
||||
|
||||
var label = atom.label;
|
||||
if (atom.atomList != null) {
|
||||
label = 'L';
|
||||
atomList_list.push(id);
|
||||
} else if (atom['pseudo']) {
|
||||
if (atom['pseudo'].length > 3) {
|
||||
label = 'A';
|
||||
atomProps_list.push({ id: id, value: "'" + atom['pseudo'] + "'" });
|
||||
}
|
||||
} else if (atom['alias']) {
|
||||
atomProps_list.push({ id: id, value: atom['alias'] });
|
||||
} else if (!element.map[label] && ['A', 'Q', 'X', '*', 'R#'].indexOf(label) == -1) { // search in generics?
|
||||
label = 'C';
|
||||
atomProps_list.push({ id: id, value: atom.label });
|
||||
}
|
||||
this.writePadded(label, 3);
|
||||
this.writePaddedNumber(0, 2);
|
||||
this.writePaddedNumber(0, 3);
|
||||
this.writePaddedNumber(0, 3);
|
||||
|
||||
if (typeof atom.hCount === "undefined")
|
||||
atom.hCount = 0;
|
||||
this.writePaddedNumber(atom.hCount, 3);
|
||||
|
||||
if (typeof atom.stereoCare === "undefined")
|
||||
atom.stereoCare = 0;
|
||||
this.writePaddedNumber(atom.stereoCare, 3);
|
||||
|
||||
this.writePaddedNumber(atom.explicitValence < 0 ? 0 : (atom.explicitValence == 0 ? 15 : atom.explicitValence), 3); // eslint-disable-line no-nested-ternary
|
||||
|
||||
this.writePaddedNumber(0, 3);
|
||||
this.writePaddedNumber(0, 3);
|
||||
this.writePaddedNumber(0, 3);
|
||||
|
||||
if (typeof atom.aam === "undefined")
|
||||
atom.aam = 0;
|
||||
this.writePaddedNumber(atom.aam, 3);
|
||||
|
||||
if (typeof atom.invRet === "undefined")
|
||||
atom.invRet = 0;
|
||||
this.writePaddedNumber(atom.invRet, 3);
|
||||
|
||||
if (typeof atom.exactChangeFlag === "undefined")
|
||||
atom.exactChangeFlag = 0;
|
||||
this.writePaddedNumber(atom.exactChangeFlag, 3);
|
||||
|
||||
this.writeCR();
|
||||
|
||||
this.mapping[id] = i;
|
||||
i++;
|
||||
}, this);
|
||||
|
||||
this.bondMapping = {};
|
||||
i = 1;
|
||||
this.molecule.bonds.each(function (id, bond) {
|
||||
this.bondMapping[id] = i++;
|
||||
this.writePaddedNumber(this.mapping[bond.begin], 3);
|
||||
this.writePaddedNumber(this.mapping[bond.end], 3);
|
||||
this.writePaddedNumber(bond.type, 3);
|
||||
|
||||
if (typeof bond.stereo === "undefined")
|
||||
bond.stereo = 0;
|
||||
this.writePaddedNumber(bond.stereo, 3);
|
||||
|
||||
this.writePadded(bond.xxx, 3);
|
||||
|
||||
if (typeof bond.topology === "undefined")
|
||||
bond.topology = 0;
|
||||
this.writePaddedNumber(bond.topology, 3);
|
||||
|
||||
if (typeof bond.reactingCenterStatus === "undefined")
|
||||
bond.reactingCenterStatus = 0;
|
||||
this.writePaddedNumber(bond.reactingCenterStatus, 3);
|
||||
|
||||
this.writeCR();
|
||||
}, this);
|
||||
|
||||
while (atomProps_list.length > 0) {
|
||||
this.write('A ');
|
||||
this.writePaddedNumber(atomProps_list[0].id + 1, 3);
|
||||
this.writeCR();
|
||||
this.writeCR(atomProps_list[0].value);
|
||||
atomProps_list.splice(0, 1);
|
||||
}
|
||||
|
||||
var chargeList = [];
|
||||
var isotopeList = [];
|
||||
var radicalList = [];
|
||||
var rglabelList = [];
|
||||
var rglogicList = [];
|
||||
var aplabelList = [];
|
||||
var rbcountList = [];
|
||||
var unsaturatedList = [];
|
||||
var substcountList = [];
|
||||
|
||||
this.molecule.atoms.each(function (id, atom) {
|
||||
if (atom.charge != 0)
|
||||
chargeList.push([id, atom.charge]);
|
||||
if (atom.isotope != 0)
|
||||
isotopeList.push([id, atom.isotope]);
|
||||
if (atom.radical != 0)
|
||||
radicalList.push([id, atom.radical]);
|
||||
if (atom.rglabel != null && atom.label == 'R#') { // TODO need to force rglabel=null when label is not 'R#'
|
||||
for (var rgi = 0; rgi < 32; rgi++)
|
||||
if (atom.rglabel & (1 << rgi)) rglabelList.push([id, rgi + 1]);
|
||||
}
|
||||
if (atom.attpnt != null)
|
||||
aplabelList.push([id, atom.attpnt]);
|
||||
if (atom.ringBondCount != 0)
|
||||
rbcountList.push([id, atom.ringBondCount]);
|
||||
if (atom.substitutionCount != 0)
|
||||
substcountList.push([id, atom.substitutionCount]);
|
||||
if (atom.unsaturatedAtom != 0)
|
||||
unsaturatedList.push([id, atom.unsaturatedAtom]);
|
||||
});
|
||||
|
||||
if (rgroups) {
|
||||
rgroups.each(function (rgid, rg) {
|
||||
if (rg.resth || rg.ifthen > 0 || rg.range.length > 0) {
|
||||
var line = ' 1 ' +
|
||||
utils.paddedNum(rgid, 3) + ' ' +
|
||||
utils.paddedNum(rg.ifthen, 3) + ' ' +
|
||||
utils.paddedNum(rg.resth ? 1 : 0, 3) + ' ' + rg.range;
|
||||
rglogicList.push(line);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function writeAtomPropList(propId, values) {
|
||||
while (values.length > 0) {
|
||||
var part = [];
|
||||
|
||||
while (values.length > 0 && part.length < 8) {
|
||||
part.push(values[0]);
|
||||
values.splice(0, 1);
|
||||
}
|
||||
|
||||
this.write(propId);
|
||||
this.writePaddedNumber(part.length, 3);
|
||||
|
||||
part.forEach(function (value) {
|
||||
this.writeWhiteSpace();
|
||||
this.writePaddedNumber(this.mapping[value[0]], 3);
|
||||
this.writeWhiteSpace();
|
||||
this.writePaddedNumber(value[1], 3);
|
||||
}, this);
|
||||
|
||||
this.writeCR();
|
||||
}
|
||||
}
|
||||
|
||||
writeAtomPropList.call(this, 'M CHG', chargeList);
|
||||
writeAtomPropList.call(this, 'M ISO', isotopeList);
|
||||
writeAtomPropList.call(this, 'M RAD', radicalList);
|
||||
writeAtomPropList.call(this, 'M RGP', rglabelList);
|
||||
for (var j = 0; j < rglogicList.length; ++j)
|
||||
this.write('M LOG' + rglogicList[j] + '\n');
|
||||
|
||||
writeAtomPropList.call(this, 'M APO', aplabelList);
|
||||
writeAtomPropList.call(this, 'M RBC', rbcountList);
|
||||
writeAtomPropList.call(this, 'M SUB', substcountList);
|
||||
writeAtomPropList.call(this, 'M UNS', unsaturatedList);
|
||||
|
||||
if (atomList_list.length > 0) {
|
||||
for (j = 0; j < atomList_list.length; ++j) {
|
||||
var aid = atomList_list[j];
|
||||
var atomList = this.molecule.atoms.get(aid).atomList;
|
||||
this.write('M ALS');
|
||||
this.writePaddedNumber(aid + 1, 4);
|
||||
this.writePaddedNumber(atomList.ids.length, 3);
|
||||
this.writeWhiteSpace();
|
||||
this.write(atomList.notList ? 'T' : 'F');
|
||||
|
||||
var labelList = atomList.labelList();
|
||||
for (var k = 0; k < labelList.length; ++k) {
|
||||
this.writeWhiteSpace();
|
||||
this.writePadded(labelList[k], 3);
|
||||
}
|
||||
this.writeCR();
|
||||
}
|
||||
}
|
||||
|
||||
var sgmap = {};
|
||||
var cnt = 1;
|
||||
var sgmapback = {};
|
||||
var sgorder = this.molecule.sGroupForest.getSGroupsBFS();
|
||||
sgorder.forEach(function (id) {
|
||||
sgmapback[cnt] = id;
|
||||
sgmap[id] = cnt++;
|
||||
}, this);
|
||||
for (var q = 1; q < cnt; ++q) { // each group on its own
|
||||
var id = sgmapback[q];
|
||||
var sgroup = this.molecule.sgroups.get(id);
|
||||
this.write('M STY');
|
||||
this.writePaddedNumber(1, 3);
|
||||
this.writeWhiteSpace(1);
|
||||
this.writePaddedNumber(q, 3);
|
||||
this.writeWhiteSpace(1);
|
||||
this.writePadded(sgroup.type, 3);
|
||||
this.writeCR();
|
||||
|
||||
// TODO: write subtype, M SST
|
||||
|
||||
this.write('M SLB');
|
||||
this.writePaddedNumber(1, 3);
|
||||
this.writeWhiteSpace(1);
|
||||
this.writePaddedNumber(q, 3);
|
||||
this.writeWhiteSpace(1);
|
||||
this.writePaddedNumber(q, 3);
|
||||
this.writeCR();
|
||||
|
||||
var parentid = this.molecule.sGroupForest.parent.get(id);
|
||||
if (parentid >= 0) {
|
||||
this.write('M SPL');
|
||||
this.writePaddedNumber(1, 3);
|
||||
this.writeWhiteSpace(1);
|
||||
this.writePaddedNumber(q, 3);
|
||||
this.writeWhiteSpace(1);
|
||||
this.writePaddedNumber(sgmap[parentid], 3);
|
||||
this.writeCR();
|
||||
}
|
||||
|
||||
// connectivity
|
||||
if (sgroup.type == 'SRU' && sgroup.data.connectivity) {
|
||||
var connectivity = '';
|
||||
connectivity += ' ';
|
||||
connectivity += q.toString().padStart(3);
|
||||
connectivity += ' ';
|
||||
connectivity += (sgroup.data.connectivity || '').padEnd(3);
|
||||
this.write('M SCN');
|
||||
this.writePaddedNumber(1, 3);
|
||||
this.write(connectivity.toUpperCase());
|
||||
this.writeCR();
|
||||
}
|
||||
|
||||
if (sgroup.type == 'SRU') {
|
||||
this.write('M SMT ');
|
||||
this.writePaddedNumber(q, 3);
|
||||
this.writeWhiteSpace();
|
||||
this.write(sgroup.data.subscript || 'n');
|
||||
this.writeCR();
|
||||
}
|
||||
|
||||
this.writeCR(common.saveToMolfile[sgroup.type](sgroup, this.molecule, sgmap, this.mapping, this.bondMapping));
|
||||
}
|
||||
|
||||
// TODO: write M APO
|
||||
// TODO: write M AAL
|
||||
// TODO: write M RGP
|
||||
// TODO: write M LOG
|
||||
|
||||
this.writeCR('M END');
|
||||
};
|
||||
|
||||
module.exports = Molfile;
|
||||
Reference in New Issue
Block a user