UNPKG

kekule

Version:

Open source JavaScript toolkit for chemoinformatics

579 lines (562 loc) 18.8 kB
/** * @fileoverview * This file contains basic classes to represent an element or isotope. * @author Partridge Jiang */ /* * requires /lan/classes.js * requires /core/kekule.common.js * requires /data/kekule.dataUtils.js * requires /localization/ */ /** * Enumeration of series of element. * These values are directly read from element data file. * Now the series should be deprecated, use {@link Kekule.ElementCategory} instead. * @enum * @deprecated */ Kekule.ElementSeries = { NONMETAL: 'Nonmetals', METAL: 'Metals', ALKALI_METAL: 'Alkali Metals', ALKALI_EARTH_METAL: 'Alkali Earth Metals', TRANSITION_METAL: 'Transition Metals', METALLOID: 'Metalloids', HALOGEN: 'Halogens', NOBLE_GAS: 'Noble Gases', LANTHANIDE: 'Lanthanides', ACTINIDE: 'Actinides' }; /** * Enumeration of element categories. * An element may belongs to multiple categories (e.g., Cl is both nonmetal and halogen). * @enum */ Kekule.ElementCategory = { NONMETAL: 1, METAL: 21, ALKALI_METAL: 22, ALKALI_EARTH_METAL: 23, TRANSITION_METAL: 24, METALLOID: 11, HALOGEN: 3, NOBLE_GAS: 2, LANTHANIDE: 25, ACTINIDE: 26 }; /** * Represent an element in periodical table. * @class * @augments Kekule.ChemObject * @param {Variant} symbolOrAtomicNumber Symbol(String) or atomic number(Int) of element. * * @property {Int} atomicNumber The atomic number of element. Read only. * @property {String} symbol The symbol of element. Read only. * @property {String} name Name of element. Read only. * @property {Int} group Group of element. Read only. * @property {Int} period Period of element. Read only. * @property {String} series Series of element, e.g. nonmetal. * @property {Float} naturalMass Natural mass of element. */ Kekule.Element = Class.create(Kekule.ChemObject, /** @lends Kekule.Element# */ { /** @private */ CLASS_NAME: 'Kekule.Element', /** * @constructs * @param {Variant} symbolOrAtomicNumber Symbol (String) or atomic number (Int) of element. */ initialize: function(/*$super, */symbolOrAtomicNumber) { this.tryApplySuper('initialize') /* $super() */; if (symbolOrAtomicNumber != Kekule.Element.UNSET_ELEMENT) { var elemInfo = this.getElementInfo(symbolOrAtomicNumber); if (elemInfo) { this.setPropStoreFieldValue('symbol', elemInfo.symbol); this.setPropStoreFieldValue('atomicNumber', elemInfo.atomicNumber); this.setPropStoreFieldValue('group', elemInfo.group); this.setPropStoreFieldValue('period', elemInfo.period); this.setPropStoreFieldValue('series', elemInfo.chemicalSerie); this.setPropStoreFieldValue('naturalMass', elemInfo.naturalMass); this.setPropStoreFieldValue('categories', this._getElementCategoriesFromSeries(elemInfo.chemicalSerie)); if (elemInfo.name) this.setPropStoreFieldValue('name', elemInfo.name); } else { Kekule.chemError( Kekule.hasLocalRes()? /*Kekule.ErrorMsg.INVALID_CHEMELEMENT*/Kekule.$L('ErrorMsg.INVALID_CHEMELEMENT') + ': ' + symbolOrAtomicNumber : 'Invalid chemical element: ' + symbolOrAtomicNumber ); } } else { this.setPropStoreFieldValue('symbol', Kekule.Element.UNSET_ELEMENT); this.setPropStoreFieldValue('atomicNumber', Kekule.Element.UNSET_ELEMENT); /* this.setPropStoreFieldValue('group', Kekule.Element.UNSET_ELEMENT); this.setPropStoreFieldValue('period', Kekule.Element.UNSET_ELEMENT); */ } }, /** @private */ initProperties: function() { this.defineProp('name', {'dataType': DataType.STRING, 'serializable': false, 'setter': null}); // Atomic number must be setted in constructor this.defineProp('atomicNumber', {'dataType': DataType.INT, 'serializable': false, 'setter': null}); // Symbol of element, read only. fsymbol must be setted in constructor this.defineProp('symbol', {'dataType': DataType.STRING, 'serializable': false, 'setter': null}); this.defineProp('group', {'dataType': DataType.INT, 'serializable': false, 'setter': null}); this.defineProp('period', {'dataType': DataType.INT, 'serializable': false, 'setter': null}); this.defineProp('series', {'dataType': DataType.STRING, 'serializable': false, 'setter': null}); this.defineProp('categories', {'dataType': DataType.ARRAY, 'serializable': false, 'setter': null}); this.defineProp('naturalMass', {'dataType': DataType.FLOAT, 'serializable': false, 'setter': null}); }, /** * Get element info of symbolOrAtomicNumber. Support pseudo element. * @param {Object} symbolOrAtomicNumber */ getElementInfo: function(symbolOrAtomicNumber) { if (symbolOrAtomicNumber == Kekule.Element.UNSET_ELEMENT) return {'symbol': Kekule.Element.UNSET_ELEMENT, 'atomicNumber': Kekule.Element.UNSET_ELEMENT}; if (Kekule.Element.isDummyElement(symbolOrAtomicNumber)) return {'symbol': Kekule.Element.DUMMY_ELEMENT, 'atomicNumber': Kekule.Element.DUMMY_ELEMENT_ATOMICNUM}; else if (Kekule.Element.isRGroup(symbolOrAtomicNumber)) return {'symbol': Kekule.Element.RGROUP_ELEMENT, 'atomicNumber': Kekule.Element.RGROUP_ELEMENT_ATMOICNUM}; else return Kekule.ChemicalElementsDataUtil.getElementInfo(symbolOrAtomicNumber); }, /** @private */ _getElementCategoriesFromSeries: function(series) { var ES = Kekule.ElementSeries; var EC = Kekule.ElementCategory; var result = []; switch (series) { case ES.NONMETAL: result = [EC.NONMETAL]; break; case ES.METAL: result = [EC.METAL]; break; case ES.ALKALI_METAL: result = [EC.METAL, EC.ALKALI_METAL]; break; case ES.ALKALI_EARTH_METAL: result = [EC.METAL, EC.ALKALI_EARTH_METAL]; break; case ES.TRANSITION_METAL: result = [EC.METAL, EC.TRANSITION_METAL]; break; case ES.METALLOID: result = [EC.METALLOID]; break; case ES.HALOGEN: result = [EC.NONMETAL, EC.HALOGEN]; break; case ES.NOBLE_GAS: result = [EC.NONMETAL, EC.NOBLE_GAS]; break; case ES.LANTHANIDE: result = [EC.METAL, EC.LANTHANIDE]; break; case ES.ACTINIDE: result = [EC.METAL, EC.ACTINIDE]; break; } return result; }, /** * Roughly get the theoretic valence of an element in a certain group. * The transitionmetal is not considered and will return null. * @returns {Int} Theoretic valence or null on transitionmetal. */ getTheoreticValence: function() { if (this.getGroup()) return Kekule.Element.getTheoreticValenceOfGroup(this.getGroup()); else return null; }, /** * Check if current element is a "dummy" one. * @returns {Bool} */ isDummyElement: function() { return this.getAtomicNumber() == Kekule.Element.DUMMY_ELEMENT_ATOMICNUM; }, /** * Check if this element is used to represent an RGroup * @returns {Bool} */ isRGroupElement: function() { return this.getAtomicNumber() == Kekule.Element.RGROUP_ELEMENT_ATMOICNUM; }, /** * Check if this is a normal element (not pseudo one, not unset one). * @returns {Bool} */ isNormalElement: function() { return Kekule.Element.isNormalElement(this.getAtomicNumber()); }, /** * Check if symbolOrAtomicNumber is same with this object. * @param {Variant} symbolOrAtomicNumber Symbol (String) or atomic number (Int) of element. * @returns {Bool} */ isSameElement: function(symbolOrAtomicNumber) { if (symbolOrAtomicNumber == Kekule.Element.UNSET_ELEMENT) return this.getAtomicNumber() == Kekule.Element.UNSET_ELEMENT; else if (typeof(symbolOrAtomicNumber) == 'number') return this.getAtomicNumber() == symbolOrAtomicNumber; else return this.getSymbol() == symbolOrAtomicNumber; }, /** * Returns whether this element is a hetero one (e.g., N, S, Cl...). * @returns {Bool} */ isHetero: function() { return ((this.getSeries() === Kekule.ElementSeries.NONMETAL) || (this.getSeries() === Kekule.ElementSeries.HALOGEN)) && (this.getAtomicNumber() !== 6) && (this.getAtomicNumber() !== 1); // not C/H }, /** * Check if this element belong to a certain category. * @param {Int} category Value from {@link Kekule.ElementCategory} * @returns {Bool} */ belongToCategory: function(category) { var cs = this.getCategories() || []; return cs.indexOf(category) >= 0; }, /** * Returns a string label to represent this element. * @returns {String} */ getLabel: function() { return this.getSymbol(); } }); // Copy all methods of Kekule.ChemicalElementsDataUtil to Element as shortcut Object.extend(Kekule.Element, Kekule.ChemicalElementsDataUtil); /** * Indicate the atomic number is unset and this is a unknown element. * @constant */ Kekule.Element.UNSET_ELEMENT = undefined; /** * A pseudo element to indicate a dummy atom. * @constant */ Kekule.Element.DUMMY_ELEMENT = 'DU'; Kekule.Element.DUMMY_ELEMENT_ATOMICNUM = -1; /** * A pseudo element to indicate a R-Group * @constant */ Kekule.Element.RGROUP_ELEMENT = 'R'; Kekule.Element.RGROUP_ELEMENT_ATOMICNUM = -2; /** * Label for deuterium. * @constant */ Kekule.Element.DEUTERIUM = 'D'; /** * Check if symbolOrAtomicNumber is a normal element (not pseudo one, not unset one). * @function * @param {Variant} symbolOrAtomicNumber * @returns {Bool} */ Kekule.Element.isNormalElement = function(symbolOrAtomicNumber) { return (symbolOrAtomicNumber != Kekule.Element.UNSET_ELEMENT) && (!Kekule.Element.isPseudoElement(symbolOrAtomicNumber)) }; /** * Check if symbolOrAtomicNumber is a pseudo element of dummy or RGroup. * @function * @param {Variant} symbolOrAtomicNumber * @returns {Bool} * @funtion */ Kekule.Element.isPseudoElement = function(symbolOrAtomicNumber) { if (typeof(symbolOrAtomicNumber) == 'string') return (symbolOrAtomicNumber == Kekule.Element.DUMMY_ELEMENT) || (symbolOrAtomicNumber == Kekule.Element.RGROUP_ELEMENT); else return (symbolOrAtomicNumber == Kekule.Element.DUMMY_ELEMENT_ATOMICNUM) || (symbolOrAtomicNumber == Kekule.Element.RGROUP_ELEMENT_ATMOICNUM); }; /** * Check if symbolOrAtomicNumber is a dummy element. * @function * @param {Variant} symbolOrAtomicNumber * @returns {Bool} */ Kekule.Element.isDummyElement = function(symbolOrAtomicNumber) { return (symbolOrAtomicNumber === Kekule.Element.DUMMY_ELEMENT) || (symbolOrAtomicNumber === Kekule.Element.DUMMY_ELEMENT_ATOMICNUM); }; /** * Check if symbolOrAtomicNumber is a RGroup * @function * @param {Variant} symbolOrAtomicNumber * @returns {Bool} */ Kekule.Element.isRGroup = function(symbolOrAtomicNumber) { return (symbolOrAtomicNumber === Kekule.Element.RGROUP_ELEMENT) || (symbolOrAtomicNumber === Kekule.Element.RGROUP_ELEMENT_ATOMICNUM); }; /** * Roughly get the theoretic valence of an element in a certain group. * The transitionmetal is not considered and will return null. * @function * @param {Int} group * @returns {Int} Theoretic valence or null on transitionmetal. */ Kekule.Element.getTheoreticValenceOfGroup = function(group) { if (group <= 2) return group; else if (group <= 12) return null; else if (group <= 17) return group - 10; else // group 18 return 0; }; /** * Roughly get the theoretic valence of an element. Only consider the group of element. * The transitionmetal is not considered and will return null. * @function * @param {Object} symbolOrAtomicNumber * @returns {Int} Theoretic valence or null on transitionmetal. */ Kekule.Element.getTheoreticValence = function(symbolOrAtomicNumber) { var atomInfo = Kekule.ChemicalElementsDataUtil.getElementInfo(symbolOrAtomicNumber); return atomInfo.group? Kekule.Element.getTheoreticValenceOfGroup(atomInfo.group): null; }; // Static methods of Kekule.Element Object.extend(Kekule.Element, /** @lends Kekule.Element */ { /** * Check if atomic number is legal in database * @param {Int} atomicNumber * @returns {Bool} */ isAtomicNumberAvailable: function(atomicNumber) { return Kekule.ChemicalElementsDataUtil.isAtomicNumberAvailable(atomicNumber); }, /** * Check if element symbol is legal in database. * @param {Int} atmoicNumber * @return {Bool} */ isElementSymbolAvailable: function(symbol) { return Kekule.ChemicalElementsDataUtil.isElementSymbolAvailable(symbol); } }); /** * Represent an isotope in periodical table. * @class * @augments Kekule.Element * @param {Variant} symbolOrAtomicNumber Symbol(String) or atomic number(Int) of isotope. * @param {Int} massNumber Mass number of isotope. * * @property {Int} massNumber The mass number of isotope. Read only. Setting to null means a genenral element. * @property {Float} exactMass Read only. * @property {Float} naturalAbundance Read only. * @property {String} isotopeAlias Alias of isotope (such as D for H2). Read only. */ Kekule.Isotope = Class.create(Kekule.Element, /** @lends Kekule.Isotope# */ { /** @private */ CLASS_NAME: 'Kekule.Isotope', /** * @constructs * @param {Variant} symbolOrAtomicNumber Symbol (String) or atomic number (Int) of element. * @param {Int} massNumber Isotope mass number. */ initialize: function(/*$super, */symbolOrAtomicNumber, massNumber) { var atomicNum = symbolOrAtomicNumber; var massNum = massNumber; // check if symbol is isotope alias var isoInfo = Kekule.IsotopesDataUtil.getIsotopeInfo(symbolOrAtomicNumber, massNumber); if (isoInfo) { //console.log(isoInfo); if (isoInfo.atomicNumber) atomicNum = isoInfo.atomicNumber; massNum = isoInfo.massNumber; } this.tryApplySuper('initialize', [atomicNum]) /* $super(atomicNum) */; this.setPropStoreFieldValue('massNumber', massNum); if (isoInfo && isoInfo.isotopeAlias) this.setPropStoreFieldValue('isotopeAlias', isoInfo.isotopeAlias); /* if ((symbolOrAtomicNumber != Kekule.Element.UNSET_ELEMENT) && (!Kekule.Element.isPseudoElement(symbolOrAtomicNumber)) && (massNumber != Kekule.Isotope.UNSET_MASSNUMBER)) */ if (Kekule.Element.isNormalElement(atomicNum) && (massNum !== Kekule.Isotope.UNSET_MASSNUMBER)) { // get rest of properties' value from isotope data var isotopeInfo = Kekule.IsotopesDataUtil.getIsotopeInfo(this.getAtomicNumber(), massNum); if (isotopeInfo) { this.setPropStoreFieldValue('exactMass', isotopeInfo.exactMass); this.setPropStoreFieldValue('naturalAbundance', isotopeInfo.naturalAbundance); } else { Kekule.chemError( (Kekule.hasLocalRes() ? /*Kekule.ErrorMsg.INVALID_ISOTOPE*/Kekule.$L('ErrorMsg.INVALID_ISOTOPE') : 'Invalid isotope') + ': ' + this.getSymbol() + '/' + massNumber); } } }, /** @private */ initProperties: function() { this.defineProp('massNumber', {'dataType': DataType.INTEGER, 'serializable': false, 'setter': null}); this.defineProp('exactMass', {'dataType': DataType.FLOAT, 'serializable': false, 'setter': null}); this.defineProp('naturalAbundance', {'dataType': DataType.FLOAT, 'serializable': false, 'setter': null}); this.defineProp('isotopeAlias', {'dataType': DataType.STRING, 'serializable': false, 'setter': null}); }, /* @ignore */ /* doGetSymbol: function($super) { if (this.getAtomicNumber() === 1 && this.getMassNumber() === 2) // DEUTERIUM return Kekule.Element.DEUTERIUM; else return $super(); }, */ /** * Check if obj is the same isotope with this one. * @param {Object} obj * @returns {Bool} */ isSame: function(obj) { if (obj instanceof Kekule.Isotope) { return (obj === this) || ((obj.getAtomicNumber() == this.getAtomicNumber()) && (obj.getMassNumber() == this.getMassNumber())); } return false; }, /** * Get an unique id for current isotope. * @returns {String} */ getIsotopeId: function() { return Kekule.IsotopesDataUtil.getIsotopeId(this.getAtomicNumber(), this.getMassNumber()); }, /** @ignore */ getLabel: function() { return '' + (this.getMassNumber() || '') + this.getSymbol(); } }); // Copy all methods of Kekule.IsotopesDataUtil to Isotope as shortcut Object.extend(Kekule.Isotope, Kekule.IsotopesDataUtil); /** * Indicate the mass number is unset and this is a general element. * @constant */ Kekule.Isotope.UNSET_MASSNUMBER = undefined; /** * Get an unique id for isotope. * @param {Variant} symbolOrAtomicNumber * @param {Int} massNumber * @returns {String} * @function */ Kekule.Isotope.getIsotopeId = function(symbolOrAtomicNumber, massNumber) { return Kekule.IsotopesDataUtil.getIsotopeId(symbolOrAtomicNumber, massNumber); }; Object.extend(Kekule.Isotope, /** @lends Kekule.Isotope */ { /** * Check if mass number is legal for an element * @param {Varaint} symbolOrAtomicNumber Element symbol(String) or atomic number(Integer). * @param {Int} massNumber * @return {Bool} */ isMassNumberAvailable: function(symbolOrAtomicNumber, massNumber) { return Kekule.IsotopesDataUtil.isMassNumberAvailable(symbolOrAtomicNumber, massNumber); } }); /** * Factory to create and get suitable isotope. * @class */ Kekule.IsotopeFactory = { /** @private */ GENERIC_ELEMENT_ID: '__GENERIC__', /** @private */ _isotopes: {}, /** @private */ getIsotopeId: function(symbolOrAtomicNumber, massNumber) { if (!symbolOrAtomicNumber) // an generic element return Kekule.IsotopeFactory.GENERIC_ELEMENT_ID; else return Kekule.IsotopesDataUtil.getIsotopeId(symbolOrAtomicNumber, massNumber); }, /** * Returns a suitable isotope object. * @param {Varaint} symbolOrAtomicNumber Element symbol(String) or atomic number(Integer). * @param {Int} massNumber * @returns {Kekule.Isotope} */ getIsotope: function(symbolOrAtomicNumber, massNumber) { var id = Kekule.IsotopeFactory.getIsotopeId(symbolOrAtomicNumber, massNumber); if (!Kekule.IsotopeFactory._isotopes[id]) { var isotope = new Kekule.Isotope(symbolOrAtomicNumber, massNumber); Kekule.IsotopeFactory._isotopes[id] = isotope; } return Kekule.IsotopeFactory._isotopes[id]; }, /** * Returns a suitable isotope by id. * @param {String} id Isotope ID, such as H2, C13. * @returns {Kekule.Isotope} */ getIsotopeById: function(id) { if (Kekule.IsotopeFactory._isotopes[id]) { return Kekule.IsotopeFactory._isotopes[id]; } else { var detail = Kekule.IsotopesDataUtil.getIsotopeIdDetail(id); if (detail) return Kekule.IsotopeFactory.getIsotope(detail.symbol, detail.massNumber); else return null; } } }; /** * @class */ Kekule.AtomType = { /** * Indicate the atom type is unset. * @constant */ UNSET_ATOMTYPE: undefined };