/* Functions that calculate the gravitational potential and its first and second derivatives for the tesseroid. References ---------- * Grombein, T.; Seitz, K.; Heck, B. (2010): Untersuchungen zur effizienten Berechnung topographischer Effekte auf den Gradiententensor am Fallbeispiel der Satellitengradiometriemission GOCE. KIT Scientific Reports 7547, ISBN 978-3-86644-510-9, KIT Scientific Publishing, Karlsruhe, Germany. */ #include #include "logger.h" #include "geometry.h" #include "glq.h" #include "constants.h" #include "grav_tess.h" #define STKSIZE 10000 /* Calculates the field of a tesseroid model at a given point. */ double calc_tess_model(TESSEROID *model, int size, double lonp, double latp, double rp, GLQ *glq_lon, GLQ *glq_lat, GLQ *glq_r, double (*field)(TESSEROID, double, double, double, GLQ, GLQ, GLQ)) { double res; int tess; res = 0; for(tess = 0; tess < size; tess++) { glq_set_limits(model[tess].w, model[tess].e, glq_lon); glq_set_limits(model[tess].s, model[tess].n, glq_lat); glq_set_limits(model[tess].r1, model[tess].r2, glq_r); glq_precompute_sincos(glq_lat); res += field(model[tess], lonp, latp, rp, *glq_lon, *glq_lat, *glq_r); } return res; } /* Adaptatively calculate the field of a tesseroid model at a given point */ double calc_tess_model_adapt(TESSEROID *model, int size, double lonp, double latp, double rp, GLQ *glq_lon, GLQ *glq_lat, GLQ *glq_r, double (*field)(TESSEROID, double, double, double, GLQ, GLQ, GLQ), double ratio) { double res, distance, lont, latt, rt, d2r = PI/180., coslatp, sinlatp, rp_sqr, rlonp, Llon, Llat, Lr, sinlatt, coslatt; int t, n, nlon, nlat, nr, stktop = 0; TESSEROID stack[STKSIZE], tess; #define SQ(x) (x)*(x) /* Pre-compute these things out of the loop */ rlonp = d2r*lonp; rp_sqr = SQ(rp); coslatp = cos(d2r*latp); sinlatp = sin(d2r*latp); res = 0; for(t = 0; t < size; t++) { /* Initialize the tesseroid division stack (a LIFO structure) */ stack[0] = model[t]; stktop = 0; while(stktop >= 0) { /* Pop the stack */ tess = stack[stktop]; stktop--; /* Compute the distance from the computation point to the * geometric center of the tesseroid. */ rt = 0.5*(tess.r2 + tess.r1); lont = d2r*0.5*(tess.w + tess.e); latt = d2r*0.5*(tess.s + tess.n); sinlatt = sin(latt); coslatt = cos(latt); distance = sqrt(rp_sqr + SQ(rt) - 2*rp*rt*( sinlatp*sinlatt + coslatp*coslatt*cos(rlonp - lont))); /* Get the size of each dimension of the tesseroid in meters */ Llon = tess.r2*acos( SQ(sinlatt) + SQ(coslatt)*cos(d2r*(tess.e - tess.w))); Llat = tess.r2*acos( sin(d2r*tess.n)*sin(d2r*tess.s) + cos(d2r*tess.n)*cos(d2r*tess.s)); Lr = tess.r2 - tess.r1; /* Number of times to split the tesseroid in each dimension */ nlon = 1; nlat = 1; nr = 1; /* Check if the tesseroid is at a suitable distance (defined * the value of "ratio"). If not, mark that dimension for * division. */ if(distance < ratio*Llon) { nlon = 2; } if(distance < ratio*Llat) { nlat = 2; } if(distance < ratio*Lr) { nr = 2; } /* In case none of the dimensions need dividing, * put the GLQ roots in the proper scale and compute the * gravitational field of the tesseroid. */ /* Also compute the effect if the tesseroid stack if full * (but warn the user that the computation might not be very * precise). */ if((nlon == 1 && nlat == 1 && nr == 1) || (nlon*nlat*nr + stktop >= STKSIZE)) { if(nlon*nlat*nr + stktop >= STKSIZE) { log_error( "Stack overflow: " "tesseroid %d in the model file on " "lon=%lf lat=%lf height=%lf." "\n Calculated without fully dividing the tesseroid. " "Accuracy of the solution cannot be guaranteed." "\n This is probably caused by a computation point " "too close to the tesseroid." "\n Try increasing the computation height." "\n *Expert users* can try modifying the " "distance-size ratio." "\n *Beware* that this might affect " "the accuracy of the solution.", t + 1, lonp, latp, rp); } glq_set_limits(tess.w, tess.e, glq_lon); glq_set_limits(tess.s, tess.n, glq_lat); glq_set_limits(tess.r1, tess.r2, glq_r); glq_precompute_sincos(glq_lat); res += field(tess, lonp, latp, rp, *glq_lon, *glq_lat, *glq_r); } else { /* Divide the tesseroid in each dimension that needs dividing * Put each of the smaller tesseroids on the stack for * computing in the next iteration. */ n = split_tess(tess, nlon, nlat, nr, &stack[stktop + 1]); stktop += n; /* Sanity check */ if(n != nlon*nlat*nr) { log_error("Splitting into %d instead of %d", n, nlon*nlat*nr); } } } } #undef SQ return res; } /* Calculates potential caused by a tesseroid. */ double tess_pot(TESSEROID tess, double lonp, double latp, double rp, GLQ glq_lon, GLQ glq_lat, GLQ glq_r) { double d2r = PI/180., l_sqr, coslatp, coslatc, sinlatp, sinlatc, coslon, rc, kappa, res, cospsi, wlon, wlat, wr, scale; register int i, j, k; coslatp = cos(d2r*latp); sinlatp = sin(d2r*latp); res = 0; for(k = 0; k < glq_lon.order; k++) { coslon = cos(d2r*(lonp - glq_lon.nodes[k])); wlon = glq_lon.weights[k]; for(j = 0; j < glq_lat.order; j++) { sinlatc = glq_lat.nodes_sin[j]; coslatc = glq_lat.nodes_cos[j]; cospsi = sinlatp*sinlatc + coslatp*coslatc*coslon; wlat = glq_lat.weights[j]; for(i = 0; i < glq_r.order; i++) { wr = glq_r.weights[i]; rc = glq_r.nodes[i]; l_sqr = rp*rp + rc*rc - 2*rp*rc*cospsi; kappa = rc*rc*coslatc; res += wlon*wlat*wr*kappa/sqrt(l_sqr); } } } scale = d2r*(tess.e - tess.w)*d2r*(tess.n - tess.s)*(tess.r2 - tess.r1)/8.; res *= G*tess.density*scale; return res; } /* Calculates gx caused by a tesseroid. */ double tess_gx(TESSEROID tess, double lonp, double latp, double rp, GLQ glq_lon, GLQ glq_lat, GLQ glq_r) { double d2r = PI/180., l_sqr, kphi, coslatp, coslatc, sinlatp, sinlatc, coslon, rc, kappa, res, cospsi, wlon, wlat, wr, scale; register int i, j, k; coslatp = cos(d2r*latp); sinlatp = sin(d2r*latp); res = 0; for(k = 0; k < glq_lon.order; k++) { coslon = cos(d2r*(lonp - glq_lon.nodes[k])); wlon = glq_lon.weights[k]; for(j = 0; j < glq_lat.order; j++) { sinlatc = glq_lat.nodes_sin[j]; coslatc = glq_lat.nodes_cos[j]; kphi = coslatp*sinlatc - sinlatp*coslatc*coslon; cospsi = sinlatp*sinlatc + coslatp*coslatc*coslon; wlat = glq_lat.weights[j]; for(i = 0; i < glq_r.order; i++) { wr = glq_r.weights[i]; rc = glq_r.nodes[i]; l_sqr = rp*rp + rc*rc - 2*rp*rc*cospsi; kappa = rc*rc*coslatc; res += wlon*wlat*wr*kappa*(rc*kphi)/pow(l_sqr, 1.5); } } } scale = d2r*(tess.e - tess.w)*d2r*(tess.n - tess.s)*(tess.r2 - tess.r1)/8.; res *= SI2MGAL*G*tess.density*scale; return res; } /* Calculates gy caused by a tesseroid. */ double tess_gy(TESSEROID tess, double lonp, double latp, double rp, GLQ glq_lon, GLQ glq_lat, GLQ glq_r) { double d2r = PI/180., l_sqr, coslatp, coslatc, sinlatp, sinlatc, coslon, sinlon, rc, kappa, res, cospsi, wlon, wlat, wr, scale; register int i, j, k; coslatp = cos(d2r*latp); sinlatp = sin(d2r*latp); res = 0; for(k = 0; k < glq_lon.order; k++) { coslon = cos(d2r*(lonp - glq_lon.nodes[k])); sinlon = sin(d2r*(glq_lon.nodes[k] - lonp)); wlon = glq_lon.weights[k]; for(j = 0; j < glq_lat.order; j++) { sinlatc = glq_lat.nodes_sin[j]; coslatc = glq_lat.nodes_cos[j]; cospsi = sinlatp*sinlatc + coslatp*coslatc*coslon; wlat = glq_lat.weights[j]; for(i = 0; i < glq_r.order; i++) { wr = glq_r.weights[i]; rc = glq_r.nodes[i]; l_sqr = rp*rp + rc*rc - 2*rp*rc*cospsi; kappa = rc*rc*coslatc; res += wlon*wlat*wr*kappa*(rc*coslatc*sinlon)/pow(l_sqr, 1.5); } } } scale = d2r*(tess.e - tess.w)*d2r*(tess.n - tess.s)*(tess.r2 - tess.r1)/8.; res *= SI2MGAL*G*tess.density*scale; return res; } /* Calculates gz caused by a tesseroid. */ double tess_gz(TESSEROID tess, double lonp, double latp, double rp, GLQ glq_lon, GLQ glq_lat, GLQ glq_r) { double d2r = PI/180., l_sqr, coslatp, coslatc, sinlatp, sinlatc, coslon, cospsi, rc, kappa, res, wlon, wlat, wr, scale; register int i, j, k; coslatp = cos(d2r*latp); sinlatp = sin(d2r*latp); res = 0; for(k = 0; k < glq_lon.order; k++) { coslon = cos(d2r*(lonp - glq_lon.nodes[k])); wlon = glq_lon.weights[k]; for(j = 0; j < glq_lat.order; j++) { sinlatc = glq_lat.nodes_sin[j]; coslatc = glq_lat.nodes_cos[j]; cospsi = sinlatp*sinlatc + coslatp*coslatc*coslon; wlat = glq_lat.weights[j]; for(i = 0; i < glq_r.order; i++) { wr = glq_r.weights[i]; rc = glq_r.nodes[i]; l_sqr = rp*rp + rc*rc - 2*rp*rc*cospsi; kappa = rc*rc*coslatc; res += wlon*wlat*wr*kappa*(rc*cospsi - rp)/pow(l_sqr, 1.5); } } } scale = d2r*(tess.e - tess.w)*d2r*(tess.n - tess.s)*(tess.r2 - tess.r1)/8.; res *= SI2MGAL*G*tess.density*scale; /* Used this to make z point down */ return -1*res; } /* Calculates gxx caused by a tesseroid. */ double tess_gxx(TESSEROID tess, double lonp, double latp, double rp, GLQ glq_lon, GLQ glq_lat, GLQ glq_r) { double d2r = PI/180., l_sqr, kphi, coslatp, coslatc, sinlatp, sinlatc, coslon, rc, kappa, res, l5, cospsi, wlon, wlat, wr, scale; register int i, j, k; coslatp = cos(d2r*latp); sinlatp = sin(d2r*latp); res = 0; for(k = 0; k < glq_lon.order; k++) { coslon = cos(d2r*(lonp - glq_lon.nodes[k])); wlon = glq_lon.weights[k]; for(j = 0; j < glq_lat.order; j++) { sinlatc = glq_lat.nodes_sin[j]; coslatc = glq_lat.nodes_cos[j]; kphi = coslatp*sinlatc - sinlatp*coslatc*coslon; cospsi = sinlatp*sinlatc + coslatp*coslatc*coslon; wlat = glq_lat.weights[j]; for(i = 0; i < glq_r.order; i++) { wr = glq_r.weights[i]; rc = glq_r.nodes[i]; l_sqr = rp*rp + rc*rc - 2*rp*rc*cospsi; l5 = pow(l_sqr, 2.5); kappa = rc*rc*coslatc; res += wlon*wlat*wr*kappa*(3*rc*kphi*rc*kphi - l_sqr)/l5; } } } scale = d2r*(tess.e - tess.w)*d2r*(tess.n - tess.s)*(tess.r2 - tess.r1)/8.; res *= SI2EOTVOS*G*tess.density*scale; return res; } /* Calculates gxy caused by a tesseroid. */ double tess_gxy(TESSEROID tess, double lonp, double latp, double rp, GLQ glq_lon, GLQ glq_lat, GLQ glq_r) { double d2r = PI/180., l_sqr, kphi, coslatp, coslatc, sinlatp, sinlatc, coslon, sinlon, rc, kappa, deltax, deltay, res, cospsi, wlon, wlat, wr, scale; register int i, j, k; coslatp = cos(d2r*latp); sinlatp = sin(d2r*latp); res = 0; for(k = 0; k < glq_lon.order; k++) { coslon = cos(d2r*(lonp - glq_lon.nodes[k])); sinlon = sin(d2r*(glq_lon.nodes[k] - lonp)); wlon = glq_lon.weights[k]; for(j = 0; j < glq_lat.order; j++) { sinlatc = glq_lat.nodes_sin[j]; coslatc = glq_lat.nodes_cos[j]; kphi = coslatp*sinlatc - sinlatp*coslatc*coslon; cospsi = sinlatp*sinlatc + coslatp*coslatc*coslon; wlat = glq_lat.weights[j]; for(i = 0; i < glq_r.order; i++) { wr = glq_r.weights[i]; rc = glq_r.nodes[i]; l_sqr = rp*rp + rc*rc - 2*rp*rc*cospsi; kappa = rc*rc*coslatc; deltax = rc*kphi; deltay = rc*coslatc*sinlon; res += wlon*wlat*wr*kappa*(3*deltax*deltay)/pow(l_sqr, 2.5); } } } scale = d2r*(tess.e - tess.w)*d2r*(tess.n - tess.s)*(tess.r2 - tess.r1)/8.; res *= SI2EOTVOS*G*tess.density*scale; return res; } /* Calculates gxz caused by a tesseroid. */ double tess_gxz(TESSEROID tess, double lonp, double latp, double rp, GLQ glq_lon, GLQ glq_lat, GLQ glq_r) { double d2r = PI/180., l_sqr, kphi, coslatp, coslatc, sinlatp, sinlatc, coslon, cospsi, rc, kappa, deltax, deltaz, res, wlon, wlat, wr, scale; register int i, j, k; coslatp = cos(d2r*latp); sinlatp = sin(d2r*latp); res = 0; for(k = 0; k < glq_lon.order; k++) { coslon = cos(d2r*(lonp - glq_lon.nodes[k])); wlon = glq_lon.weights[k]; for(j = 0; j < glq_lat.order; j++) { sinlatc = glq_lat.nodes_sin[j]; coslatc = glq_lat.nodes_cos[j]; cospsi = sinlatp*sinlatc + coslatp*coslatc*coslon; kphi = coslatp*sinlatc - sinlatp*coslatc*coslon; wlat = glq_lat.weights[j]; for(i = 0; i < glq_r.order; i++) { wr = glq_r.weights[i]; rc = glq_r.nodes[i]; l_sqr = rp*rp + rc*rc - 2*rp*rc*cospsi; kappa = rc*rc*coslatc; deltax = rc*kphi; deltaz = rc*cospsi - rp; res += wlon*wlat*wr*kappa*(3*deltax*deltaz)/pow(l_sqr, 2.5); } } } scale = d2r*(tess.e - tess.w)*d2r*(tess.n - tess.s)*(tess.r2 - tess.r1)/8.; res *= SI2EOTVOS*G*tess.density*scale; return res; } /* Calculates gyy caused by a tesseroid. */ double tess_gyy(TESSEROID tess, double lonp, double latp, double rp, GLQ glq_lon, GLQ glq_lat, GLQ glq_r) { double d2r = PI/180., l_sqr, coslatp, coslatc, sinlatp, sinlatc, coslon, sinlon, rc, kappa, deltay, res, l5, cospsi, wlon, wlat, wr, scale; register int i, j, k; coslatp = cos(d2r*latp); sinlatp = sin(d2r*latp); res = 0; for(k = 0; k < glq_lon.order; k++) { coslon = cos(d2r*(lonp - glq_lon.nodes[k])); sinlon = sin(d2r*(glq_lon.nodes[k] - lonp)); wlon = glq_lon.weights[k]; for(j = 0; j < glq_lat.order; j++) { sinlatc = glq_lat.nodes_sin[j]; coslatc = glq_lat.nodes_cos[j]; cospsi = sinlatp*sinlatc + coslatp*coslatc*coslon; wlat = glq_lat.weights[j]; for(i = 0; i < glq_r.order; i++) { wr = glq_r.weights[i]; rc = glq_r.nodes[i]; l_sqr = rp*rp + rc*rc - 2*rp*rc*cospsi; l5 = pow(l_sqr, 2.5); kappa = rc*rc*coslatc; deltay = rc*coslatc*sinlon; res += wlon*wlat*wr*kappa*(3*deltay*deltay - l_sqr)/l5; } } } scale = d2r*(tess.e - tess.w)*d2r*(tess.n - tess.s)*(tess.r2 - tess.r1)/8.; res *= SI2EOTVOS*G*tess.density*scale; return res; } /* Calculates gyz caused by a tesseroid. */ double tess_gyz(TESSEROID tess, double lonp, double latp, double rp, GLQ glq_lon, GLQ glq_lat, GLQ glq_r) { double d2r = PI/180., l_sqr, coslatp, coslatc, sinlatp, sinlatc, coslon, sinlon, cospsi, rc, kappa, deltay, deltaz, res, wlon, wlat, wr, scale; register int i, j, k; coslatp = cos(d2r*latp); sinlatp = sin(d2r*latp); res = 0; for(k = 0; k < glq_lon.order; k++) { coslon = cos(d2r*(lonp - glq_lon.nodes[k])); sinlon = sin(d2r*(glq_lon.nodes[k] - lonp)); wlon = glq_lon.weights[k]; for(j = 0; j < glq_lat.order; j++) { sinlatc = glq_lat.nodes_sin[j]; coslatc = glq_lat.nodes_cos[j]; cospsi = sinlatp*sinlatc + coslatp*coslatc*coslon; wlat = glq_lat.weights[j]; for(i = 0; i < glq_r.order; i++) { wr = glq_r.weights[i]; rc = glq_r.nodes[i]; l_sqr = rp*rp + rc*rc - 2*rp*rc*cospsi; kappa = rc*rc*coslatc; deltay = rc*coslatc*sinlon; deltaz = rc*cospsi - rp; res += wlon*wlat*wr*kappa*(3*deltay*deltaz)/pow(l_sqr, 2.5); } } } scale = d2r*(tess.e - tess.w)*d2r*(tess.n - tess.s)*(tess.r2 - tess.r1)/8.; res *= SI2EOTVOS*G*tess.density*scale; return res; } /* Calculates gzz caused by a tesseroid. */ double tess_gzz(TESSEROID tess, double lonp, double latp, double rp, GLQ glq_lon, GLQ glq_lat, GLQ glq_r) { double d2r = PI/180., l_sqr, coslatp, coslatc, sinlatp, sinlatc, coslon, cospsi, rc, kappa, deltaz, res, wlon, wlat, wr, scale, l5; register int i, j, k; coslatp = cos(d2r*latp); sinlatp = sin(d2r*latp); res = 0; for(k = 0; k < glq_lon.order; k++) { coslon = cos(d2r*(lonp - glq_lon.nodes[k])); wlon = glq_lon.weights[k]; for(j = 0; j < glq_lat.order; j++) { sinlatc = glq_lat.nodes_sin[j]; coslatc = glq_lat.nodes_cos[j]; cospsi = sinlatp*sinlatc + coslatp*coslatc*coslon; wlat = glq_lat.weights[j]; for(i = 0; i < glq_r.order; i++) { wr = glq_r.weights[i]; rc = glq_r.nodes[i]; l_sqr = rp*rp + rc*rc - 2*rp*rc*cospsi; l5 = pow(l_sqr, 2.5); kappa = rc*rc*coslatc; deltaz = rc*cospsi - rp; res += wlon*wlat*wr*kappa*(3*deltaz*deltaz - l_sqr)/l5; } } } scale = d2r*(tess.e - tess.w)*d2r*(tess.n - tess.s)*(tess.r2 - tess.r1)/8.; res *= SI2EOTVOS*G*tess.density*scale; return res; }