aboutsummaryrefslogtreecommitdiff
path: root/src/likelihood.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/likelihood.c')
-rw-r--r--src/likelihood.c149
1 files changed, 97 insertions, 52 deletions
diff --git a/src/likelihood.c b/src/likelihood.c
index 786b221..8e12f57 100644
--- a/src/likelihood.c
+++ b/src/likelihood.c
@@ -42,16 +42,19 @@ particle *particle_init(int id, double T0, size_t n)
* which we solve by dividing the track up into `n` segments and then
* numerically computing the energy at each step. */
size_t i;
- double dEdx;
+ double dEdx, rad;
particle *p = malloc(sizeof(particle));
p->id = id;
p->x = malloc(sizeof(double)*n);
p->T = malloc(sizeof(double)*n);
p->n = n;
- p->rad = 0.0;
p->a = 0.0;
p->b = 0.0;
+ p->shower_photons = 0.0;
+ p->delta_ray_a = 0.0;
+ p->delta_ray_b = 0.0;
+ p->delta_ray_photons = 0.0;
p->x[0] = 0;
p->T[0] = T0;
@@ -67,14 +70,17 @@ particle *particle_init(int id, double T0, size_t n)
p->a = electron_get_angular_distribution_alpha(T0);
p->b = electron_get_angular_distribution_beta(T0);
+ p->delta_ray_photons = 0.0;
+
/* Now we loop over the points along the track and assume dE/dx is
* constant between points. */
+ rad = 0.0;
for (i = 1; i < n; i++) {
p->x[i] = p->range*i/(n-1);
dEdx = electron_get_dEdx(p->T[i-1], WATER_DENSITY);
p->T[i] = p->T[i-1] - dEdx*(p->x[i]-p->x[i-1]);
dEdx = electron_get_dEdx_rad(p->T[i-1], WATER_DENSITY);
- p->rad += dEdx*(p->x[i]-p->x[i-1]);
+ rad += dEdx*(p->x[i]-p->x[i-1]);
}
/* Make sure that the energy is zero at the last step. This is so that
* when we try to bisect the point along the track where the speed of
@@ -85,6 +91,8 @@ particle *particle_init(int id, double T0, size_t n)
* range here using the dE/dx table instead of reading in the range. */
p->T[n-1] = 0;
+ p->shower_photons = rad*ELECTRON_PHOTONS_PER_MEV;
+
break;
case IDP_MU_MINUS:
case IDP_MU_PLUS:
@@ -96,14 +104,21 @@ particle *particle_init(int id, double T0, size_t n)
* load the correct table based on the media. */
p->range = muon_get_range(T0, HEAVY_WATER_DENSITY);
+ p->a = muon_get_angular_distribution_alpha(T0);
+ p->b = muon_get_angular_distribution_beta(T0);
+
+ muon_get_delta_ray_distribution_parameters(T0, &p->delta_ray_a, &p->delta_ray_b);
+ p->delta_ray_photons = muon_get_delta_ray_photons(T0);
+
/* Now we loop over the points along the track and assume dE/dx is
* constant between points. */
+ rad = 0.0;
for (i = 1; i < n; i++) {
p->x[i] = p->range*i/(n-1);
dEdx = muon_get_dEdx(p->T[i-1], HEAVY_WATER_DENSITY);
p->T[i] = p->T[i-1] - dEdx*(p->x[i]-p->x[i-1]);
dEdx = muon_get_dEdx_rad(p->T[i-1], WATER_DENSITY);
- p->rad += dEdx*(p->x[i]-p->x[i-1]);
+ rad += dEdx*(p->x[i]-p->x[i-1]);
}
/* Make sure that the energy is zero at the last step. This is so that
* when we try to bisect the point along the track where the speed of
@@ -114,6 +129,8 @@ particle *particle_init(int id, double T0, size_t n)
* range here using the dE/dx table instead of reading in the range. */
p->T[n-1] = 0;
+ p->shower_photons = rad*MUON_PHOTONS_PER_MEV;
+
break;
case IDP_PROTON:
p->mass = PROTON_MASS;
@@ -121,14 +138,17 @@ particle *particle_init(int id, double T0, size_t n)
* tables for heavy water for protons. */
p->range = proton_get_range(T0, WATER_DENSITY);
+ p->delta_ray_photons = 0.0;
+
/* Now we loop over the points along the track and assume dE/dx is
* constant between points. */
+ rad = 0.0;
for (i = 1; i < n; i++) {
p->x[i] = p->range*i/(n-1);
dEdx = proton_get_dEdx(p->T[i-1], WATER_DENSITY);
p->T[i] = p->T[i-1] - dEdx*(p->x[i]-p->x[i-1]);
dEdx = proton_get_dEdx_rad(p->T[i-1], WATER_DENSITY);
- p->rad += dEdx*(p->x[i]-p->x[i-1]);
+ rad += dEdx*(p->x[i]-p->x[i-1]);
}
/* Make sure that the energy is zero at the last step. This is so that
* when we try to bisect the point along the track where the speed of
@@ -139,6 +159,8 @@ particle *particle_init(int id, double T0, size_t n)
* range here using the dE/dx table instead of reading in the range. */
p->T[n-1] = 0;
+ p->shower_photons = rad*PROTON_PHOTONS_PER_MEV;
+
break;
default:
fprintf(stderr, "unknown particle id %i\n", id);
@@ -170,9 +192,9 @@ void particle_free(particle *p)
free(p);
}
-static double get_expected_charge_shower(double *pos, double *dir, double *pmt_pos, double *pmt_normal, double r, double *reflected, double n_d2o, double n_h2o, double l_d2o, double l_h2o, double alpha, double beta, double rad)
+static double get_expected_charge_shower(particle *p, double *pos, double *dir, double *pmt_pos, double *pmt_normal, double r, double *reflected, double n_d2o, double n_h2o, double l_d2o, double l_h2o, double *q_delta_ray, double *q_indirect_delta_ray)
{
- double pmt_dir[3], cos_theta, omega, f, f_reflec, cos_theta_pmt, absorption_length_h2o, absorption_length_d2o, absorption_length_acrylic, l_acrylic, theta_pmt, charge, scattering_length_h2o, scattering_length_d2o, scatter;
+ double pmt_dir[3], cos_theta, omega, f, f_reflec, cos_theta_pmt, absorption_length_h2o, absorption_length_d2o, absorption_length_acrylic, l_acrylic, theta_pmt, charge, scattering_length_h2o, scattering_length_d2o, scatter, constant;
SUB(pmt_dir,pmt_pos,pos);
@@ -180,6 +202,9 @@ static double get_expected_charge_shower(double *pos, double *dir, double *pmt_p
cos_theta_pmt = DOT(pmt_dir,pmt_normal);
+ charge = 0.0;
+ *q_delta_ray = 0.0;
+ *q_indirect_delta_ray = 0.0;
*reflected = 0.0;
if (cos_theta_pmt > 0) return 0.0;
@@ -201,11 +226,26 @@ static double get_expected_charge_shower(double *pos, double *dir, double *pmt_p
l_acrylic = AV_RADIUS_OUTER - AV_RADIUS_INNER;
- charge = exp(-l_d2o/absorption_length_d2o-l_h2o/absorption_length_h2o-l_acrylic/absorption_length_acrylic)*get_weighted_quantum_efficiency()*rad*PHOTONS_PER_MEV*electron_get_angular_pdf(cos_theta,alpha,beta,1/n_d2o)*omega/(2*M_PI);
+ constant = exp(-l_d2o/absorption_length_d2o-l_h2o/absorption_length_h2o-l_acrylic/absorption_length_acrylic)*get_weighted_quantum_efficiency()*omega/(2*M_PI);
+
+ /* Note: We assume here that the peak of the angular distribution is at the
+ * Cerenkov angle for a particle with beta = 1. This seems to be an OK
+ * assumption for high energy showers, but is not exactly correct for
+ * showers from lower energy electrons or for delta rays from lower energy
+ * muons. It seems good enough, but in the future it would be nice to
+ * parameterize this. */
+ if (p->shower_photons > 0)
+ charge = constant*p->shower_photons*electron_get_angular_pdf(cos_theta,p->a,p->b,1/n_d2o);
+
+ if (p->delta_ray_photons > 0)
+ *q_delta_ray = constant*p->delta_ray_photons*electron_get_angular_pdf_delta_ray(cos_theta,p->delta_ray_a,p->delta_ray_b,1/n_d2o);
scatter = exp(-l_d2o/scattering_length_d2o-l_h2o/scattering_length_h2o);
*reflected = (f_reflec + 1.0 - scatter)*charge;
+ *q_indirect_delta_ray = (f_reflec + 1.0 - scatter)*(*q_delta_ray);
+
+ *q_delta_ray *= f*scatter;
return f*charge*scatter;
}
@@ -345,14 +385,14 @@ double log_pt(double t, size_t n, double mu_noise, double mu_indirect, double *m
return ln(n) + (n-1)*log1p(-time_cdf(t,mu_noise,mu_indirect,mu_direct,mu_shower,n2,ts,ts_shower,tmean,sigma,ts_sigma)) + log(time_pdf(t,mu_noise,mu_indirect,mu_direct,mu_shower,n2,ts,ts_shower,tmean,sigma,ts_sigma));
}
-static void integrate_path_shower(particle *p, double *x, double *pdf, double T0, double *pos0, double *dir0, int pmt, size_t n, double *mu_direct, double *mu_indirect, double *time, double *sigma, double rad)
+static void integrate_path_shower(particle *p, double *x, double *pdf, double T0, double *pos0, double *dir0, int pmt, size_t n, double *mu_direct, double *mu_indirect, double *time, double *sigma)
{
size_t i;
static double qs[MAX_NPOINTS];
static double q_indirects[MAX_NPOINTS];
static double ts[MAX_NPOINTS];
static double ts2[MAX_NPOINTS];
- double pos[3], n_d2o, n_h2o, wavelength0, t, l_d2o, l_h2o;
+ double pos[3], n_d2o, n_h2o, wavelength0, t, l_d2o, l_h2o, q_delta_ray, q_indirect_delta_ray;
if (n > MAX_NPOINTS) {
fprintf(stderr, "number of points is greater than MAX_NPOINTS!\n");
@@ -364,6 +404,8 @@ static void integrate_path_shower(particle *p, double *x, double *pdf, double T0
n_d2o = get_index_snoman_d2o(wavelength0);
n_h2o = get_index_snoman_h2o(wavelength0);
+ double range = x[n-1] - x[0];
+
for (i = 0; i < n; i++) {
pos[0] = pos0[0] + x[i]*dir0[0];
pos[1] = pos0[1] + x[i]*dir0[1];
@@ -373,8 +415,9 @@ static void integrate_path_shower(particle *p, double *x, double *pdf, double T0
t = x[i]/SPEED_OF_LIGHT + l_d2o*n_d2o/SPEED_OF_LIGHT + l_h2o*n_h2o/SPEED_OF_LIGHT;
- qs[i] = get_expected_charge_shower(pos, dir0, pmts[pmt].pos, pmts[pmt].normal, PMT_RADIUS, q_indirects+i, n_d2o, n_h2o, l_d2o, l_h2o, p->a, p->b, rad)*pdf[i];
- q_indirects[i] *= pdf[i];
+ qs[i] = get_expected_charge_shower(p, pos, dir0, pmts[pmt].pos, pmts[pmt].normal, PMT_RADIUS, q_indirects+i, n_d2o, n_h2o, l_d2o, l_h2o, &q_delta_ray, &q_indirect_delta_ray);
+ qs[i] = qs[i]*pdf[i] + q_delta_ray/range;
+ q_indirects[i] = q_indirects[i]*pdf[i] + q_indirect_delta_ray/range;
ts[i] = t*qs[i];
ts2[i] = t*t*qs[i];
}
@@ -593,10 +636,11 @@ static double get_total_charge_approx(double beta0, double *pos, double *dir, pa
charge = abs_prob*n_d2o*x*beta0*prob*(1/sin_theta)*omega*(erf((a+b*(smax-s)+n_d2o*(smax-z)*beta0)/frac) + erf((-a+b*s+n_d2o*z*beta0)/frac))/(b+n_d2o*beta0)/(4*M_PI);
- /* Add expected number of photons from electromagnetic shower for electrons
- * and positrons. */
- if (p->id == IDP_E_MINUS || p->id == IDP_E_PLUS)
- charge += abs_prob*get_weighted_quantum_efficiency()*p->rad*PHOTONS_PER_MEV*electron_get_angular_pdf(cos_theta,p->a,p->b,1.0/n_d2o)*omega/(2*M_PI);
+ /* Add expected number of photons from electromagnetic shower. */
+ if (p->shower_photons > 0)
+ charge += abs_prob*get_weighted_quantum_efficiency()*p->shower_photons*electron_get_angular_pdf(cos_theta,p->a,p->b,1.0/n_d2o)*omega/(2*M_PI);
+ if (p->delta_ray_photons > 0)
+ charge += abs_prob*get_weighted_quantum_efficiency()*p->delta_ray_photons*electron_get_angular_pdf_delta_ray(cos_theta,p->delta_ray_a,p->delta_ray_b,1.0/n_d2o)*omega/(2*M_PI);
*mu_reflected = f_reflected*charge;
@@ -891,6 +935,7 @@ double nll(event *ev, vertex *v, size_t n, double dx, double dx_shower, int fast
npoints = (size_t) (range/dx + 0.5);
if (npoints < MIN_NPOINTS) npoints = MIN_NPOINTS;
+ if (npoints > MAX_NPOINTS) npoints = MAX_NPOINTS;
/* Calculate total energy */
E0 = v[j].T0 + p->mass;
@@ -903,32 +948,43 @@ double nll(event *ev, vertex *v, size_t n, double dx, double dx_shower, int fast
if (!fast) {
path = path_init(v[j].pos, v[j].dir, v[j].T0, range, npoints, theta0, getKineticEnergy, p, v[j].z1, v[j].z2, v[j].n, p->mass);
- if (v[j].id == IDP_E_MINUS || v[j].id == IDP_E_PLUS) {
+ switch (v[j].id) {
+ case IDP_E_MINUS:
+ case IDP_E_PLUS:
electron_get_position_distribution_parameters(v[j].T0, &pos_a, &pos_b);
+ break;
+ case IDP_MU_MINUS:
+ case IDP_MU_PLUS:
+ muon_get_position_distribution_parameters(v[j].T0, &pos_a, &pos_b);
+ break;
+ default:
+ fprintf(stderr, "unknown particle id %i\n", v[j].id);
+ exit(1);
+ }
- /* Lower and upper limits to integrate for the electromagnetic
- * shower. */
- xlo = 0.0;
- xhi = gsl_cdf_gamma_Pinv(0.99,pos_a,pos_b);
+ /* Lower and upper limits to integrate for the electromagnetic
+ * shower. */
+ xlo = 0.0;
+ xhi = gsl_cdf_gamma_Pinv(0.99,pos_a,pos_b);
- /* Number of points to sample along the longitudinal direction for
- * the electromagnetic shower. */
- npoints_shower = (size_t) ((xhi-xlo)/dx_shower + 0.5);
+ /* Number of points to sample along the longitudinal direction for
+ * the electromagnetic shower. */
+ npoints_shower = (size_t) ((xhi-xlo)/dx_shower + 0.5);
- if (npoints_shower < MIN_NPOINTS) npoints_shower = MIN_NPOINTS;
+ if (npoints_shower < MIN_NPOINTS) npoints_shower = MIN_NPOINTS;
+ if (npoints_shower > MAX_NPOINTS) npoints_shower = MAX_NPOINTS;
- x = malloc(sizeof(double)*npoints_shower);
- pdf = malloc(sizeof(double)*npoints_shower);
- for (i = 0; i < npoints_shower; i++) {
- x[i] = xlo + i*(xhi-xlo)/(npoints_shower-1);
- pdf[i] = gamma_pdf(x[i],pos_a,pos_b);
- }
+ x = malloc(sizeof(double)*npoints_shower);
+ pdf = malloc(sizeof(double)*npoints_shower);
+ for (i = 0; i < npoints_shower; i++) {
+ x[i] = xlo + i*(xhi-xlo)/(npoints_shower-1);
+ pdf[i] = gamma_pdf(x[i],pos_a,pos_b);
+ }
- /* Make sure the PDF is normalized. */
- double norm = trapz(pdf,x[1]-x[0],npoints_shower);
- for (i = 0; i < npoints_shower; i++) {
- pdf[i] /= norm;
- }
+ /* Make sure the PDF is normalized. */
+ double norm = trapz(pdf,x[1]-x[0],npoints_shower);
+ for (i = 0; i < npoints_shower; i++) {
+ pdf[i] /= norm;
}
}
@@ -982,27 +1038,16 @@ double nll(event *ev, vertex *v, size_t n, double dx, double dx_shower, int fast
ts[i][j] = v[j].t0 + guess_time(v[j].pos,v[j].dir,pmts[i].pos,smax,n_d2o,n_h2o,cos_theta_cerenkov,sin_theta_cerenkov);
}
- if (v[j].id == IDP_E_MINUS || v[j].id == IDP_E_PLUS) {
- /* If we are fitting for an electron or positron, we need to
- * estimate the expected number of photons produced by the
- * electromagnetic shower.
- *
- * Technically we should also have a term for the electromagnetic
- * shower produced by muons, but for the energy range I'm
- * considering right now, it is very small. */
- integrate_path_shower(p,x,pdf,v[j].T0,v[j].pos,v[j].dir,i,npoints_shower,&mu_shower[i][j],&q_indirect,&result,&ts_sigma[i][j],p->rad);
- mu_indirect[j] += q_indirect;
- ts_shower[i][j] = v[j].t0 + result;
- }
+ integrate_path_shower(p,x,pdf,v[j].T0,v[j].pos,v[j].dir,i,npoints_shower,&mu_shower[i][j],&q_indirect,&result,&ts_sigma[i][j]);
+ mu_indirect[j] += q_indirect;
+ ts_shower[i][j] = v[j].t0 + result;
}
if (!fast) {
path_free(path);
- if (v[j].id == IDP_E_MINUS || v[j].id == IDP_E_PLUS) {
- free(x);
- free(pdf);
- }
+ free(x);
+ free(pdf);
}
particle_free(p);