kekule
Version:
Open source JavaScript toolkit for chemoinformatics
506 lines (465 loc) • 17.6 kB
JavaScript
/**
* @fileoverview
* Constants and functions about basic metrics.
* @author Partridge Jiang
*/
/*
* requires /lan/classes.js
* requires /core/kekule.root.js
* requires /localization
*/
(function(){
"use strict";
/**
* Root name space for units.
* @namespace
*/
Kekule.Unit = {
/** @private */
ALL: {},
/** @private */
_KEY_MAP: {},
/**
* Register a unit.
* After register, a Kekule.Unit[category][name.capitalized] will be set with value symbol (e.g., Kekule.Unit.Frequency.HERTZ = 'Hz').
* @param {String} symbol The symbol of unit, e.g. 'Hz'.
* This value will be regarded as key of unit. If this value is empty, name will be regarded as key instead.
* @param {String} name Name of unit, e.g., 'Hertz'.
* @param {String} category Category of unit, e.g. 'Frequency'.
* Note the first letter of category is recommended to be capitalized.
* @param {Number} rateToStandard The conversion rate to SI standard unit in this category.
* For example, this value should be 1e-3 to millimeter.
* If this value is 1, this unit will be treated as the standard one in this category.
* @param {Hash} extraParams Additional information of unit.
* @returns {Object} Unit object created.
*/
register: function(symbol, name, category, rateToStandard, extraParams)
{
var categoryObj = KU[category];
if (!categoryObj)
{
categoryObj = KU._createCategoryObj(category);
KU[category] = categoryObj;
}
var id = name.toUpperCase();
categoryObj[id] = KU._createUnitObj(symbol, name, categoryObj, rateToStandard, extraParams);
if (rateToStandard === 1)
categoryObj.setStandardUnit(categoryObj[id]);
KU.ALL[id] = categoryObj[id]; // also set ALL value
KU._KEY_MAP[categoryObj[id].getKey()] = categoryObj[id]; // register a key map item
return categoryObj[id];
},
/**
* Unregister a unit object from category.
* @param {Object} symbolOrId
*/
unregister: function(symbolOrId)
{
var unitObj = KU.getUnit(symbolOrId);
if (unitObj)
{
var id = unitObj.getKey().toUpperCase();
delete KU.ALL[id];
delete KU._KEY_MAP[id];
var category = unitObj.category;
delete category[id];
}
},
/**
* Returns an unit object by unit key (usually the unit symbol).
* @param {String} key
* @param {Bool} ignoreCase Whether ignore the case of key in searching for unit objects.
* @returns {Object}
*/
getUnit: function(key, ignoreCase)
{
if (typeof(key) === 'object') // already an object?
return key.getKey? key: null;
else
{
var result = KU._KEY_MAP[key];
if (!result && ignoreCase)
{
var keyLower = key.toLowerCase();
var names = Kekule.ObjUtils.getOwnedFieldNames(KU._KEY_MAP);
for (var i = 0, l = names.length; i < l; ++i)
{
if (keyLower === names[i].toLowerCase())
{
result = KU._KEY_MAP[names[i]];
break;
}
}
}
return result;
}
},
/**
* Returns an unit object by unit name.
* @param {String} key
* @param {Bool} ignoreCase Whether ignore the case of key in searching for unit objects.
* @returns {Object}
*/
getUnitByName: function(name, ignoreCase)
{
var keys = Kekule.ObjUtils.getOwnedFieldNames(KU._KEY_MAP);
var nameLower = ignoreCase && name.toLowerCase();
for (var i = 0, l = keys.length; i < l; ++i)
{
var unitObj = KU._KEY_MAP[keys[i]];
var unitName = unitObj.name;
if (name === unitName || (ignoreCase && nameLower === unitName.toLowerCase()))
{
return unitObj;
}
}
return null;
},
/** @private */
_createCategoryObj: function(category)
{
var result = Object.create(KU._categoryObjProto);
result.name = category;
return result;
},
/** @private */
_createUnitObj: function(symbol, name, categoryObj, rateToStandard, params)
{
var result = Object.create(Kekule.Unit._unitObjProto);
Object.extend(result, {
'symbol': symbol,
'name': name,
'category': categoryObj,
'rateToStandard': rateToStandard
});
if (params)
Object.extend(result, params);
return result;
}
};
var KU = Kekule.Unit;
/**
* Prototype of unit category objects.
* @object
* @private
*/
Kekule.Unit._categoryObjProto = {
getName: function()
{
return this.name;
},
getStandardUnit: function()
{
return this._standardUnit;
},
setStandardUnit: function(unitObj)
{
this._standardUnit = unitObj;
},
/**
* Returns all unit objects matches filter in this category.
* @param {Func} filter With param (unitObj), returns a bool value.
* @returns {Array}
*/
getUnitsOf: function(filter)
{
var keys = Kekule.ObjUtils.getOwnedFieldNames(this, false);
var result = [];
for (var i = 0, l = keys.length; i < l; ++i)
{
if (keys[i] !== '_standardUnit')
{
var v = this[keys[i]];
if (Kekule.ObjUtils.getPrototypeOf(v) === Kekule.Unit._unitObjProto && (!filter || filter(v)))
result.push(v);
}
}
return result;
},
/**
* Returns all unit objects of this category.
* @returns {Array}
*/
getAllUnits: function()
{
return this.getUnitsOf();
},
/**
* Returns all unit objects which can be converted between each other with a simple ratio multiplier of this category.
* @returns {Array}
*/
getConvertableUnits: function()
{
return this.getUnitsOf(function(unit){
return !!unit.rateToStandard;
});
}
};
/**
* Prototype of all unit objects.
* @object
* @private
*/
Kekule.Unit._unitObjProto = {
getKey: function()
{
return this.symbol || this.name;
},
/**
* Check whether this object is the standard one in category.
* @returns {Bool}
*/
isStandard: function()
{
return this.rateToStandard === 1;
},
/**
* Returns the value with this unit can be converted to another unit.
* @param {Object} toUnitObj
* @param {Hash} extraParams
* @returns {Bool}
*/
canConvertValueTo: function(toUnitObj, extraParams)
{
return (toUnitObj.category === this.category);
},
/**
* Convert a value with this unit to another one.
* @param {Number} value
* @param {Variant} toUnit Unit object or name.
* @param {Hash} extraParams
* @returns {Number}
*/
convertValueTo: function(value, toUnit, extraParams)
{
var toUnitObj = (typeof(toUnit) === 'string')? KU.getUnit(toUnit): toUnit;
if (toUnitObj)
{
return this.doConvertValueTo(value, toUnitObj, extraParams);
}
else
{
Kekule.error(Kekule.$L('ErrorMsg.UNIT_NOT_FOUND').format(toUnit.symbol || toUnit.name || toUnit));
}
},
/**
* Convert a value with the standard unit of this category to this unit.
* @param {Number} value
* @param {Hash} extraParams
* @return {Number}
*/
convertValueFromStandard: function(value, extraParams)
{
var sunit = this.category.getStandardUnit();
if (!sunit)
Kekule.error(Kekule.$L('ErrorMsg.STANDARD_UNIT_OF_CATEGORY_NOT_FOUND').format(this.category.name));
else
{
var currValue = this._convFromStandard? this._convFromStandard(value, extraParams): sunit.convertValueTo(value, this, extraParams);
return currValue;
}
},
/**
* Convert a value with this unit to the standard unit of this category.
* @param {Number} value
* @param {Hash} extraParams
* @return {Number}
*/
convertValueToStandard: function(value, extraParams)
{
return this.convertValueToStandardEx(value, extraParams).value;
},
/**
* Convert a value with this unit to the standard unit of this category.
* @param {Number} value
* @param {Hash} extraParams
* @return {Hash} A {value, unit} hash.
*/
convertValueToStandardEx: function(value, extraParams)
{
var sunit = this.category.getStandardUnit();
if (!sunit)
Kekule.error(Kekule.$L('ErrorMsg.STANDARD_UNIT_OF_CATEGORY_NOT_FOUND').format(this.category.name));
else
{
var stdValue = this._convToStandard? this._convToStandard(value, extraParams): this.convertValueTo(value, sunit, extraParams);
return {'value': stdValue, 'unit': sunit};
}
},
/** @private */
doConvertValueTo: function(value, toUnitObj, extraParams)
{
if (toUnitObj === this)
return value;
// check if category is same
if (/*toUnitObj.category !== this.category*/ !this.canConvertValueTo(toUnitObj, extraParams)) // defaultly we can not convert this
Kekule.error(Kekule.$L('ErrorMsg.UNABLE_TO_CONVERT_BETWEEN_UNITS').format(this.getKey(), toUnitObj.getKey()));
else
{
var r1 = toUnitObj.rateToStandard;
var r0 = this.rateToStandard;
if (r1 && r0)
return value * r0 / r1;
else // try to convert to/from standard
{
var canConvThisToStd = (r0 || this._convToStandard); // can convert this to standard?
var canConvStdToDest = (r1 || toUnitObj._convFromStandard); // can convert standard to dest?
if (canConvThisToStd && canConvStdToDest)
{
var stdValue = this.convertValueToStandard(value, extraParams);
return toUnitObj.convertValueFromStandard(stdValue, extraParams);
}
else
Kekule.error(Kekule.$L('ErrorMsg.UNABLE_TO_CONVERT_BETWEEN_UNITS').format(this.getKey(), toUnitObj.getKey()));
}
/*
if (!r1 || !r0)
Kekule.error(Kekule.$L('ErrorMsg.UNABLE_TO_CONVERT_BETWEEN_UNITS').format(this.getKey(), toUnitObj.getKey()));
else
{
return value * r0 / r1;
}
*/
}
}
};
/**
* Util methods about metrics units.
* @class
*/
Kekule.UnitUtils = {
/**
* Convert to a value based with another unit.
* @param {Number} fromValue
* @param {Variant} fromUnit String or unit object.
* @param {Variant} toUnit String or unit object.
* @param {Hash} extraParams
* @returns {Number}
*/
convertValue: function(fromValue, fromUnit, toUnit, extraParams)
{
var fuObj = KU.getUnit(fromUnit);
return fuObj.convertValueTo(fromValue, toUnit, extraParams);
}
};
var register = KU.register;
// register common used units
register('unk', 'Unknown', 'Unknown', null);
register('', 'counts', 'Counts', 1);
register('arb', 'arbitrary', 'Arbitrary', null);
register('', 'one', 'Dimensionless', 1);
register('%', 'percent', 'Dimensionless', 1e-2);
register('‰', 'thousandth', 'Dimensionless', 1e-3);
register('ppm', 'parts_per_million', 'Dimensionless', 1.0E-6);
register('pH', 'pH', 'AcidicScake', 1);
register('sec', 'second', 'Time', 1.0);
register('hr', 'hour', 'Time', 3600);
register('min', 'minute', 'Time', 60);
register('ms', 'millisecond', 'Time', 1e-3);
register('μs', 'microsecond', 'Time', 1e-6);
register('ns', 'nanosecond', 'Time', 1e-9);
register('m', 'meter', 'Length', 1.0);
register('cm', 'centimeter', 'Length', 1e-2);
register('mm', 'millimeter', 'Length', 1e-3);
register('μm', 'micrometer', 'Length', 1e-6);
register('nm', 'nanometer', 'Length', 1e-9);
register('Å', 'Angstrom', 'Length', 1e-10);
register('A-1', 'Angstrom-1', 'InverseLength', 1.0E10, {'symbolHtml': 'A<sup>-1</sup>'});
register('A', 'ampere', 'ElectricCurrent', 1.0);
register('μA', 'microampere', 'ElectricCurrent', 1e-6);
register('nA', 'nanoampere', 'ElectricCurrent', 1e-9);
register('pA', 'picoampere', 'ElectricCurrent', 1e-12);
register('kg', 'kilogram', 'Mass', 1.0);
register('g', 'gram', 'Mass', 1e-3);
register('K', 'kelvin', 'Temperature', 1.0);
register('℃', 'Celsius', 'Temperature', null,
{
'_convToStandard': function(value) { return value + 273.15; },
'_convFromStandard': function(value) { return value - 273.15; }
}
);
register('mol', 'mole', 'AmountOfSubstance', 1.0);
register('cd', 'candela', 'LuminousIntensity', 1.0);
register('rad', 'radian', 'Angle', 1.0);
register('deg', 'degree', 'Angle', 0.01745329, {'symbolHtml': '°'});
register('sr', 'steradian', 'SolidAngle', 1.0);
register('Hz', 'Hertz', 'Frequency', 1.0);
register('kHz', 'kilohertz', 'Frequency', 1e3);
register('MHz', 'megahertz', 'Frequency', 1e6);
register('N', 'Newton', 'Force', 1.0);
register('kcal/Å', 'kilocalorie_per_angstrom', 'Force', null);
register('J', 'Joule', 'Energy', 1.0);
register('cal', 'calorie', 'Energy', 4.184);
register('kcal', 'kilocalorie', 'Energy', 4184);
register('kcal/rad', 'kilocalorie_per_radian', 'Energy', null);
register('eV', 'electron_volt', 'Energy', 1.60217733E-19);
register('hart', 'Hartree', 'Energy', 4.3597482E-18);
register('W', 'Watt', 'Power', 1.0);
register('Pa', 'Pascal', 'Pressure', 1.0);
register('GPa', 'gigaPascal', 'Pressure', 1.0E+09);
register('bar', 'Bar', 'Pressure', 1E5);
register('kbar', 'kbar', 'Pressure', 1E8);
register('atm', 'Atmosphere', 'Pressure', 1.01325027E5);
register('GPa-1', 'gigaPascal-1', 'InversePressure', 1.0E-09, {'symbolHtml': 'GPa<sup>-1</sup>'});
register('C', 'Coulomb', 'ElectricCharge', 1.0);
register('e', 'electron_charge', 'ElectricCharge', 1.60217733E-19);
register('V', 'Volt', 'ElectricPotentialDifference', 1.0);
register('Ω', 'Ohm', 'ElectricResistance', 1.0);
register('F', 'Farad', 'ElectricCapacitance', 1.0);
register('S', 'Siemens', 'ElectricConductance', 1.0);
register('Wb', 'Weber', 'MagneticFlux', 1.0);
register('T', 'Tesla', 'MagneticFluxDensity', 1.0);
register('H', 'Henry', 'MagneticInductance', 1.0);
register('Bq', 'Becquerel', 'Radioactivity', 1.0);
register('Gy', 'Gray', 'RadioactiveAbsorbedDose', 1.0);
register('Sv', 'Sievert', 'RadioactiveEquivalentDose', 1.0);
register('kat', 'Katal', 'CatalyticActivity', 1.0);
register('cp', 'Centipoise', 'DynamicVicosity', null);
register('mol/L', 'Molarity', 'Molarity', 1.0);
register('mol/kg', 'Molality', 'Molality', 1.0);
register('m2', 'Square_meter', 'Area', 1.0, {'symbolHtml': 'm<sup>2</sup>'});
register('cm2', 'centimeter_squared', 'Area', 1.0E-04, {'symbolHtml': 'cm<sup>2</sup>'});
register('m3', 'Cubic_meter', 'Volume', 1.0, {'symbolHtml': 'm<sup>3</sup>'});
register('Å3', 'Angstrom_cubed', 'Volume', 1.0E-30, {'symbolHtml': 'Å<sup>3</sup>'});
register('L', 'litre', 'Volume', 1.0E-03);
register('mL', 'millilitre', 'Volume', 1.0E-06);
register('m/s', 'Meter_per_second', 'Velocity', 1.0);
register('km/s', 'kilometers_per_second', 'Velcity', 1000);
register('m·s-2', 'Meter_per_second_squared', 'Acceleration', 1.0, {'symbolHtml': 'm·s<sup>-2</sup>'});
register('rad/s', 'radian_per_second', 'AngularVelocity', 1.0);
register('N·s', 'newton_second', 'Momentum', 1.0);
register('N·m·s', 'newton_meter_second', 'AngularMomentum', 1.0);
register('N·m', 'newton_meter', 'Torque', 1.0);
register('m-1', 'reciprocal_meter', 'WaveNumber', 1.0, {'symbolHtml': 'm<sup>-1</sup>'});
register('cm-1', 'reciprocal_centimeter', 'WaveNumber', 1e2, {'symbolHtml': 'cm<sup>-1</sup>'});
register('kg·m-3', 'Kilogram_per_cubic_meter', 'MassDensity', 1.0, {'symbolHtml': 'kg·m<sup>-3</sup>'});
register('kg-1·m3', 'cubic_meter_per_kilogram', 'SpecificVolume', 1.0, {'symbolHtml': 'kg<sup>-1</sup>·m<sup>3</sup>'});
register('m-3·mol', 'mole_per_cubic_meter', 'AmountConcentration', 1.0, {'symbolHtml': 'm<sup>-3</sup>·mol'});
register('m3/mol', 'cubic_meter_per_mole', 'MolarVolume', 1.0, {'symbolHtml': 'm<sup>3</sup>/mol'});
register('J/K', 'joule_per_kelvin', 'HeatCapacity', 1.0);
register('J·K-1·mol-1', 'joule_per_kelvin_mole', 'MolarHeatCapacity', 1.0, {'symbolHtml': 'J·K<sup>-1</sup>·mol<sup>-1</sup>'});
register('J·K-1·kg-1', 'joule_per_kilogram_kelvin', 'SpecificHeatCapacity', 1.0, {'symbolHtml': 'J·K<sup>-1</sup>·kg<sup>-1</sup>'});
register('J/mol', 'joule_per_mole', 'MolarEnergy', 1.0);
register('J/kg', 'joule_per_kilogram', 'SpecificEnergy', 1.0);
register('J·m-3', 'joule_per_cubic_meter', 'EnergyDensity', 1.0, {'symbolHtml': 'J·m<sup>-3</sup>'});
register('N/m', 'newton_per_meter', 'SurfaceTension', 1.0);
register('W·m-2', 'watt_per_square_meter', 'HeatFluxDensity', 1.0, {'symbolHtml': 'W·m<sup>-2</sup>'});
register('W·m-1·K-1', 'watt_per_meter_kelvin', 'ThermalConductivity', 1.0, {'symbolHtml': 'W·m<sup>-1</sup>·K<sup>-1</sup>'});
register('m2/s', 'square_meter_per_second', 'KinematicViscosity', 1.0, {'symbolHtml': 'm<sup>2</sup>/s'});
register('Pa·s', 'Pascal_second', 'DynamicViscosity', 1.0);
register('C·m-3', 'coulomb_per_cubic_meter', 'ElectricChargeDensity', 1.0, {'symbolHtml': 'C·m<sup>-3</sup>'});
register('A·m-2', 'ampere_per_square_meter', 'ElectricCurrentDensity', 1.0, {'symbolHtml': 'A·m<sup>-2</sup>'});
register('S/m', 'siemens_per_meter', 'ElectricalConductivity', 1.0);
register('S·m2/mol', 'siemens_square_meter_per_mole', 'MolarConductivity', 1.0, {'symbolHtml': 'S·m<sup>2</sup>/mol'});
register('F/m', 'farad_per_meter', 'Permittivity', 1.0);
register('H/m', 'henry_per_meter', 'Permeability', 1.0);
register('V/m', 'volt_per_meter', 'ElectricFieldStrength', 1.0);
register('A/m', 'ampere_per_meter', 'MagneticFieldStrength', 1.0);
register('cd·m-2', 'candela_per_square_meter', 'Luminance', 1.0, {'symbolHtml': 'cd·m<sup>-2</sup>'});
register('C/kg', 'coulomb_per_kilogram', 'Exposure', 1.0);
register('Gy/s', 'gray_per_second', 'AbsorbedDoseRate', 1.0);
register('J/m', 'joule_per_meter', 'EnergyLengthGradient', 1.0);
register('kj/mol', 'kj_per_mole', 'MolarEnergy', 1000);
//register('kcal·mol-1·ang-1', 'kcal_per_mole_per_Angstrom', 'Xx', null, {'symbolHtml': 'kcal·mol<sup>-1</sup>·ang<sup>-1</sup>'});
register('D', 'debye', 'Dipole', 3.335641E-30);
})();