diff options
Diffstat (limited to 'src/likelihood.c')
-rw-r--r-- | src/likelihood.c | 269 |
1 files changed, 193 insertions, 76 deletions
diff --git a/src/likelihood.c b/src/likelihood.c index 0228bd8..1919a53 100644 --- a/src/likelihood.c +++ b/src/likelihood.c @@ -208,7 +208,7 @@ void particle_free(particle *p) free(p); } -static void get_expected_charge_shower(particle *p, double *pos, double *dir, int pmt, double *q, double *reflected, double *q_delta_ray, double *q_indirect_delta_ray, double *t) +static void get_expected_charge_delta_ray(particle *p, double *pos, double *dir, int pmt, double *q, double *reflected, double *t) { double pmt_dir[3], cos_theta, omega, f, f_reflec, cos_theta_pmt, charge, constant, prob_abs, prob_sct, l_d2o, l_h2o; @@ -221,8 +221,6 @@ static void get_expected_charge_shower(particle *p, double *pos, double *dir, in *t = 0.0; *q = 0.0; *reflected = 0.0; - *q_delta_ray = 0.0; - *q_indirect_delta_ray = 0.0; if (cos_theta_pmt > 0) return; /* Calculate the cosine of the angle between the track direction and the @@ -247,17 +245,55 @@ static void get_expected_charge_shower(particle *p, double *pos, double *dir, in * 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. */ - charge = 0.0; - if (p->shower_photons > 0) - charge = constant*p->shower_photons*electron_get_angular_pdf(cos_theta,p->a,p->b,1/avg_index_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/avg_index_d2o); + charge = constant*p->delta_ray_photons*electron_get_angular_pdf_delta_ray(cos_theta,p->delta_ray_a,p->delta_ray_b,1/avg_index_d2o); *reflected = (1.0-prob_abs)*(1.0-prob_sct)*f_reflec*charge + prob_sct*charge; - *q_indirect_delta_ray = (1.0-prob_abs)*(1.0-prob_sct)*f_reflec*(*q_delta_ray) + prob_sct*(*q_delta_ray); - *q_delta_ray *= (1.0-prob_abs)*(1.0-prob_sct)*f; + *t = (l_d2o*avg_index_d2o + l_h2o*avg_index_h2o)/SPEED_OF_LIGHT; + + *q = (1.0-prob_abs)*(1.0-prob_sct)*f*charge; +} + +static void get_expected_charge_shower(particle *p, double *pos, double *dir, int pmt, double *q, double *reflected, double *t) +{ + double pmt_dir[3], cos_theta, omega, f, f_reflec, cos_theta_pmt, charge, constant, prob_abs, prob_sct, l_d2o, l_h2o; + + SUB(pmt_dir,pmts[pmt].pos,pos); + + normalize(pmt_dir); + + cos_theta_pmt = DOT(pmt_dir,pmts[pmt].normal); + + *t = 0.0; + *q = 0.0; + *reflected = 0.0; + if (cos_theta_pmt > 0) return; + + /* Calculate the cosine of the angle between the track direction and the + * vector to the PMT. */ + cos_theta = DOT(dir,pmt_dir); + + omega = get_solid_angle_fast(pos,pmts[pmt].pos,pmts[pmt].normal,PMT_RADIUS); + + f_reflec = get_weighted_pmt_reflectivity(-cos_theta_pmt); + f = get_weighted_pmt_response(-cos_theta_pmt); + + get_path_length(pos,pmts[pmt].pos,AV_RADIUS,&l_d2o,&l_h2o); + + prob_abs = 1.0 - get_fabs_d2o(l_d2o)*get_fabs_h2o(l_h2o)*get_fabs_acrylic(AV_THICKNESS); + prob_sct = 1.0 - get_fsct_d2o(l_d2o)*get_fsct_h2o(l_h2o); + + constant = 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. */ + charge = constant*p->shower_photons*electron_get_angular_pdf(cos_theta,p->a,p->b,1/avg_index_d2o); + + *reflected = (1.0-prob_abs)*(1.0-prob_sct)*f_reflec*charge + prob_sct*charge; *t = (l_d2o*avg_index_d2o + l_h2o*avg_index_h2o)/SPEED_OF_LIGHT; @@ -327,7 +363,7 @@ static void get_expected_charge(double beta, double theta0, double *pos, double *q = (1.0-prob_abs)*(1.0-prob_sct)*f*charge; } -double time_cdf(double t, double mu_noise, double mu_indirect, double *mu_direct, double *mu_shower, size_t n, double *ts, double *ts_shower, double tmean, double sigma, double *ts_sigma) +double time_cdf(double t, double mu_noise, double mu_indirect, double *mu, size_t n, double *ts, double tmean, double *ts_sigma) { /* Returns the CDF for the time distribution of photons at time `t`. */ size_t i; @@ -344,20 +380,15 @@ double time_cdf(double t, double mu_noise, double mu_indirect, double *mu_direct mu_total = mu_noise + mu_indirect; for (i = 0; i < n; i++) { - if (mu_direct[i] == 0.0) continue; - p += mu_direct[i]*norm_cdf(t,ts[i],sigma); - mu_total += mu_direct[i]; - } - for (i = 0; i < n; i++) { - if (mu_shower[i] == 0.0) continue; - p += mu_shower[i]*norm_cdf(t,ts_shower[i],ts_sigma[i]); - mu_total += mu_shower[i]; + if (mu[i] == 0.0) continue; + p += mu[i]*norm_cdf(t,ts[i],ts_sigma[i]); + mu_total += mu[i]; } return p/mu_total; } -double time_pdf(double t, double mu_noise, double mu_indirect, double *mu_direct, double *mu_shower, size_t n, double *ts, double *ts_shower, double tmean, double sigma, double *ts_sigma) +double time_pdf(double t, double mu_noise, double mu_indirect, double *mu, size_t n, double *ts, double tmean, double *ts_sigma) { /* Returns the probability that a photon is detected at time `t`. * @@ -376,24 +407,19 @@ double time_pdf(double t, double mu_noise, double mu_indirect, double *mu_direct size_t i; double p, mu_total; - p = mu_noise/GTVALID + mu_indirect*(erf((t-tmean)/(sqrt(2)*sigma))-erf((t-tmean-PSUP_REFLECTION_TIME)/(sqrt(2)*sigma)))/(2*PSUP_REFLECTION_TIME); + p = mu_noise/GTVALID + mu_indirect*(erf((t-tmean)/(sqrt(2)*PMT_TTS))-erf((t-tmean-PSUP_REFLECTION_TIME)/(sqrt(2)*PMT_TTS)))/(2*PSUP_REFLECTION_TIME); mu_total = mu_noise + mu_indirect; for (i = 0; i < n; i++) { - if (mu_direct[i] == 0.0) continue; - p += mu_direct[i]*norm(t,ts[i],sigma); - mu_total += mu_direct[i]; - } - for (i = 0; i < n; i++) { - if (mu_shower[i] == 0.0) continue; - p += mu_shower[i]*norm(t,ts_shower[i],ts_sigma[i]); - mu_total += mu_shower[i]; + if (mu[i] == 0.0) continue; + p += mu[i]*norm(t,ts[i],ts_sigma[i]); + mu_total += mu[i]; } return p/mu_total; } -double log_pt(double t, size_t n, double mu_noise, double mu_indirect, double *mu_direct, double *mu_shower, size_t n2, double *ts, double *ts_shower, double tmean, double sigma, double *ts_sigma) +double log_pt(double t, size_t n, double mu_noise, double mu_indirect, double *mu, size_t n2, double *ts, double tmean, double *ts_sigma) { /* Returns the first order statistic for observing a PMT hit at time `t` * given `n` hits. @@ -406,15 +432,71 @@ double log_pt(double t, size_t n, double mu_noise, double mu_indirect, double *m * convolve first which is what we do here. In addition, the problem is not * analytically tractable if you do things the other way around. */ if (n == 1) - return ln(n) + log(time_pdf(t,mu_noise,mu_indirect,mu_direct,mu_shower,n2,ts,ts_shower,tmean,sigma,ts_sigma)); + return ln(n) + log(time_pdf(t,mu_noise,mu_indirect,mu,n2,ts,tmean,ts_sigma)); else - 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)); + return ln(n) + (n-1)*log1p(-time_cdf(t,mu_noise,mu_indirect,mu,n2,ts,tmean,ts_sigma)) + log(time_pdf(t,mu_noise,mu_indirect,mu,n2,ts,tmean,ts_sigma)); +} + +static void integrate_path_delta_ray(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; + double pos[3], t, q, r, dx, tmp, q_sum, r_sum, t_sum, t2_sum; + + q_sum = 0.0; + r_sum = 0.0; + t_sum = 0.0; + t2_sum = 0.0; + for (i = 0; i < n; i++) { + pos[0] = pos0[0] + x[i]*dir0[0]; + pos[1] = pos0[1] + x[i]*dir0[1]; + pos[2] = pos0[2] + x[i]*dir0[2]; + + get_expected_charge_delta_ray(p, pos, dir0, pmt, &q, &r, &t); + + q = q*pdf[i]; + r = r*pdf[i]; + t += x[i]/SPEED_OF_LIGHT; + + if (i == 0 || i == (n - 1)) { + q_sum += q; + r_sum += r; + t_sum += t*q; + t2_sum += t*t*q; + } else { + q_sum += 2*q; + r_sum += 2*r; + t_sum += 2*t*q; + t2_sum += 2*t*t*q; + } + } + + dx = x[1] - x[0]; + *mu_direct = q_sum*dx*0.5; + *mu_indirect = r_sum*dx*0.5; + + if (*mu_direct == 0.0) { + *time = 0.0; + *sigma = PMT_TTS; + } else { + *time = t_sum*dx*0.5/(*mu_direct); + + /* Variance in the time = E(t^2) - E(t)^2. */ + tmp = t2_sum*dx*0.5/(*mu_direct) - (*time)*(*time); + + if (tmp >= 0) { + *sigma = sqrt(tmp + PMT_TTS*PMT_TTS); + } else { + /* This should never happen but does occasionally due to floating + * point rounding error. */ + *sigma = PMT_TTS; + } + } } 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; - double pos[3], t, q, r, qd, rd, dx, tmp, q_sum, r_sum, t_sum, t2_sum; + double pos[3], t, q, r, dx, tmp, q_sum, r_sum, t_sum, t2_sum; q_sum = 0.0; r_sum = 0.0; @@ -425,10 +507,10 @@ static void integrate_path_shower(particle *p, double *x, double *pdf, double T0 pos[1] = pos0[1] + x[i]*dir0[1]; pos[2] = pos0[2] + x[i]*dir0[2]; - get_expected_charge_shower(p, pos, dir0, pmt, &q, &r, &qd, &rd, &t); + get_expected_charge_shower(p, pos, dir0, pmt, &q, &r, &t); - q = q*pdf[i] + qd/p->range; - r = r*pdf[i] + rd/p->range; + q = q*pdf[i]; + r = r*pdf[i]; t += x[i]/SPEED_OF_LIGHT; if (i == 0 || i == (n - 1)) { @@ -803,7 +885,7 @@ double nll_best(event *ev) * hypothesis and this ideal case. See Chapter 9 in Jayne's "Probability * Theory: The Logic of Science" for more details. */ size_t i, j, nhit, maxj; - static double logp[MAX_PE], nll[MAX_PMTS], mu[MAX_PMTS], ts[MAX_PMTS], ts_shower, ts_sigma, mu_shower; + static double logp[MAX_PE], nll[MAX_PMTS], mu[MAX_PMTS], ts[MAX_PMTS], ts_sigma; double log_mu, max_logp, mu_noise, mu_indirect_total, min_ratio; mu_noise = DARK_RATE*GTVALID*1e-9; @@ -840,8 +922,6 @@ double nll_best(event *ev) ts[i] = ev->pmt_hits[i].t; } - mu_shower = 0; - ts_shower = 0; ts_sigma = PMT_TTS; mu_indirect_total = 0.0; @@ -861,7 +941,7 @@ double nll_best(event *ev) if (ev->pmt_hits[i].hit) { for (j = 1; j < MAX_PE; j++) { - logp[j] = get_log_pq(ev->pmt_hits[i].qhs,j) - mu[i] + j*log_mu - lnfact(j) + log_pt(ev->pmt_hits[i].t, j, mu_noise, mu_indirect_total, &mu[i], &mu_shower, 1, &ts[i], &ts_shower, ts[i], PMT_TTS, &ts_sigma); + logp[j] = get_log_pq(ev->pmt_hits[i].qhs,j) - mu[i] + j*log_mu - lnfact(j) + log_pt(ev->pmt_hits[i].t, j, mu_noise, mu_indirect_total, &mu[i], 1, &ts[i], ts[i], &ts_sigma); if (j == 1 || logp[j] > max_logp) max_logp = logp[j]; @@ -897,7 +977,7 @@ double nll(event *ev, vertex *v, size_t n, double dx, double dx_shower, int fast * `z1` and `z2` should be arrays of length `n` and represent coefficients * in the Karhunen Loeve expansion of the track path. These are only used * when `fast` is zero. */ - size_t i, j, nhit; + size_t i, j, k, nhit; static double logp[MAX_PE], nll[MAX_PMTS]; double range, theta0, E0, p0, beta0, smax, log_mu, max_logp; particle *p; @@ -906,15 +986,29 @@ double nll(event *ev, vertex *v, size_t n, double dx, double dx_shower, int fast double mu_reflected; path *path; - static double mu_direct[MAX_PMTS][MAX_VERTICES]; - static double mu_shower[MAX_PMTS][MAX_VERTICES]; - static double ts[MAX_PMTS][MAX_VERTICES]; - static double ts_shower[MAX_PMTS][MAX_VERTICES]; - static double ts_sigma[MAX_PMTS][MAX_VERTICES]; - static double mu[MAX_PMTS]; + /* Array representing the expected number of photons at each PMT for each + * vertex. The last axis represents the contributions from direct, shower, + * and delta ray photons respectively, i.e. + * + * mu[0][2][0] - expected number of direct photons from vertex 2 at PMT 0 + * mu[0][2][1] - expected number of shower photons from vertex 2 at PMT 0 + * mu[0][2][2] - expected number of delta ray photons from vertex 2 at PMT 0 + * ... + * etc. + * + */ + static double mu[MAX_PMTS][MAX_VERTICES][3]; + /* Array representing the average time of arrival at each PMT for each + * vertex. See above for the how the array is indexed. */ + static double ts[MAX_PMTS][MAX_VERTICES][3]; + /* Array representing the standard deviation of the time at each PMT for + * each vertex. See above for the how the array is indexed. */ + static double ts_sigma[MAX_PMTS][MAX_VERTICES][3]; + static double mu_sum[MAX_PMTS]; double mu_noise, mu_indirect[MAX_VERTICES], mu_indirect_total; - double *x, *pdf; + static double x[MAX_NPOINTS], pdf[MAX_NPOINTS]; + static double x_delta[MAX_NPOINTS], pdf_delta[MAX_NPOINTS]; double result; @@ -935,13 +1029,13 @@ double nll(event *ev, vertex *v, size_t n, double dx, double dx_shower, int fast /* Initialize static arrays. */ for (i = 0; i < MAX_PMTS; i++) { - mu[i] = 0.0; + mu_sum[i] = 0.0; for (j = 0; j < n; j++) { - mu_direct[i][j] = 0.0; - mu_shower[i][j] = 0.0; - ts[i][j] = 0.0; - ts_shower[i][j] = 0.0; - ts_sigma[i][j] = PMT_TTS; + for (k = 0; k < 3; k++) { + mu[i][j][k] = 0.0; + ts[i][j][k] = 0.0; + ts_sigma[i][j][k] = PMT_TTS; + } } } @@ -996,8 +1090,6 @@ double nll(event *ev, vertex *v, size_t n, double dx, double dx_shower, int fast 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); @@ -1008,6 +1100,23 @@ double nll(event *ev, vertex *v, size_t n, double dx, double dx_shower, int fast for (i = 0; i < npoints_shower; i++) { pdf[i] /= norm; } + + for (i = 0; i < npoints_shower; i++) { + x_delta[i] = i*p->range/(npoints_shower-1); + /* For now, we assume the delta rays are produced uniformly + * along the path. This isn't quite correct, but the + * distribution is close to flat. + * + * FIXME: Figure out a good parameterization for the + * distribution of delta ray photons along the path. */ + pdf_delta[i] = 1.0; + } + + /* Make sure the PDF is normalized. */ + norm = trapz(pdf_delta,x_delta[1]-x_delta[0],npoints_shower); + for (i = 0; i < npoints_shower; i++) { + pdf_delta[i] /= norm; + } } if (beta0 > BETA_MIN) @@ -1031,18 +1140,18 @@ double nll(event *ev, vertex *v, size_t n, double dx, double dx_shower, int fast if (hit_only && !ev->pmt_hits[i].hit) continue; if (fast) { - mu_direct[i][j] = get_total_charge_approx(beta0, v[j].pos, v[j].dir, p, i, smax, theta0, &ts[i][j], &mu_reflected, cos_theta_cerenkov, sin_theta_cerenkov); - ts[i][j] += v[j].t0; + mu[i][j][0] = get_total_charge_approx(beta0, v[j].pos, v[j].dir, p, i, smax, theta0, &ts[i][j][0], &mu_reflected, cos_theta_cerenkov, sin_theta_cerenkov); + ts[i][j][0] += v[j].t0; mu_indirect[j] += mu_reflected; continue; } double q_indirect; - integrate_path(path, i, &mu_direct[i][j], &q_indirect, &result); + integrate_path(path, i, &mu[i][j][0], &q_indirect, &result); mu_indirect[j] += q_indirect; - if (mu_direct[i][j] > 1e-9) { - ts[i][j] = v[j].t0 + result/mu_direct[i][j]; + if (mu[i][j][0] > 1e-9) { + ts[i][j][0] = v[j].t0 + result/mu[i][j][0]; } else { /* If the expected number of PE is very small, our estimate of * the time can be crazy since the error in the total charge is @@ -1054,19 +1163,24 @@ double nll(event *ev, vertex *v, size_t n, double dx, double dx_shower, int fast * I should test it to see if we can get away with something even * simpler (like just computing the distance from the start of the * track to the PMT). */ - ts[i][j] = v[j].t0 + guess_time(v[j].pos,v[j].dir,pmts[i].pos,smax,cos_theta_cerenkov,sin_theta_cerenkov); + ts[i][j][0] = v[j].t0 + guess_time(v[j].pos,v[j].dir,pmts[i].pos,smax,cos_theta_cerenkov,sin_theta_cerenkov); } - 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 (p->shower_photons > 0) { + integrate_path_shower(p,x,pdf,v[j].T0,v[j].pos,v[j].dir,i,npoints_shower,&mu[i][j][1],&q_indirect,&result,&ts_sigma[i][j][1]); + mu_indirect[j] += q_indirect; + ts[i][j][1] = v[j].t0 + result; + } + + if (p->delta_ray_photons > 0) { + integrate_path_delta_ray(p,x_delta,pdf_delta,v[j].T0,v[j].pos,v[j].dir,i,npoints_shower,&mu[i][j][2],&q_indirect,&result,&ts_sigma[i][j][2]); + mu_indirect[j] += q_indirect; + ts[i][j][2] = v[j].t0 + result; + } } if (!fast) { path_free(path); - - free(x); - free(pdf); } particle_free(p); @@ -1089,8 +1203,11 @@ double nll(event *ev, vertex *v, size_t n, double dx, double dx_shower, int fast * calculation. */ if (hit_only && !ev->pmt_hits[i].hit) continue; + mu_sum[i] = mu_indirect_total + mu_noise; for (j = 0; j < n; j++) { - mu[i] += mu_direct[i][j] + mu_shower[i][j] + mu_indirect_total + mu_noise; + for (k = 0; k < 3; k++) { + mu_sum[i] += mu[i][j][k]; + } } } @@ -1108,20 +1225,20 @@ double nll(event *ev, vertex *v, size_t n, double dx, double dx_shower, int fast * calculation. */ if (hit_only && !ev->pmt_hits[i].hit) continue; - log_mu = log(mu[i]); + log_mu = log(mu_sum[i]); if (fast) - log_pt_fast = log_pt(ev->pmt_hits[i].t, 1, mu_noise, mu_indirect_total, &mu_direct[i][0], &mu_shower[i][0], n, &ts[i][0], &ts_shower[i][0], ts[i][0], PMT_TTS, &ts_sigma[i][0]); + log_pt_fast = log_pt(ev->pmt_hits[i].t, 1, mu_noise, mu_indirect_total, &mu[i][0][0], n*3, &ts[i][0][0], ts[i][0][0], &ts_sigma[i][0][0]); if (ev->pmt_hits[i].hit) { for (j = 1; j < MAX_PE; j++) { - logp[j] = get_log_pq(ev->pmt_hits[i].qhs,j) - mu[i] + j*log_mu - lnfact(j); + logp[j] = get_log_pq(ev->pmt_hits[i].qhs,j) - mu_sum[i] + j*log_mu - lnfact(j); if (!charge_only) { if (fast) logp[j] += log_pt_fast; else - logp[j] += log_pt(ev->pmt_hits[i].t, j, mu_noise, mu_indirect_total, &mu_direct[i][0], &mu_shower[i][0], n, &ts[i][0], &ts_shower[i][0], ts[i][0], PMT_TTS, &ts_sigma[i][0]); + logp[j] += log_pt(ev->pmt_hits[i].t, j, mu_noise, mu_indirect_total, &mu[i][0][0], n*3, &ts[i][0][0], ts[i][0][0], &ts_sigma[i][0][0]); } if (j == 1 || logp[j] > max_logp) max_logp = logp[j]; @@ -1134,13 +1251,13 @@ double nll(event *ev, vertex *v, size_t n, double dx, double dx_shower, int fast nll[nhit++] = -logsumexp(logp+1, j-1); } else { - logp[0] = -mu[i]; + logp[0] = -mu_sum[i]; if (fast) { nll[nhit++] = -logp[0]; continue; } for (j = 1; j < MAX_PE_NO_HIT; j++) { - logp[j] = get_log_pmiss(j) - mu[i] + j*log_mu - lnfact(j); + logp[j] = get_log_pmiss(j) - mu_sum[i] + j*log_mu - lnfact(j); } nll[nhit++] = -logsumexp(logp, MAX_PE_NO_HIT); } |