diff options
| author | tlatorre <tlatorre@uchicago.edu> | 2018-08-27 10:12:17 -0500 | 
|---|---|---|
| committer | tlatorre <tlatorre@uchicago.edu> | 2018-08-27 10:12:17 -0500 | 
| commit | 779266ec72a5c76ee52043ab3ae17479ba6a9788 (patch) | |
| tree | 9a03c335731aeff2cadb728bc3fe4309aafef874 /src | |
| parent | 4e3eb54e364f578b3ea6608b0b3fc34125985ab8 (diff) | |
| download | sddm-779266ec72a5c76ee52043ab3ae17479ba6a9788.tar.gz sddm-779266ec72a5c76ee52043ab3ae17479ba6a9788.tar.bz2 sddm-779266ec72a5c76ee52043ab3ae17479ba6a9788.zip | |
add code to expand the track of a particle using a KL expansion
To fit the path of muons and electrons I use the Karhunen-Loeve expansion of a
random 2D walk in the polar angle in x and y. This allows you to decompose the
path into a sum over sine functions whose coefficients become random variables.
The nice thing about fitting the path in this way is that you can capture
*most* of the variation in the path using a small number of variables by only
summing over the first N terms in the expansion and it is easy to calculate the
probability of the coefficients since they are all uncorrelated.
Diffstat (limited to 'src')
| -rw-r--r-- | src/Makefile | 6 | ||||
| -rw-r--r-- | src/path.c | 161 | ||||
| -rw-r--r-- | src/path.h | 21 | ||||
| -rw-r--r-- | src/random.c | 16 | ||||
| -rw-r--r-- | src/random.h | 1 | ||||
| -rw-r--r-- | src/test-path.c | 185 | ||||
| -rw-r--r-- | src/test.c | 83 | 
7 files changed, 470 insertions, 3 deletions
| diff --git a/src/Makefile b/src/Makefile index ae8807b..1ea1685 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,7 +1,7 @@  CFLAGS=-O0 -Wall -g -DSWAP_BYTES  LDLIBS=-lm -lgsl -lgslcblas -lnlopt_cxx -lstdc++ -all: test test-vector test-likelihood fit test-charge +all: test test-vector test-likelihood fit test-charge test-path  Makefile.dep:  	-$(CC) -MM *.c > Makefile.dep @@ -12,12 +12,14 @@ calculate_limits: calculate_limits.c  solid_angle.o: solid_angle.c -test: test.o solid_angle.o optics.o muon.o vector.o quantum_efficiency.o pdg.o scattering.o misc.o mt19937ar.o sno_charge.o +test: test.o solid_angle.o optics.o muon.o vector.o quantum_efficiency.o pdg.o scattering.o misc.o mt19937ar.o sno_charge.o path.o random.o  test-vector: test-vector.o vector.o mt19937ar.o  test-likelihood: test-likelihood.o muon.o random.o optics.o quantum_efficiency.o mt19937ar.o pdg.o vector.o solid_angle.o scattering.o +test-path: test-path.o mt19937ar.o vector.o path.o random.o +  test-charge: test-charge.o sno_charge.o misc.o  test-zebra: test-zebra.o zebra.o pack2b.o diff --git a/src/path.c b/src/path.c new file mode 100644 index 0000000..f09ec45 --- /dev/null +++ b/src/path.c @@ -0,0 +1,161 @@ +#include "path.h" +#include <math.h> +#include <gsl/gsl_spline.h> +#include <stddef.h> /* for size_t */ +#include <gsl/gsl_integration.h> +#include "pdg.h" +#include "vector.h" +#include <stdlib.h> /* for malloc(), calloc(), etc. */ + +#define N 100 + +static double foo(double s, double range, double theta0, int k) +{ +    return sqrt(2*pow(theta0,2)*range)*sin(M_PI*s*(k-0.5)/range)/(M_PI*(k-0.5)); +} + +double path_get_coefficient(unsigned int k, double *s, double *x, double theta0, size_t n) +{ +    size_t i; +    double sum, range; + +    range = s[n-1]; + +    sum = 0.0; +    for (i = 1; i < n; i++) { +        sum += (foo(s[i],range,theta0,k)*x[i] + foo(s[i-1],range,theta0,k)*x[i-1])*(s[i]-s[i-1])/2.0; +    } + +    return sum*pow(k-0.5,2)*pow(M_PI,2)/pow(theta0*range,2); +} + +path *path_init(double *pos, double *dir, double T0, double range, double theta0, getKineticEnergyFunc *fun, double *z1, double *z2, size_t n, double m) +{ +    size_t i, j; +    double E, mom, beta, theta, phi; + +    path *p = malloc(sizeof(path)); + +    p->pos[0] = pos[0]; +    p->pos[1] = pos[1]; +    p->pos[2] = pos[2]; + +    p->dir[0] = dir[0]; +    p->dir[1] = dir[1]; +    p->dir[2] = dir[2]; + +    double *s = malloc(sizeof(double)*N); +    double *theta1 = calloc(N,sizeof(double)); +    double *theta2 = calloc(N,sizeof(double)); +    double *x = calloc(N,sizeof(double)); +    double *y = calloc(N,sizeof(double)); +    double *z = calloc(N,sizeof(double)); +    double *T = calloc(N,sizeof(double)); +    double *t = calloc(N,sizeof(double)); +    double *dx = calloc(N,sizeof(double)); +    double *dy = calloc(N,sizeof(double)); +    double *dz = calloc(N,sizeof(double)); + +    dz[0] = 1.0; +    for (i = 0; i < N; i++) { +        s[i] = range*i/(N-1); +        for (j = 0; j < n; j++) { +            theta1[i] += z1[j]*foo(s[i],range,theta0,j+1); +            theta2[i] += z2[j]*foo(s[i],range,theta0,j+1); +        } +        T[i] = fun(s[i],T0); +        if (i > 0) { +            theta = sqrt(theta1[i]*theta1[i] + theta2[i]*theta2[i]); +            phi = atan2(theta2[i],theta1[i]); + +            dx[i] = (s[i]-s[i-1])*sin(theta)*cos(phi); +            dy[i] = (s[i]-s[i-1])*sin(theta)*sin(phi); +            dz[i] = (s[i]-s[i-1])*cos(theta); + +            /* Calculate total energy */ +            E = T[i] + m; +            mom = sqrt(E*E - m*m); +            beta = mom/E; + +            t[i] = t[i-1] + (s[i]-s[i-1])/(beta*SPEED_OF_LIGHT); + +            x[i] = x[i-1] + dx[i]; +            y[i] = y[i-1] + dy[i]; +            z[i] = z[i-1] + dz[i]; +        } +    } + +    for (i = 0; i < 8; i++) { +        p->acc[i] = gsl_interp_accel_alloc(); +        p->spline[i] = gsl_spline_alloc(gsl_interp_linear,N); +    } + +    gsl_spline_init(p->spline[0],s,x,N); +    gsl_spline_init(p->spline[1],s,y,N); +    gsl_spline_init(p->spline[2],s,z,N); +    gsl_spline_init(p->spline[3],s,T,N); +    gsl_spline_init(p->spline[4],s,t,N); +    gsl_spline_init(p->spline[5],s,dx,N); +    gsl_spline_init(p->spline[6],s,dy,N); +    gsl_spline_init(p->spline[7],s,dz,N); + +    free(s); +    free(theta1); +    free(theta2); +    free(x); +    free(y); +    free(z); +    free(T); +    free(t); +    free(dx); +    free(dy); +    free(dz); + +    return p; +} + +void path_eval(path *p, double s, double *pos, double *dir, double *T, double *t) +{ +    double normal[3], k[3], tmp[3], phi; + +    tmp[0] = gsl_spline_eval(p->spline[0],s,p->acc[0]); +    tmp[1] = gsl_spline_eval(p->spline[1],s,p->acc[1]); +    tmp[2] = gsl_spline_eval(p->spline[2],s,p->acc[2]); + +    *T = gsl_spline_eval(p->spline[3],s,p->acc[3]); +    *t = gsl_spline_eval(p->spline[4],s,p->acc[4]); + +    k[0] = 0.0; +    k[1] = 0.0; +    k[2] = 1.0; + +    CROSS(normal,k,p->dir); + +    normalize(normal); + +    phi = acos(DOT(k,p->dir)); + +    rotate(pos,tmp,normal,phi); + +    ADD(pos,pos,p->pos); + +    tmp[0] = gsl_spline_eval(p->spline[5],s,p->acc[5]); +    tmp[1] = gsl_spline_eval(p->spline[6],s,p->acc[6]); +    tmp[2] = gsl_spline_eval(p->spline[7],s,p->acc[7]); + +    normalize(tmp); + +    rotate(dir,tmp,normal,phi); +} + +void path_free(path *p) +{ +    size_t i; + +    for (i = 0; i < 5; i++) { +        gsl_interp_accel_free(p->acc[i]); +        gsl_spline_free(p->spline[i]); +    } + +    free(p); +} diff --git a/src/path.h b/src/path.h new file mode 100644 index 0000000..8aaa024 --- /dev/null +++ b/src/path.h @@ -0,0 +1,21 @@ +#ifndef PATH_H +#define PATH_H + +#include <gsl/gsl_spline.h> + +typedef double getKineticEnergyFunc(double x, double T0); + +typedef struct path { +    double pos[3]; +    double dir[3]; +    double theta0; +    gsl_interp_accel *acc[8]; +    gsl_spline *spline[8]; +} path; + +double path_get_coefficient(unsigned int k, double *s, double *x, double theta0, size_t n); +path *path_init(double *pos, double *dir, double T0, double range, double theta0, getKineticEnergyFunc *fun, double *z1, double *z2, size_t n, double m); +void path_eval(path *p, double s, double *pos, double *dir, double *T, double *t); +void path_free(path *p); + +#endif diff --git a/src/random.c b/src/random.c index f6cf85e..e2e5641 100644 --- a/src/random.c +++ b/src/random.c @@ -12,3 +12,19 @@ double randn(void)      return sqrt(-2*log(u1))*cos(2*M_PI*u2);  } + +void rand_sphere(double *dir) +{ +    /* Generates a random point on the unit sphere. */ +    double u, v, theta, phi; + +    u = genrand_real1(); +    v = genrand_real1(); + +    phi = 2*M_PI*u; +    theta = acos(2*v-1); + +    dir[0] = sin(theta)*cos(phi); +    dir[1] = sin(theta)*sin(phi); +    dir[2] = cos(theta); +} diff --git a/src/random.h b/src/random.h index 309cd71..733279c 100644 --- a/src/random.h +++ b/src/random.h @@ -4,5 +4,6 @@  #include "mt19937ar.h"  double randn(void); +void rand_sphere(double *dir);  #endif diff --git a/src/test-path.c b/src/test-path.c new file mode 100644 index 0000000..fc46908 --- /dev/null +++ b/src/test-path.c @@ -0,0 +1,185 @@ +#include <stdio.h> +#include "path.h" +#include <stddef.h> /* for size_t */ +#include "mt19937ar.h" +#include <math.h> /* for sin(), cos(), etc. */ +#include "vector.h" +#include <errno.h> /* for errno */ +#include <string.h> /* for strerror() */ +#include "random.h" + +typedef struct sim { +    double *pos; +    path *p; +} sim; + +static double getKineticEnergy(double x, double T0) +{ +    return 1.0; +} + +sim *simulate_path(double *pos0, double *dir0, double range, double theta0, size_t N, size_t n) +{ +    size_t i; +    double *s, *theta1, *theta2, *x, *y, *z, *z1, *z2, tmp1[3], tmp2[3], k[3], normal[3]; +    double dx, dy, dz, theta, phi; + +    sim *simvalue = malloc(sizeof(sim)); + +    s = calloc(N,sizeof(double)); +    theta1 = calloc(N,sizeof(double)); +    theta2 = calloc(N,sizeof(double)); +    x = calloc(N,sizeof(double)); +    y = calloc(N,sizeof(double)); +    z = calloc(N,sizeof(double)); +    simvalue->pos = calloc(N*3,sizeof(double)); +    z1 = calloc(n,sizeof(double)); +    z2 = calloc(n,sizeof(double)); + +    init_genrand(0); + +    simvalue->pos[0] = pos0[0]; +    simvalue->pos[1] = pos0[1]; +    simvalue->pos[2] = pos0[2]; + +    for (i = 1; i < N; i++) { +        s[i] = range*i/(N-1); + +        theta1[i] = theta1[i-1] + randn()*theta0*sqrt(s[i]-s[i-1]); +        theta2[i] = theta2[i-1] + randn()*theta0*sqrt(s[i]-s[i-1]); + +        theta = sqrt(theta1[i]*theta1[i] + theta2[i]*theta2[i]); +        phi = atan2(theta2[i],theta1[i]); + +        dx = (s[i]-s[i-1])*sin(theta)*cos(phi); +        dy = (s[i]-s[i-1])*sin(theta)*sin(phi); +        dz = (s[i]-s[i-1])*cos(theta); + +        x[i] = x[i-1] + dx; +        y[i] = y[i-1] + dy; +        z[i] = z[i-1] + dz; + +        tmp1[0] = x[i]; +        tmp1[1] = y[i]; +        tmp1[2] = z[i]; + +        k[0] = 0.0; +        k[1] = 0.0; +        k[2] = 1.0; + +        CROSS(normal,k,dir0); + +        normalize(normal); + +        phi = acos(DOT(k,dir0)); + +        rotate(tmp2,tmp1,normal,phi); + +        simvalue->pos[i*3] = pos0[0] + tmp2[0]; +        simvalue->pos[i*3+1] = pos0[1] + tmp2[1]; +        simvalue->pos[i*3+2] = pos0[2] + tmp2[2]; +    } + +    for (i = 0; i < n; i++) { +        z1[i] = path_get_coefficient(i+1,s,theta1,theta0,N); +        z2[i] = path_get_coefficient(i+1,s,theta2,theta0,N); +    } + +    simvalue->p = path_init(pos0,dir0,1.0,range,theta0,getKineticEnergy,z1,z2,n,1.0); + +    free(s); +    free(theta1); +    free(theta2); +    free(x); +    free(y); +    free(z); +    free(z1); +    free(z2); + +    return simvalue; +} + +void usage(void) +{ +    fprintf(stderr,"Usage: ./test-path [options]\n"); +    fprintf(stderr,"  -N      number of points along track\n"); +    fprintf(stderr,"  -n      number of terms in KL expansion\n"); +    fprintf(stderr,"  -t      standard deviation of angular distribution\n"); +    fprintf(stderr,"  --range range of track\n"); +    fprintf(stderr,"  -h      display this help message\n"); +    exit(1); +} + +int main(int argc, char **argv) +{ +    size_t i, n, N; +    double pos0[3], dir0[3], theta0, range, pos2[3], dir[3], T, t; + +    n = 2; +    N = 1000; +    theta0 = 0.1; +    range = 100.0; + +    for (i = 1; i < argc; i++) { +        if (!strncmp(argv[i], "--", 2)) { +            if (!strcmp(argv[i]+2,"range")) { +                range = strtod(argv[++i],NULL); +                continue; +            } +        } else if (argv[i][0] == '-') { +            switch (argv[i][1]) { +            case 'N': +                N = atoi(argv[++i]); +                break; +            case 'n': +                n = atoi(argv[++i]); +                break; +            case 't': +                theta0 = strtod(argv[++i],NULL); +                break; +            case 'h': +                usage(); +            default: +                fprintf(stderr, "unrecognized option '%s'\n", argv[i]); +                exit(1); +            } +        } +    } + +    pos0[0] = 0.0; +    pos0[1] = 0.0; +    pos0[2] = 0.0; + +    dir0[0] = 1.0; +    dir0[1] = 0.0; +    dir0[2] = 0.0; + +    sim *simvalue = simulate_path(pos0,dir0,range,theta0,N,n); + +    FILE *pipe = popen("graph -T X --bitmap-size 2000x2000 -X X -Y Y", "w"); + +    if (!pipe) { +        fprintf(stderr, "error running graph command: %s\n", strerror(errno)); +        exit(1); +    } + +    for (i = 0; i < N; i++) { +        fprintf(pipe,"%.10g %.10g\n", simvalue->pos[i*3], simvalue->pos[i*3+1]); +        path_eval(simvalue->p,i*range/9999.0,pos2,dir,&T,&t); +    } +    fprintf(pipe,"\n\n"); + +    for (i = 0; i < N; i++) { +        path_eval(simvalue->p,i*range/(N-1),pos2,dir,&T,&t); +        fprintf(pipe,"%.10g %.10g\n", pos2[0], pos2[1]); +    } +    fprintf(pipe,"\n\n"); + +    if (pclose(pipe)) { +        fprintf(stderr, "error closing graph command: %s\n", strerror(errno)); +        exit(1); +    } + +    return 0; +} + @@ -8,6 +8,9 @@  #include "misc.h"  #include "sno_charge.h"  #include <gsl/gsl_integration.h> +#include "path.h" +#include "random.h" +#include "pdg.h"  typedef int testFunction(char *err); @@ -324,6 +327,83 @@ int test_logsumexp(char *err)      return 0;  } +double getKineticEnergy(double x, double T0) +{ +    return 1.0; +} + +int test_path(char *err) +{ +    /* Tests the logsumexp() function. */ +    int i, j, k; +    double pos0[3], dir0[3], T0, range, m; +    double T, t; +    double pos_expected[3], t_expected; +    double pos[3], dir[3]; +    double E, mom, beta; +    double s; + +    T0 = 1.0; +    range = 1.0; +    m = 1.0; + +    init_genrand(0); + +    for (i = 0; i < 100; i++) { +        pos0[0] = genrand_real2(); +        pos0[1] = genrand_real2(); +        pos0[2] = genrand_real2(); + +        rand_sphere(dir0); + +        path *p = path_init(pos0,dir0,T0,range,0.1,getKineticEnergy,NULL,NULL,0,m); + +        for (j = 0; j < 100; j++) { +            s = range*j/99; +            pos_expected[0] = pos0[0] + dir0[0]*s; +            pos_expected[1] = pos0[1] + dir0[1]*s; +            pos_expected[2] = pos0[2] + dir0[2]*s; + +            /* Calculate total energy */ +            E = T0 + m; +            mom = sqrt(E*E - m*m); +            beta = mom/E; + +            t_expected = s/(beta*SPEED_OF_LIGHT); + +            path_eval(p,s,pos,dir,&T,&t); + +            for (k = 0; k < 3; k++) { +                if (!isclose(pos[k], pos_expected[k], 1e-9, 1e-9)) { +                    sprintf(err, "path_eval(%.2g) returned pos[%i] = %.5g, but expected %.5g", s, k, pos[k], pos_expected[k]); +                    return 1; +                } +            } + +            for (k = 0; k < 3; k++) { +                if (!isclose(dir[k], dir0[k], 1e-9, 1e-9)) { +                    sprintf(err, "path_eval(%.2g) returned dir[%i] = %.5g, but expected %.5g", s, k, dir[k], dir0[k]); +                    return 1; +                } +            } + +            if (!isclose(T, 1.0, 1e-9, 1e-9)) { +                sprintf(err, "path_eval(%.2g) returned T = %.5g, but expected %.5g", s, T, 1.0); +                return 1; +            } + +            if (!isclose(t, t_expected, 1e-9, 1e-9)) { +                sprintf(err, "path_eval(%.2g) returned t = %.5g, but expected %.5g", s, t, t_expected); +                return 1; +            } +        } + +        path_free(p); +    } + +    return 0; +} +  struct tests {      testFunction *test;      char *name; @@ -335,7 +415,8 @@ struct tests {      {test_muon_get_range, "test_muon_get_range"},      {test_muon_get_T, "test_muon_get_T"},      {test_logsumexp, "test_logsumexp"}, -    {test_sno_charge_integral, "test_sno_charge_integral"} +    {test_sno_charge_integral, "test_sno_charge_integral"}, +    {test_path, "test_path"}  };  int main(int argc, char **argv) | 
