{ "metadata": { "name": "", "signature": "sha256:605b3d8b11e0c89a9419012e93a56234e2513e922e80be4fc54e914529d5c831" }, "nbformat": 3, "nbformat_minor": 0, "worksheets": [ { "cells": [ { "cell_type": "heading", "level": 1, "metadata": {}, "source": [ "Unidades f\u00edsicas en Python: el paquete quantities" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Autor: [Eduardo Mart\u00edn Calleja](http://balbuceosastropy.blogspot.com.es/)" ] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Importaciones y referencias" ] }, { "cell_type": "code", "collapsed": false, "input": [ "from __future__ import division\n", "\n", "import quantities as pq\n", "import numpy as np\n", "import inspect\n", "\n", "# Generar un cuadro con versiones de las librer\u00edas utilizadas en este notebook\n", "#https://github.com/jrjohansson/version_information\n", "%load_ext version_information\n", "%version_information numpy, quantities" ], "language": "python", "metadata": {}, "outputs": [ { "html": [ "
SoftwareVersion
Python2.7.9 64bit [GCC 4.4.7 20120313 (Red Hat 4.4.7-1)]
IPython2.3.1
OSLinux 3.13.0 45 generic x86_64 with debian jessie sid
numpy1.9.1
quantities0.10.1
Thu Feb 05 12:26:14 2015 CET
" ], "json": [ "{\"Software versions\": [{\"version\": \"2.7.9 64bit [GCC 4.4.7 20120313 (Red Hat 4.4.7-1)]\", \"module\": \"Python\"}, {\"version\": \"2.3.1\", \"module\": \"IPython\"}, {\"version\": \"Linux 3.13.0 45 generic x86_64 with debian jessie sid\", \"module\": \"OS\"}, {\"version\": \"1.9.1\", \"module\": \"numpy\"}, {\"version\": \"0.10.1\", \"module\": \"quantities\"}]}" ], "latex": [ "\\begin{tabular}{|l|l|}\\hline\n", "{\\bf Software} & {\\bf Version} \\\\ \\hline\\hline\n", "Python & 2.7.9 64bit [GCC 4.4.7 20120313 (Red Hat 4.4.7-1)] \\\\ \\hline\n", "IPython & 2.3.1 \\\\ \\hline\n", "OS & Linux 3.13.0 45 generic x86\\_64 with debian jessie sid \\\\ \\hline\n", "numpy & 1.9.1 \\\\ \\hline\n", "quantities & 0.10.1 \\\\ \\hline\n", "\\hline \\multicolumn{2}{|l|}{Thu Feb 05 12:26:14 2015 CET} \\\\ \\hline\n", "\\end{tabular}\n" ], "metadata": {}, "output_type": "pyout", "prompt_number": 1, "text": [ "Software versions\n", "Python 2.7.9 64bit [GCC 4.4.7 20120313 (Red Hat 4.4.7-1)]\n", "IPython 2.3.1\n", "OS Linux 3.13.0 45 generic x86_64 with debian jessie sid\n", "numpy 1.9.1\n", "quantities 0.10.1\n", "Thu Feb 05 12:26:14 2015 CET" ] } ], "prompt_number": 1 }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "\u00bfPor qu\u00e9 utilizar unidades f\u00edsicas en Python?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Existen varios paquetes de Python que permiten trabajar con unidades y constantes f\u00edsicas. He probado algunos de ellos y mi preferencia personal es el paquete [quantities](http://pythonhosted.org/quantities/). Su utilizaci\u00f3n me resulta particularmente sencilla y natural, y para m\u00ed posee el nivel de funcionalidad suficiente, por lo que ser\u00e1 el que utilizar\u00e9 en los siguientes ejemplos.\n", "\n", "La primera cuesti\u00f3n que se puede plantear es: \u00bfpor que necesito que mi lenguaje de programaci\u00f3n preferido soporte unidades f\u00edsicas?. Bien, supongamos que en un momento determinado asigno a una variable el valor de una longitud igual a 3 medida en metros. En otro momento creo otra variable con una longitud de 5 medida en pies. Y m\u00e1s tarde sumo ambas cantidades. Sin un control de unidades f\u00edsicas obtendr\u00eda un resultado de 8, carente de sentido y erroneo en cualquier sistema de unidades. Imaginemos por el contrario que estoy trabajando con *quantities*:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "x = 3 * pq.m\n", "y = 5 * pq.foot\n", "print x + y" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "4.524 m\n" ] } ], "prompt_number": 2 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Como se ve, obtenemos un resultado correcto expresado en metros." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pero... \u00bfa qui\u00e9n va a ocurrirsele sumar metros y pies?. Bueno, en el a\u00f1o 1998 la sonda espacial [Mars Climate Orbiter (MCO)](http://es.wikipedia.org/wiki/Mars_Climate_Orbiter) de la NASA se desintegr\u00f3 en la atm\u00f3sfera marciana porque el software de la estaci\u00f3n de seguimiento en Tierra hac\u00eda uso del sistema de unidades anglosaj\u00f3n (pulgadas, pies y libras) mientras que el software embarcado en la nave utilizaba, de acuerdo con las especificaciones, el sistema m\u00e9trico decimal (mil\u00edmetros, metros, kil\u00f3metros y kilos), sin que se llevaran a cabo las conversiones requeridas." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Por supuesto existen muchas otras posibilidades de error cuando se trabaja con cantidades que expresan magnitudes con unidades f\u00edsicas. En particular puede no respetarse la coherencia dimensional. Supongamos por ejemplo que inadvertidamente pretendemos sumar dos cantidades, una que representa una magnitud de fuerza (en Newtons) y otra de energ\u00eda (en Julios):" ] }, { "cell_type": "code", "collapsed": false, "input": [ "x = 3.5 * pq.newton\n", "y = 0.7 * pq.joule\n", "try:\n", " x + y # da un error si se intenta realizar una operaci\u00f3n no v\u00e1lida con esas unidades\n", "except Exception as e:\n", " print u'*** \u00a1Atenci\u00f3n! ***: ',e" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "*** \u00a1Atenci\u00f3n! ***: Unable to convert between units of \"J\" and \"N\"\n" ] } ], "prompt_number": 3 }, { "cell_type": "markdown", "metadata": {}, "source": [ "En este caso, *quantities* ha generado una excepci\u00f3n que nos avisa de la incoherencia dimensional de la operaci\u00f3n que queremos llevar a cabo.\n", "\n", "La utilidad de *quantities* va mucho m\u00e1s all\u00e1, al permitir que sea el int\u00e9rprete el que lleva el control de las unidades compuestas en que se expresa el resultado de un c\u00e1lculo y realiza autom\u00e1ticamente las conversiones adecuadas para que el resultado sea correcto. Esto significa que podemos introducir los valores de las magnitudes en las unidades que en cada momento nos resulten m\u00e1s c\u00f3modas:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "x = 0.3 * pq.km # longitud en Km\n", "t = 250 * pq.ms # tiempo en milisegundos\n", "acel = x/t**2\n", "masa = 7 * pq.g # masa en gramos\n", "fuerza = masa * acel\n", "print fuerza" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "3.36e-05 g*km/ms**2\n" ] } ], "prompt_number": 4 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Podemos pedir que el resultado se exprese en el sistema de unidades de base (SI por defecto):" ] }, { "cell_type": "code", "collapsed": false, "input": [ "print fuerza.simplified" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "33.6 kg*m/s**2\n" ] } ], "prompt_number": 5 }, { "cell_type": "markdown", "metadata": {}, "source": [ "O bien que se exprese en alguna unidad derivada, siempre que se respete la coherencia dimensional:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "print fuerza.rescale(pq.newton)\n", "print fuerza.rescale(pq.dyne)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "33.6 N\n", "3360000.0 dyn\n" ] } ], "prompt_number": 6 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Lo importante es que las unidades correctas se ir\u00e1n computando en todos los c\u00e1lculos intermedios, realizandose las conversiones adecuadas de forma autom\u00e1tica, y avisandonos si en alg\u00fan momento cometemos un error dimensional. Adem\u00e1s el resultado final se podr\u00e1 expresar en las unidades que deseemos, siempre que sean dimensionalmente compatibles con las unidades computadas." ] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Unidades m\u00e1s utilizadas en astronom\u00eda" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Hemos visto anteriormente algunos ejemplos elementales de utilizaci\u00f3n de unidades f\u00edsicas con el paquete *quantities*. En astronom\u00eda su utilizaci\u00f3n puede ser particularmente \u00fatil debido a la diversidad de las unidades utilizadas. A continuaci\u00f3n indicaremos algunas de las m\u00e1s habituales. Mostraremos adem\u00e1s la expresi\u00f3n \"simplificada\" para poder comprobar el valor num\u00e9rico en unidades del SI:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# Unidades de tiempo y distancias\n", "print pq.year # tiempo en a\u00f1os\n", "print pq.au, pq.au.simplified # distancia en unidades astron\u00f3micas\n", "print pq.light_year, pq.light_year.simplified # distancia en a\u00f1os luz\n", "print pq.pc, pq.pc.simplified # distancia en parsecs" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "1 yr (year)\n", "1 au (astronomical_unit) 1.49597870691e+11 m\n", "1 ly (light_year) 9.46073047258e+15 m\n", "1 pc (parsec) 3.08568025e+16 m\n" ] } ], "prompt_number": 7 }, { "cell_type": "code", "collapsed": false, "input": [ "# Unidades de temperatura\n", "print pq.K\n", "print pq.celsius" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "1 K (Kelvin)\n", "1 degC (Celsius)\n" ] } ], "prompt_number": 8 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pero, mucho cuidado, ya que *quantities* no realiza conversiones entre escalas de temperaturas absolutas y temperaturas en grados, ya que eso implica tener en cuenta un punto de referencia (el cero absoluto) para lo que no est\u00e1 preparado el paquete. Las temperaturas se consideran siempre diferencias de temperaturas." ] }, { "cell_type": "code", "collapsed": false, "input": [ "# En este ejemplo se v\u00e9 que la conversi\u00f3n no se lleva a cabo\n", "temp = 345 * pq.celsius\n", "print temp.rescale(pq.K)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "345.0 K\n" ] } ], "prompt_number": 9 }, { "cell_type": "code", "collapsed": false, "input": [ "# Tampoco es correcta la conversi\u00f3n entre las escalas Farhrenheit y Celsius\n", "# Sin embargo, si se considera el valor como un incremento de temperaturas,\n", "# el resultado es el correcto\n", "temp = 170 * pq.fahrenheit\n", "print temp.rescale(pq.celsius)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "94.4444444444 degC\n" ] } ], "prompt_number": 10 }, { "cell_type": "code", "collapsed": false, "input": [ "# Medida de \u00e1ngulos\n", "print pq.deg\n", "print pq.rad\n", "print pq.arcmin\n", "print pq.arcsec" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "1 deg (arcdegree)\n", "1 rad (radian)\n", "1 arcmin (arcminute)\n", "1 arcsec (arcsecond)\n" ] } ], "prompt_number": 11 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ejemplos de utilizaci\u00f3n de unidades angulares:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "print (np.pi/2 * pq.rad).rescale(pq.deg)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "90.0 deg\n" ] } ], "prompt_number": 12 }, { "cell_type": "code", "collapsed": false, "input": [ "print (1 * pq.deg).rescale(pq.arcmin)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "60.0 arcmin\n" ] } ], "prompt_number": 13 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Es posible definir nuevas unidades que no existen en el paquete:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# Definici\u00f3n de nuevas unidades\n", "Mly = pq.UnitQuantity('megalight_year', 1e6*pq.ly, symbol = 'Mly')\n", "Mpc = pq.UnitQuantity('megaparsec', 1e6*pq.pc, symbol = 'Mpc')" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 14 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Por ejemplo, \u00bfcuantos a\u00f1os luz hay en un megaparsec?" ] }, { "cell_type": "code", "collapsed": false, "input": [ "print (1 * Mpc).rescale(pq.ly)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "3261566.59778 ly\n" ] } ], "prompt_number": 15 }, { "cell_type": "markdown", "metadata": {}, "source": [ "En el ejemplo anterior puede verse que la nueva unidad definida no forma parte del espacio de nombres de *quantities*, es decir, escribimos Mpc, no pq.Mpc" ] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Constantes" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "El paquete *quantities* tambi\u00e9n posee un amplio repertorio de constantes f\u00edsicas. A t\u00edtulo de ejmplo citaremos las siguientes, de inter\u00e9s en astronom\u00eda:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "print pq.c, pq.c.simplified # Velocidad de la luz\n", "print pq.constants.G, pq.constants.G.simplified # Constante newtoniana de gravitaci\u00f3n" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "1 c (speed_of_light) 299792458.0 m/s\n", "1 G (Newtonian_constant_of_gravitation) 6.67428e-11 m**3/(kg*s**2)\n" ] } ], "prompt_number": 16 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Y se pueden definir nuevas constantes. Personalmente utilizo las siguientes:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# Definici\u00f3n de nuevas constantes:\n", "earth_radius = 6378160.0 * pq.m\n", "moon_radius = 1740000.0 * pq.m\n", "sun_radius = 695000000.0 * pq.m\n", "earth_mass = 5.97219e24 * pq.kg\n", "sun_mass = 332946 * earth_mass\n", "Hubble_constant = 67.80 * (pq.km/pq.s)/(1e6*pq.pc)" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 17 }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Soporte de NumPy" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Una gran cualidad de *quantities* es que es totalmente compatible con los arrays de NumPy. Puede definirse el tipo de unidades de un array:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "xa = np.arange(10) * pq.N" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 18 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Cada elemento del array anterior tendr\u00e1 unidades en Newton" ] }, { "cell_type": "code", "collapsed": false, "input": [ "print xa[5]" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "5.0 N\n" ] } ], "prompt_number": 19 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Y se soportan operaciones con arrays: en el ejemplo siguiente, al dividir el array anterior por una megnitud de masa, todos los elementos del array tendr\u00e1n unidades de aceleraci\u00f3n:\n" ] }, { "cell_type": "code", "collapsed": false, "input": [ "print (xa / (3.0 * pq.kg)).simplified" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "[ 0. 0.33333333 0.66666667 1. 1.33333333 1.66666667\n", " 2. 2.33333333 2.66666667 3. ] m/s**2\n" ] } ], "prompt_number": 20 }, { "cell_type": "markdown", "metadata": {}, "source": [ "En ocasiones es necesario obtener el valor num\u00e9rico de la variable sin incluir el tipo de unidad. Esto se puede hacer de dos maneras, seg\u00fan queramos obtener un valor num\u00e9rico o un array de NumPy:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# Un solo valor num\u00e9rico\n", "f = 30 * pq.N\n", "print f.item(), type(f.item())\n", "\n", "# Un array de valores\n", "print xa.magnitude, type(f.magnitude)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "30.0 \n", "[ 0. 1. 2. 3. 4. 5. 6. 7. 8. 9.] \n" ] } ], "prompt_number": 21 }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Explorando el contenido del paquete *quantities*" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "El paquete *quantities.units* incluye un gran n\u00famero de unidades organizadas en una serie de subm\u00f3dulos, uno para cada tipo de unidades. Para listar los diferentes subm\u00f3dulos se puede escribir lo siguiente:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "for nombre, datos in inspect.getmembers(pq.units, inspect.ismodule):\n", " print nombre, ','," ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "acceleration , angle , area , compound , electromagnetism , energy , force , frequency , heat , information , length , mass , power , prefixes , pressure , radiation , substance , temperature , time , velocity , viscosity , volume ,\n" ] } ], "prompt_number": 22 }, { "cell_type": "markdown", "metadata": {}, "source": [ "A continuaci\u00f3n, se puede obtener una lista de los nombres de las unidades en cada paquete. Por ejemplo, para ver cuales son las unidades de tipo 'mass':" ] }, { "cell_type": "code", "collapsed": false, "input": [ "for u in dir(pq.mass):\n", " print u,','," ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "Da , UnitMass , __builtins__ , __doc__ , __file__ , __name__ , __package__ , absolute_import , amu , apdram , apothecary_ounce , apothecary_pound , apounce , appound , atomic_mass_unit , avoirdupois_ounce , avoirdupois_pound , bag , carat , dalton , denier , dr , drachm , dram , dtex , dwt , g , gr , grain , gram , kg , kilogram , lb , long_hundredweight , long_ton , metric_ton , mg , milligram , ounce , oz , pennyweight , pound , scruple , short_hundredweight , short_ton , slug , slugs , st , stone , t , tex , ton , tonne , toz , troy_ounce , troy_pound , u ,\n" ] } ], "prompt_number": 23 }, { "cell_type": "markdown", "metadata": {}, "source": [ "El otro package importante es el de constantes:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "for nombre, datos in inspect.getmembers(pq.constants, inspect.ismodule):\n", " print nombre, ','," ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "_codata , _utils , astronomy , atomicunits , deuteron , electromagnetism , electron , helion , mathematical , muon , naturalunits , neutron , proton , quantum , relationships , statisticalmechanics , tau , triton , weak , xray ,\n" ] } ], "prompt_number": 24 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Y para ver por ejemplo el contenido del m\u00f3dulo de constantes astron\u00f3micas:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "dir(pq.constants.astronomy)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 25, "text": [ "['G',\n", " 'Newtonian_constant_of_gravitation',\n", " '__builtins__',\n", " '__doc__',\n", " '__file__',\n", " '__name__',\n", " '__package__',\n", " 'absolute_import',\n", " 'astronomical_unit',\n", " 'au']" ] } ], "prompt_number": 25 } ], "metadata": {} } ] }