astropy/astropy — pull request #745 by iguananaut (2013-02-07 23:57:07+00:00)
Title: Alternate constants API 2

This is the much promised followup to #572. It includes the changes from #572 in some form or another, plus many additional changes as discussed in that issue.

Now it is possible to do `from astropy.constants import `. For example:

```
In [1]: from astropy.constants import G

In [2]: G
Out[2]:
```

Most constants can now be combined directly with other quantities and values as before:

```
In [10]: from astropy import units as u

In [11]: u.Quantity(1.2, 'solMass').to('kg') * G
Out[11]:
```

It can also be converted to other systems of units with different bases:

```
In [12]: G.cgs
Out[12]:
```

However, one of the primary motivations of these changes was to be able to deal sensibly with constants that are defined in units that are incompatible with each other between different systems of units--primarily SI and EM extensions to CGS, `e` (electron charge) being the primary example. In this case, it is possible to import `e` as we did with `G`:

```
In [13]: from astropy.constants import e

In [14]: e
Out[14]:
```

And as you can see, the value of e is given in Coulombs (SI). However, this is too ambiguous for general use. What if we try to combine `e` with some other quantity? The result could have completely different physical units depending on which system we're working in. So for constants that are defined with incompatible units depending on the system it's necessary to *specify* a system in order to combine it with anything:

```
In [46]: 100 * e
ERROR: TypeError: Constant 'e' does not have physically compatible units across all systems of units and cannot be combined with other values without specifying a system (eg. e.esu) [astropy.constants.constant]
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
in ()
----> 1 100 * e

/bray/sc1/root/src/astropy/astropy/astropy/constants/constant.py in wrapper(self, *args, **kwargs)
35 'combined with other values without specifying a '
36 'system (eg. {1}.{2})'.format(self.abbrev, self.abbrev,
---> 37 systems[0]))
38
39 return meth(self, *args, **kwargs)

TypeError: Constant 'e' does not have physically compatible units across all systems of units and cannot be combined with other values without specifying a system (eg. e.esu)

In [47]: 100 * e.si
Out[47]:
```

Electromagnetic constants are further a special case, as there are multiple EM estensions to CGS:

```
In [3]: e.cgs
ERROR: TypeError: Cannot convert EM constants to cgs because there are different systems for E.M constants within the c.g.s system (ESU, Gaussian, etc.). Instead, directly use the constant with the appropriate suffix (e.g. e.esu, e.gauss, etc.). [astropy.constants.constant]
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
in ()
----> 1 e.cgs

/bray/sc1/root/src/astropy/astropy/astropy/constants/constant.py in cgs(self)
172 @property
173 def cgs(self):
--> 174 raise TypeError("Cannot convert EM constants to cgs because there "
175 "are different systems for E.M constants within the "
176 "c.g.s system (ESU, Gaussian, etc.). Instead, "

TypeError: Cannot convert EM constants to cgs because there are different systems for E.M constants within the c.g.s system (ESU, Gaussian, etc.). Instead, directly use the constant with the appropriate suffix (e.g. e.esu, e.gauss, etc.).

In [4]: e.gauss
Out[4]:
```

Another major change made in this PR is that there is no longer a separate `_constants` hidden package. Constants are defined once, in the `astropy.constants` package. This PR shuffles things around in such a way as to carefully avoid any problems with the circular dependency between the constants and units subpackages (though it's a bit fragile, and at a later date we may wish to further revisit this--I think it might make sense to combine units and constants into a single package, while only maintaining the top-level `astropy.units` and `astropy.constants` namespaces for convenience).