Note

This tutorial was generated from a Jupyter notebook that can be downloaded here. If you’d like to reproduce the results in the notebook, or make changes to the code, we recommend downloading this notebook and running it with Jupyter as certain cells (mostly those that change plot styles) are excluded from the tutorials.

Understanding units in LEGWORK

In this tutorial, we explain how units work in LEGWORK, how to create proper input for functions and how to convert output to units of your choice. Note that LEGWORK uses the astropy.units module for units and so this tutorial draws heavily from their documentation!

We’ve this tutorial up as a sort of FAQ so feel free to skip to the most relevant part!

Let’s start by important the LEGWORK source module as well as astropy.units and numpy.

[1]:
import legwork.source as source
import astropy.units as u

import numpy as np

What units can I use?

We follow the Standard Units defined in Astropy. This means that

  • lengths are defined in terms of metres (or equivalent units)

  • masses are defined in terms of kilograms (or equivalent units)

  • times are defined in terms of seconds (or equivalent units)

However, if you’re planning to try to measure the gravitational waves from a source for which kilograms is a sensible unit for the mass, I’ve got some bad news for you…

Therefore, for LEGWORK you are most likely to focus on the following units:

  • mass: \(\rm M_{\odot}\), accessed via u.Msun

  • frequency: \(\rm Hz\), accessed via u.Hz

  • distance: \(\rm kpc, Mpc, Gpc\), accessed via u.kpc, u.Mpc, u.Gpc

  • separation: \(\rm AU\), accessed via u.AU or perhaps \(\rm R_{\odot}\), accessed via u.Rsun

  • ages: \(\rm yr, Myr, Gyr\), accessed via u.yr, u.Myr, u.Gyr

But that doesn’t mean you have to use these units because of the flexibility of Astropy. LEGWORK will accept any equivalent unit to those listed above.

Astropy provides a very convenient method for getting equivalent units. Say you know you could input the mass of a source in kilograms but you know that this isn’t the best unit. You can find some equivalent choices by running

[2]:
u.kg.find_equivalent_units()
[2]:
Primary nameUnit definitionAliases
M_e9.10938e-31 kg
M_p1.67262e-27 kg
earthMass5.97217e+24 kgM_earth, Mearth
g0.001 kggram
jupiterMass1.89812e+27 kgM_jup, Mjup, M_jupiter, Mjupiter
kgirreduciblekilogram
solMass1.98841e+30 kgM_sun, Msun
t1000 kgtonne
u1.66054e-27 kgDa, Dalton

And thus you can see that you could even use the mass of an electron (u.M_e) as your unit if that is your heart’s desire.

How do I give a variable units?

Okay great, so you know what unit you want to use, now you just need to apply it to a variable. Say you have a list of masses that looks like this

[3]:
# a list of masses
masses = [1.0, 1.4, 10, 30, 50]
print(masses)
[1.0, 1.4, 10, 30, 50]

and you know that each mass is in terms of solar masses. To make sure LEGWORK knows this you multiply your variable by the unit.

[4]:
masses_with_units = masses * u.Msun
print(masses_with_units)
[ 1.   1.4 10.  30.  50. ] solMass

And…that’s it! Your input has been transformed into an Astropy Quantity rather than a simple Python list and you’re good to go!

Could you show me an example of using units with LEGWORK input?

Well, how could I say no when you asked so nicely? Let’s create a collection sources and get their SNRs.

[5]:
# let's define the primary in solar masses
m_1 = [10, 12, 30] * u.Msun

# and the secondary in electron masses (because why not)
m_2 = [1e60, 5e60, 7.5e60] * u.M_e

# then the frequencies are generally defined in terms of Hertz
f_orb = [1e-3, 1e-4, 1e-2] * u.Hz

# and the distances with kiloparsecs
dist = [1, 8, 50] * u.kpc

# finally, eccentricity has no unit
ecc = [0.7, 0.0, 0.2]

sources = source.Source(m_1=m_1, m_2=m_2, f_orb=f_orb, dist=dist, ecc=ecc, interpolate_g=False)

Then if we ask the class for the signal-to-noise ratio it will handle the units cleanly and fully simplify.

[6]:
sources.get_snr()
[6]:
array([3.76050048e+03, 8.61372971e-01, 4.14167952e+03])

Be careful though, if you don’t use correct units then you’ll get a UnitConversionError that may be hard to isolate.

[7]:
try:
    # give frequency units of length
    f_orb = f_orb.value * u.m
    # try to create a source
    sources = source.Source(m_1=m_1, m_2=m_2, f_orb=f_orb, dist=dist, ecc=ecc, interpolate_g=False)
except u.UnitConversionError as error:
    print(error)
'm(1/3) solMass(1/3) / (kg(1/3) s(2/3))' and 'AU' (length) are not convertible

How do you convert between units?

Great work, if you’ve got this far then you can now provide input to LEGWORK with any unit of your choice.

But what about the output? LEGWORK tries to choose some sensible units for the output but maybe you want something else and can’t for the life of you remember the difference between a kiloparsec and a light year. Never fear, Astropy has you covered!

In order to convert between units you can use the .to() method of an Astropy quanitity. Let’s get the merger times of the sources that we defined in the earlier example and convert the result to different units.

[8]:
# get the merger times
t_merge = sources.get_merger_time()

# by default LEGWORK uses Gyr for merger times
t_merge
[8]:
$[1.4564593 \times 10^{-5},~0.013231627,~1.8741491 \times 10^{-8}] \; \mathrm{Gyr}$
[9]:
# but we could look at how many years this is
t_merge.to(u.yr)
[9]:
$[14564.593,~13231627,~18.741491] \; \mathrm{yr}$
[10]:
# or maybe you just really want to know how many fortnights until your favourite source merges
t_merge.to(u.fortnight)
[10]:
$[379979.83,~3.4520369 \times 10^{8},~488.95211] \; \mathrm{fortnight}$

You can also convert to any combination of units as long as they simplify to an equivalent unit.

[11]:
# let's convert to a combination of units
t_merge.to(u.yr * u.Msun / u.kg)
[11]:
$[7.3247438 \times 10^{-27},~6.6543759 \times 10^{-24},~9.425366 \times 10^{-30}] \; \mathrm{\frac{M_{\odot}\,yr}{kg}}$

Beware though, if you try to convert to a unit isn’t equivalent then you’ll get an UnitConversionerror

[12]:
try:
    t_merge.to(u.yr * u.Msun)
except u.UnitConversionError as error:
    print(error)
'Gyr' (time) and 'solMass yr' are not convertible

How do I decompose a variable’s value and unit?

So you’ve got the result and now the pesky unit is perhaps getting in the way of saving the result or doesn’t work with another of your functions. If you want to get the value back then just use .value like this

[13]:
masses_with_units = [1.0, 1.4, 10, 30, 50] * u.Msun
print(masses_with_units)
print(masses_with_units.value)
[ 1.   1.4 10.  30.  50. ] solMass
[ 1.   1.4 10.  30.  50. ]

You can also use .unit to get the unit of a variable (this can be very useful when plotting and labelled axes)

[14]:
print(masses_with_units)
print(masses_with_units.unit)
[ 1.   1.4 10.  30.  50. ] solMass
solMass

That’s all for this tutorial, be sure to check out the other ones to find other ways to keep your feet up and let us do the LEGWORK! If you still have questions about units we recommend that you take a look at the Astropy documentation directly.