tesseroids/lib/grav_tess.c
2021-05-05 10:58:03 +08:00

581 lines
20 KiB
C

/*
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 <math.h>
#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;
}