aboutsummaryrefslogtreecommitdiff
path: root/src/muon.c
diff options
context:
space:
mode:
authortlatorre <tlatorre@uchicago.edu>2018-08-14 10:08:27 -0500
committertlatorre <tlatorre@uchicago.edu>2018-08-14 10:08:27 -0500
commit24c8bcfe7f76b20124e2862ea050f815c0f768e7 (patch)
treee5bdbd638a2c7f38f1c094cc9e95cbdfe05b9481 /src/muon.c
parent0b7f199c0d93074484ea580504485a32dc29f5e2 (diff)
downloadsddm-24c8bcfe7f76b20124e2862ea050f815c0f768e7.tar.gz
sddm-24c8bcfe7f76b20124e2862ea050f815c0f768e7.tar.bz2
sddm-24c8bcfe7f76b20124e2862ea050f815c0f768e7.zip
move everything to src directory
Diffstat (limited to 'src/muon.c')
-rw-r--r--src/muon.c260
1 files changed, 260 insertions, 0 deletions
diff --git a/src/muon.c b/src/muon.c
new file mode 100644
index 0000000..4b46769
--- /dev/null
+++ b/src/muon.c
@@ -0,0 +1,260 @@
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <gsl/gsl_errno.h>
+#include <gsl/gsl_spline.h>
+#include <math.h>
+#include "optics.h"
+#include "quantum_efficiency.h"
+#include "solid_angle.h"
+#include "pdg.h"
+#include "vector.h"
+#include "muon.h"
+#include "sno.h"
+#include "scattering.h"
+
+static int initialized = 0;
+
+static double *x, *dEdx, *csda_range;
+static size_t size;
+
+static gsl_interp_accel *acc_dEdx;
+static gsl_spline *spline_dEdx;
+
+static gsl_interp_accel *acc_range;
+static gsl_spline *spline_range;
+
+static const double MUON_CRITICAL_ENERGY = 1.029e6;
+
+static int init()
+{
+ int i, j;
+ char line[256];
+ char *str;
+ double value;
+ int n;
+
+ FILE *f = fopen("muE_water_liquid.txt", "r");
+
+ if (!f) {
+ fprintf(stderr, "failed to open muE_water_liquid.txt: %s", strerror(errno));
+ return -1;
+ }
+
+ i = 0;
+ n = 0;
+ /* For the first pass, we just count how many values there are. */
+ while (fgets(line, sizeof(line), f)) {
+ size_t len = strlen(line);
+ if (len && (line[len-1] != '\n')) {
+ fprintf(stderr, "got incomplete line on line %i: '%s'\n", i, line);
+ goto err;
+ }
+
+ i += 1;
+
+ /* Skip the first 10 lines since it's just a header. */
+ if (i <= 10) continue;
+
+ if (!len) continue;
+ else if (line[0] == '#') continue;
+ else if (strstr(line, "Minimum ionization")) continue;
+ else if (strstr(line, "Muon critical energy")) continue;
+
+ str = strtok(line," \n");
+
+ while (str) {
+ value = strtod(str, NULL);
+ str = strtok(NULL," \n");
+ }
+
+ n += 1;
+ }
+
+ x = malloc(sizeof(double)*n);
+ dEdx = malloc(sizeof(double)*n);
+ csda_range = malloc(sizeof(double)*n);
+ size = n;
+
+ i = 0;
+ n = 0;
+ /* Now, we actually store the values. */
+ rewind(f);
+ while (fgets(line, sizeof(line), f)) {
+ size_t len = strlen(line);
+ if (len && (line[len-1] != '\n')) {
+ fprintf(stderr, "got incomplete line on line %i: '%s'\n", i, line);
+ goto err;
+ }
+
+ i += 1;
+
+ /* Skip the first 10 lines since it's just a header. */
+ if (i <= 10) continue;
+
+ if (!len) continue;
+ else if (line[0] == '#') continue;
+ else if (strstr(line, "Minimum ionization")) continue;
+ else if (strstr(line, "Muon critical energy")) continue;
+
+ str = strtok(line," \n");
+
+ j = 0;
+ while (str) {
+ value = strtod(str, NULL);
+ switch (j) {
+ case 0:
+ x[n] = value;
+ break;
+ case 7:
+ dEdx[n] = value;
+ break;
+ case 8:
+ csda_range[n] = value;
+ break;
+ }
+ j += 1;
+ str = strtok(NULL," \n");
+ }
+
+ n += 1;
+ }
+
+ fclose(f);
+
+ acc_dEdx = gsl_interp_accel_alloc();
+ spline_dEdx = gsl_spline_alloc(gsl_interp_linear, size);
+ gsl_spline_init(spline_dEdx, x, dEdx, size);
+
+ acc_range = gsl_interp_accel_alloc();
+ spline_range = gsl_spline_alloc(gsl_interp_linear, size);
+ gsl_spline_init(spline_range, x, csda_range, size);
+
+ initialized = 1;
+
+ return 0;
+
+err:
+ fclose(f);
+
+ return -1;
+}
+
+double get_range(double T, double rho)
+{
+ /* Returns the approximate range a muon with kinetic energy `T` will travel
+ * in water before losing all of its energy. This range is interpolated
+ * based on data from the PDG which uses the continuous slowing down
+ * approximation.
+ *
+ * `T` should be in MeV, and `rho` should be in g/cm^3.
+ *
+ * Return value is in cm.
+ *
+ * See http://pdg.lbl.gov/2018/AtomicNuclearProperties/adndt.pdf. */
+ if (!initialized) {
+ if (init()) {
+ exit(1);
+ }
+ }
+
+ return gsl_spline_eval(spline_range, T, acc_range)/rho;
+}
+
+double get_T(double T0, double x, double rho)
+{
+ /* Returns the approximate kinetic energy of a muon in water after
+ * travelling `x` cm with an initial kinetic energy `T`.
+ *
+ * `T` should be in MeV, `x` in cm, and `rho` in g/cm^3.
+ *
+ * Return value is in MeV.
+ *
+ * See http://pdg.lbl.gov/2018/AtomicNuclearProperties/adndt.pdf. */
+ double a, b, range, T;
+
+ if (!initialized) {
+ if (init()) {
+ exit(1);
+ }
+ }
+
+ range = get_range(T0, rho);
+
+ /* This comes from Equation 33.42 in the PDG Passage of Particles Through
+ * Matter article. */
+ b = log(1 + T0/MUON_CRITICAL_ENERGY)/range;
+ /* Now we compute the ionization energy loss from the known range and b. */
+ a = b*T0/(exp(b*range)-1.0);
+
+ /* Compute the kinetic energy after travelling a distance `x` in the
+ * continuous slowing down approximation. */
+ T = -a/b + (T0+a/b)*exp(-b*x);
+
+ if (T < 0) return 0;
+
+ return T;
+}
+
+double get_dEdx(double T, double rho)
+{
+ /* Returns the approximate dE/dx for a muon in water with kinetic energy
+ * `T`.
+ *
+ * `T` should be in MeV and `rho` in g/cm^3.
+ *
+ * Return value is in MeV/cm.
+ *
+ * See http://pdg.lbl.gov/2018/AtomicNuclearProperties/adndt.pdf. */
+ if (!initialized) {
+ if (init()) {
+ exit(1);
+ }
+ }
+
+ return gsl_spline_eval(spline_dEdx, T, acc_dEdx)/rho;
+}
+
+double get_expected_charge(double x, double T, double *pos, double *dir, double *pmt_pos, double *pmt_normal, double r)
+{
+ double pmt_dir[3], cos_theta, n, wavelength0, omega, theta0, E, p, beta, z, rho, R;
+
+ z = 1.0;
+
+ SUB(pmt_dir,pmt_pos,pos);
+ normalize(pmt_dir);
+
+ if (DOT(pmt_dir,pmt_normal) > 0) return 0;
+
+ /* Calculate the cosine of the angle between the track direction and the
+ * vector to the PMT. */
+ cos_theta = DOT(dir,pmt_dir);
+
+ /* Calculate total energy */
+ E = T + MUON_MASS;
+ p = sqrt(E*E - MUON_MASS*MUON_MASS);
+ beta = p/E;
+
+ omega = get_solid_angle_approx(pos,pmt_pos,pmt_normal,r);
+
+ R = NORM(pos);
+
+ if (R <= AV_RADIUS) {
+ rho = HEAVY_WATER_DENSITY;
+ } else {
+ rho = WATER_DENSITY;
+ }
+
+ /* FIXME: I just calculate delta assuming 400 nm light. */
+ wavelength0 = 400.0;
+ n = get_index(rho, wavelength0, 10.0);
+
+ if (beta < 1/n) return 0;
+
+ /* FIXME: is this formula valid for muons? */
+ theta0 = get_scattering_rms(x,p,beta,z,rho);
+
+ /* FIXME: add angular response and scattering/absorption. */
+ return 2*omega*2*M_PI*FINE_STRUCTURE_CONSTANT*z*z*(1-(1/(beta*beta*n*n)))*get_probability(beta, cos_theta, theta0)/(sqrt(2*M_PI)*theta0);
+}