Skip to content
Snippets Groups Projects
radioactive_decay.py 2.78 KiB
Newer Older
#!/usr/bin/env python
An example of how to use bilby to perform parameter estimation for
non-gravitational wave data. In this case, fitting the half-life and
initial radionuclide number for Polonium 214.
import bilby
import numpy as np
import matplotlib.pyplot as plt

from bilby.core.likelihood import PoissonLikelihood
from bilby.core.prior import LogUniform

# A few simple setup steps
label = 'radioactive_decay'
outdir = 'outdir'
bilby.utils.check_directory_exists_and_if_not_mkdir(outdir)
# generate a set of counts per minute for n_init atoms of
# Polonium 214 in atto-moles with a half-life of 20 minutes
n_avogadro = 6.02214078e23
n_init = 1e-19 / atto

def decay_rate(delta_t, halflife, n_init):
    """
    Get the decay rate of a radioactive substance in a range of time intervals
    (in minutes). n_init is in moles.

    Parameters
    ----------
    delta_t: float, array-like
        Time step in minutes
    halflife: float
        Halflife of atom in minutes
    n_init: int, float
        Initial nummber of atoms
    times = np.cumsum(delta_t)
    times = np.insert(times, 0, 0.0)
    n_atoms = n_init * atto * n_avogadro
Colm Talbot's avatar
Colm Talbot committed
    rates = (np.exp(-np.log(2) * (times[:-1] / halflife)) -
             np.exp(- np.log(2) * (times[1:] / halflife))) * n_atoms / delta_t
# Now we define the injection parameters which we make simulated data with
injection_parameters = dict(halflife=halflife, n_init=n_init)

# These lines of code generate the fake data. Note the ** just unpacks the
# contents of the injection_parameters when calling the model function.
sampling_frequency = 1
time_duration = 300
time = np.arange(0, time_duration, 1 / sampling_frequency)
delta_t = np.diff(time)
rates = decay_rate(delta_t, **injection_parameters)
# get radioactive counts
counts = np.random.poisson(rates)
theoretical = decay_rate(delta_t, **injection_parameters)

# We quickly plot the data to check it looks sensible
fig, ax = plt.subplots()
ax.semilogy(time[:-1], counts, 'o', label='data')
ax.semilogy(time[:-1], theoretical, '--r', label='signal')
ax.set_ylabel('counts')
ax.legend()
fig.savefig('{}/{}_data.png'.format(outdir, label))

# Now lets instantiate a version of the Poisson Likelihood, giving it
# the time intervals, counts and rate model
likelihood = PoissonLikelihood(delta_t, counts, decay_rate)
priors = dict()
priors['halflife'] = LogUniform(
    1e-5, 1e5, latex_label='$t_{1/2}$', unit='min')
priors['n_init'] = LogUniform(
    1e-25 / atto, 1e-10 / atto, latex_label='$N_0$', unit='attomole')
result = bilby.run_sampler(
    likelihood=likelihood, priors=priors, sampler='dynesty',
Gregory Ashton's avatar
Gregory Ashton committed
    nlive=1000, injection_parameters=injection_parameters,
    outdir=outdir, label=label)