iMKS -- An advanced physical quantity calculator
iMKS is an IPython extension to allows the use of physical quantities (Value's) in mathematical expressions. A physical quantity is simply a number (typically, a float number) followed by a unit specification. Within iMKS, one can use physical quantities in normal expressions: the result will be a physical quantity with the correct unit.
- Allow the use of physical quantities in mathematical expressions and performs consistency checks (so that, for example, an error is generated if one tries to add a length with a time).
- Unit systems make it easy to perform conversions of physical quantities in different units. A unit system provides a list of units that should be used, alone or in combination, to represent physical values. Large unit systems are effectively useful to define preferences in unit representations.
- Completely modular: one can define base units, prefixes, derived units, and unit systems using magic commands. Definitions can be stored in an external file for reuse.
- Currencies are considered as physical units. Automatically retrieves a large list of currencies and exchange rates from openexchangerates.org and stores them in a file for offline use.
- A list of physical constants can be retrieved from the NIST server http://physics.nist.gov/constants and are available as variable in interactive mode.
- Add autocompletion for units and constants.
- Allow the use of documentation strings for all quantities (base units, prefixes, units, unit systems, and values) using the ... # "doc string" notation.
- Optionally allow the use of the standard exponent (caret, ^) in mathematical expressions in addition to the Python notation (**). Also, optionally allow the use of unicode characters in identifiers, units, and prefixes.
- Physical quantities are displayed in a proper way (using the LaTeX notation) in the notebook.
- Allow the use of "lazy" quantities (variables, units, prefixes) whose value is evaluated each time the quantity is required.
- Add an easy way to write documentation strings for any quantity, including variables.
- Several mathematical engines can be used: the standard Python math module, mpmath, fpmath (mpmath with fixed point arithmetic), numpy, and umath (based on the uncertanties package), soerp (higher order error analysis), and mcerp (Monte Carlo error analysis). The engines are used to perform calculations involving mathematical functions.
- Engines with error analysis (umath, soerp, and mcerp) will keep track of the error propagation within all define variables or physical constants.
- Advanced input transformations and output formats for special quantities such as angles in sexagesimal format, times, dates...
Use of iMKS
iMKS extends the standard Python syntax in several ways:
Physical quantities can be entered using the format 9.8[m s^-2] or 9.8[m/s^2], i.e. with a number followed by a unit specification in brackets. In simple cases, quantities can also be entered without the brackets: 9.8 m/s^2 or 9.8 m s^-2. This shorter notation however can generate ambiguities when the quantity is exponentiated (2m^2 is 2[m^2], while 2[m]^2 is 4[m^2]) or when a unit has the same name of a variable (12 km/h is a speed, 12[km/h] or 12[km] over the Planck constant?). Moreover, in the shorter notation parentheses in units, such as 3[m/(s K)], are not accepted.
A unit specification follows a standard notation: it is a space-separated list of prefixed units followed by an optional exponent. A prefixed unit is a unit with a prefix, such as [km] = prefix k + simple unit m. The exponent in the unit must be a simple number or a fraction: it cannot involve any other term (for example, [km^a] is not a valid unit, while [km^2/3] is). In a unit specification one can use an arbitrary number of parentheses; within a parenthesis, a single division can be used, as in 9.8[m/s^2]. Additionally, a unit specification can contain (at most) one divide (/) sign: in this case all units following the sign will have their exponents reversed.
Physical quantities can be used in expressions with the standard operators.
The result of an expression can be converted into a different unit by using the @ operator:
> 12 m + 3 cm @ km --> 0.01203[km]
Multiple units can be used after the @ operator: in that case, they are combined as necessary to obtain the requested quantity:
> 20[m/s] @ [km|hour] --> 72.0[km hour^-1]
When the quantity can be represented using multiple combinations, the shorter one is used (i.e., the one that involves the smallest number of elements in the list at the right of the @ operator):
> 20[m/s] @ [km|hour|mph] --> 44.738725841088[mph]
In some cases, it might be useful to use a variable as a unit. To do this, one can use single quotes around a variable name in the unit specification:
> 5[s 'c'] @ [km] --> 1498962.29[km] > 3['g'] --> 29.41995[m s^-2]
The same operation can be performed in the argument of the at (@) operator:
> 300000[km] @ [s 'c'] --> 1.00069228559446[s 'c'] > 1e30[kg] @ ['c'|'G'] --> 742.564845009288['c'^2 m 'G'^-1] > 1K @ ['k'|eV] --> 8.61733238496096e-5[eV 'k'^-1]
The output unit, set by the @ operator, can also contain double quoted variables. In this case the value is completely transformed using the appropriate combination of the variables, thus removing any appearance of the variable in the final result. This technique generally changes the unit of the quantity (and not only the displayed unit):
> 300000[km] @ [s|"c"] --> 1.00069228559446[s] > 1e30[kg] @ ["c"|"G"] --> 742.564845009288[m]
iMKS understands special units, such as the temperature related ones, that are defined together with a zero point, i.e. in terms of an affine transformation. Quantities involving these units alone are called absolute:
> 300[K] @ [Celsius] --> 26.85[Celsius] > 122°F @ °C --> 50.0[°C]
Absolute quantities can be made relative (and vice-versa) by using the tilde (~) operator. For example, 300[K] is understood as an absolute temperature, but ~300[K] as a relative one (i.e. a temperature difference):
> ~300[K] @ [Celsius] --> ~300.0[Celsius]
Another example of absolute quantities are calendar dates or datetimes:
> %load_imks_ext calendars > Egyptian("today") + 2[day] --> 1 First of Akhet (Thoth) 2764
The @ operator can be used to force the display of a result using a specific prefix or a set of prefixes. For this, one can just specify the desired prefix(es) after the @ inside the brackets:
> 1200[s] @ [k] --> 1.2[ks]
If multiple prefixes are used, the most convenient one is selected:
> 1200[s] @ [k|M] --> 1.2[ks]
A dot (.) can be used to specify that no prefix is also accepted; a star (*) to select all known prefixes:
> 8[m] @ [.|k|M] --> 8.0[m] > 0.12[cm] @ [m|*] --> 1.2[mm]
Note that if a prefix has the same name as a valid unit, one needs to use a star after the prefix name, to indicate that the name must be understood as a prefix rather than a unit:
> 12[cm] @ [m] --> 0.12[m] > 12[cm] @ [m*] --> 120.0[mm]
The star can also be used within a single unit to indicate that an arbitrary prefix is allowed:
> 1200[m] @ [*m] --> 12.0[km]
Unit systems work just like list of units that can be used after the @ operator:
> 72[km/hour] @ [SI] --> 20.0[m s^-1]
A star prepended or appended to a unit system indicates that a prefix among all known ones can be used to make the expression simpler; the same effect is obtained if the star is specified within the brackets with the unit system:
> 5600[K] @ [*SI] --> 5.6[kK] > 5600[K] @ [SI|*] --> 5.6[kK]
Unit systems containing multiple units are especially convenient to reduce expressions to the simplest form:
> %newsystem easy=[m|s|kg|K|A|lx|mol|EUR|N|J|W|Pa|C|V|ohm|F|H|T|lx] > 6.63e-34[kg m^2 s^-1] @ [easy] --> 6.63e-34[s J]
Unit systems can also contain variable units: this is especially useful to define natural unit systems:
> %newsystem planck=["c"|"hbar"|"G"|"ke"|"k"] # "Planck's natural system" > 1[m] @ [planck] --> 6.18735589978243e+34
The reverse conversion can be performed by indicating the output unit at the beginning of the unit specification, and by adding the unit system:
> 6.2e34 @ [m|planck] --> 1.00204353853607[m] > 1 @ [kg|planck] --> 2.17650925244531e-8[kg]
Note that this notation is an exception to the general rule that pure numbers are not influenced by unit specifications. Note also that the order is relevant here: first the final unit, than all the natural system:
> 1 @ [planck|kg] --> 1 > 1 @ [kg/m|"c"|"G"] --> 1.34668373640485e+27[kg m^-1] > 1 @ [kg|m|"c"|"G"] --> 1
When a natural unit system is used enclosed within single or double quotes, all the quotes of the unit system are replaced by the one used: hence, ['planck'] is identical to ['c'|'hbar'|'G'|'ke'|'k']. This is useful to explicitly show all the converting factors in the result:
> 1 @ [kg|'planck'] --> 2.17650925244531e-8['G'^1/2 'c'^-1/2 'hbar'^-1/2 kg] > 2.17650925244531e-8['G'^1/2 'c'^-1/2 'hbar'^-1/2 kg] --> 0.999999999999999
The @ operator can also be used in variable definitions to set the default display unit to be used for a variable:
> v = 20[m/s] @ [km|hour] > v --> 72.0[km hour^-1]
When an engine with error analysis is used (umath, soerp, or mcerp), the special syntax value +/- error can be used to input quantities with errors:
> %imks -c umath > %reset > v = (3+/-0.1)[m/s] > v^2 @ [mph] --> (45.0+/-3.0)[mph^2]
Uncertainties are silently ignored with engines not supporting them. The same quantity, (3+/-0.1)[m/s] can also be entered without parentheses, 3+/-0.1m/s, using the ± sign instead of the +/-, or using a shorter notation 3.0(1)[m/s] (see uncertainties for a list of formats accepted).
Correlation among variables is automatically taken into account:
> w = (3+/-0.1)[m/s] > v + w --> (6.00+/-0.14)[m s^-1] > v + v --> (6.00+/-0.20)[m s^-1]
Input transformer make it easy to enter special quantities such as sexagesimal angles or times:
> 18d 24' 32" --> 0.321295722745 > 12h 34m 56s --> 45296.0[s]
The reverse is also possible using output formats:
> 1[rad] @ [dms] --> 57d 17' 44.806s" > 10[ks] @ [hms] --> 2h 46m 40s
Documentation strings can be entered even for variables:
> v = 50[km/hour] # "Standard maximum speed within towns"
When launched, iMKS load definitions from the configuration file Startup.imks. This file is searched in the current directory, and if not found in the ~/.imks directory. The file should contain the standard definitions that one is likely to need for any computation. Typically the file uses the following magic commands:
- %newbaseunit <name>
- Define a new base unit. Base units are the building blocks for all subsequent units definitions. A base unit does not have a value: for example, one cannot express a meter in any other unit if no unit of length is known.
- %newbasecurrency <name>
- Define a new base currency, used for all currency conversions. A base currency is also a base unit.
- %newprefix <name>=<expression>
- Define a new prefix. The <expression> value evaluate to a simple number.
- %newunit <name>=<expression>
- Define a new unit. <expression> should evaluate to a Value'd number. To define a new absolute unit, expression should evaluate to a 2-tuple with identical units (indicating the zero-point, and the scale).
- %newsystem <name>=[u1], [u2], ...
- Define a new unit system. A unit system is simply a list of units.
- %defaultsystem <name>
- Set the unit system to use in case no @ is used.
- %newtransformer <name>="regex":<transformer>
- Define a new input transformer: the regular expression regex is applied to each input line, and if a match is found the <transformer> function is called together with all named matching groups which must return the transformed input.
- %newformat <name>=<transformer>
- Define a new output format. When <name> is entered (alone) in a unit specification, <transformer> is called with the result of the expression: it must return a string that will be displayed on the screen.
Other magic commands
- %imks [<options>]
- Show this help page or set configuration options for iMKS.
- %delprefix, %delunit, %delsystem, %deltransformer, or %delformat <name>
- Delete a previously define prefix, unit, unit system, transformer, or format.
- %lazy <name>=<expression>
- Define the variable <name> as <expression> lazily: that is, <expression> is evaluated only when <name> is used or displayed. This is implemented by making <name> a function with no arguments, and by automatically adding a function call name() when name is used in the input.
- %dellazy <name>
- Delete a previously defined %lazy variable.
- %lazyvalue <name>=<expression>
- Define the variable <name> as <expression> lazily: that is, <expression> is evaluated only when <name> is used or displayed. This only works for simple values, and not for more general objects such as %lazy.
- %lazyprefix <name>=<expression>
- Define a lazy prefix (whose <expression> is evaluate only when the prefix is used).
- %lazyunit <name>=<expression>
- Define a lazy unit (whose <expression> is evaluated only when the unit is used).
- %compatible <stuff>
- Find out the known variables or units that are compatible to <stuff>. <stuff> can be either a unit (in brackets) or an expression.
- %load_imks <filename>
- Load an external <filename> with definitions in iMKS format.
- %load_imks_ext <filename>
- Load an imks extension.
- %uinfo <name>
- Display an help page for a prefix, unit, or unit system. This is the equivalent of %pinfo (which works for objects in the user namespace). A short notation for %uinfo <name> is <name>! (i.e., the name of an imks object followed by the exclamation mark). When used with the -a flag, as in %uinfo -a <text>, shows all quantities with <text> in their docstring. The same effect is achieved using <text>!!
- %pickle <filename>
- Save all current variables into a filename, in the Python pickle format.
- %unpickle <filename>
- Load all previously %pickle'd variables from a filename.
- Perform a full reset of the iMKS interpreter.
iMKS comes with a number of extensions that define new commands, new variables, or new units. Extensions are loaded with the command %load_imks_ext. The currently defined extensions are
- calendars: defines several new functions, one for each calendar (for example Gregorian, Julian, Roman, Egyptian...). Each calendar accepts a date in several formats: as an integer (number of days from 1 January 1 C.E.), one of the strings "today", "tomorrow", "yesterday", or "now", as a year followed by a holiday name such as Gregorian(2017, "Easter"), or as a full year (with a number of arguments depending on the calendar). Optionally, one can also add a time, counted from midnight, noon, sunset, or sunrise depending on the specification of the calendar. When calendars are loaded, one can input a date using dot-separated integers. For example, 1973.5.7 is interpreted as May 7th, 1973. The default calendar used to interpret dates is the Gregorian one, but it can be changed using %imks -d <calendar>. A date can be also followed by a time, in the format hh:mm[:ss.d].
- constants: loads a large list of constants from the NIST database. Constants are then inserted into the variable const, a dictionary.
- currencies: loads a large list of currencies from the online database openexachangerages.org. Currencies are then used as usual units. Note that in order this to work, you first need to set a variable called openexchangerates_id as a string holding your id.
- geolocation: defines two functions to set the current geographic location and to retrieve it.
- jpl: loads the JPL database and creates two dictionaries, planets and moons, where it stores the physical and orbital data. Additionally, it defines a function called minorplanet, which allows one to search a minor body database.
- wiki: experimental interface to Wikipedia.
Internally, iMKS works by converting an input string into Python expressions. The following rules are used:
Physical quantities are converted into Value's:
> 72[km hour^-1] --> Value(72,"km hour^-1")
The @ operator is converted into the | operator, and what follows is put in a unit System:
> 72[km hour^-1] @ [m], [s] --> Value(72,"km hour^-1") | System("m", "s")
Quoted comments, used to enter documentation strings, are transformed into the & operator followed by a Doc object:
> a=2 # "Simple string" --> a=2 & Doc("Simple string")
Normal Python operator precedence applies to @=| and to #=&: that is, @ has a quite low precedence, which makes it possible to write expressions like:
> v = 15[m/s] + 10[m]/2[s] @ [km/hour]
with the unit specification after @ applying to the result of the other operations. Note also that since
&binds stronger than
|, in theory a # documentation string after the @ operator would apply to the unit specification, and not the the expression: however, this is handled internally by System (that is, the documentation string that System gets from the #=& operator is actually transferred to the result of the unit conversion).
The ! operator, if used at the end of a string, is converted into a %uinfo magic (this is similar to the ? operator, that is converted by ipython into a %pinfo magic):
> hour! --> %uinfo hour
If used alone, instead, it is equivalent to %imks -h. Note that the same ! symbol can still be used to perform shell operations if used at the beginning of a line:
Two exclamation marks are converted into the %uinfo -a magic:
> mile!! --> mile, mph, nmi, mi
Unicode characters appearing outside strings are converted into strings of the form _uTf_xx_xx_xx..., where each xx is the hexadecimal representation of a byte of the character in UTF8 encoding.
Value, System, and Doc are defined in units.py, and for these objects the standard operators are redefined to include tracking of physical units.
If necessary, one can directly use the Value, System, and Doc objects to make more complicated expressions.
The know prefixes, units, and unit systems are stored in the dictionaries prefixes, units, and systems, freely accessible from the user space.