Skip to content

Commit

Permalink
Merge pull request #419 from mdboom/unit/docstring_fixes
Browse files Browse the repository at this point in the history
Update `units` docstrings and doc pages to reflect new None and UnrecognizedUnit stuff
  • Loading branch information
eteq committed Nov 13, 2012
2 parents 4af1f9c + b1a2f12 commit edcee79
Show file tree
Hide file tree
Showing 8 changed files with 201 additions and 87 deletions.
3 changes: 3 additions & 0 deletions astropy/units/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
from .equivalencies import *
from .physical import *

# Create a special singleton for the dimensionless unit
dimensionless = Unit(1)

# After importing the unit definitions above, set the unit namespace
# to this top-level module so that new units are added here.
UnitBase._set_namespace(globals())
11 changes: 10 additions & 1 deletion astropy/units/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -638,6 +638,9 @@ class UnrecognizedUnit(IrreducibleUnit):
def __init__(self, st):
IrreducibleUnit.__init__(self, st)

def __repr__(self):
return "UnrecognizedUnit({0})".format(str(self))

def __str__(self):
return self.name

Expand Down Expand Up @@ -669,7 +672,7 @@ def is_equivalent(self, other, equivs=[]):
def get_converter(self, other, equivs=[]):
raise ValueError(
"The unit {0!r} is unrecognized. It can not be converted "
"to other units.")
"to other units.".format(self.name))

def get_format_name(self, format):
return self.name
Expand Down Expand Up @@ -865,6 +868,12 @@ def __init__(self, scale, bases, powers):
self._bases = bases
self._powers = powers

def __repr__(self):
if len(self._bases):
return super(CompositeUnit, self).__repr__()
else:
return 'Unit(dimensionless)'

