{ "metadata": { "name": "" }, "nbformat": 3, "nbformat_minor": 0, "worksheets": [ { "cells": [ { "cell_type": "heading", "level": 1, "metadata": {}, "source": [ "natu Tutorial" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This tutorial shows how to use the [natu module](http://kdavies4.github.io/natu/) to work with physical quantities in [Python](https://www.python.org/)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Table of contents:**\n", "- [Basic use](#Basic-use)\n", " - [Entering and displaying a quantity](#Entering-and-displaying-a-quantity)\n", " - [Prefixed units](#Prefixed-units)\n", " - [Nonscalar units](#Nonscalar-units)\n", " - [Arrays and other data types](#Arrays-and-other-data-types)\n", " - [Accessing groups of units](#Accessing-groups-of-units)\n", " - [Simplifying units](#Simplifying-units)\n", "- [Defining new units](#Defining-new-units)\n", " - [In code](#In-code)\n", " - [Via definition files](#Via definition-files)\n", "- [Advanced topics](#Advanced-topics)\n", " - [String formatting](#String-formatting)\n", " - [Changing the display unit of a unit](#Changing-the-display-unit-of-a-unit)\n", " - [Conversion factors](#Conversion-factors)\n", " - [Disabling quantities](#Disabling-quantities)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Before we get started, we'll set the display precision for this IPython notebook." ] }, { "cell_type": "code", "collapsed": false, "input": [ "%precision 4" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 1, "text": [ "u'%.4f'" ] } ], "prompt_number": 1 }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Basic use" ] }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Entering and displaying a quantity" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First, we'll import the [metre](http://en.wikipedia.org/wiki/Metre) (m):" ] }, { "cell_type": "code", "collapsed": false, "input": [ "from natu.units import m\n", "m" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 2, "text": [ "ScalarUnit m with dimension L (prefixable)" ] } ], "prompt_number": 2 }, { "cell_type": "markdown", "metadata": {}, "source": [ "It's a [scalar unit](http://kdavies4.github.io/natu/natu.core.html#natu.core.ScalarUnit) that displays as \"m\". Its dimension is length (L) and it can be used with [SI prefixes](http://en.wikipedia.org/wiki/Metric_prefix)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To enter a [quantity](http://kdavies4.github.io/natu/natu.core.html#natu.core.Quantity), we multiply a number by a unit:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "length = 0.0254*m\n", "length" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 3, "text": [ "0.0254 m" ] } ], "prompt_number": 3 }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can change the display unit by setting the quantity's `display` property:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "length.display_unit = 'inch'\n", "length" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 4, "text": [ "0.0254 m" ] } ], "prompt_number": 4 }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also express the length as a number of inches by dividing it by the [inch](http://en.wikipedia.org/wiki/Inch):" ] }, { "cell_type": "code", "collapsed": false, "input": [ "from natu.units import inch\n", "length/inch" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 5, "text": [ "1.0000" ] } ], "prompt_number": 5 }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Prefixed units" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We saw that the [metre](http://en.wikipedia.org/wiki/Metre) is prefixable. Let's import the [kilometre](http://en.wikipedia.org/wiki/Kilometre):" ] }, { "cell_type": "code", "collapsed": false, "input": [ "from natu.units import km\n", "km" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 6, "text": [ "ScalarUnit km with dimension L (not prefixable)" ] } ], "prompt_number": 6 }, { "cell_type": "markdown", "metadata": {}, "source": [ "It can't be prefixed any further. As expected, it's 1000 times larger than the [metre](http://en.wikipedia.org/wiki/Metre):" ] }, { "cell_type": "code", "collapsed": false, "input": [ "km/m" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 7, "text": [ "1000.0000" ] } ], "prompt_number": 7 }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can access units directly from the [units module](http://kdavies4.github.io/natu/natu.units.html), with or without prefixes:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "from natu import units as U\n", "U.km/U.m" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 8, "text": [ "1000.0000" ] } ], "prompt_number": 8 }, { "cell_type": "markdown", "metadata": {}, "source": [ "However, if we use a wildcard import (`from natu.units import *`), we only get the units which are explicitly given in the the [definition files](http://kdavies4.github.io/natu/definitions.html). Typically this doesn't include prefixed units." ] }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Nonscalar units" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Nonscalar units such as the [degree Celsius](http://en.wikipedia.org/wiki/Celsius), [degree Fahrenheit](http://en.wikipedia.org/wiki/Fahrenheit), and [decibel](http://en.wikipedia.org/wiki/Decibel) are available: " ] }, { "cell_type": "code", "collapsed": false, "input": [ "from natu.units import degC, degF, dB" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 9 }, { "cell_type": "markdown", "metadata": {}, "source": [ "These units are called [lambda units](http://kdavies4.github.io/natu/natu.core.html#natu.core.LambdaUnit) because they involve invertible functions that are not limited to multiplication and division. For convenience, however, [lambda units](http://kdavies4.github.io/natu/natu.core.html#natu.core.LambdaUnit) are overloaded to use the multiplication and division operators (`*` and `/`). The unit must always be on the right side of the operator:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "temperature = 25*degC" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 10 }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can display this temperature in [kelvin](http://en.wikipedia.org/wiki/Kelvin) (a [scalar unit](http://kdavies4.github.io/natu/natu.core.html#natu.core.ScalarUnit)):" ] }, { "cell_type": "code", "collapsed": false, "input": [ "from natu.units import K\n", "temperature/K" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 11, "text": [ "298.1500" ] } ], "prompt_number": 11 }, { "cell_type": "markdown", "metadata": {}, "source": [ "or in [Fahrenheit](http://en.wikipedia.org/wiki/Fahrenheit) (another [lambda unit](http://kdavies4.github.io/natu/natu.core.html#natu.core.LambdaUnit)):" ] }, { "cell_type": "code", "collapsed": false, "input": [ "temperature/degF" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 12, "text": [ "77.0000" ] } ], "prompt_number": 12 }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can add temperatures that have been created from different units:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "0*degC + 100*K" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 13, "text": [ "100.0 degC" ] } ], "prompt_number": 13 }, { "cell_type": "markdown", "metadata": {}, "source": [ "The display unit of the first quantity takes precedence. [natu](http://kdavies4.github.io/natu/) checks that the dimensions are compatible when performing arithmetic, so a temperature can only be added to another temperature. Temperatures are absolute quantities, so the sum of 25 \u2103 and 25 \u2103 is 323.15 \u2103 (or 596.3 K, but not 50 \u2103):" ] }, { "cell_type": "code", "collapsed": false, "input": [ "25*degC + 25*degC" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 14, "text": [ "323.15 degC" ] } ], "prompt_number": 14 }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can use the [decibel](http://en.wikipedia.org/wiki/Decibel) to multiply two numbers by adding their logarithms:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "(10/dB + 10/dB)*dB" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 15, "text": [ "100.0000" ] } ], "prompt_number": 15 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Lambda units can be prefixed if they're set to allow it. In fact, the [decibel](http://en.wikipedia.org/wiki/Decibel) is simply defined as the bel (B) with the [deci (d)](https://en.wikipedia.org/wiki/Deci-) prefix. To retrieve a prefixed unit, import it as before:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "from natu.units import mdegC\n", "100*mdegC/K" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 16, "text": [ "273.2500" ] } ], "prompt_number": 16 }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Arrays and other data types" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Quantities can be used in [numpy](http://www.numpy.org/) arrays:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "import numpy as np\n", "np.array([1, 2, 3])*m" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 17, "text": [ "array([1.0 m, 2.0 m, 3.0 m], dtype=object)" ] } ], "prompt_number": 17 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice that the result is an array of quantities. To get a quantity with a value that is an array, put the unit before the array:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "m*np.array([1, 2, 3])" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 18, "text": [ "[ 1. 2. 3.] m" ] } ], "prompt_number": 18 }, { "cell_type": "markdown", "metadata": {}, "source": [ "The previous two lines of code are mathematically equivalent. However, when working with large arrays of quanties with the same physical dimension, the second form will generally save memory and processing time. It's also possible to create arrays of quantities with mixed dimensions, for example:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "np.array([1*m, 100*degC])" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 19, "text": [ "array([1.0 m, 100.0 degC], dtype=object)" ] } ], "prompt_number": 19 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that a quantity with a value that is an array can be indexed like an array. For example:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "x = m*np.array([1, 2, 3])\n", "x[:2]" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 20, "text": [ "[ 1. 2.] m" ] } ], "prompt_number": 20 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Also, properties and methods of the array are available from the quantity. For example:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "x.clip(0, 2)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 21, "text": [ "[ 1. 2. 2.] m" ] } ], "prompt_number": 21 }, { "cell_type": "markdown", "metadata": {}, "source": [ "The value of a quantity can be complex:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "(1 + 1j)*m" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 22, "text": [ "(1+1j) m" ] } ], "prompt_number": 22 }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Accessing groups of units" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For convenience, the contents of [natu.units](http://kdavies4.github.io/natu/natu.units.html) are sorted into submodules of [natu.groups](http://kdavies4.github.io/natu/natu.groups.html). There are submodules for physical constants ([constants](http://kdavies4.github.io/natu/natu.groups.constants.html)), SI units ([si](http://kdavies4.github.io/natu/natu.groups.si.html)), and units of various dimensions ([length](http://kdavies4.github.io/natu/natu.groups.length.html), [time](http://kdavies4.github.io/natu/natu.groups.time.html), [pressure](http://kdavies4.github.io/natu/natu.groups.pressure.html), etc.). We can access units from these submodules in several ways, just like those from [natu.units](http://kdavies4.github.io/natu/natu.units.html):" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# Individually:\n", "from natu.groups.length import m\n", "\n", "# By access:\n", "from natu.groups import length\n", "m = length.m\n", "\n", "# By wildcard:\n", "from natu.groups.length import *" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 23 }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Simplifying units" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "By default, units are simplified using [coherent relations](http://en.wikipedia.org/wiki/Coherence_%28units_of_measurement%29). For example," ] }, { "cell_type": "code", "collapsed": false, "input": [ "from natu.units import m, kg, s\n", "1*kg*m**2/s**2" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 24, "text": [ "1.0 J" ] } ], "prompt_number": 24 }, { "cell_type": "markdown", "metadata": {}, "source": [ "The extent of simplification can be adjusted using `simplification_level` in [natu.config](http://kdavies4.github.io/natu/natu.config.html)." ] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Defining new units" ] }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "In code" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can derive a new coherent unit from existing units since the product or quotient of two units is generally another unit (as discussed [here](http://kdavies4.github.io/natu/natu.core.html#module-natu.core)). For example, we can represent the [cubic inch](https://en.wikipedia.org/wiki/Cubic_inch) as a stand-alone unit:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "cinch = inch**3\n", "cinch" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 25, "text": [ "ScalarUnit inch3 with dimension L3 (not prefixable)" ] } ], "prompt_number": 25 }, { "cell_type": "markdown", "metadata": {}, "source": [ "However, notice that the display unit is not the new unit but rather inch3:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "10*cinch" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 26, "text": [ "10.0 inch3" ] } ], "prompt_number": 26 }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can update the display unit, but we must insert the new unit into the unit space to make it available:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "cinch.display_unit = 'cinch'\n", "from natu import units\n", "units.cinch = cinch" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 27 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we get the desired result:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "10*cinch" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 28, "text": [ "10.0 inch3" ] } ], "prompt_number": 28 }, { "cell_type": "markdown", "metadata": {}, "source": [ "If the expression of the new unit requires a numeric factor (not coherently derived), then the result is a quantity that we need to explicitly cast as a unit:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "from natu.core import ScalarUnit\n", "from natu.units import ns\n", "shake = ScalarUnit.from_quantity(10*ns, 'shake')" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 29 }, { "cell_type": "markdown", "metadata": {}, "source": [ "As before, we need to insert the unit into the unit space:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "units.shake = shake" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 30 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now it's ready for use:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "time = 500*ns\n", "time.display_unit = 'shake'\n", "time" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 31, "text": [ "500.0 ns" ] } ], "prompt_number": 31 }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Via definition files" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also introduce new units by swapping or adding [definition files](http://kdavies4.github.io/natu/definitions.html). That way we don't need to re-enter the units during each [Python](https://www.python.org/) session as with the methods in the previous section. Also, coherent units are automatically added to the list of coherent relations for use in [simplifying units](#Simplifying-units). **However, it's important to be sure that the [definition files](http://kdavies4.github.io/natu/definitions.html) are from a trusted source since [natu](http://kdavies4.github.io/natu/) uses `eval()` to process them.**" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The [config submodule](http://kdavies4.github.io/natu/natu.config.html) contains a list of [definition files](http://kdavies4.github.io/natu/definitions.html):" ] }, { "cell_type": "code", "collapsed": false, "input": [ "from natu import config\n", "config.definitions" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 32, "text": [ "['natu/config/base-SI.ini',\n", " 'natu/config/derived.ini',\n", " 'natu/config/BIPM.ini',\n", " 'natu/config/other.ini']" ] } ], "prompt_number": 32 }, { "cell_type": "markdown", "metadata": {}, "source": [ "These files are loaded and processed the first time any units are imported. Before that, we can change the list of files. For example:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "config.definitions.append(\"custom.ini\")" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 33 }, { "cell_type": "markdown", "metadata": {}, "source": [ "where [custom.ini](https://github.com/kdavies4/natu/blob/master/examples/custom.ini) is in the current directory. Since units were imported before this section, it's necessary to restart the IPython notebook's kernel just before running this section so that the new file will be loaded and processed. Only then will the new units be available for import from [natu.units](http://kdavies4.github.io/natu/natu.units.html) or the appropriate submodules of [natu.groups](http://kdavies4.github.io/natu/natu.groups.html)." ] }, { "cell_type": "code", "collapsed": false, "input": [ "from natu.units import shake\n", "shake" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 34, "text": [ "ScalarUnit shake with dimension T (not prefixable)" ] } ], "prompt_number": 34 }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Advanced topics" ] }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "String formatting" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "By default, units and dimensions are formatted with \"\\*\" as the multiplication operator. Exponents directly follow the units and dimensions (without \"\\**\" or \"^\"). If necessary, a division sign is used (instead of negative exponents) and the denominator is placed in parentheses if it has more than one factor. Let's see how this looks for the [Ampere constant](http://en.wikipedia.org/wiki/Amp%C3%A8re's_force_law) (kA):" ] }, { "cell_type": "code", "collapsed": false, "input": [ "from natu.units import k_A\n", "k_A.display_unit = 'N/A2'\n", "print('{0} (dimension {0.dimension})'.format(k_A))" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "dyn/abA2 (dimension L*M/(I2*T2))\n" ] } ], "prompt_number": 35 }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can change the format using [string format syntax](https://docs.python.org/2/library/string.html#format-string-syntax). For example, the multiplication is shown by periods in [Modelica](https://www.modelica.org/documents) format:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "print('{0:M} (dimension {0.dimension:M})'.format(k_A))" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "dyn/abA2 (dimension L.M/(I2.T2))\n" ] } ], "prompt_number": 36 }, { "cell_type": "markdown", "metadata": {}, "source": [ "In verbose format, the exponents are written as [Python](https://www.python.org/) code:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "print('{0:V} (dimension {0.dimension:V})'.format(k_A))" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "dyn / abA**2 (dimension L * M / (I**2 * T**2))\n" ] } ], "prompt_number": 37 }, { "cell_type": "markdown", "metadata": {}, "source": [ "In [Unicode](https://en.wikipedia.org/wiki/Unicode) format, the exponents are written as superscripts:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "print(u'{0:U} (dimension {0.dimension:U})'.format(k_A))" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "dyn abA\u207b\u00b2 (dimension L M I\u207b\u00b2 T\u207b\u00b2)\n" ] } ], "prompt_number": 38 }, { "cell_type": "markdown", "metadata": {}, "source": [ "This format can only be used with integer exponents. In HTML format:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "print('{0:H} (dimension {0.dimension:H})'.format(k_A))" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "dyn abA-2 (dimension L M I-2 T-2)\n" ] } ], "prompt_number": 39 }, { "cell_type": "markdown", "metadata": {}, "source": [ "This renders as 1×10-7 N A-2 (dimension L M I-2 T-2)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, in LaTeX math format:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "print('{0:L} (dimension ${0.dimension:L}$)'.format(k_A))" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "\\mathrm{dyn}\\,\\mathrm{abA}^{-2} (dimension $\\mathrm{L}\\,\\mathrm{M}\\,\\mathrm{I}^{-2}\\,\\mathrm{T}^{-2}$)\n" ] } ], "prompt_number": 40 }, { "cell_type": "markdown", "metadata": {}, "source": [ "This renders as $1 \\times 10^{-7}\\,\\mathrm{N}\\,\\mathrm{A}^{-2}$ (dimension $\\mathrm{L}\\,\\mathrm{M}\\,\\mathrm{I}^{-2}\\,\\mathrm{T}^{-2}$)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also change the format of the number using Python's built-in formatting. For example, to use [Unicode](https://en.wikipedia.org/wiki/Unicode) format with seven decimal places:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "print(u'{0:.7fU}'.format(k_A))" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "dyn abA\u207b\u00b2\n" ] } ], "prompt_number": 41 }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Changing the display unit of a unit" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Each unit has display unit. It defaults to the unit itself, but it can be changed. We can use this feature to automatically convert units. For example, to enter a length in yards but display it in meters:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "from natu.units import yd\n", "yd.display_unit = 'm'\n", "1*yd" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 42, "text": [ "1.0 yd" ] } ], "prompt_number": 42 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Besides the display unit, a unit is essentially [immutable](https://en.wikipedia.org/wiki/Immutable_object). Its value is a protected property (`_value`) and its `dimension` and `prefixable` properties can't be changed." ] }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Conversion factors" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[natu](http://kdavies4.github.io/natu/) doesn't use [conversion factors](https://en.wikipedia.org/wiki/Conversion_factor) directly, but can divide units to determine them:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "%precision 4\n", "from natu.units import inch, m\n", "inch/m" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 43, "text": [ "0.0254" ] } ], "prompt_number": 43 }, { "cell_type": "markdown", "metadata": {}, "source": [ "The result may seem counterintuitive at first. We divided the [inch](http://en.wikipedia.org/wiki/Inch) by the [metre](http://en.wikipedia.org/wiki/Metre), but we got the conversion factor from inches to metres. The conversion factor from unit A to unit B is the number of units B in one unit A. Mathematically, this is *x*\\*B = 1\\*A, where *x* is the conversion factor. The solution is *x* = A/B. In this case A is the [inch](http://en.wikipedia.org/wiki/Inch) and B is the [metre](http://en.wikipedia.org/wiki/Metre), so we have the conversion factor from inches to metres (which happens to be the number we used in the [first section](#Entering-and-displaying-a-quantity))." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In [natu](http://kdavies4.github.io/natu/) we deal with quantities, not numbers. Numbers are unit-dependent, but quantities are not. A quantity is expressed as the product of a number and a unit (*q* = *n*\\*U). When we say \"in unit,\" we generally mean \"divided by unit.\" So \"quantity in unit\" is *q*/U or *n*, the number." ] }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Disabling quantities" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Mathematical operations on [natu's quantities](http://kdavies4.github.io/natu/natu.core.html#natu.core.Quantity) are slower than those on floats because the quantities track dimension and display unit in addition to the value. However, quantities may only be needed to check dimensions during code development and to display values as the product of a number and a unit for debugging. Later, one can speed up the code by disabling quantities and using their values directly." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First, let's see how long it takes to express a simple quantity:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "%timeit 1*m" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "10000 loops, best of 3: 25.9 \u00b5s per loop\n" ] } ], "prompt_number": 44 }, { "cell_type": "markdown", "metadata": {}, "source": [ "We'll disable quantities and check again. Restart the IPython kernel now, since it's only possible to disable quantities before any units are imported. Then," ] }, { "cell_type": "code", "collapsed": false, "input": [ "%precision 4\n", "from natu import config\n", "config.use_quantities = False" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 1 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, import the desired units:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "from natu.units import m\n", "%timeit 1*m" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "10000000 loops, best of 3: 54.4 ns per loop\n" ] } ], "prompt_number": 2 }, { "cell_type": "markdown", "metadata": {}, "source": [ "The operation is about 300 times faster because it's just floating point multiplication. The [metre](http://en.wikipedia.org/wiki/Metre) is now a float:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "type(m)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 3, "text": [ "float" ] } ], "prompt_number": 3 }, { "cell_type": "markdown", "metadata": {}, "source": [ "However, the core functionality is still available. As in the [first section of the tutorial](#Entering-and-displaying-a-quantity), where quantities were enabled, we can express a length as a number of inches by dividing it by the [inch](http://en.wikipedia.org/wiki/Inch):" ] }, { "cell_type": "code", "collapsed": false, "input": [ "from natu.units import inch\n", "length = 0.0254*m\n", "length/inch" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 4, "text": [ "1.0000" ] } ], "prompt_number": 4 }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we inspect the length directly, we find that it's only a number:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "length" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 5, "text": [ "0.0254" ] } ], "prompt_number": 5 }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is the number of [metres](http://en.wikipedia.org/wiki/Metre), since the [metre](http://en.wikipedia.org/wiki/Metre) has a value of 1.0:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "m" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 6, "text": [ "1.0000" ] } ], "prompt_number": 6 }, { "cell_type": "markdown", "metadata": {}, "source": [ "The [metre](http://en.wikipedia.org/wiki/Metre) is normalized because the units were based on [SI](https://en.wikipedia.org/wiki/SI) using [base-SI.ini](https://github.com/kdavies4/natu/blob/master/natu/config/base-SI.ini) (the first definition file, by default). This can be changed (see [the documentation](http://kdavies4.github.io/natu/base-ini.html) and [the earlier section on units via definition files](#Via-definition-files)), and code generally shouldn't be written so that it requires a particular unit system." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Although scalar units are now floats, [lambda units](http://kdavies4.github.io/natu/natu.core.html#natu.core.LambdaUnit) remain. Let's look at the [degree Celsius](http://en.wikipedia.org/wiki/Celsius):" ] }, { "cell_type": "code", "collapsed": false, "input": [ "from natu.units import degC\n", "degC" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 7, "text": [ "dimensionless LambdaUnit degC (prefixable)" ] } ], "prompt_number": 7 }, { "cell_type": "markdown", "metadata": {}, "source": [ "It's considered dimensionless because dimensions are no longer tracked; it yields a float instead of a quantity. The usage is the same though:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "from natu.units import K\n", "temperature = 0*degC + 100*K\n", "temperature/degC" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 8, "text": [ "100.0000" ] } ], "prompt_number": 8 } ], "metadata": {} } ] }