jitr quickstart example

We will

  • compile a solver for a particular reaction and kinematics

  • define a parametric potential and generate samples of the interaction parameters

  • propagate parameter samples through our compiled solver to generate predictive distributions of reaction observables

To illustrate, we will use the example of \(\alpha\) elastic scattering on \(^{48}\)Ca at 29 MeV, comparing to experimental data from EXFOR.

 1import numpy as np
 2from matplotlib import pyplot as plt
 3from scipy import stats
 4from tqdm import tqdm
 5
 6from jitr.optical_potentials.potential_forms import (
 7    coulomb_charged_sphere as coulomb,
 8)
 9from jitr.optical_potentials.potential_forms import (
10    woods_saxon_safe as ws,
11)
12from jitr.reactions.reaction import Reaction
13from jitr.rmatrix import Solver as SolverKernel
14from jitr.utils import utils
15from jitr.xs import elastic
16
17# define reaction system
18alpha = (4, 2)
19Ca48 = (48, 20)
20reaction = Reaction(target=Ca48, projectile=alpha, process="El")
21
22# calculate kinematics for a given lab energy
23energy_lab = 28.2
24kinematics = reaction.kinematics(energy_lab)
25
26# set the channel radius, number of nodes, and number of partial waves
27interaction_range_fm = 1.2 * (48 ** (1 / 3) + 4 ** (1 / 3)) + 2
28channel_radius_dimensionless = utils.suggested_dimensionless_channel_radius(
29    interaction_range_fm, kinematics.k
30)
31channel_radius = channel_radius_dimensionless / kinematics.k
32N = utils.suggested_basis_size(channel_radius_dimensionless)
33lmax = 180
34
35# build a solver for the system and reaction of interest
36print(f"Compiling solver for {reaction} at {energy_lab} MeV")
37print(f" - channel radius {channel_radius:1.2f} fm")
38print(f" - {N} nodes")
39print(f" - {lmax} partial waves")
40
41solver = elastic.DifferentialWorkspace.build_from_system(
42    reaction=reaction,
43    kinematics=kinematics,
44    channel_radius_fm=channel_radius,
45    solver=SolverKernel(N),
46    lmax=lmax,
47    angles=np.linspace(0.1, np.pi, 180),
48)
49rgrid = solver.radial_grid()
50# jit warmup
51_ = solver.xs(central_potential=np.zeros_like(solver.radial_grid()))
52print("Done!")
Compiling solver for 48-Ca(alpha,el) at 28.2 MeV
 - channel radius 11.19 fm
 - 40 nodes
 - 180 partial waves
Done!
 1# define interaction
 2def U_central(r, Vv, Wv, Rv, av):
 3    fr = ws(r, Rv, av)
 4    return -(Vv * fr + 1j * Wv * fr)
 5
 6
 7def V_Coulomb(r, Zz, RC):
 8    return coulomb(r, Zz, RC)
 9
10
11# define parameter distribution and draw samples
12# just
13param_means = np.array([185, 20, 1.0, 0.6, 1.2])
14param_std_devs = np.array([6, 2, 0.05, 0.05, 0.01])
15num_samples = 1000
16param_draws = stats.multivariate_normal(
17    mean=param_means, cov=np.diag(param_std_devs) ** 2
18).rvs(num_samples)
19
20print(f"Running {num_samples} calculations...")
21prediction_samples = np.zeros((num_samples, solver.angles.size))
22for i in tqdm(range(param_draws.shape[0])):
23    Vv, Wv, rv, av, rC = param_draws[i]
24    A_factor = reaction.target.A ** (1 / 3) + reaction.projectile.A ** (1 / 3)
25    Zz = reaction.target.Z * reaction.projectile.Z
26    xs = solver.xs(
27        central_potential=U_central(
28            rgrid,
29            Vv,
30            Wv,
31            rv * A_factor,
32            av,
33        ),
34        coulomb_potential=V_Coulomb(rgrid, Zz, rC * A_factor),
35    )
36    prediction_samples[i, :] = xs.dsdo / solver.rutherford
37
38print("Done!")
Running 1000 calculations...
100%|██████████████████████████████████████████████████████████| 1000/1000 [00:05<00:00, 168.00it/s]
Done!

We will compare to some experimental data from EXFOR

1import pandas as pd
2
3df = pd.read_csv("./alpha_ca48_ratio_ruth.txt", names=["angle", "xs"], sep=r"\s+")

Plot predictive cross section distribution

 1plt.figure()
 2plt.scatter(df["angle"], df["xs"], color="k", marker=".")
 3
 4plt.fill_between(
 5    np.rad2deg(solver.angles),
 6    *np.percentile(prediction_samples, [16, 84], axis=0),
 7    alpha=0.5,
 8)
 9plt.yscale("log")
10plt.xlabel(r"$\theta$ [deg]")
11plt.ylabel(r"$(\frac{d\sigma}{d\Omega}) / (\frac{d\sigma}{d\Omega})_{R}$")
12plt.title(f"${reaction.reaction_latex}$ at {kinematics.Elab:1.0f} MeV")
13plt.tight_layout()
14plt.show()
../../_images/a66a5a1a2a72da845fa12b9be563186ce9601fe09c1b0d63806ff4ead72cc4c0.png

This looks like a reasonable prior! The next steps would be to use Bayesian calibration to determine a posterior…