aboutsummaryrefslogtreecommitdiff
path: root/src/solid_angle.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/solid_angle.c')
-rw-r--r--src/solid_angle.c80
1 files changed, 79 insertions, 1 deletions
diff --git a/src/solid_angle.c b/src/solid_angle.c
index 7201533..354547e 100644
--- a/src/solid_angle.c
+++ b/src/solid_angle.c
@@ -4,6 +4,7 @@
#include <gsl/gsl_interp2d.h>
#include <gsl/gsl_spline2d.h>
#include "vector.h"
+#include "misc.h"
static double hd[11] = {0.1,0.125,0.150,0.175,0.2,0.3,0.4,0.5,0.6,0.8,1.0};
static double Rd[13] = {0.1,0.5,0.6,0.7,0.8,0.9,1.0,1.1,1.2,1.3,1.4,1.5,2.0};
@@ -40,7 +41,8 @@ double get_solid_angle_approx(double *pos, double *pmt, double *n, double r)
* regimes, the approximation is not very good and so it is modified by a
* correction factor which comes from a lookup table.
*
- * This formula comes from "The Solid Angle Subtended at a Point by a * Circular Disk." Gardener et al. 1969. */
+ * This formula comes from "The Solid Angle Subtended at a Point by a
+ * Circular Disk." Gardener et al. 1969. */
double dir[3];
double h, r0, D, a, u1, u2, F;
static gsl_spline2d *spline;
@@ -78,6 +80,82 @@ double get_solid_angle_approx(double *pos, double *pmt, double *n, double r)
}
}
+double get_solid_angle2(double L2, double r2)
+{
+ double k = 2*sqrt(r2/(pow(L2,2)+pow(1+r2,2)));
+ double a2 = 4*r2/pow(1+r2,2);
+ double L_Rmax = L2/sqrt(pow(L2,2)+pow(1+r2,2));
+ double r0_r = (r2-1)/(r2+1);
+ double K, P;
+
+ if (fabs(r2-1) < 1e-5) {
+ /* 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_Rmax*K;
+ } else if (r2 <= 1) {
+ 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_Rmax*K + (2*L_Rmax)*r0_r*P;
+ } else {
+ K = gsl_sf_ellint_Kcomp(k, GSL_PREC_DOUBLE);
+ P = gsl_sf_ellint_Pcomp(k, -a2, GSL_PREC_DOUBLE);
+ return -2*L_Rmax*K + (2*L_Rmax)*r0_r*P;
+ }
+}
+
+double get_solid_angle_lookup(double L2, double r2)
+{
+ size_t i, j;
+ static int initialized = 0;
+ static double xs[1000];
+ static double ys[1000];
+ static double zs[1000*1000];
+ static double xlo = 0.0;
+ static double xhi = 1000;
+ static double ylo = 0.0;
+ static double yhi = 1000;
+
+ if (!initialized) {
+ for (i = 0; i < LEN(xs); i++) {
+ xs[i] = xlo + (xhi-xlo)*i/(LEN(xs)-1);
+ }
+ for (i = 0; i < LEN(ys); i++) {
+ ys[i] = ylo + (yhi-ylo)*i/(LEN(ys)-1);
+ }
+ for (i = 0; i < LEN(xs); i++) {
+ for (j = 0; j < LEN(ys); j++) {
+ zs[i*LEN(ys) + j] = get_solid_angle2(xs[i],ys[j]);
+ }
+ }
+ initialized = 1;
+ }
+
+ if (L2 < xlo || L2 > xhi || r2 < ylo || r2 > yhi) {
+ /* If the arguments are out of bounds of the lookup table, just call
+ * get_solid_angle2(). */
+ return get_solid_angle2(L2,r2);
+ }
+
+ return interp2d(L2,r2,xs,ys,zs,LEN(xs),LEN(ys));
+}
+
+double get_solid_angle_fast(double *pos, double *pmt, double *n, double r)
+{
+ double dir[3];
+ double L, r0, R;
+
+ 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);
+
+ return get_solid_angle_lookup(L/r,r0/r);
+}
+
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