03 - Generic unitsΒΆ

In many community codes and for a lot of algorithms it makes sense to use units for which the base units are not (fully) specified. This is often the case when e.g. the equations solved are scale free or for initial conditions where similar models can be scaled to different sizes. Although the quantities involved do not have a specific unit base, they still have a dimension (mass, length etc.). In AMUSE we can use generic units in this case. In other words, you can specify if a value has a mass, length or time dimension, or any combination thereof, such as length per time.

First import everything from amuse.lab:

In[1]:

```from amuse.lab import *
```

AMUSE includes two generic unit systems, the generic_unit_system is the most general, the nbody_system is a special case and always defines the gravitational constant to be G=1. For gravity calculations the nbody_system module is recommended as this follows the general practice in most n-body codes.

The generic units are defined in the generic_system and nbody_system modules.

In[2]:

```print 10.0 | nbody_system.length
print 10.0 | generic_unit_system.length
```
```10.0 length
10.0 length
```

Quantities with generic units work exactly the same as quantities with normal (S.I.) units.

In[3]:

```cluster_mass = 1.0 | generic_unit_system.length
mean_speed = 0.1 | generic_unit_system.length / generic_unit_system.time
print mean_speed * cluster_mass
```
```0.1 length**2 * time**-1
```

Generic quantities are very useful and can be applied almost everywhere in AMUSE.

To convert to a specific system of units you’ll need a converter. For nbody units you can create a converter like this:

In[4]:

```converter = nbody_system.nbody_to_si(1 | units.MSun, 1 | units.AU)
```

An nbody_system converter always needs two orthogonal quantities, besides G=1 which is already defined. These quantities can be simple (like 1 solar mass) or combined (like 10 km/s). The two quantities fix the scaling and will be used to convert to and from the nbody units:

In[5]:

```print "Mass of the sun, scaled:" , converter.to_nbody(1 | units.MSun)
print "10 nbody masses, in S.I.:" , converter.to_si(10 | nbody_system.mass)
print "1 nbody time, in S.I:" , converter.to_si(1 | nbody_system.time).as_quantity_in(units.yr)
print "10 km/s, in nbody:" , converter.to_nbody(10.0 | units.km / units.s)
```
```Mass of the sun, scaled: 1.0 mass
10 nbody masses, in S.I.: 1.98892e+31 kg
1 nbody time, in S.I: 0.159141174195 yr
10 km/s, in nbody: 0.335700383529 length * time**-1
```

For the generic unit converter, you can specify up to 7 quantities (as there are 7 base properties). Any combination of quantities is possible as long as it results in a orthogonal set of converters.

In[6]:

```converter = generic_unit_converter.ConvertBetweenGenericAndSiUnits(1 | units.MSun, 1 | units.AU, constants.G)
print "Mass of the sun, scaled:" , converter.to_nbody(1 | units.MSun)
print "10 generic masses, in S.I.:" , converter.to_si(10 | nbody_system.mass)
print "1 generic time, in S.I:" , converter.to_si(1 | nbody_system.time).as_quantity_in(units.yr)
print "10 km/s, in generic:" , converter.to_nbody(10.0 | units.km / units.s)
```
```Mass of the sun, scaled: 1.0 mass
10 generic masses, in S.I.: 1.98892e+31 kg
1 generic time, in S.I: 0.159141174195 yr
10 km/s, in generic: 0.335700383529 length * time**-1
```

In[7]:

```converter = generic_unit_converter.ConvertBetweenGenericAndSiUnits(1 | units.MSun, 1 | units.AU, 1 | units.yr)
print "Mass of the sun, scaled:" , converter.to_nbody(1 | units.MSun)
print "10 generic masses, in S.I.:" , converter.to_si(10 | nbody_system.mass)
print "1 generic time, in S.I:" , converter.to_si(1 | nbody_system.time).as_quantity_in(units.yr)
print "10 km/s, in generic:" , converter.to_nbody(10.0 | units.km / units.s)
```
```Mass of the sun, scaled: 1.0 mass
10 generic masses, in S.I.: 1.98892e+31 kg
1 generic time, in S.I: 1.0 yr
10 km/s, in generic: 2.10945021128 length * time**-1
```

