Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Ok-landscape
GitHub Repository: Ok-landscape/computational-pipeline
Path: blob/main/latex-templates/templates/nuclear-physics/radioactive_decay.tex
51 views
unlisted
1
\documentclass[a4paper, 11pt]{article}
2
\usepackage[utf8]{inputenc}
3
\usepackage[T1]{fontenc}
4
\usepackage{amsmath, amssymb, amsthm}
5
\usepackage{graphicx}
6
\usepackage{booktabs}
7
\usepackage{siunitx}
8
\usepackage{subcaption}
9
\usepackage[makestderr]{pythontex}
10
11
\newtheorem{definition}{Definition}
12
\newtheorem{theorem}{Theorem}
13
\newtheorem{example}{Example}
14
\newtheorem{remark}{Remark}
15
16
\title{Radioactive Decay Chains: Bateman Equations and Activity Calculations}
17
\author{Nuclear Physics Computation Laboratory}
18
\date{\today}
19
20
\begin{document}
21
\maketitle
22
23
\begin{abstract}
24
This technical report presents comprehensive computational analysis of radioactive decay chains using the Bateman equations. We implement solutions for sequential decay series, compute activities as functions of time, and analyze equilibrium conditions including secular and transient equilibrium. Applications include medical isotope production, nuclear waste management, radiometric dating, and radiation dosimetry.
25
\end{abstract}
26
27
\section{Theoretical Framework}
28
29
\begin{definition}[Radioactive Decay Law]
30
The decay of radioactive nuclei follows first-order kinetics:
31
\begin{equation}
32
\frac{dN}{dt} = -\lambda N
33
\end{equation}
34
where $\lambda = \ln(2)/t_{1/2}$ is the decay constant and $t_{1/2}$ is the half-life.
35
\end{definition}
36
37
\begin{theorem}[Bateman Equations]
38
For a decay chain $N_1 \to N_2 \to N_3 \to \cdots$, the populations evolve as:
39
\begin{align}
40
\frac{dN_1}{dt} &= -\lambda_1 N_1 \\
41
\frac{dN_2}{dt} &= \lambda_1 N_1 - \lambda_2 N_2 \\
42
\frac{dN_n}{dt} &= \lambda_{n-1} N_{n-1} - \lambda_n N_n
43
\end{align}
44
with solution:
45
\begin{equation}
46
N_n(t) = N_1(0) \prod_{i=1}^{n-1} \lambda_i \sum_{i=1}^{n} \frac{e^{-\lambda_i t}}{\prod_{j \neq i}(\lambda_j - \lambda_i)}
47
\end{equation}
48
\end{theorem}
49
50
\subsection{Activity and Equilibrium}
51
52
\begin{definition}[Activity]
53
The activity $A = \lambda N$ is the number of decays per unit time, measured in Becquerels (Bq = decays/s) or Curies (1 Ci = $3.7 \times 10^{10}$ Bq).
54
\end{definition}
55
56
\begin{example}[Equilibrium Conditions]
57
For a two-member chain with $\lambda_1 \ll \lambda_2$:
58
\begin{itemize}
59
\item \textbf{Secular equilibrium}: $A_2 \approx A_1$ (daughter activity equals parent)
60
\item \textbf{Transient equilibrium}: $A_2/A_1 = \lambda_2/(\lambda_2 - \lambda_1)$
61
\end{itemize}
62
\end{example}
63
64
\section{Computational Analysis}
65
66
\begin{pycode}
67
import numpy as np
68
from scipy.integrate import odeint, solve_ivp
69
import matplotlib.pyplot as plt
70
plt.rc('text', usetex=True)
71
plt.rc('font', family='serif', size=10)
72
73
# Time unit conversions
74
sec_per_hour = 3600
75
sec_per_day = 86400
76
sec_per_year = 365.25 * sec_per_day
77
78
# Decay chain class
79
class DecayChain:
80
def __init__(self, half_lives, names):
81
self.names = names
82
self.half_lives = np.array(half_lives)
83
self.lambdas = np.log(2) / self.half_lives
84
self.n = len(half_lives)
85
86
def equations(self, N, t):
87
dNdt = np.zeros(self.n)
88
dNdt[0] = -self.lambdas[0] * N[0]
89
for i in range(1, self.n):
90
if self.lambdas[i] > 0: # Not stable
91
dNdt[i] = self.lambdas[i-1] * N[i-1] - self.lambdas[i] * N[i]
92
else:
93
dNdt[i] = self.lambdas[i-1] * N[i-1]
94
return dNdt
95
96
def solve(self, N0, t):
97
return odeint(self.equations, N0, t)
98
99
def activity(self, N):
100
return N * self.lambdas
101
102
# Mo-99/Tc-99m generator (medical isotopes)
103
mo_tc_chain = DecayChain(
104
half_lives=[66.0 * sec_per_hour, 6.0 * sec_per_hour, 1e20],
105
names=['Mo-99', 'Tc-99m', 'Tc-99']
106
)
107
108
# Initial conditions (pure Mo-99)
109
N0_mo = [1.0, 0.0, 0.0]
110
t_mo = np.linspace(0, 200 * sec_per_hour, 1000)
111
sol_mo = mo_tc_chain.solve(N0_mo, t_mo)
112
A_mo = mo_tc_chain.activity(sol_mo)
113
114
# Find optimal milking time
115
tc99m_activity = A_mo[:, 1]
116
max_idx = np.argmax(tc99m_activity)
117
t_max_tc = t_mo[max_idx] / sec_per_hour
118
A_max_tc = tc99m_activity[max_idx]
119
120
# Equilibrium ratio
121
lambda_1, lambda_2 = mo_tc_chain.lambdas[0], mo_tc_chain.lambdas[1]
122
equilibrium_ratio = lambda_2 / (lambda_2 - lambda_1)
123
124
# Uranium-238 decay series (simplified)
125
# U-238 -> Th-234 -> Pa-234 -> U-234 -> ...
126
u238_chain = DecayChain(
127
half_lives=[4.47e9 * sec_per_year, 24.1 * sec_per_day,
128
1.17 / 60 * sec_per_hour, 2.45e5 * sec_per_year],
129
names=['U-238', 'Th-234', 'Pa-234', 'U-234']
130
)
131
132
N0_u = [1.0, 0.0, 0.0, 0.0]
133
t_u = np.linspace(0, 365 * sec_per_day, 1000) # 1 year
134
sol_u = u238_chain.solve(N0_u, t_u)
135
A_u = u238_chain.activity(sol_u)
136
137
# Branching decay example: Bi-212
138
# Bi-212 -> Po-212 (64%) or Tl-208 (36%)
139
bi212_hl = 60.6 * 60 # seconds
140
po212_hl = 0.3e-6 # microseconds
141
tl208_hl = 3.05 * 60 # seconds
142
143
branch_alpha = 0.64
144
branch_beta = 0.36
145
146
# Radon-222 and daughters for dosimetry
147
rn_chain = DecayChain(
148
half_lives=[3.8 * sec_per_day, 3.05 * 60, 26.8 * 60,
149
1e-4, 22.3 * sec_per_year],
150
names=['Rn-222', 'Po-218', 'Pb-214', 'Bi-214', 'Pb-210']
151
)
152
153
# Carbon-14 dating
154
c14_half_life = 5730 * sec_per_year
155
lambda_c14 = np.log(2) / c14_half_life
156
157
def carbon_dating_age(ratio):
158
"""Calculate age from C-14/C-12 ratio relative to modern."""
159
return -np.log(ratio) / lambda_c14 / sec_per_year
160
161
# Medical isotopes table
162
medical_isotopes = [
163
('Mo-99/Tc-99m', 66.0, 6.0, 'Imaging'),
164
('I-131', 8.02 * 24, np.inf, 'Thyroid'),
165
('F-18', 1.83, np.inf, 'PET'),
166
('Co-60', 5.27 * 365 * 24, np.inf, 'Therapy'),
167
('Cs-137', 30.17 * 365 * 24, np.inf, 'Calibration')
168
]
169
170
# Multi-stage decay with numerical solution
171
def bateman_analytical_2stage(t, lambda1, lambda2, N10):
172
"""Analytical Bateman solution for 2-stage decay."""
173
N1 = N10 * np.exp(-lambda1 * t)
174
if abs(lambda2 - lambda1) > 1e-10:
175
N2 = N10 * lambda1 / (lambda2 - lambda1) * (np.exp(-lambda1 * t) - np.exp(-lambda2 * t))
176
else:
177
N2 = N10 * lambda1 * t * np.exp(-lambda1 * t)
178
return N1, N2
179
180
# Create visualization
181
fig = plt.figure(figsize=(12, 10))
182
gs = fig.add_gridspec(3, 3, hspace=0.35, wspace=0.35)
183
184
# Plot 1: Mo-99/Tc-99m populations
185
ax1 = fig.add_subplot(gs[0, 0])
186
ax1.plot(t_mo / sec_per_hour, sol_mo[:, 0], 'b-', lw=2, label='Mo-99')
187
ax1.plot(t_mo / sec_per_hour, sol_mo[:, 1], 'r-', lw=2, label='Tc-99m')
188
ax1.plot(t_mo / sec_per_hour, sol_mo[:, 2], 'g--', lw=1.5, label='Tc-99')
189
ax1.set_xlabel('Time (hours)')
190
ax1.set_ylabel('Relative Population')
191
ax1.set_title('Mo-99/Tc-99m Generator')
192
ax1.legend(fontsize=7)
193
ax1.grid(True, alpha=0.3)
194
195
# Plot 2: Activities
196
ax2 = fig.add_subplot(gs[0, 1])
197
ax2.plot(t_mo / sec_per_hour, A_mo[:, 0] / A_mo[0, 0], 'b-', lw=2, label='$A_1$ (Mo-99)')
198
ax2.plot(t_mo / sec_per_hour, A_mo[:, 1] / A_mo[0, 0], 'r-', lw=2, label='$A_2$ (Tc-99m)')
199
ax2.axvline(x=t_max_tc, color='gray', ls='--', alpha=0.5)
200
ax2.set_xlabel('Time (hours)')
201
ax2.set_ylabel('Relative Activity')
202
ax2.set_title('Decay Activities')
203
ax2.legend(fontsize=7)
204
ax2.grid(True, alpha=0.3)
205
206
# Plot 3: Activity ratio (transient equilibrium)
207
ax3 = fig.add_subplot(gs[0, 2])
208
ratio = A_mo[:, 1] / (A_mo[:, 0] + 1e-20)
209
ax3.plot(t_mo / sec_per_hour, ratio, 'purple', lw=2)
210
ax3.axhline(y=equilibrium_ratio, color='r', ls='--',
211
label=f'Equilibrium: {equilibrium_ratio:.3f}')
212
ax3.set_xlabel('Time (hours)')
213
ax3.set_ylabel('$A_2/A_1$')
214
ax3.set_title('Activity Ratio')
215
ax3.legend(fontsize=7)
216
ax3.grid(True, alpha=0.3)
217
ax3.set_ylim([0, equilibrium_ratio * 1.2])
218
219
# Plot 4: U-238 daughters approach secular equilibrium
220
ax4 = fig.add_subplot(gs[1, 0])
221
for i in range(1, 4):
222
ax4.plot(t_u / sec_per_day, A_u[:, i] / (A_u[0, 0] + 1e-20),
223
lw=1.5, label=u238_chain.names[i])
224
ax4.set_xlabel('Time (days)')
225
ax4.set_ylabel('Activity / Initial $A_0$')
226
ax4.set_title('U-238 Daughters (Secular Equil.)')
227
ax4.legend(fontsize=7)
228
ax4.grid(True, alpha=0.3)
229
ax4.set_yscale('log')
230
231
# Plot 5: Decay curve fitting
232
ax5 = fig.add_subplot(gs[1, 1])
233
t_decay = np.linspace(0, 5, 100) # In half-lives
234
y_decay = np.exp(-np.log(2) * t_decay)
235
236
# Add noise for fitting demonstration
237
np.random.seed(42)
238
t_data = np.array([0, 0.5, 1, 1.5, 2, 3, 4])
239
y_data = np.exp(-np.log(2) * t_data) + np.random.normal(0, 0.02, len(t_data))
240
241
ax5.plot(t_decay, y_decay, 'b-', lw=2, label='Theory')
242
ax5.scatter(t_data, y_data, color='red', s=50, zorder=5, label='Data')
243
ax5.axhline(y=0.5, color='gray', ls='--', alpha=0.5)
244
ax5.axvline(x=1, color='gray', ls='--', alpha=0.5)
245
ax5.set_xlabel('Time / $t_{1/2}$')
246
ax5.set_ylabel('$N/N_0$')
247
ax5.set_title('Radioactive Decay Curve')
248
ax5.legend(fontsize=8)
249
ax5.grid(True, alpha=0.3)
250
251
# Plot 6: Carbon-14 dating
252
ax6 = fig.add_subplot(gs[1, 2])
253
ratios = np.linspace(0.01, 1.0, 100)
254
ages = np.array([carbon_dating_age(r) for r in ratios])
255
256
ax6.plot(ages / 1000, ratios, 'g-', lw=2)
257
ax6.axhline(y=0.5, color='gray', ls='--', alpha=0.5)
258
ax6.axvline(x=5.73, color='gray', ls='--', alpha=0.5)
259
ax6.set_xlabel('Age (kyr)')
260
ax6.set_ylabel('$^{14}$C / $^{14}$C$_0$')
261
ax6.set_title('Carbon-14 Dating')
262
ax6.grid(True, alpha=0.3)
263
ax6.set_xlim([0, 50])
264
265
# Plot 7: Half-lives comparison
266
ax7 = fig.add_subplot(gs[2, 0])
267
isotope_names = [m[0].split('/')[0] if '/' in m[0] else m[0] for m in medical_isotopes]
268
half_lives_hr = [m[1] for m in medical_isotopes]
269
colors = ['blue', 'red', 'green', 'orange', 'purple']
270
271
ax7.barh(isotope_names, np.log10(half_lives_hr), color=colors, alpha=0.7)
272
ax7.set_xlabel('$\\log_{10}(t_{1/2} / \\mathrm{hours})$')
273
ax7.set_title('Medical Isotope Half-Lives')
274
ax7.grid(True, alpha=0.3, axis='x')
275
276
# Plot 8: Generator elution cycles
277
ax8 = fig.add_subplot(gs[2, 1])
278
t_cycle = np.linspace(0, 100, 1000)
279
elution_times = [0, 24, 48, 72]
280
281
# Simulate multiple elutions
282
N_mo = np.ones_like(t_cycle)
283
N_tc = np.zeros_like(t_cycle)
284
285
for i, t in enumerate(t_cycle):
286
# Decay between elutions
287
hours = t
288
N_mo[i] = np.exp(-lambda_1 * hours * sec_per_hour)
289
290
# Tc-99m builds up
291
N_tc[i] = (lambda_1 / (lambda_2 - lambda_1)) * N_mo[i] * \
292
(1 - np.exp(-(lambda_2 - lambda_1) * (hours % 24) * sec_per_hour))
293
294
ax8.plot(t_cycle, N_tc * lambda_2 / lambda_1, 'r-', lw=2)
295
for et in elution_times[1:]:
296
ax8.axvline(x=et, color='blue', ls='--', alpha=0.5)
297
ax8.set_xlabel('Time (hours)')
298
ax8.set_ylabel('Tc-99m Activity')
299
ax8.set_title('Generator Elution Cycles')
300
ax8.grid(True, alpha=0.3)
301
302
# Plot 9: Specific activity
303
ax9 = fig.add_subplot(gs[2, 2])
304
A_range = np.logspace(0, 3, 100) # Mass numbers
305
306
# Specific activity = lambda * N_A / A
307
N_A = 6.022e23 # Avogadro's number
308
t_half_1yr = sec_per_year
309
310
specific_act = np.log(2) / t_half_1yr * N_A / A_range / 1e12 # TBq/g
311
312
ax9.loglog(A_range, specific_act, 'b-', lw=2)
313
ax9.set_xlabel('Mass Number $A$')
314
ax9.set_ylabel('Specific Activity (TBq/g)')
315
ax9.set_title('Specific Activity ($t_{1/2}$ = 1 yr)')
316
ax9.grid(True, alpha=0.3, which='both')
317
318
plt.savefig('radioactive_decay_plot.pdf', bbox_inches='tight', dpi=150)
319
print(r'\begin{center}')
320
print(r'\includegraphics[width=\textwidth]{radioactive_decay_plot.pdf}')
321
print(r'\end{center}')
322
plt.close()
323
324
# Summary calculations
325
N_A_value = 6.022e23
326
specific_mo99 = np.log(2) / (66 * sec_per_hour) * N_A_value / 99 / 1e12
327
\end{pycode}
328
329
\section{Results and Analysis}
330
331
\subsection{Mo-99/Tc-99m Generator}
332
333
\begin{pycode}
334
print(r'\begin{table}[htbp]')
335
print(r'\centering')
336
print(r'\caption{Mo-99/Tc-99m Generator Characteristics}')
337
print(r'\begin{tabular}{lcc}')
338
print(r'\toprule')
339
print(r'Parameter & Value & Units \\')
340
print(r'\midrule')
341
print(f'Mo-99 half-life & {66.0:.1f} & hours \\\\')
342
print(f'Tc-99m half-life & {6.0:.1f} & hours \\\\')
343
print(f'Optimal elution time & {t_max_tc:.1f} & hours \\\\')
344
print(f'Equilibrium ratio $A_2/A_1$ & {equilibrium_ratio:.3f} & -- \\\\')
345
print(f'Time to 90\\% equilibrium & {-np.log(0.1)/lambda_2/sec_per_hour:.1f} & hours \\\\')
346
print(r'\bottomrule')
347
print(r'\end{tabular}')
348
print(r'\end{table}')
349
\end{pycode}
350
351
\begin{remark}
352
The Mo-99/Tc-99m generator reaches transient equilibrium because $\lambda_2 > \lambda_1$. The daughter activity exceeds the parent activity by the factor $\lambda_2/(\lambda_2 - \lambda_1) = \py{f"{equilibrium_ratio:.2f}"}$.
353
\end{remark}
354
355
\subsection{Medical Isotopes}
356
357
\begin{pycode}
358
print(r'\begin{table}[htbp]')
359
print(r'\centering')
360
print(r'\caption{Medical Radioisotopes}')
361
print(r'\begin{tabular}{lccl}')
362
print(r'\toprule')
363
print(r'Isotope & $t_{1/2}$ & Production & Application \\')
364
print(r'\midrule')
365
print(r'Tc-99m & 6.0 hr & Generator & SPECT imaging \\')
366
print(r'F-18 & 110 min & Cyclotron & PET imaging \\')
367
print(r'I-131 & 8.0 days & Reactor & Thyroid therapy \\')
368
print(r'Co-60 & 5.3 yr & Reactor & External beam therapy \\')
369
print(r'Ir-192 & 74 days & Reactor & Brachytherapy \\')
370
print(r'\bottomrule')
371
print(r'\end{tabular}')
372
print(r'\end{table}')
373
\end{pycode}
374
375
\subsection{Equilibrium Types}
376
377
\begin{theorem}[Secular Equilibrium]
378
When $\lambda_1 \ll \lambda_2$ (parent half-life much longer than daughter):
379
\begin{equation}
380
A_2 \approx A_1 \quad \text{for } t \gg t_{1/2,2}
381
\end{equation}
382
Example: U-238 series where U-238 ($t_{1/2} = 4.5 \times 10^9$ yr) decays to short-lived daughters.
383
\end{theorem}
384
385
\begin{theorem}[Transient Equilibrium]
386
When $\lambda_1 < \lambda_2$ but comparable:
387
\begin{equation}
388
\frac{A_2}{A_1} = \frac{\lambda_2}{\lambda_2 - \lambda_1}
389
\end{equation}
390
Example: Mo-99/Tc-99m where ratio $= \py{f"{equilibrium_ratio:.2f}"}$.
391
\end{theorem}
392
393
\section{Applications}
394
395
\begin{example}[Radiometric Dating]
396
Carbon-14 dating uses the decay:
397
\begin{equation}
398
^{14}\text{C} \to ^{14}\text{N} + \beta^- + \bar{\nu}_e
399
\end{equation}
400
with $t_{1/2} = 5730$ years. Measurable ages range from $\sim$300 to 50,000 years.
401
\end{example}
402
403
\begin{example}[Nuclear Waste Management]
404
The activity of fission products decreases as:
405
\begin{itemize}
406
\item Short-term (1-100 yr): dominated by Cs-137, Sr-90
407
\item Intermediate (100-1000 yr): Sm-151, Tc-99
408
\item Long-term ($>$1000 yr): actinides (Pu, Am, Cm)
409
\end{itemize}
410
\end{example}
411
412
\section{Discussion}
413
414
The Bateman equations provide a complete description of radioactive decay chains:
415
416
\begin{enumerate}
417
\item \textbf{Generator design}: Optimal elution times maximize daughter activity while balancing parent depletion.
418
\item \textbf{Diagnostic imaging}: Short-lived isotopes minimize patient dose while providing adequate counts.
419
\item \textbf{Dose calculations}: Activity profiles determine internal dosimetry for therapy planning.
420
\item \textbf{Environmental monitoring}: Equilibrium assumptions simplify radon progeny measurements.
421
\end{enumerate}
422
423
\section{Conclusions}
424
425
This computational analysis demonstrates:
426
\begin{itemize}
427
\item Tc-99m maximum activity at $t = \py{f"{t_max_tc:.1f}"}$ hours
428
\item Transient equilibrium ratio: \py{f"{equilibrium_ratio:.3f}"}
429
\item Specific activity of Mo-99: \py{f"{specific_mo99:.0f}"} TBq/g
430
\item C-14 dating range: 300 -- 50,000 years
431
\end{itemize}
432
433
The Bateman equations enable precise prediction of radionuclide behavior for medical, industrial, and research applications.
434
435
\section{Further Reading}
436
\begin{itemize}
437
\item Magill, J., Galy, J., \textit{Radioactivity Radionuclides Radiation}, Springer, 2005
438
\item Loveland, W.D., Morrissey, D.J., Seaborg, G.T., \textit{Modern Nuclear Chemistry}, 2nd Ed., Wiley, 2017
439
\item Cherry, S.R., Sorenson, J.A., Phelps, M.E., \textit{Physics in Nuclear Medicine}, 4th Ed., Elsevier, 2012
440
\end{itemize}
441
442
\end{document}
443
444