aboutsummaryrefslogtreecommitdiff
path: root/solid_angle.c
blob: d84ff22316523615ccb9cdecfd962c8939c73eec (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include "solid_angle.h"
#include <gsl/gsl_sf_ellint.h>
#include <math.h>

double get_solid_angle(double *pos, double *pmt, double *n, double r)
{
    /* Returns the solid angle subtended by a circular disk of radius r at a
     * position `pmt` with a normal vector `n` from a position `pos`.
     *
     * See http://www.umich.edu/~ners312/CourseLibrary/SolidAngleOfADiskOffAxis.pdf. */
    double dir[3];
    double L, r0, R, a2, k, Rmax, K, P;

    dir[0] = pos[0] - pmt[0];
    dir[1] = pos[1] - pmt[1];
    dir[2] = pos[2] - pmt[2];

    L = fabs(dir[0]*n[0] + dir[1]*n[1] + dir[2]*n[2]);
    R = sqrt(dir[0]*dir[0] + dir[1]*dir[1] + dir[2]*dir[2]);
    r0 = sqrt(R*R - L*L);

    a2 = 4*r0*r/pow(r0+r,2);
    Rmax = sqrt(L*L + (r0+r)*(r0+r));
    k = sqrt(4*r0*r/(L*L + pow(r0+r,2)));

    if (fabs(r0-r) < 1e-9) {
        /* If r0 is very close to r, the GSL routines below will occasionally
         * throw a domain error. */
        K = gsl_sf_ellint_Kcomp(k, GSL_PREC_DOUBLE);
        return M_PI - 2*L*K/Rmax;
    } else if (r0 <= r) {
        K = gsl_sf_ellint_Kcomp(k, GSL_PREC_DOUBLE);
        P = gsl_sf_ellint_Pcomp(k, -a2, GSL_PREC_DOUBLE);
        return 2*M_PI - 2*L*K/Rmax + (2*L/Rmax)*(r0-r)*P/(r0+r);
    } else {
        K = gsl_sf_ellint_Kcomp(k, GSL_PREC_DOUBLE);
        P = gsl_sf_ellint_Pcomp(k, -a2, GSL_PREC_DOUBLE);
        return -2*L*K/Rmax + (2*L/Rmax)*(r0-r)*P/(r0+r);
    }
}