MCMC Analysis of Copper Half-Life

Short Half Life
Cu66 has a half-life of 5.12 minutes

Long Half Life

Cu64 has a half-life of 12.7 hours (762 minutes)

Introduction

Naturally occuring copper is composed principally of two isotopes, Cu63 and Cu65. These isotopes are stable and do not emit radiation. Through thermal neutron bombardment we form isotopes with an additional neutron. After some time we will have produced radioactive Cu63 and Cu65 in differing quantities. Using a simple Geiger Tube, a counter, and a clock we will attempt to determine the half lives of the two isotopes. Numerous methods of analysis could be used. Here I present a Bayesian method using Markov Chain Monte Carlo (MCMC). In essence, this calculates the probability that the parameters of our model would have produced the observed data and searches for the most probable model.


Files for this exercise can be found here:https://github.com/paulnord/bayesian_analysis

The Model

Even with good shielding around our detector and sample, there is some level of background radiation. For the purposes of this model we will assume that the number of background count rate is constant. The total number of observed background counts will simply be a function of the background rate and time.

The total number of observed counts at time t will be a function of five parameters. These include the initial number of atoms for each species a and b, the half lives for each species, and the background count rate.

We model this using the pystan code as follows.

import pystan

import pandas as pd

import pickle


import multiprocessing

multiprocessing.set_start_method("fork")


model = """

data {

int<lower=0> N;

vector[N] a;

int d[N];

}

parameters {

real<lower=0> A0; // Initial number of atoms a

real<lower=0.0, upper=20> t0; // Half Life a

real<lower=0> A1; // Initial number of atoms b

real<lower=400, upper=1000> t1; // Half Life b

real<lower=0, upper=20> background;

}

model {

for(n in 1:N) {

d[n] ~ poisson( A0 + A1 - A0*(0.5^((a[n])/t0)) - A1*(0.5^((a[n])/t1)) + background*a[n] );

}

}

"""


def main():


df = pd.read_csv("my_data.csv")

x = df["t"].to_numpy()

y = df["count"].to_numpy()


# Put our data in a dictionary

data = {'N': len(x), 'a': x, 'd': y}


# Compile the model

sm = pystan.StanModel(model_code=model)


# Train the model and generate samples


fit = sm.sampling(data=data, iter=2000, chains=5, warmup=1000, thin=1, seed=91801, n_jobs=4, control=dict(adapt_delta=0.9))


pickle.dump( sm, open( "model.p", "wb" ) )

pickle.dump( fit, open( "fit.p", "wb" ) )

pickle.dump( data, open( "data.p", "wb" ) )


## Diagnostics #################################################################


print(fit)


if __name__ == '__main__':

main()


Note that the number observed at time t, denoted in the data as d[n], is predicted to be a Poisson distribution around the precise values generated by the mathematical model.

Data

t,count

0.5,87

1,164

1.5,259

2,331

3.5,494

4,551

4.5,604

5,659

5.5,714

6,766

6.5,825

7,875

7.5,924

8,971

8.5,1007

9,1054

9.5,1100

10,1143

10.5,1179

11,1221

11.5,1260

12,1305

12.5,1346

13,1388

13.5,1418

14,1456

14.5,1490

192.3833333,11184

196.8333333,11407

307.15,16679

312.1833333,16919

916.6666667,40583

918.6666667,40660

1086.633333,46063

1089.583333,46138

1271.666667,51484

1273.666667,51538

1446.666667,56114

1448.666667,56166

1629.1,60640

1631.85,60697

1747.716667,63394

1748.716667,63414

Results

A0 738.2109 +/- 37.703469

t0 4.674207 +/- 0.411689 minutes

A1 48328.88 +/- 6742.159141

t1 765.6829 +/- 74.729073 minutes

background 13.99500 +/- 2.212040 counts/minute

Expected values for the two decay rates are well within the uncertainties described here. The plots above show an overlay of 1000 models selected from a sample of the models used in this analysis. They are plotted in a light color so that they form a fuzzy edge around the plotted line described by the mean of the parameters. All of these models generate curves which are very close to the data. While the errors seem very large, they are actually a better representation of the true uncertainty in applying this model to this data. Many least-squares fitting functions will give uncertainties which give too much confidence in the model predictions.

Thanks

I'd like to thank students William Bakke and Anand Agrawal for sharing the data from their lab exercise.