/**
* @fileoverview
* Standardization routines for chemical structures, especially for ctab based ones.
* Standardization may include canonicalization, aromatic perception and so on.
* @author Partridge Jiang
*/
/*
* requires /lan/classes.js
* requires /core/kekule.common.js
* requires /core/kekule.structures.js
* requires /core/kekule.chemUtils.js
* requires /utils/kekule.utils.js
* requires /algorithm/kekule.structure.canonicalizers.js
* requires /algorithm/kekule.structure.aromatics.js
* requires /algorithm/kekule.structure.stereos.js
*/
(function(){
"use strict";
/**
* Default options to do structure standardize.
* @object
*/
Kekule.globalOptions.add('algorithm.molStandardization', {
cleanStructure: true,
unmarshalSubFragments: true,
doCanonicalization: true,
doAromaticPerception: true,
doStereoPerception: true
});
/**
* A helper class to standardize molecule.
* @class
*/
Kekule.MolStandardizer = {
/**
* Standardize a structure fragment (molecule).
* Standardization may include canonicalization, aromatic perception and so on.
* @param {Kekule.StructureFragment} structureFragment
* @param {Hash} options Standardization options, including the following fields:
* {
* cleanStructure: bool, whether clean the structure before standardization.
* unmarshalSubFragments: bool, whether unmarshal all sub structures cascadedly of molecule, default is true.
* doCanonicalization: bool, whether do canonicalization to molecule, default is true.
* canonicalizerExecutorId: string, which canonicalizer executor should be used. If this
* value is not set, default one will be used instead.
* doAromaticPerception: bool, whether do aromatic ring perception to molecule, default is true.
* doStereoPerception: bool, whether detect and mark all stereo factors of nodes and connectors, default is true.
* }.
*/
standardize: function(structureFragment, options)
{
var defOptions = Object.extend({}, Kekule.globalOptions.algorithm.molStandardization);
var mol = structureFragment;
var op = Object.extend(defOptions, options);
if (op.unmarshalSubFragments)
mol.unmarshalAllSubFragments(true);
if (op.cleanStructure)
mol.clean();
/*
if (op.doStereoPerception)
{
Kekule.canonicalizer.canonicalize(mol, op.canonicalizerExecutorId || null);
mol.perceiveStereos(null, true); // stereo detection should do canonicalization first
}
else*/ if (op.doCanonicalization)
Kekule.canonicalizer.canonicalize(mol, op.canonicalizerExecutorId || null);
if (op.doAromaticPerception)
{
mol.perceiveAromaticRings();
//console.log('perceive aromatics');
}
if (op.doStereoPerception)
mol.perceiveStereos(null, true); // already canonicalized, no need to do again, what's more, canonicalization may clear the ring info already perceived
return mol;
}
};
Object.extend(Kekule.ChemStructureUtils,
/** @lends Kekule.ChemStructureUtils */
{
/**
* Compare two structure fragment (molecule) in chem level (atoms, bonds and structures).
* @param {Kekule.StructureFragment} mol1
* @param {Kekule.StructureFragment} mol2
* @param {Hash} compareOptions
* @returns {Int}
*/
compareStructFragment: function(mol1, mol2, compareOptions)
{
// clone the structure to avoid change original molecule objects
var m1 = mol1.clone(false);
var m2 = mol2.clone(false);
// set cano index of two root molecules to null, avoid affect the following comparison
m1.setCanonicalizationIndex(null);
m2.setCanonicalizationIndex(null);
// standardize each
m1 = Kekule.MolStandardizer.standardize(m1);
m2 = Kekule.MolStandardizer.standardize(m2);
// compare options
var op = Object.create(compareOptions || {}); //Object.extend(compareOptions || {});
op.doStandardize = false; // flag that notify the molecule do not do standardize again (that will invoke recursion)
op.extraComparisonProperties = ['canonicalizationIndex']; // flag that indicate the cano step has been done and the canoIndex can be used in comparison
// compare
//return Kekule.UnivChemStructObjComparer.compare(m1, m2, op) === 0;
return m1.compareStructure(m2, op);
},
/**
* Check if two structure fragment (molecule) is same in chem level (same
* atoms, bonds and structures).
* @param {Kekule.StructureFragment} mol1
* @param {Kekule.StructureFragment} mol2
* @param {Hash} compareOptions
* @returns {Bool}
*/
isSameStructure: function(mol1, mol2, compareOptions)
{
return Kekule.ChemStructureUtils.compareStructFragment(mol1, mol2, compareOptions) === 0;
}
});
ClassEx.extend(Kekule.ChemObject,
/** @lends Kekule.ChemObject# */
{
/**
* Standardize this structure fragment (molecule).
* Standardization may include canonicalization, aromatic perception and so on.
* @param {Hash} options Standardization options, including the following fields:
* {
* unmarshalSubFragments: bool, whether unmarshal all sub structures cascadedly of molecule, default is true.
* doCanonicalization: bool, whether do canonicalization to molecule, default is true.
* canonicalizerExecutorId: string, which canonicalizer executor should be used. If this
* value is not set, default one will be used instead.
* doAromaticPerception: bool, whether do aromatic ring perception to molecule, default is true.
* }.
*/
standardize: function(options)
{
// find out all molecule
var mols = Kekule.ChemStructureUtils.getAllStructFragments(this, true);
for (var i = 0, l = mols.length; i < l; ++i)
{
mols[i].standardize(options);
}
return this;
}
});
ClassEx.extend(Kekule.StructureFragment,
/** @lends Kekule.StructureFragment# */
{
/**
* Standardize this structure fragment (molecule).
* Standardization may include canonicalization, aromatic perception and so on.
* @param {Hash} options Standardization options, including the following fields:
* {
* unmarshalSubFragments: bool, whether unmarshal all sub structures cascadedly of molecule, default is true.
* doCanonicalization: bool, whether do canonicalization to molecule, default is true.
* canonicalizerExecutorId: string, which canonicalizer executor should be used. If this
* value is not set, default one will be used instead.
* doAromaticPerception: bool, whether do aromatic ring perception to molecule, default is true.
* }.
*/
standardize: function(options)
{
return Kekule.MolStandardizer.standardize(this, options);
},
/**
* Check if two structure fragment (molecule) is same in chem level (same
* atoms, bonds and structures).
* @param {Kekule.StructureFragment} target
* @param {Hash} compareOptions
* @returns {Bool}
*/
isSameStructureWith: function(target, compareOptions)
{
return Kekule.ChemStructureUtils.isSameStructure(this, target, compareOptions);
}
});
ClassEx.extendMethod(Kekule.StructureFragment, 'compare', function($origin, targetObj, options){
if (options.method === Kekule.ComparisonMethod.CHEM_STRUCTURE
&& (options.doStandardize !== false)
&& (targetObj instanceof Kekule.StructureFragment))
{
return Kekule.ChemStructureUtils.compareStructFragment(this, targetObj, options);
}
else
return $origin(targetObj, options);
});
})();