Specifying a length twice or specifying a speed and a length and a time will result in an error.

In[8]:

```generic_unit_converter.ConvertBetweenGenericAndSiUnits(
1 | units.MSun,
1 | units.AU,
1 | units.m
)
```
```---------------------------------------------------------------------------
UnitsNotOrtogonalException                Traceback (most recent call last)
<ipython-input-8-2872816a1ff9> in <module>()
2     1 | units.MSun,
3     1 | units.AU,
----> 4     1 | units.m
5 )

/net/biesbosch/data2/vanelteren/workspace/amuse/src/amuse/units/generic_unit_converter.pyc in __init__(self, *arguments_list)
99         if not len(available_units) is self.system_rank:
--> 100             raise UnitsNotOrtogonalException(self.system_rank, len(available_units))
101         self.list_of_available_units = list(available_units)
102

UnitsNotOrtogonalException: The number of orthoganal units is incorrect, expected 3 but found 2. To convert between S.I. units and another system of units a set of quantities with orthogonal units is needed. These can be quantities with a single unit (such as length or time) or quantities with a derived units (such as velocity or force)
```

As an example, the following defines a converter for Planck units:

In[9]:

```natural_units_convert = generic_unit_converter.ConvertBetweenGenericAndSiUnits(constants.c,
constants.G,
constants.hbar,
1/(4*numpy.pi*constants.eps0),
constants.kB)

M = 1 | generic_unit_system.mass
T = 1 | generic_unit_system.time
L = 1 | generic_unit_system.length
Q = 1 | generic_unit_system.charge
THETA= 1| generic_unit_system.temperature

print natural_units_convert.to_si(M).in_(units.kg)
print natural_units_convert.to_si(T).in_(units.s)
print natural_units_convert.to_si(L).in_(units.m)
print natural_units_convert.to_si(Q).in_(units.C)
print natural_units_convert.to_si(THETA).in_(units.K)
```
```2.17643740821e-08 kg
5.39123788657e-44 s
1.61625245768e-35 m
1.87554586987e-18 C
1.41678472104e+32 K
```

Ofcourse unit commensurability is still enforced:

In[10]:

```print (10.0 | nbody_system.length) + (10.0 | generic_unit_system.time)
```
```---------------------------------------------------------------------------
IncompatibleUnitsException                Traceback (most recent call last)
<ipython-input-10-3dcb52ea36d7> in <module>()
----> 1 print (10.0 | nbody_system.length) + (10.0 | generic_unit_system.time)

98             return new_quantity(other.number, other.unit)
99         else:
--> 100             other_in_my_units = to_quantity(other).as_quantity_in(self.unit)
101             return new_quantity(self.number + other_in_my_units.number, self.unit)

/net/biesbosch/data2/vanelteren/workspace/amuse/src/amuse/units/quantities.pyc in as_quantity_in(self, another_unit)
166         :returns: quantity converted to new unit
167         """
--> 168         value_of_unit_in_another_unit = self.unit.as_quantity_in(another_unit)
169         return new_quantity(self.number * value_of_unit_in_another_unit.number, another_unit)
170

/net/biesbosch/data2/vanelteren/workspace/amuse/src/amuse/units/core.pyc in as_quantity_in(self, unit)
285             raise exceptions.AmuseException("Cannot expres a unit in a quantity")
286         else:
--> 287             factor = self.conversion_factor_from(unit)
288             return quantities.new_quantity(factor, unit)
289

/net/biesbosch/data2/vanelteren/workspace/amuse/src/amuse/units/core.pyc in conversion_factor_from(self, x)
249             return this_factor / other_factor
250         else:
--> 251             raise IncompatibleUnitsException(x, self)
252
253     def in_(self, x):

IncompatibleUnitsException: Cannot express time in length, the units do not have the same bases
```

In[ ]: