diff options
author | tlatorre <tlatorre@uchicago.edu> | 2018-11-30 16:02:14 -0600 |
---|---|---|
committer | tlatorre <tlatorre@uchicago.edu> | 2018-11-30 16:02:14 -0600 |
commit | 7892fb4b0814308c6eb612b6d8434bd974af5908 (patch) | |
tree | 6fb15babc41f02df6fca30a404120de1831b4297 /src/likelihood.c | |
parent | 2207f442dd78a69ed0fc45321fcb4ea821d5d409 (diff) | |
download | sddm-7892fb4b0814308c6eb612b6d8434bd974af5908.tar.gz sddm-7892fb4b0814308c6eb612b6d8434bd974af5908.tar.bz2 sddm-7892fb4b0814308c6eb612b6d8434bd974af5908.zip |
add ability to fit for multiple vertices
Diffstat (limited to 'src/likelihood.c')
-rw-r--r-- | src/likelihood.c | 269 |
1 files changed, 143 insertions, 126 deletions
diff --git a/src/likelihood.c b/src/likelihood.c index 93740a0..4e766b6 100644 --- a/src/likelihood.c +++ b/src/likelihood.c @@ -720,7 +720,7 @@ static double getKineticEnergy(double x, void *p) return particle_get_energy(x, (particle *) p); } -double nll_muon(event *ev, int id, double T0, double *pos, double *dir, double t0, double *z1, double *z2, size_t n, double dx, double dx_shower, int fast) +double nll_muon(event *ev, vertex *v, size_t n, double dx, double dx_shower, int fast) { /* Returns the negative log likelihood for event `ev` given a particle with * id `id`, initial kinetic energy `T0`, position `pos`, direction `dir` and @@ -742,13 +742,13 @@ double nll_muon(event *ev, int id, double T0, double *pos, double *dir, double t double mu_reflected; path *path; - static double mu_direct[MAX_PMTS]; - static double mu_shower[MAX_PMTS]; - static double ts[MAX_PMTS]; - static double ts_shower[MAX_PMTS]; - static double ts_sigma[MAX_PMTS]; + 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]; - double mu_noise, mu_indirect; + double mu_noise, mu_indirect[MAX_VERTICES], mu_indirect_total; double *x, *pdf; @@ -764,148 +764,162 @@ double nll_muon(event *ev, int id, double T0, double *pos, double *dir, double t double log_pt_fast; + if (n > MAX_VERTICES) { + fprintf(stderr, "maximum number of vertices is %i\n", MAX_VERTICES); + exit(1); + } + /* Initialize static arrays. */ - for (i = 0; i < MAX_PMTS; i++) mu_direct[i] = 0.0; - for (i = 0; i < MAX_PMTS; i++) mu_shower[i] = 0.0; - for (i = 0; i < MAX_PMTS; i++) ts[i] = 0.0; - for (i = 0; i < MAX_PMTS; i++) ts_shower[i] = 0.0; - for (i = 0; i < MAX_PMTS; i++) ts_sigma[i] = PMT_TTS; - for (i = 0; i < MAX_PMTS; i++) mu[i] = 0.0; + for (i = 0; i < MAX_PMTS; i++) { + mu[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; + } + } - if (fast) - p = particle_init(id, T0, 1000); - else - p = particle_init(id, T0, 10000); + for (j = 0; j < n; j++) { + if (fast) + p = particle_init(v[j].id, v[j].T0, 1000); + else + p = particle_init(v[j].id, v[j].T0, 10000); - range = p->range; + range = p->range; - /* Number of points to sample along the particle track. */ - npoints = (size_t) (range/dx + 0.5); + /* Number of points to sample along the particle track. */ + npoints = (size_t) (range/dx + 0.5); - if (npoints < MIN_NPOINTS) npoints = MIN_NPOINTS; + if (npoints < MIN_NPOINTS) npoints = MIN_NPOINTS; - /* Calculate total energy */ - E0 = T0 + p->mass; - p0 = sqrt(E0*E0 - p->mass*p->mass); - beta0 = p0/E0; + /* Calculate total energy */ + E0 = v[j].T0 + p->mass; + p0 = sqrt(E0*E0 - p->mass*p->mass); + beta0 = p0/E0; - /* FIXME: is this formula valid for muons? */ - theta0 = get_scattering_rms(range/2,p0,beta0,1.0)/sqrt(range/2); + /* FIXME: is this formula valid for muons? */ + theta0 = get_scattering_rms(range/2,p0,beta0,1.0)/sqrt(range/2); - if (!fast) { - path = path_init(pos, dir, T0, range, npoints, theta0, getKineticEnergy, p, z1, z2, n, p->mass); + 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 (id == IDP_E_MINUS || id == IDP_E_PLUS) { - electron_get_position_distribution_parameters(T0, &pos_a, &pos_b); + if (v[j].id == IDP_E_MINUS || v[j].id == IDP_E_PLUS) { + electron_get_position_distribution_parameters(v[j].T0, &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); + /* 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; - 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; + } } - } - } - if (beta0 > BETA_MIN) - get_smax(p, BETA_MIN, range, &smax); - else - smax = 0.0; + if (beta0 > BETA_MIN) + get_smax(p, BETA_MIN, range, &smax); + else + smax = 0.0; - /* Compute the Cerenkov angle at the start of the track. - * - * These are computed at the beginning of this function and then passed to - * the different functions to avoid recomputing them on the fly. */ - wavelength0 = 400.0; - n_d2o = get_index_snoman_d2o(wavelength0); - n_h2o = get_index_snoman_h2o(wavelength0); - cos_theta_cerenkov = 1/(n_d2o*beta0); - sin_theta_cerenkov = sqrt(1-pow(cos_theta_cerenkov,2)); + /* Compute the Cerenkov angle at the start of the track. + * + * These are computed at the beginning of this function and then passed to + * the different functions to avoid recomputing them on the fly. */ + wavelength0 = 400.0; + n_d2o = get_index_snoman_d2o(wavelength0); + n_h2o = get_index_snoman_h2o(wavelength0); + cos_theta_cerenkov = 1/(n_d2o*beta0); + sin_theta_cerenkov = sqrt(1-pow(cos_theta_cerenkov,2)); + + mu_indirect[j] = 0.0; + for (i = 0; i < MAX_PMTS; i++) { + if (pmts[i].pmt_type != PMT_NORMAL) continue; + + /* Skip PMTs which weren't hit when doing the "fast" likelihood + * calculation. */ + if (fast && !ev->pmt_hits[i].hit) continue; - mu_indirect = 0.0; - for (i = 0; i < MAX_PMTS; i++) { - if (pmts[i].pmt_type != PMT_NORMAL) 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, n_d2o, n_h2o, cos_theta_cerenkov, sin_theta_cerenkov); + ts[i][j] += v[j].t0; + mu_indirect[j] += mu_reflected; + continue; + } - /* Skip PMTs which weren't hit when doing the "fast" likelihood - * calculation. */ - if (fast && !ev->pmt_hits[i].hit) continue; + double q_indirect; + integrate_path(path, i, &mu_direct[i][j], &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]; + } 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 + * in the denominator. Therefore, we estimate the time using + * the same technique as in get_total_charge_approx(). See that + * function for more details. + * + * Note: I don't really know how much this affects the likelihood. + * 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,n_d2o,n_h2o,cos_theta_cerenkov,sin_theta_cerenkov); + } - if (fast) { - mu_direct[i] = get_total_charge_approx(beta0, pos, dir, p, i, smax, theta0, &ts[i], &mu_reflected, n_d2o, n_h2o, cos_theta_cerenkov, sin_theta_cerenkov); - ts[i] += t0; - mu_indirect += mu_reflected; - continue; + 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; + } } - double q_indirect; - integrate_path(path, i, mu_direct+i, &q_indirect, &result); - mu_indirect += q_indirect; + if (!fast) { + path_free(path); - if (mu_direct[i] > 1e-9) { - ts[i] = t0 + result/mu_direct[i]; - } 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 - * in the denominator. Therefore, we estimate the time using - * the same technique as in get_total_charge_approx(). See that - * function for more details. - * - * Note: I don't really know how much this affects the likelihood. - * 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] = t0 + guess_time(pos,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) { + free(x); + free(pdf); + } } - if (id == IDP_E_MINUS || 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,T0,pos,dir,i,npoints_shower,mu_shower+i,&q_indirect,&result,&ts_sigma[i],p->rad); - mu_indirect += q_indirect; - ts_shower[i] = t0 + result; - } + particle_free(p); } - if (!fast) { - path_free(path); + mu_noise = DARK_RATE*GTVALID*1e-9; - if (id == IDP_E_MINUS || id == IDP_E_PLUS) { - free(x); - free(pdf); - } + mu_indirect_total = 0.0; + for (j = 0; j < n; j++) { + if (v[j].id == IDP_E_MINUS || v[j].id == IDP_E_PLUS) + mu_indirect_total += mu_indirect[j]*CHARGE_FRACTION_ELECTRON/10000.0; + else + mu_indirect_total += mu_indirect[j]*CHARGE_FRACTION_MUON/10000.0; } - particle_free(p); - - mu_noise = DARK_RATE*GTVALID*1e-9; - if (id == IDP_E_MINUS || id == IDP_E_PLUS) - mu_indirect *= CHARGE_FRACTION_ELECTRON/10000.0; - else - mu_indirect *= CHARGE_FRACTION_MUON/10000.0; - /* Compute the expected number of photons reaching each PMT by adding up * the contributions from the noise hits and the direct, indirect, and * shower light. */ @@ -916,10 +930,12 @@ double nll_muon(event *ev, int id, double T0, double *pos, double *dir, double t * calculation. */ if (fast && !ev->pmt_hits[i].hit) continue; - if (fast) - mu[i] = mu_direct[i] + mu_noise; - else - mu[i] = mu_direct[i] + mu_shower[i] + mu_indirect + mu_noise; + for (j = 0; j < n; j++) { + if (fast) + mu[i] += mu_direct[i][j] + mu_noise; + else + mu[i] += mu_direct[i][j] + mu_shower[i][j] + mu_indirect_total + mu_noise; + } } if (fast) @@ -939,14 +955,14 @@ double nll_muon(event *ev, int id, double T0, double *pos, double *dir, double t log_mu = log(mu[i]); if (fast) - log_pt_fast = log_pt(ev->pmt_hits[i].t, 1, mu_noise, mu_indirect, &mu_direct[i], &mu_shower[i], 1, &ts[i], &ts_shower[i], ts[i], PMT_TTS, &ts_sigma[i]); + 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]); if (ev->pmt_hits[i].hit) { for (j = 1; j < MAX_PE; j++) { if (fast) logp[j] = get_log_pq(ev->pmt_hits[i].qhs,j) - mu[i] + j*log_mu - lnfact(j) + log_pt_fast; else - 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, &mu_direct[i], &mu_shower[i], 1, &ts[i], &ts_shower[i], ts[i], PMT_TTS, &ts_sigma[i]); + 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_direct[i][0], &mu_shower[i][0], n, &ts[i][0], &ts_shower[i][0], ts[i][0], PMT_TTS, &ts_sigma[i][0]); if (j == 1 || logp[j] > max_logp) max_logp = logp[j]; @@ -971,8 +987,9 @@ double nll_muon(event *ev, int id, double T0, double *pos, double *dir, double t } logp_path = 0.0; - for (i = 0; i < n; i++) - logp_path += log_norm(z1[i],0,1) + log_norm(z2[i],0,1); + for (j = 0; j < n; j++) + for (i = 0; i < v[j].n; i++) + logp_path += log_norm(v[j].z1[i],0,1) + log_norm(v[j].z2[i],0,1); /* Add up the negative log likelihood terms from each PMT. I use a kahan * sum here instead of just summing all the values to help prevent round |