@property
def scale(self):
"""
Expand Down
9 changes: 5 additions & 4 deletions docs/units/composing_and_defining.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ Composing and defining units
Units can be composed together using the regular Python numeric
operators. For example::

>>> from astropy import units as u
>>> fluxunit = u.erg / (u.cm ** 2 * u.s)
>>> fluxunit
Unit("erg / (s cm2)")
Unit("erg / (cm2 s)")

Users are free to define new units, either fundamental or compound
using the `def_unit` function. For example::
using the `~astropy.units.core.def_unit` function. For example::

>>> bakers_fortnight = u.def_unit('bakers_fortnight', 13 * u.day)

Expand All @@ -30,8 +31,8 @@ Creating a new fundamental unit is simple::
Reducing a unit to its irreducible parts
----------------------------------------

A unit can be decomposed into its irreducible parts using the `decompose`
method::
A unit can be decomposed into its irreducible parts using the
`~astropy.units.core.UnitBase.decompose` method::

>>> u.Ry
Unit("Ry")
Expand Down
22 changes: 12 additions & 10 deletions docs/units/conversion.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Direct Conversion
In this case, given a source and destination unit, the value(s) in the
new units is(are) returned.

>>> from astropy import units as u
>>> u.pc.to(u.m, 3.26)
1.0059317615e+17

Expand All @@ -24,34 +25,35 @@ Obtaining a Conversion Function

Finally, one may obtain a function that can be used to convert to the
new unit. Normally this may seem like overkill when all one needs to
do is multiply by a scale factor, but there are cases where it is not
so simple, for example when there are equivalencies in use.
do is multiply by a scale factor, but there are cases when the
transformation between units may not be as simple as a single scale
factor, for example when a custom equivalency table is in use.

Conversion to different units involves obtaining a conversion function
and then applying it to the value, or values to be converted.

>>> speed_unit = u.cm / u.s
>>> speed_converter = speed_unit.get_converter(u.mile / u.hour)
>>> speed_converter(100.)
>>> cms = u.cm / u.s
>>> cms_to_mph = cms.get_converter(u.mile / u.hour)
>>> cms_to_mph(100.)
2.2366936292054402
>>> speed_converter([1000, 2000])
>>> cms_to_mph([1000, 2000])
array([ 22.36936292, 44.73872584])

Incompatible Conversions
------------------------

If you attempt to convert to a incompatible unit, an exception will result:

>>> speed_unit.to(u.mile)
>>> cms.to(u.mile)
...
UnitsException: 'cm / (s)' and 'mi' are not convertible
UnitsException: 'cm / (s)' (speed) and 'mi' (length) are not convertible

You can check whether a particular conversion is possible using the
`is_equivalent` method::
`~astropy.units.core.UnitBase.is_equivalent` method::

>>> u.m.is_equivalent(u.foot)
True
>>> u.m.is_equivalent("second")
False
>>> (u.m**2).is_equivalent(u.acre)
>>> (u.m ** 2).is_equivalent(u.acre)
True
98 changes: 53 additions & 45 deletions docs/units/equivalencies.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,41 +8,45 @@ different units in certain contexts. Namely when equations can
uniquely relate a value in one unit to a different unit. A good
example is the equivalence between wavelength, frequency and energy
for specifying a wavelength of radiation. Normally these units are not
convertable, but when understood as representing light, they are
convertable. This will describe how to use two of the equivalencies
convertible, but when understood as representing light, they are
convertible. This will describe how to use two of the equivalencies
include in `astropy.units` and then describe how to define new
equivalencies.

Equivalencies are used by passing a list of equivalency pairs to the
`to` or `get_converter` methods.
`equivs` keyword argument of `~astropy.units.core.UnitBase.to` or
`~astropy.units.core.UnitBase.get_converter` methods.

Built-in equivalencies
----------------------

Spectral Units
^^^^^^^^^^^^^^

`sp` is a function that returns an equivalency list to handle
conversions between wavelength, frequency and energy.
`~astropy.units.equivalencies.sp` is a function that returns an
equivalency list to handle conversions between wavelength, frequency
and energy.

Length and frequency are not normally convertible, so
`to` raises an exception::
`~astropy.units.core.UnitBase.to` raises an exception::

>>> from astropy import units as u
>>> u.nm.to(u.Hz, [1000, 2000])
UnitsException: 'nm' and 'Hz' are not convertible
UnitsException: 'nm' (length) and 'Hz' (frequency) are not convertible

However, when passing the result of `sp` as the third argument to the
`to` method, wavelength, frequency and energy can be converted.
However, when passing the result of `~astropy.units.equivalencies.sp`
as the third argument to the `~astropy.units.core.UnitBase.to` method,
wavelength, frequency and energy can be converted.

>>> u.nm.to(u.Hz, [1000, 2000], u.sp())
>>> u.nm.to(u.Hz, [1000, 2000], equivs=u.sp())
array([ 2.99792458e+14, 1.49896229e+14])
>>> u.nm.to(u.eV, [1000, 2000], u.sp())
>>> u.nm.to(u.eV, [1000, 2000], equivs=u.sp())
array([ 1.23984201, 0.61992101])

These equivalencies even work with non-base units::

>>> # Inches to calories
>>> u.inch.to(u.Cal, 1, u.sp())
>>> u.inch.to(u.Cal, 1, equivs=u.sp())
1.869180759162485e-27

Spectral Flux Density Units
Expand All @@ -55,10 +59,10 @@ those spectral locations. The function that handles these unit
conversions is `sd`. This function takes as its arguments the unit
and value for the spectral location. For example::

>>> u.Jy.to(u.erg / u.cm**2 / u.s / u.Hz, 1., u.sd(u.AA, 3500))
>>> u.Jy.to(u.erg / u.cm**2 / u.s / u.Hz, 1., equivs=u.sd(u.AA, 3500))
1.0000000000000001e-23

>>> u.Jy.to(u.erg / u.cm**2 / u.s / u.micron, 1., u.sd(u.AA, 3500))
>>> u.Jy.to(u.erg / u.cm**2 / u.s / u.micron, 1., equivs=u.sd(u.AA, 3500))
2.4472853714285712e-08

Writing new equivalencies
Expand All @@ -72,26 +76,26 @@ elements::
`from_unit` and `to_unit` are the equivalent units. `forward` and
`backward` are functions that convert values between those units.

For example, in an old definition of the metric liter, it was defined
as the volume of 1000 grams of water at 4∘C, a standard pressure and a
bunch of other assumptions. Volumes and masses are not normally
directly convertible, but if we hold those assumptions as true, we
could build an equivalency for them::
For example, until 1964 the metric liter was defined as the volume of
1kg of water at 4°C at 760mm mercury pressure. Volumes and masses are
not normally directly convertible, but if we hold the constants in the
1964 definition of the liter as true, we could build an equivalency
for them::

>>> liters_water = [
(u.l, u.g, lambda x: 1000.0 * x, lambda x: x / 1000.0)
]
>>> u.l.to(u.kg, 1, liters_water)
>>> u.l.to(u.kg, 1, equivs=liters_water)
1.0

Note that the equivalency can be used with any other compatible units::

>>> u.gallon.to(u.pound, 1, liters_water)
>>> u.gallon.to(u.pound, 1, equivs=liters_water)
8.345404463333525

And it also works in the other direction::

>>> u.lb.to(u.pint, 1, liters_water)
>>> u.lb.to(u.pint, 1, equivs=liters_water)
0.9586114172355458

Displaying available equivalencies
Expand All @@ -102,30 +106,34 @@ For example, without passing equivalencies, there are no compatible
units for `Hz` in the standard set::

>>> u.Hz.find_equivalent_units()
Primary name | Unit definition | Aliases
Hz | 1 / (s) | Hertz, hertz
Primary name | Unit definition | Aliases
[
Hz | 1 / (s) | Hertz, hertz ,
]

However, when passing the spectral equivalency, you can see there are
all kinds of things that `Hz` can be converted to::

>>> u.Hz.find_equivalent_units(u.sp())
Primary name | Unit definition | Aliases
AA | 1.00e-10 m | Angstrom, angstrom
AU | 1.50e+11 m |
BTU | 1.06e+03 m2 kg / (s2) | btu
Hz | 1 / (s) | Hertz, hertz
J | m2 kg / (s2) | Joule, joule
Ry | 2.18e-18 m2 kg / (s2) | rydberg
cal | 4.18e+00 m2 kg / (s2) | calorie
eV | 1.60e-19 m2 kg / (s2) | electron_volt
erg | 1.00e-07 m2 kg / (s2) |
ft | 3.05e-01 m | foot
inch | 2.54e-02 m |
kcal | 4.18e+03 m2 kg / (s2) | Cal, Calorie, kilocal, kilocalorie
lyr | 9.46e+15 m |
m | irreducible | meter
mi | 1.61e+03 m | mile
micron | 1.00e-06 m |
pc | 3.09e+16 m | parsec
solRad | 6.96e+08 m |
yd | 9.14e-01 m | yard
>>> u.Hz.find_equivalent_units(equivs=u.sp())
Primary name | Unit definition | Aliases
[
AU | 1.495979e+11 m | au ,
Angstrom | 1.000000e-10 m | AA, angstrom ,
BTU | 1.055056e+03 kg m2 / (s2) | btu ,
Hz | 1 / (s) | Hertz, hertz ,
J | kg m2 / (s2) | Joule, joule ,
Ry | 2.179872e-18 kg m2 / (s2) | rydberg ,
cal | 4.184000e+00 kg m2 / (s2) | calorie ,
eV | 1.602177e-19 kg m2 / (s2) | electronvolt ,
erg | 1.000000e-07 kg m2 / (s2) | ,
ft | 3.048000e-01 m | foot ,
inch | 2.540000e-02 m | ,
kcal | 4.184000e+03 kg m2 / (s2) | Cal, Calorie, kilocal, kilocalorie ,
lyr | 9.460730e+15 m | ,
m | irreducible | meter ,
mi | 1.609344e+03 m | mile ,
micron | 1.000000e-06 m | ,
pc | 3.085678e+16 m | parsec ,
solRad | 6.955080e+08 m | ,
yd | 9.144000e-01 m | yard ,
]
65 changes: 57 additions & 8 deletions docs/units/format.rst
Original file line number Diff line number Diff line change
@@ -1,26 +1,29 @@
Unit formats
============

Units can be created from strings using the `Unit` class::
Units can be created from strings using the `~astropy.units.core.Unit`
class::

>>> from astropy import units as u
>>> u.Unit("m")
Unit("m")
>>> u.Unit("erg / (s cm2)")
Unit("erg / (s cm2)")

Units can be converted to strings using the `to_string` method::
Units can be converted to strings using the
`~astropy.units.core.UnitBase.to_string` method::

>>> fluxunit = u.erg / (u.cm ** 2 * u.s)
>>> fluxunit.to_string()
u'erg / (s cm2)'
u'erg / (cm2 s)'

By default, the string format used is referred to as the "generic"
format, which is based on syntax of the FITS standard's format for
representing units, but supports all of the units defined within the
`astropy.units` framework, including user-defined units. The `Unit`
and `to_string` functions also take an optional `format` parameter to
select a different format. This parameter may be either a string or a
`astropy.units.format.Base` instance.
`astropy.units` framework, including user-defined units. The
`~astropy.units.core.Unit` and
`~astropy.units.core.UnitBase.to_string` functions also take an
optional `format` parameter to select a different format.

Built-in formats
----------------
Expand Down Expand Up @@ -48,7 +51,8 @@ formats:
- OGIP Units: A standard for storing units in `OGIP FITS files
<http://heasarc.gsfc.nasa.gov/docs/heasarc/ofwg/docs/general/ogip_93_001/>`_.
`astropy.units` also is able to write units in the following formats:
`astropy.units` is also able to write, but not read, units in the
following formats:

- ``"latex"``: Writes units out using LaTeX math syntax using the
`IAU Style Manual
Expand Down Expand Up @@ -77,3 +81,48 @@ formats:
m² kg
2.18×10-¹⁸ ─────

Unrecognized Units
------------------

Since many files in found in the wild have unit strings that do not
correspond to any given standard, `astropy.units` also has a
consistent way to store and pass around unit strings that did not
parse.

Normally, passing an unrecognized unit string raises an exception::

>>> u.Unit("m/s/s") # The FITS standard only allows one '/'
ValueError: Expected end of text (at char 3) in 'm/s/s'

However, the `~astropy.units.core.Unit` constructor has the keyword
argument `parse_strict` that can take one of three values to control
this behavior:

- ``'raise'``: (default) raise a ValueError exception.

- ``'warn'``: emit a Warning, and return an
`~astropy.units.core.UnrecognizedUnit` instance.

- ``'silent'``: return an `~astropy.units.core.UnrecognizedUnit`
instance.

So, for example, one can do::

>>> x = u.Unit("m/s/s", parse_strict="warn")
WARNING: UnitsWarning: 'm/s/s' did not parse using format 'generic'.
Expected end of text (at char 3) in 'm/s/s' [astropy.units.core]

This `~astropy.units.core.UnrecognizedUnit` object remembers the
original string it was created with, so it can be written back out,
but any meaningful operations on it, such as converting to another
unit or composing with other units, will fail.

>>> x.to_string()
'm/s/s'
>>> x.to(u.km / u.s / u.s)
ValueError: The unit 'm/s/s' is unrecognized. It can not be converted to
other units.
>>> x / u.m
ValueError: The unit 'm/s/s' is unrecognized, so all arithmetic operations
with it are invalid.
Loading

0 comments on commit edcee79

Please sign in to comment.