initial upload

This commit is contained in:
2021-05-05 10:58:03 +08:00
parent d6ffc7f33c
commit f1cf25db22
114 changed files with 83953 additions and 0 deletions

41
lib/CMakeLists.txt Normal file
View File

@@ -0,0 +1,41 @@
# 设置编译选项
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2")
if(WIN32)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11 -O2")
endif()
# 设置库文件的输出地址
set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
# 设定库源文件
aux_source_directory(. LIBTESS_SRC)
# 以下部分为库的编译
# 注意目标名必须唯一 所以不能直接生成相同名称的动态库与静态库
# 注意此处不必为目标名称添加lib前缀和相应后缀cmake会自行添加
add_library(tesseroids SHARED ${LIBTESS_SRC})
# 首先添加静态库的生成命令
add_library(tesseroids_static STATIC ${LIBTESS_SRC})
# 设置静态库的输出名称从而获得与动态库名称相同的静态库
set_target_properties(tesseroids_static PROPERTIES OUTPUT_NAME "tesseroids")
# 设置输出目标属性以同时输出动态库与静态库
set_target_properties(tesseroids PROPERTIES CLEAN_DIRECT_OUTPUT 1)
set_target_properties(tesseroids_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)
# 设置动态库的版本号
set_target_properties(tesseroids PROPERTIES VERSION 1.6 SOVERSION 1.6)
# 库的安装命令
if(WIN32)
install(TARGETS tesseroids DESTINATION lib)
install(TARGETS tesseroids_static DESTINATION lib)
else()
install(TARGETS tesseroids tesseroids_static
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib)
endif()
# 头文件安装命令
file(GLOB LIBTESS_HEAD *.h)
install(FILES ${LIBTESS_HEAD} DESTINATION include/tesseroids)

39
lib/constants.c Normal file
View File

@@ -0,0 +1,39 @@
/*
Define constants used, like the gravitational constant and unit conversions.
All values are in SI units!
*/
#include "constants.h"
/* Mean Earth radius [\f$ m \f$] */
const double MEAN_EARTH_RADIUS = 6378137.0;
/* The gravitational constant [\f$ m^3*kg^{-1}*s^{-1} \f$] */
const double G = 0.00000000006673;
/* Conversion factor from SI units to Eotvos
[\f$ \frac{1}{s^2} = 10^9\ Eotvos \f$] */
const double SI2EOTVOS = 1000000000.0;
/* Conversion factor from SI units to mGal
[\f$ 1 \frac{m}{s^2} = 10^5\ mGal \f$] */
const double SI2MGAL = 100000.0;
/* Pi */
const double PI = 3.1415926535897932384626433832795;
/* minimum distance-to-size ratio for potential computations to be accurate */
const double TESSEROID_POT_SIZE_RATIO = 1;
/* Minimum distance-to-size ratio for gravity computations to be accurate */
const double TESSEROID_GX_SIZE_RATIO = 1.5;
const double TESSEROID_GY_SIZE_RATIO = 1.5;
const double TESSEROID_GZ_SIZE_RATIO = 1.5;
/* Minimum distance-to-size ratio for gravity gradient computations to be
accurate */
const double TESSEROID_GXX_SIZE_RATIO = 8;
const double TESSEROID_GXY_SIZE_RATIO = 8;
const double TESSEROID_GXZ_SIZE_RATIO = 8;
const double TESSEROID_GYY_SIZE_RATIO = 8;
const double TESSEROID_GYZ_SIZE_RATIO = 8;
const double TESSEROID_GZZ_SIZE_RATIO = 8;

44
lib/constants.h Normal file
View File

@@ -0,0 +1,44 @@
/*
Define constants used, like the gravitational constant and unit conversions.
Values are assigned in file constants.c
All values are in SI units!
*/
#ifndef _TESSEROIDS_CONSTANTS_H_
#define _TESSEROIDS_CONSTANTS_H_
/* Mean Earth radius [\f$ m \f$] */
extern const double MEAN_EARTH_RADIUS;
/* The gravitational constant [\f$ m^3*kg^{-1}*s^{-1} \f$] */
extern const double G;
/* Conversion factor from SI units to Eotvos
[\f$ \frac{1}{s^2} = 10^9\ Eotvos \f$] */
extern const double SI2EOTVOS;
/* Conversion factor from SI units to mGal
[\f$ 1 \frac{m}{s^2} = 10^5\ mGal \f$] */
extern const double SI2MGAL;
/* Pi */
extern const double PI;
/* Minimum distance-to-size ratio for potential computations to be accurate */
extern const double TESSEROID_POT_SIZE_RATIO;
/* Minimum distance-to-size ratio for gravity computations to be accurate */
extern const double TESSEROID_GX_SIZE_RATIO;
extern const double TESSEROID_GY_SIZE_RATIO;
extern const double TESSEROID_GZ_SIZE_RATIO;
/* Minimum distance-to-size ratio for gravity gradient computations to be
accurate */
extern const double TESSEROID_GXX_SIZE_RATIO;
extern const double TESSEROID_GXY_SIZE_RATIO;
extern const double TESSEROID_GXZ_SIZE_RATIO;
extern const double TESSEROID_GYY_SIZE_RATIO;
extern const double TESSEROID_GYZ_SIZE_RATIO;
extern const double TESSEROID_GZZ_SIZE_RATIO;
#endif

175
lib/geometry.c Normal file
View File

@@ -0,0 +1,175 @@
/*
Data structures for geometric elements and functions that operate on them.
Defines the TESSEROID, SPHERE, and PRISM structures.
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include "constants.h"
#include "logger.h"
#include "geometry.h"
/* Split a tesseroid. */
int split_tess(TESSEROID tess, int nlon, int nlat, int nr, TESSEROID *split)
{
double dlon, dlat, dr, w, s, r1;
int i, j, k, t = 0;
dlon = (double)(tess.e - tess.w)/nlon;
dlat = (double)(tess.n - tess.s)/nlat;
dr = (double)(tess.r2 - tess.r1)/nr;
for(r1=tess.r1, k=0; r1 + dr <= tess.r2 || k < nr; r1 += dr, k++)
{
for(s=tess.s, j=0; s + dlat <= tess.n || j < nlat; s += dlat, j++)
{
for(w=tess.w, i=0; w + dlon <= tess.e || i < nlon; w += dlon, i++)
{
split[t].w = w;
split[t].e = w + dlon;
split[t].s = s;
split[t].n = s + dlat;
split[t].r1 = r1;
split[t].r2 = r1 + dr;
split[t].density = tess.density;
t++;
}
}
}
return t;
}
/* Calculate the total mass of a tesseroid model. */
double tess_total_mass(TESSEROID *model, int size)
{
double mass;
int i;
for(mass = 0, i = 0; i < size; i++)
{
mass += model[i].density*tess_volume(model[i]);
}
return mass;
}
/* Calculate the mass of a tesseroid model within a density range. */
double tess_range_mass(TESSEROID *model, int size, double low_dens,
double high_dens)
{
double mass;
int i;
for(mass = 0, i = 0; i < size; i++)
{
if(model[i].density >= low_dens && model[i].density <= high_dens)
{
mass += model[i].density*tess_volume(model[i]);
}
}
return mass;
}
/* Convert a tesseroid to a rectangular prism of equal volume and append
* the spherical coordinates of the center top surface (needed to calculate
* the effect in spherical coordinates). */
void tess2prism(TESSEROID tess, PRISM *prism)
{
double deg2rad = PI/180., r0, dx, dy;
r0 = 0.5*(tess.r1 + tess.r2);
dx = r0*deg2rad*(tess.n - tess.s);
dy = r0*cos(deg2rad*0.5*(tess.n + tess.s))*deg2rad*(tess.e - tess.w);
prism->x1 = -0.5*dx;
prism->x2 = 0.5*dx;
prism->y1 = -0.5*dy;
prism->y2 = 0.5*dy;
/* z1 = 0 because the center of the top face of the prism is the origin of
the coordiante system */
prism->z1 = 0.;
prism->z2 = tess.r2 - tess.r1;
/* Calculate the density of the prism so that they will have exactly
the same mass */
prism->density = (double)tess.density*
tess_volume(tess)/prism_volume(*prism);
/* Set the coordinates of the center of the prisms top face */
prism->lon = 0.5*(tess.e + tess.w);
prism->lat = 0.5*(tess.n + tess.s);
prism->r = tess.r2; /* The top face */
}
/* Convert a tesseroid to a rectangular prism of equal volume by approximating
* 1 degree by 111.11 km. */
void tess2prism_flatten(TESSEROID tess, PRISM *prism)
{
prism->x1 = tess.s*111110.;
prism->x2 = tess.n*111110.;
prism->y1 = tess.w*111110.;
prism->y2 = tess.e*111110.;
/* r1 is not z1 because r1 is the bottom face (because Nagy et al., 2000,
use z->Down) */
prism->z1 = MEAN_EARTH_RADIUS - tess.r2;
prism->z2 = MEAN_EARTH_RADIUS - tess.r1;
/* Calculate the density of the prism so that they will have exactly
the same mass */
prism->density = (double)tess.density*
tess_volume(tess)/prism_volume(*prism);
}
/* Convert a tesseroid to a sphere of equal volume. */
void tess2sphere(TESSEROID tess, SPHERE *sphere)
{
sphere->density = tess.density;
sphere->lonc = 0.5*(tess.e + tess.w);
sphere->latc = 0.5*(tess.n + tess.s);
sphere->rc = 0.5*(tess.r1 + tess.r2);
sphere->r = pow(3*tess_volume(tess)/(4.*PI), (double)1./3.);
}
/* Convert a rectangular prism into a sphere of equal volume. */
void prism2sphere(PRISM prism, double lonc, double latc, double rc,
SPHERE *sphere)
{
sphere->density = prism.density;
sphere->lonc = lonc;
sphere->latc = latc;
sphere->rc = rc;
sphere->r = pow(3*prism_volume(prism)/(4.*PI), (double)1./3.);
}
/* Calculate the volume of a tesseroid */
double tess_volume(TESSEROID tess)
{
double d2r = PI/180., vol;
vol = d2r*(tess.e - tess.w)*(pow(tess.r2, 3) - pow(tess.r1, 3))*
(sin(d2r*tess.n) - sin(d2r*tess.s))/3.;
return vol;
}
/* Calculate the volume of a sphere */
double sphere_volume(SPHERE sphere)
{
return 4.*PI*pow(sphere.r, 3)/3.;
}
/* Calculate the volume of a prism */
double prism_volume(PRISM prism)
{
return (prism.x2 - prism.x1)*(prism.y2 - prism.y1)*(prism.z2 - prism.z1);
}

168
lib/geometry.h Normal file
View File

@@ -0,0 +1,168 @@
/*
Data structures for geometric elements and functions that operate on them.
Defines the TESSEROID, SPHERE, and PRISM structures.
*/
#ifndef _TESSEROIDS_GEOMETRY_H_
#define _TESSEROIDS_GEOMETRY_H_
/* Store information on a tesseroid */
typedef struct tess_struct {
/* s, n, w, e in degrees. r1 and r2 are the smaller and larger radius */
double density; /* in SI units */
double w; /* western longitude border in degrees */
double e; /* eastern longitude border in degrees */
double s; /* southern latitude border in degrees */
double n; /* northern latitude border in degrees */
double r1; /* smallest radius border in SI units */
double r2; /* largest radius border in SI units */
} TESSEROID;
/* Store information on a rectangular prism */
typedef struct prism_struct {
double density; /* in SI units */
double x1; /* in SI units */
double x2; /* in SI units */
double y1; /* in SI units */
double y2; /* in SI units */
double z1; /* in SI units */
double z2; /* in SI units */
/* Geodetic coordinates of the center of the top face of the prism */
double lon, lat, r;
} PRISM;
/* Store information on a sphere */
typedef struct sphere_struct {
double density; /* in SI units */
double r; /* radius of the sphere in SI units */
double lonc; /* longitude of the center of the sphere in degrees */
double latc; /* latitude of the center of the sphere in degrees */
double rc; /* radial coordinate of the center of the sphere in SI units */
} SPHERE;
/* Split a tesseroid.
@param tess tesseroid that will be split
@param split array of nlon*nlat*nr tesseroids with memory allocated.
Returns:
Number of tesseroids in split.
*/
extern int split_tess(TESSEROID tess, int nlon, int nlat, int nr,
TESSEROID *split);
/* Calculate the total mass of a tesseroid model.
Give all in SI units and degrees!
@param model array of tesseroids
@param size size of the model
@return The calculated mass
*/
extern double tess_total_mass(TESSEROID *model, int size);
/* Calculate the mass of a tesseroid model within a density range.
Give all in SI units and degrees!
@param model array of tesseroids
@param size size of the model
@param low_dens lower bound of the density range
@param high_dens upper bound of the density range
@return The calculated mass
*/
extern double tess_range_mass(TESSEROID *model, int size, double low_dens,
double high_dens);
/* Convert a tesseroid into a rectangular prism of equal volume (Wild-Pfeiffer, 2008).
\f[
\Delta x = \frac{r_1 + r_2}{2} \Delta \phi,
\f]
\f[
\Delta y = \frac{r_1 + r_2}{2} \cos\left(\frac{\phi_1 + \phi_2}{2}\right) \Delta\lambda,
\f]
\f[
\Delta z = \Delta r,
\f]
<b>References</b>
- Wild-Pfeiffer, F. (2008). A comparison of different mass elements for use in
gravity gradiometry. Journal of Geodesy, 82(10), 637-653.
@param tess tesseroid to convert
@param prism prism with equal volume of the tesseroid (used to return)
*/
extern void tess2prism(TESSEROID tess, PRISM *prism);
/* Convert a tesseroid into a rectangular prism of equal volume by
approximating 1 degree by 111.11 km.
@param tess tesseroid to convert
@param prism prism with equal volume of the tesseroid (used to return)
*/
extern void tess2prism_flatten(TESSEROID tess, PRISM *prism);
/* Convert a tesseroid into a sphere of equal volume.
Parameters:
@param tess tesseroid to convert
@param sphere sphere with equal volume of the tesseroid (used to return)
*/
extern void tess2sphere(TESSEROID tess, SPHERE *sphere);
/* Convert a rectangular prism into a sphere of equal volume.
Parameters:
@param prism prism to convert
@param lonc longitude of the desired center of the sphere, in degrees
@param latc latitude of the desired center of the sphere, in degrees
@param rc radial coordinate of the desired center of the sphere, in SI units
@param sphere sphere with equal volume of the prism (used to return)
*/
extern void prism2sphere(PRISM prism, double lonc, double latc, double rc,
SPHERE *sphere);
/* Calculate the volume of a tesseroid.
@param tess the tesseroid whose volume will be calculated
@return the volume in the respective units
*/
extern double tess_volume(TESSEROID tess);
/* Calculate the volume of a sphere.
@param sphere the sphere whose volume will be calculated
@return the volume in the respective units
*/
extern double sphere_volume(SPHERE sphere);
/* Calculate the volume of a prism
@param prism the prism whose volume will be calculated
@return the volume in the respective units
*/
extern double prism_volume(PRISM prism);
#endif

308
lib/glq.c Normal file
View File

@@ -0,0 +1,308 @@
/*
Functions for implementing a Gauss-Legendre Quadrature numerical integration.
*/
#include <stdlib.h>
#include <math.h>
#include "constants.h"
#include "logger.h"
#include "glq.h"
/** \var GLQ_MAXIT
Max iterations of the root-finder algorithm */
const int GLQ_MAXIT = 1000;
/** \var GLQ_MAXERROR
Max error allowed for the root-finder algorithm */
const double GLQ_MAXERROR = 0.000000000000001;
/* Make a new GLQ structure and set all the parameters needed */
GLQ * glq_new(int order, double lower, double upper)
{
GLQ *glq;
int rc;
glq = (GLQ *)malloc(sizeof(GLQ));
if(glq == NULL)
{
return NULL;
}
glq->order = order;
glq->nodes = (double *)malloc(sizeof(double)*order);
if(glq->nodes == NULL)
{
free(glq);
return NULL;
}
glq->nodes_unscaled = (double *)malloc(sizeof(double)*order);
if(glq->nodes_unscaled == NULL)
{
free(glq);
free(glq->nodes);
return NULL;
}
glq->weights = (double *)malloc(sizeof(double)*order);
if(glq->weights == NULL)
{
free(glq);
free(glq->nodes);
free(glq->nodes_unscaled);
return NULL;
}
glq->nodes_sin = (double *)malloc(sizeof(double)*order);
if(glq->nodes_sin == NULL)
{
free(glq);
free(glq->nodes);
free(glq->nodes_unscaled);
free(glq->weights);
return NULL;
}
glq->nodes_cos = (double *)malloc(sizeof(double)*order);
if(glq->nodes_cos == NULL)
{
free(glq);
free(glq->nodes);
free(glq->nodes_unscaled);
free(glq->weights);
free(glq->nodes_sin);
return NULL;
}
rc = glq_nodes(order, glq->nodes_unscaled);
if(rc != 0 && rc != 3)
{
switch(rc)
{
case 1:
log_error("glq_nodes invalid GLQ order %d. Should be >= 2.",
order);
break;
case 2:
log_error("glq_nodes NULL pointer for nodes");
break;
default:
log_error("glq_nodes unknown error code %g", rc);
break;
}
glq_free(glq);
return NULL;
}
else if(rc == 3)
{
log_warning("glq_nodes max iterations reached in root finder");
log_warning("nodes might not have desired accuracy %g", GLQ_MAXERROR);
}
rc = glq_weights(order, glq->nodes_unscaled, glq->weights);
if(rc != 0)
{
switch(rc)
{
case 1:
log_error("glq_weights invalid GLQ order %d. Should be >= 2.",
order);
break;
case 2:
log_error("glq_weights NULL pointer for nodes");
break;
case 3:
log_error("glq_weights NULL pointer for weights");
break;
default:
log_error("glq_weights unknown error code %d\n", rc);
break;
}
glq_free(glq);
return NULL;
}
if(glq_set_limits(lower, upper, glq) != 0)
{
glq_free(glq);
return NULL;
}
return glq;
}
/* Free the memory allocated to make a GLQ structure */
void glq_free(GLQ *glq)
{
free(glq->nodes);
free(glq->nodes_unscaled);
free(glq->weights);
free(glq->nodes_sin);
free(glq->nodes_cos);
free(glq);
}
/* Calculates the GLQ nodes using glq_next_root. */
int glq_nodes(int order, double *nodes)
{
register int i;
int rc = 0;
double initial;
if(order < 2)
{
return 1;
}
if(nodes == NULL)
{
return 2;
}
for(i = 0; i < order; i++)
{
initial = cos(PI*(order - i - 0.25)/(order + 0.5));
if(glq_next_root(initial, i, order, nodes) == 3)
{
rc = 3;
}
}
return rc;
}
/* Put the GLQ nodes to the integration limits IN PLACE. */
int glq_set_limits(double lower, double upper, GLQ *glq)
{
/* Only calculate once to optimize the code */
double tmpplus = 0.5*(upper + lower), tmpminus = 0.5*(upper - lower);
register int i;
if(glq->order < 2)
{
return 1;
}
if(glq->nodes == NULL)
{
return 2;
}
if(glq->nodes_unscaled == NULL)
{
return 2;
}
for(i = 0; i < glq->order; i++)
{
glq->nodes[i] = tmpminus*glq->nodes_unscaled[i] + tmpplus;
}
return 0;
}
/* Calculate the next Legendre polynomial root given the previous root found.
* Uses the method of Barrera-Figueroa et al. (2006). */
int glq_next_root(double initial, int root_index, int order, double *roots)
{
double x1, x0, pn, pn_2, pn_1, pn_line, sum;
int it = 0;
register int n;
if(order < 2)
{
return 1;
}
if(root_index < 0 || root_index >= order)
{
return 2;
}
x1 = initial;
do
{
x0 = x1;
/* Calculate Pn(x0) */
/* Starting from P0(x) and P1(x), */
/* find the others using the recursive relation: */
/* Pn(x)=(2n-1)xPn_1(x)/n - (n-1)Pn_2(x)/n */
pn_1 = 1.; /* This is Po(x) */
pn = x0; /* and this P1(x) */
for(n = 2; n <= order; n++)
{
pn_2 = pn_1;
pn_1 = pn;
pn = ( ((2*n - 1)*x0*pn_1) - ((n - 1)*pn_2) )/n;
}
/* Now calculate Pn'(x0) using another recursive relation: */
/* Pn'(x)=n(xPn(x)-Pn_1(x))/(x*x-1) */
pn_line = order*(x0*pn - pn_1)/(x0*x0 - 1);
/* Sum the roots found so far */
for(n = 0, sum = 0; n < root_index; n++)
{
sum += 1./(x0 - roots[n]);
}
/* Update the estimate for the root */
x1 = x0 - (double)pn/(pn_line - pn*sum);
/** Compute the absolute value of x */
#define GLQ_ABS(x) ((x) < 0 ? -1*(x) : (x))
} while(GLQ_ABS(x1 - x0) > GLQ_MAXERROR && ++it <= GLQ_MAXIT);
#undef GLQ_ABS
roots[root_index] = x1;
/* Tell the user if stagnation occurred */
if(it > GLQ_MAXIT)
{
return 3;
}
return 0;
}
/* Calculates the weighting coefficients for the GLQ integration. */
int glq_weights(int order, double *nodes, double *weights)
{
register int i, n;
double xi, pn, pn_2, pn_1, pn_line;
if(order < 2)
{
return 1;
}
if(nodes == NULL)
{
return 2;
}
if(weights == NULL)
{
return 3;
}
for(i = 0; i < order; i++){
xi = nodes[i];
/* Find Pn'(xi) with the recursive relation to find Pn and Pn-1: */
/* Pn(x)=(2n-1)xPn_1(x)/n - (n-1)Pn_2(x)/n */
/* Then use: Pn'(x)=n(xPn(x)-Pn_1(x))/(x*x-1) */
/* Find Pn and Pn-1 stating from P0 and P1 */
pn_1 = 1; /* This is Po(x) */
pn = xi; /* and this P1(x) */
for(n = 2; n <= order; n++)
{
pn_2 = pn_1;
pn_1 = pn;
pn = ((2*n - 1)*xi*pn_1 - (n - 1)*pn_2)/n;
}
pn_line = order*(xi*pn - pn_1)/(xi*xi - 1.);
/* ith weight is: wi = 2/(1 - xi^2)(Pn'(xi)^2) */
weights[i] = 2./((1 - xi*xi)*pn_line*pn_line);
}
return 0;
}
void glq_precompute_sincos(GLQ *glq)
{
double d2r = PI/180.;
register int i;
for(i = 0; i < glq->order; i++)
{
glq->nodes_sin[i] = sin(d2r*glq->nodes[i]);
glq->nodes_cos[i] = cos(d2r*glq->nodes[i]);
}
}

192
lib/glq.h Normal file
View File

@@ -0,0 +1,192 @@
/*
Functions for implementing a Gauss-Legendre Quadrature numerical integration
(Hildebrand, 1987).
Usage example
-------------
To integrate the cossine function from 0 to 90 degrees:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "src/c/glq.h"
int main(){
// Create a new glq structure
GLQ *glq;
double result = 0, a = 0, b = 0.5*3.14;
int i;
glq = glq_new(5, a, b);
if(glq == NULL){
printf("malloc error");
return 1;
}
// Calculate the integral
for(i = 0; i < glq->order; i++)
result += glq->weights[i]*cos(glq->nodes[i]);
// Need to multiply by a scale factor of the integration limits
result *= 0.5*(b - a);
printf("Integral of cossine from 0 to 90 degrees = %lf\n", result);
// Free allocated memory
glq_free(glq);
return 0;
}
References
----------
* Hildebrand, F.B (1987): Introduction to numerical analysis.
Courier Dover Publications, 2. ed.
*/
#ifndef _TESSEROIDS_GLQ_H_
#define _TESSEROIDS_GLQ_H_
/** \var GLQ_MAXIT
Max iterations of the root-finder algorithm */
extern const int GLQ_MAXIT;
/** \var GLQ_MAXERROR
Max error allowed for the root-finder algorithm */
extern const double GLQ_MAXERROR;
/** Store the nodes and weights needed for a GLQ integration */
typedef struct glq_struct
{
int order; /**< order of the quadrature, ie number of nodes */
double *nodes; /**< abscissas or discretization points of the quadrature */
double *weights; /**< weighting coefficients of the quadrature */
double *nodes_unscaled; /**< nodes in [-1,1] interval */
/* Used to store the pre-computed sine and cossine of the nodes, if needed.
* Can be useful for the latitude, which is always used as sin and cos */
double *nodes_sin;
double *nodes_cos;
} GLQ;
/** Make a new GLQ structure and set all the parameters needed
<b>WARNING</b>: Don't forget to free the memory malloced by this function using
glq_free()!
Prints error and warning messages using the logging.h module.
@param order order of the quadrature, ie number of nodes
@param lower lower integration limit
@param upper upper integration limit
@return GLQ data structure with the nodes and weights calculated. NULL if there
was an error with allocation.
*/
extern GLQ * glq_new(int order, double lower, double upper);
/** Free the memory allocated to make a GLQ structure
@param glq pointer to the allocated memory
*/
extern void glq_free(GLQ *glq);
/** Put the GLQ nodes to the integration limits <b>IN PLACE</b>.
Will replace the values of glq.nodes with ones in the specified integration
limits.
In case the GLQ structure was created with glq_new(), the integration limits can
be reset using this function.
@param lower lower integration limit
@param upper upper integration limit
@param glq pointer to a GLQ structure created with glq_new() and with all
necessary memory allocated
@return Return code:
- 0: if everything went OK
- 1: if invalid order
- 2: if NULL pointer for nodes or nodes_unscaled
*/
extern int glq_set_limits(double lower, double upper, GLQ *glq);
/** Calculates the GLQ nodes using glq_next_root.
Nodes will be in the [-1,1] interval. To convert them to the integration limits
use glq_scale_nodes
@param order order of the quadrature, ie how many nodes. Must be >= 2.
@param nodes pre-allocated array to return the nodes.
@return Return code:
- 0: if everything went OK
- 1: if invalid order
- 2: if NULL pointer for nodes
- 3: if number of maximum iterations was reached when calculating the root.
This usually means that the desired accuracy was not achieved. Default
desired accuracy is GLQ_MAXERROR. Default maximum iterations is
GLQ_MAXIT.
*/
extern int glq_nodes(int order, double *nodes);
/** Calculate the next Legendre polynomial root given the previous root found.
Uses the root-finder algorithm of:
Barrera-Figueroa, V., Sosa-Pedroza, J. and López-Bonilla, J., 2006,
"Multiple root finder algorithm for Legendre and Chebyshev polynomials via
Newton's method", 2006, Annales mathematicae et Informaticae, 33, pp 3-13
@param initial initial estimate of the next root. I recommend the use of
\f$ \cos\left(\pi\frac{(N - i - 0.25)}{N + 0.5}\right) \f$,
where \f$ i \f$ is the index of the desired root
@param root_index index of the desired root, starting from 0
@param order order of the Legendre polynomial, ie number of roots.
@param roots array with the roots found so far. Will return the next root in
roots[root_index], so make sure to malloc enough space.
@return Return code:
- 0: if everything went OK
- 1: if order is not valid
- 2: if root_index is not valid (negative)
- 3: if number of maximum iterations was reached when calculating the root.
This usually means that the desired accuracy was not achieved. Default
desired accuracy is GLQ_MAXERROR. Default maximum iterations is
GLQ_MAXIT.
*/
extern int glq_next_root(double initial, int root_index, int order,
double *roots);
/** Calculates the weighting coefficients for the GLQ integration.
@param order order of the quadrature, ie number of nodes and weights.
@param nodes array containing the GLQ nodes calculated by glq_nodes.
<b>IMPORTANT</b>: needs the nodes in [-1,1] interval! Scaled nodes
will result in wrong weights!
@param weights pre-allocated array to return the weights
@return Return code:
- 0: if everything went OK
- 1: if order is not valid
- 2: if nodes is a NULL pointer
- 3: if weights is a NULL pointer
*/
extern int glq_weights(int order, double *nodes, double *weights);
/* Precompute the sine and cossine of the GLQ nodes and store them in the
* structure */
extern void glq_precompute_sincos(GLQ *glq);
#endif

469
lib/grav_prism.c Normal file
View File

@@ -0,0 +1,469 @@
/*
Functions that calculate the gravitational potential and its first and second
derivatives for the rectangular prism using the formulas in Nagy et al. (2000).
The coordinate system used is that of the article, ie:
x -> North y -> East z -> Down
References
----------
* Nagy, D., Papp, G., Benedek, J. (2000): The gravitational potential and its
derivatives for the prism. Journal of Geodesy, 74, 552560.
*/
#include <math.h>
#include <stdlib.h>
#include "geometry.h"
#include "constants.h"
#include "grav_prism.h"
double safe_atan2(double y, double x)
{
if(y == 0)
{
return 0;
}
if((y > 0) && (x < 0))
{
return atan2(y, x) - PI;
}
if((y < 0) && (x < 0))
{
return atan2(y, x) + PI;
}
return atan2(y, x);
}
double safe_log(double x)
{
if(x == 0)
{
return 0;
}
else
{
return log(x);
}
}
/* Calculates the potential cause by a prism. */
double prism_pot(PRISM prism, double xp, double yp, double zp)
{
double x[2], y[2], z[2], kernel, res, r;
register int i, j, k;
/* First thing to do is make P the origin of the coordinate system */
x[0] = prism.x2 - xp;
x[1] = prism.x1 - xp;
y[0] = prism.y2 - yp;
y[1] = prism.y1 - yp;
z[0] = prism.z2 - zp;
z[1] = prism.z1 - zp;
res = 0;
/* Evaluate the integration limits */
for(k=0; k<=1; k++)
{
for(j=0; j<=1; j++)
{
for(i=0; i<=1; i++)
{
r = sqrt(x[i]*x[i] + y[j]*y[j] + z[k]*z[k]);
kernel = (x[i]*y[j]*safe_log(z[k] + r)
+ y[j]*z[k]*safe_log(x[i] + r)
+ x[i]*z[k]*safe_log(y[j] + r)
- 0.5*x[i]*x[i]*safe_atan2(z[k]*y[j], x[i]*r)
- 0.5*y[j]*y[j]*safe_atan2(z[k]*x[i], y[j]*r)
- 0.5*z[k]*z[k]*safe_atan2(x[i]*y[j], z[k]*r));
res += pow(-1, i + j + k)*kernel;
}
}
}
/* Now all that is left is to multiply res by the gravitational constant and
density */
res *= G*prism.density;
return res;
}
/* Calculates the x component of gravitational attraction cause by a prism. */
double prism_gx(PRISM prism, double xp, double yp, double zp)
{
double x[2], y[2], z[2], kernel, res, r;
register int i, j, k;
/* First thing to do is make P the origin of the coordinate system */
x[0] = prism.x2 - xp;
x[1] = prism.x1 - xp;
y[0] = prism.y2 - yp;
y[1] = prism.y1 - yp;
z[0] = prism.z2 - zp;
z[1] = prism.z1 - zp;
res = 0;
/* Evaluate the integration limits */
for(k=0; k<=1; k++)
{
for(j=0; j<=1; j++)
{
for(i=0; i<=1; i++)
{
r = sqrt(x[i]*x[i] + y[j]*y[j] + z[k]*z[k]);
kernel = -(y[j]*safe_log(z[k] + r) + z[k]*safe_log(y[j] + r)
- x[i]*safe_atan2(z[k]*y[j], x[i]*r));
res += pow(-1, i + j + k)*kernel;
}
}
}
/* Now all that is left is to multiply res by the gravitational constant and
density and convert it to mGal units */
res *= G*SI2MGAL*prism.density;
return res;
}
/* Calculates the y component of gravitational attraction cause by a prism. */
double prism_gy(PRISM prism, double xp, double yp, double zp)
{
double x[2], y[2], z[2], kernel, res, r;
register int i, j, k;
/* First thing to do is make P the origin of the coordinate system */
x[0] = prism.x2 - xp;
x[1] = prism.x1 - xp;
y[0] = prism.y2 - yp;
y[1] = prism.y1 - yp;
z[0] = prism.z2 - zp;
z[1] = prism.z1 - zp;
res = 0;
/* Evaluate the integration limits */
for(k=0; k<=1; k++)
{
for(j=0; j<=1; j++)
{
for(i=0; i<=1; i++)
{
r = sqrt(x[i]*x[i] + y[j]*y[j] + z[k]*z[k]);
kernel = -(z[k]*safe_log(x[i] + r) + x[i]*safe_log(z[k] + r)
- y[j]*safe_atan2(z[k]*x[i], y[j]*r));
res += pow(-1, i + j + k)*kernel;
}
}
}
/* Now all that is left is to multiply res by the gravitational constant and
density and convert it to mGal units */
res *= G*SI2MGAL*prism.density;
return res;
}
/* Calculates the z component of gravitational attraction cause by a prism. */
double prism_gz(PRISM prism, double xp, double yp, double zp)
{
double x[2], y[2], z[2], kernel, res, r;
register int i, j, k;
/* First thing to do is make P the origin of the coordinate system */
x[0] = prism.x2 - xp;
x[1] = prism.x1 - xp;
y[0] = prism.y2 - yp;
y[1] = prism.y1 - yp;
z[0] = prism.z2 - zp;
z[1] = prism.z1 - zp;
res = 0;
/* Evaluate the integration limits */
for(k=0; k<=1; k++)
{
for(j=0; j<=1; j++)
{
for(i=0; i<=1; i++)
{
r = sqrt(x[i]*x[i] + y[j]*y[j] + z[k]*z[k]);
kernel = -(x[i]*safe_log(y[j] + r) + y[j]*safe_log(x[i] + r)
- z[k]*safe_atan2(x[i]*y[j], z[k]*r));
res += pow(-1, i + j + k)*kernel;
}
}
}
/* Now all that is left is to multiply res by the gravitational constant and
density and convert it to mGal units */
res *= G*SI2MGAL*prism.density;
return res;
}
/* Calculates the gxx gravity gradient tensor component cause by a prism. */
double prism_gxx(PRISM prism, double xp, double yp, double zp)
{
double x[2], y[2], z[2], kernel, res, r;
register int i, j, k;
/* First thing to do is make P the origin of the coordinate system */
x[0] = prism.x2 - xp;
x[1] = prism.x1 - xp;
y[0] = prism.y2 - yp;
y[1] = prism.y1 - yp;
z[0] = prism.z2 - zp;
z[1] = prism.z1 - zp;
res = 0;
/* Evaluate the integration limits */
for(k=0; k<=1; k++)
{
for(j=0; j<=1; j++)
{
for(i=0; i<=1; i++)
{
r = sqrt(x[i]*x[i] + y[j]*y[j] + z[k]*z[k]);
kernel = -safe_atan2(z[k]*y[j], x[i]*r);
res += pow(-1, i + j + k)*kernel;
}
}
}
/* Now all that is left is to multiply res by the gravitational constant and
density and convert it to Eotvos units */
res *= G*SI2EOTVOS*prism.density;
return res;
}
/* Calculates the gxy gravity gradient tensor component cause by a prism. */
double prism_gxy(PRISM prism, double xp, double yp, double zp)
{
double x[2], y[2], z[2], kernel, res, r, xtmp, ytmp;
register int i, j, k;
/* First thing to do is make P the origin of the coordinate system */
x[0] = prism.x2 - xp;
x[1] = prism.x1 - xp;
y[0] = prism.y2 - yp;
y[1] = prism.y1 - yp;
z[0] = prism.z2 - zp;
z[1] = prism.z1 - zp;
res = 0;
/* Evaluate the integration limits */
for(k=0; k<=1; k++)
{
for(j=0; j<=1; j++)
{
for(i=0; i<=1; i++)
{
if(x[i] == 0 && y[j] == 0 && z[k] < 0)
{
xtmp = 0.0001*(prism.x2 - prism.x1);
ytmp = 0.0001*(prism.y2 - prism.y1);
r = sqrt(xtmp*xtmp + ytmp*ytmp + z[k]*z[k]);
}
else
{
r = sqrt(x[i]*x[i] + y[j]*y[j] + z[k]*z[k]);
}
kernel = safe_log(z[k] + r);
res += pow(-1, i + j + k)*kernel;
}
}
}
/* Now all that is left is to multiply res by the gravitational constant and
density and convert it to Eotvos units */
res *= G*SI2EOTVOS*prism.density;
return res;
}
/* Calculates the gxz gravity gradient tensor component cause by a prism. */
double prism_gxz(PRISM prism, double xp, double yp, double zp)
{
double x[2], y[2], z[2], kernel, res, r, xtmp, ztmp;
register int i, j, k;
/* First thing to do is make P the origin of the coordinate system */
x[0] = prism.x2 - xp;
x[1] = prism.x1 - xp;
y[0] = prism.y2 - yp;
y[1] = prism.y1 - yp;
z[0] = prism.z2 - zp;
z[1] = prism.z1 - zp;
res = 0;
/* Evaluate the integration limits */
for(k=0; k<=1; k++)
{
for(j=0; j<=1; j++)
{
for(i=0; i<=1; i++)
{
if(x[i] == 0 && z[k] == 0 && y[j] < 0)
{
xtmp = 0.0001*(prism.x2 - prism.x1);
ztmp = 0.0001*(prism.z2 - prism.z1);
r = sqrt(xtmp*xtmp + ztmp*ztmp + y[j]*y[j]);
}
else
{
r = sqrt(x[i]*x[i] + y[j]*y[j] + z[k]*z[k]);
}
kernel = safe_log(y[j] + r);
res += pow(-1, i + j + k)*kernel;
}
}
}
/* Now all that is left is to multiply res by the gravitational constant and
density and convert it to Eotvos units */
res *= G*SI2EOTVOS*prism.density;
return res;
}
/* Calculates the gyy gravity gradient tensor component cause by a prism. */
double prism_gyy(PRISM prism, double xp, double yp, double zp)
{
double x[2], y[2], z[2], kernel, res, r;
register int i, j, k;
/* First thing to do is make P the origin of the coordinate system */
x[0] = prism.x2 - xp;
x[1] = prism.x1 - xp;
y[0] = prism.y2 - yp;
y[1] = prism.y1 - yp;
z[0] = prism.z2 - zp;
z[1] = prism.z1 - zp;
res = 0;
/* Evaluate the integration limits */
for(k=0; k<=1; k++)
{
for(j=0; j<=1; j++)
{
for(i=0; i<=1; i++)
{
r = sqrt(x[i]*x[i] + y[j]*y[j] + z[k]*z[k]);
kernel = -safe_atan2(z[k]*x[i], y[j]*r);
res += pow(-1, i + j + k)*kernel;
}
}
}
/* Now all that is left is to multiply res by the gravitational constant and
density and convert it to Eotvos units */
res *= G*SI2EOTVOS*prism.density;
return res;
}
/* Calculates the gyz gravity gradient tensor component cause by a prism. */
double prism_gyz(PRISM prism, double xp, double yp, double zp)
{
double x[2], y[2], z[2], kernel, res, r, ytmp, ztmp;
register int i, j, k;
/* First thing to do is make P the origin of the coordinate system */
x[0] = prism.x2 - xp;
x[1] = prism.x1 - xp;
y[0] = prism.y2 - yp;
y[1] = prism.y1 - yp;
z[0] = prism.z2 - zp;
z[1] = prism.z1 - zp;
res = 0;
/* Evaluate the integration limits */
for(k=0; k<=1; k++)
{
for(j=0; j<=1; j++)
{
for(i=0; i<=1; i++)
{
if(z[k] == 0 && y[j] == 0 && x[i] < 0)
{
ytmp = 0.0001*(prism.y2 - prism.y1);
ztmp = 0.0001*(prism.z2 - prism.z1);
r = sqrt(ztmp*ztmp + ytmp*ytmp + x[i]*x[i]);
}
else
{
r = sqrt(x[i]*x[i] + y[j]*y[j] + z[k]*z[k]);
}
kernel = safe_log(x[i] + r);
res += pow(-1, i + j + k)*kernel;
}
}
}
/* Now all that is left is to multiply res by the gravitational constant and
density and convert it to Eotvos units */
res *= G*SI2EOTVOS*prism.density;
return res;
}
/* Calculates the gzz gravity gradient tensor component cause by a prism. */
double prism_gzz(PRISM prism, double xp, double yp, double zp)
{
double x[2], y[2], z[2], kernel, res, r;
register int i, j, k;
/* First thing to do is make P the origin of the coordinate system */
x[0] = prism.x2 - xp;
x[1] = prism.x1 - xp;
y[0] = prism.y2 - yp;
y[1] = prism.y1 - yp;
z[0] = prism.z2 - zp;
z[1] = prism.z1 - zp;
res = 0;
/* Evaluate the integration limits */
for(k=0; k<=1; k++)
{
for(j=0; j<=1; j++)
{
for(i=0; i<=1; i++)
{
r = sqrt(x[i]*x[i] + y[j]*y[j] + z[k]*z[k]);
kernel = -safe_atan2(x[i]*y[j], z[k]*r);
res += pow(-1, i + j + k)*kernel;
}
}
}
/* Now all that is left is to multiply res by the gravitational constant and
density and convert it to Eotvos units */
res *= G*SI2EOTVOS*prism.density;
return res;
}

36
lib/grav_prism.h Normal file
View File

@@ -0,0 +1,36 @@
/*
Functions that calculate the gravitational potential and its first and second
derivatives for the rectangular prism using the formulas in Nagy et al. (2000).
The coordinate system used is that of the article, ie:
x -> North y -> East z -> Down
References
----------
* Nagy, D., Papp, G., Benedek, J. (2000): The gravitational potential and its
derivatives for the prism. Journal of Geodesy, 74, 552560.
*/
#ifndef _TESSEROIDS_GRAV_PRISM_H_
#define _TESSEROIDS_GRAV_PRISM_H_
/* Needed for definition of PRISM */
#include "geometry.h"
extern double safe_atan2(double y, double x);
extern double prism_pot(PRISM prism, double xp, double yp, double zp);
extern double prism_gx(PRISM prism, double xp, double yp, double zp);
extern double prism_gy(PRISM prism, double xp, double yp, double zp);
extern double prism_gz(PRISM prism, double xp, double yp, double zp);
extern double prism_gxx(PRISM prism, double xp, double yp, double zp);
extern double prism_gxy(PRISM prism, double xp, double yp, double zp);
extern double prism_gxz(PRISM prism, double xp, double yp, double zp);
extern double prism_gyy(PRISM prism, double xp, double yp, double zp);
extern double prism_gyz(PRISM prism, double xp, double yp, double zp);
extern double prism_gzz(PRISM prism, double xp, double yp, double zp);
#endif

219
lib/grav_prism_sph.c Normal file
View File

@@ -0,0 +1,219 @@
/*
Functions that calculate the gravitational potential and its first and second
derivatives for the rectangular prism in spherical coordinates.
Uses the formulas in Nagy et al. (2000).
References
----------
* Nagy, D., Papp, G., Benedek, J. (2000): The gravitational potential and its
derivatives for the prism. Journal of Geodesy, 74, 552560.
*/
#include <math.h>
#include "geometry.h"
#include "constants.h"
#include "grav_prism_sph.h"
#include "grav_prism.h"
/* Transform spherical coordinates to local Cartesian coordinates of the prism*/
int global2local(double lon, double lat, double r, PRISM prism, double *x,
double *y, double *z)
{
double cosa, cosb, sina, sinb, d2r, X, Y, Z;
/* degrees to radians */
d2r = PI/180.;
X = r*cos(d2r*lat)*cos(d2r*lon) -
prism.r*cos(d2r*prism.lat)*cos(d2r*prism.lon);
Y = r*cos(d2r*lat)*sin(d2r*lon) -
prism.r*cos(d2r*prism.lat)*sin(d2r*prism.lon);
Z = r*sin(d2r*lat) - prism.r*sin(d2r*prism.lat);
cosa = cos(d2r*(90 - prism.lat));
sina = sin(d2r*(90 - prism.lat));
cosb = cos(d2r*(180 - prism.lon));
sinb = sin(d2r*(180 - prism.lon));
*x = X*cosa*cosb - Y*cosa*sinb + Z*sina;
*y = -X*sinb - Y*cosb;
/* -1 because Nagy et al. (2000) use z->down */
*z = -1*(-X*sina*cosb + Y*sina*sinb + Z*cosa);
return 0;
}
/* Rotate the gravity vector from the prisms coordinate system to the local
system of the computation point. */
int g_prism2point(double *atprism, PRISM prism, double lon, double lat,
double r, double *atpoint)
{
#define POS(x, y, cols) (((x)*(cols))+(y))
register int i, k;
double R[9], d2r, cosbeta, sinbeta, cosphi, sinphi, cosphil, sinphil;
/* degrees to radians */
d2r = PI/180.;
cosbeta = cos(d2r*(prism.lon - lon));
sinbeta = sin(d2r*(prism.lon - lon));
cosphi = cos(d2r*lat);
sinphi = sin(d2r*lat);
cosphil = cos(d2r*prism.lat);
sinphil = sin(d2r*prism.lat);
/* The transformation matrix */
R[0] = cosbeta*sinphi*sinphil + cosphi*cosphil;
R[1] = sinbeta*sinphi;
R[2] = -cosbeta*sinphi*cosphil + cosphi*sinphil;
R[3] = -sinbeta*sinphil;
R[4] = cosbeta;
R[5] = sinbeta*cosphil;
R[6] = -cosbeta*cosphi*sinphil + sinphi*cosphil;
R[7] = -sinbeta*cosphi;
R[8] = cosbeta*cosphi*cosphil + sinphi*sinphil;
/* Matrix-vector multiplication */
for(i = 0; i < 3; i++)
{
atpoint[i] = 0;
for(k = 0; k < 3; k++)
{
atpoint[i] += R[POS(i, k, 3)]*atprism[k];
}
}
#undef POS
return 0;
}
/* Rotate the gravity tensor from the prisms coordinate system to the local
system of the computation point. */
int ggt_prism2point(double *atprism, PRISM prism, double lon, double lat,
double r, double *atpoint)
{
#define POS(x, y, cols) (((x)*(cols))+(y))
register int i, j, k;
double R[9], tmp[9], d2r, cosbeta, sinbeta, cosphi, sinphi, cosphil, sinphil;
/* degrees to radians */
d2r = PI/180.;
cosbeta = cos(d2r*(prism.lon - lon));
sinbeta = sin(d2r*(prism.lon - lon));
cosphi = cos(d2r*lat);
sinphi = sin(d2r*lat);
cosphil = cos(d2r*prism.lat);
sinphil = sin(d2r*prism.lat);
/* The transformation matrix */
R[0] = cosbeta*sinphi*sinphil + cosphi*cosphil;
R[1] = sinbeta*sinphi;
R[2] = -cosbeta*sinphi*cosphil + cosphi*sinphil;
R[3] = -sinbeta*sinphil;
R[4] = cosbeta;
R[5] = sinbeta*cosphil;
R[6] = -cosbeta*cosphi*sinphil + sinphi*cosphil;
R[7] = -sinbeta*cosphi;
R[8] = cosbeta*cosphi*cosphil + sinphi*sinphil;
/* Multiply tmp = R*Tensor */
for(i = 0; i < 3; i++)
{
for(j = 0; j < 3; j++)
{
tmp[POS(i, j, 3)] = 0;
for(k = 0; k < 3; k++)
{
tmp[POS(i, j, 3)] += R[POS(i, k, 3)]*atprism[POS(k, j, 3)];
}
}
}
/* Multiply tmp*R^T */
for(i = 0; i < 3; i++)
{
for(j = 0; j < 3; j++)
{
atpoint[POS(i, j, 3)] = 0;
for(k = 0; k < 3; k++)
{
atpoint[POS(i, j, 3)] += tmp[POS(i, k, 3)]*R[POS(j, k, 3)];
}
}
}
#undef POS
return 0;
}
/* Calculates the gravity gradient tensor caused by a prism. */
int prism_ggt_sph(PRISM prism, double lonp, double latp, double rp, double *ggt)
{
double x = 0, y = 0, z = 0, ggtprism[9], ggtpoint[9];
global2local(lonp, latp, rp, prism, &x, &y, &z);
ggtprism[0] = prism_gxx(prism, x, y, z);
ggtprism[1] = prism_gxy(prism, x, y, z);
/* -1 because the prisms z is Down, but transformation assumes z is Up */
/* z -> Up is the system of the tesseroid */
ggtprism[2] = -1*prism_gxz(prism, x, y, z);
ggtprism[3] = ggtprism[1];
ggtprism[4] = prism_gyy(prism, x, y, z);
/* Same as xz */
ggtprism[5] = -1*prism_gyz(prism, x, y, z);
ggtprism[6] = ggtprism[2];
ggtprism[7] = ggtprism[5];
ggtprism[8] = -(ggtprism[0] + ggtprism[4]);
ggt_prism2point(ggtprism, prism, lonp, latp, rp, ggtpoint);
ggt[0] = ggtpoint[0];
ggt[1] = ggtpoint[1];
ggt[2] = ggtpoint[2];
ggt[3] = ggtpoint[4];
ggt[4] = ggtpoint[5];
ggt[5] = ggtpoint[8];
return 0;
}
/* Calculates the gravitational attraction caused by a prism. */
int prism_g_sph(PRISM prism, double lonp, double latp, double rp, double *gx,
double *gy, double *gz)
{
double x = 0, y = 0, z = 0, gprism[3], gpoint[3];
global2local(lonp, latp, rp, prism, &x, &y, &z);
gprism[0] = prism_gx(prism, x, y, z);
gprism[1] = prism_gy(prism, x, y, z);
/* Nagy wants z down, but the transformation assumes z up */
gprism[2] = -prism_gz(prism, x, y, z);
g_prism2point(gprism, prism, lonp, latp, rp, gpoint);
*gx = gpoint[0];
*gy = gpoint[1];
/* Put z back down again to maintain the normal convention for gz */
*gz = -gpoint[2];
return 0;
}
/* Calculates the potential caused by a prism. */
double prism_pot_sph(PRISM prism, double lonp, double latp, double rp)
{
double x = 0, y = 0, z = 0, res;
global2local(lonp, latp, rp, prism, &x, &y, &z);
res = prism_pot(prism, x, y, z);
return res;
}

100
lib/grav_prism_sph.h Normal file
View File

@@ -0,0 +1,100 @@
/*
Functions that calculate the gravitational potential and its first and second
derivatives for the rectangular prism in spherical coordinates.
Uses the formulas in Nagy et al. (2000).
References
----------
* Nagy, D., Papp, G., Benedek, J. (2000): The gravitational potential and its
derivatives for the prism. Journal of Geodesy, 74, 552560.
*/
#ifndef _TESSEROIDS_GRAV_PRISM_SPH_H_
#define _TESSEROIDS_GRAV_PRISM_SPH_H_
/* Needed for definition of PRISM */
#include "geometry.h"
/* Transform spherical coordinates to local Cartesian coordinates of the prism
Parameters:
* lon, lat, r: spherical coordinates of the point.
* prism: a prism whose lon, lat, r values will be used as the origin of the
local coordinate system.
* x, y, z: used to return the x, y, z Cartesian coordinates of the point.
*/
extern int global2local(double lon, double lat, double r, PRISM prism,
double *x, double *y, double *z);
/* Rotate the g vector from the prisms coordinate system to the local
system of the computation point.
Parameters:
* atprism: the 3 component gravity vector in the coordinates of the prism.
* prism: the prism used to calculate atprism.
* lon, lat, r: coordinates of the computation point.
* atpoint: used to return the 3 component gravity vector in the coordinates of
the computation point.
*/
extern int g_prism2point(double *atprism, PRISM prism, double lon, double lat,
double r, double *atpoint);
/* Rotate the g vector from the prisms coordinate system to the local
system of the computation point.
Parameters:
* atprism: the 9 component gravity tensor in the coordinates of the prism.
The order is: gxx, gxy, gxz, gyx, gyy, gyz, gzx, gzy, gzz
* prism: the prism used to calculate atprism.
* lon, lat, r: coordinates of the computation point.
* atpoint: used to return the 9 component gravity tensor in the coordinates of
the computation point.
*/
extern int ggt_prism2point(double *atprism, PRISM prism, double lon, double lat,
double r, double *atpoint);
/* Calculates the gravity gradient tensor caused by a prism.
Parameters:
* prism: the prism whose effect will be calculated.
* lonp, latp, rp: coordinates of the computation point.
* ggt: 6 element array used to return the gradient tensor. The order is:
gxx, gxy, gxz, gyy, gyz, gzz
*/
extern int prism_ggt_sph(PRISM prism, double lonp, double latp, double rp,
double *ggt);
/* Calculates the gravitational attraction caused by a prism.
Parameters:
* prism: the prism whose effect will be calculated.
* lonp, latp, rp: coordinates of the computation point.
* gx, gy, gz: used to return the 3 components of the gravity vector
*/
extern int prism_g_sph(PRISM prism, double lonp, double latp, double rp,
double *gx, double *gy, double *gz);
/* Calculates the potential caused by a prism.
Parameters:
* prism: the prism whose effect will be calculated.
* lonp, latp, rp: coordinates of the computation point.
Returns:
* the calculated potential
*/
extern double prism_pot_sph(PRISM prism, double lonp, double latp, double rp);
#endif

252
lib/grav_sphere.c Normal file
View File

@@ -0,0 +1,252 @@
/*
This module contains a set of functions that calculate the gravitational
potential and its first and second derivatives for the sphere in spherical
coordinates.
The position of the sphere and computation point are in spherical coordinates.
The derivatives of the potential are made with respect to the local coordinate
system x->North, y->East, z->out. So it would be normal for a sphere of positive
density to have negative gz
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 "geometry.h"
#include "constants.h"
#include "grav_sphere.h"
/* Calculates the potential caused by a sphere */
double sphere_pot(SPHERE sphere, double lonp, double latp, double rp)
{
double mass, l_sqr, d2r = PI/180., coslatp, coslatc, sinlatp, sinlatc,
coslon;
mass = (double)(sphere.density*4.*PI*sphere.r*sphere.r*sphere.r)/3.;
coslatp = cos(d2r*latp);
coslatc = cos(d2r*sphere.latc);
sinlatp = sin(d2r*latp);
sinlatc = sin(d2r*sphere.latc);
coslon = cos(d2r*(lonp - sphere.lonc));
l_sqr = rp*rp + sphere.rc*sphere.rc - 2*rp*sphere.rc*(
sinlatp*sinlatc + coslatp*coslatc*coslon);
return G*mass/sqrt(l_sqr);
}
/* Calculates the gx component of gravitational attraction caused by a sphere */
double sphere_gx(SPHERE sphere, double lonp, double latp, double rp)
{
double mass, l_sqr, d2r = PI/180., kphi, coslatp, coslatc, sinlatp, sinlatc,
coslon;
mass = (double)(sphere.density*4.*PI*sphere.r*sphere.r*sphere.r)/3.;
coslatp = cos(d2r*latp);
coslatc = cos(d2r*sphere.latc);
sinlatp = sin(d2r*latp);
sinlatc = sin(d2r*sphere.latc);
coslon = cos(d2r*(lonp - sphere.lonc));
l_sqr = rp*rp + sphere.rc*sphere.rc - 2*rp*sphere.rc*(
sinlatp*sinlatc + coslatp*coslatc*coslon);
kphi = coslatp*sinlatc - sinlatp*coslatc*coslon;
return G*SI2MGAL*mass*(sphere.rc*kphi)/pow(l_sqr, 1.5);
}
/* Calculates the gy component of gravitational attraction caused by a sphere */
double sphere_gy(SPHERE sphere, double lonp, double latp, double rp)
{
double mass, l_sqr, d2r = PI/180., cospsi, coslatc, kern;
mass = (double)(sphere.density*4.*PI*sphere.r*sphere.r*sphere.r)/3.;
coslatc = cos(d2r*sphere.latc);
cospsi = sin(d2r*latp)*sin(d2r*sphere.latc) + cos(d2r*latp)*coslatc*
cos(d2r*(lonp - sphere.lonc));
l_sqr = rp*rp + sphere.rc*sphere.rc - 2*rp*sphere.rc*cospsi;
kern = (sphere.rc*coslatc*sin(d2r*(sphere.lonc - lonp)))/pow(l_sqr, 1.5);
return G*SI2MGAL*mass*kern;
}
/* Calculates the gz component of gravitational attraction caused by a sphere */
double sphere_gz(SPHERE sphere, double lonp, double latp, double rp)
{
double mass, l_sqr, d2r = PI/180., cospsi;
mass = (double)(sphere.density*4.*PI*sphere.r*sphere.r*sphere.r)/3.;
cospsi = sin(d2r*latp)*sin(d2r*sphere.latc) + cos(d2r*latp)*
cos(d2r*sphere.latc)*cos(d2r*(lonp - sphere.lonc));
l_sqr = rp*rp + sphere.rc*sphere.rc - 2*rp*sphere.rc*cospsi;
return G*SI2MGAL*mass*(sphere.rc*cospsi - rp)/pow(l_sqr, 1.5);
}
/* Calculate the xx component of gravity gradient tensor cause by a sphere */
double sphere_gxx(SPHERE sphere, double lonp, double latp, double rp)
{
double mass, l_sqr, d2r = PI/180., kphi, coslatp, coslatc, sinlatp, sinlatc,
coslon, kern;
mass = (double)(sphere.density*4.*PI*sphere.r*sphere.r*sphere.r)/3.;
coslatp = cos(d2r*latp);
coslatc = cos(d2r*sphere.latc);
sinlatp = sin(d2r*latp);
sinlatc = sin(d2r*sphere.latc);
coslon = cos(d2r*(lonp - sphere.lonc));
l_sqr = rp*rp + sphere.rc*sphere.rc - 2*rp*sphere.rc*(sinlatp*sinlatc +
coslatp*coslatc*coslon);
kphi = coslatp*sinlatc - sinlatp*coslatc*coslon;
kern = (3*sphere.rc*kphi*sphere.rc*kphi - l_sqr)/pow(l_sqr, 2.5);
return G*SI2EOTVOS*mass*kern;
}
/* Calculate the xy component of gravity gradient tensor cause by a sphere */
double sphere_gxy(SPHERE sphere, double lonp, double latp, double rp)
{
double mass, l_sqr, d2r = PI/180., kphi, coslatp, coslatc, sinlatp, sinlatc,
coslon, kern;
mass = (double)(sphere.density*4.*PI*sphere.r*sphere.r*sphere.r)/3.;
coslatp = cos(d2r*latp);
coslatc = cos(d2r*sphere.latc);
sinlatp = sin(d2r*latp);
sinlatc = sin(d2r*sphere.latc);
coslon = cos(d2r*(lonp - sphere.lonc));
l_sqr = rp*rp + sphere.rc*sphere.rc - 2*rp*sphere.rc*(sinlatp*sinlatc +
coslatp*coslatc*coslon);
kphi = coslatp*sinlatc - sinlatp*coslatc*coslon;
kern = (3*sphere.rc*sphere.rc*kphi*coslatp*sin(d2r*(sphere.lonc - lonp)))/
pow(l_sqr, 2.5);
return G*SI2EOTVOS*mass*kern;
}
/* Calculate the xz component of gravity gradient tensor cause by a sphere */
double sphere_gxz(SPHERE sphere, double lonp, double latp, double rp)
{
double mass, l_sqr, d2r = PI/180., kphi, coslatp, coslatc, sinlatp, sinlatc,
coslon, kern, cospsi;
mass = (double)(sphere.density*4.*PI*sphere.r*sphere.r*sphere.r)/3.;
coslatp = cos(d2r*latp);
coslatc = cos(d2r*sphere.latc);
sinlatp = sin(d2r*latp);
sinlatc = sin(d2r*sphere.latc);
coslon = cos(d2r*(lonp - sphere.lonc));
cospsi = sinlatp*sinlatc + coslatp*coslatc*coslon;
l_sqr = rp*rp + sphere.rc*sphere.rc - 2*rp*sphere.rc*cospsi;
kphi = coslatp*sinlatc - sinlatp*coslatc*coslon;
kern = 3*sphere.rc*kphi*(sphere.rc*cospsi - rp)/pow(l_sqr, 2.5);
return G*SI2EOTVOS*mass*kern;
}
/* Calculate the yy component of gravity gradient tensor cause by a sphere */
double sphere_gyy(SPHERE sphere, double lonp, double latp, double rp)
{
double mass, l_sqr, d2r = PI/180., coslatp, coslatc, sinlatp, sinlatc,
coslon, sinlon, kern, cospsi;
mass = (double)(sphere.density*4.*PI*sphere.r*sphere.r*sphere.r)/3.;
coslatp = cos(d2r*latp);
coslatc = cos(d2r*sphere.latc);
sinlatp = sin(d2r*latp);
sinlatc = sin(d2r*sphere.latc);
coslon = cos(d2r*(lonp - sphere.lonc));
sinlon = sin(d2r*(sphere.lonc - lonp));
cospsi = sinlatp*sinlatc + coslatp*coslatc*coslon;
l_sqr = rp*rp + sphere.rc*sphere.rc - 2*rp*sphere.rc*cospsi;
kern = (3*sphere.rc*sphere.rc*coslatc*coslatc*sinlon*sinlon - l_sqr)/
pow(l_sqr, 2.5);
return G*SI2EOTVOS*mass*kern;
}
/* Calculate the yz component of gravity gradient tensor cause by a sphere */
double sphere_gyz(SPHERE sphere, double lonp, double latp, double rp)
{
double mass, l_sqr, d2r = PI/180., coslatp, coslatc, sinlatp, sinlatc,
coslon, sinlon, kern, cospsi;
mass = (double)(sphere.density*4.*PI*sphere.r*sphere.r*sphere.r)/3.;
coslatp = cos(d2r*latp);
coslatc = cos(d2r*sphere.latc);
sinlatp = sin(d2r*latp);
sinlatc = sin(d2r*sphere.latc);
coslon = cos(d2r*(lonp - sphere.lonc));
sinlon = sin(d2r*(sphere.lonc - lonp));
cospsi = sinlatp*sinlatc + coslatp*coslatc*coslon;
l_sqr = rp*rp + sphere.rc*sphere.rc - 2*rp*sphere.rc*cospsi;
kern = 3*sphere.rc*coslatc*sinlon*(sphere.rc*cospsi - rp)/pow(l_sqr, 2.5);
return G*SI2EOTVOS*mass*kern;
}
/* Calculate the zz component of gravity gradient tensor cause by a sphere */
double sphere_gzz(SPHERE sphere, double lonp, double latp, double rp)
{
double mass, l_sqr, d2r = PI/180., deltaz, cospsi;
mass = (double)(sphere.density*4.*PI*sphere.r*sphere.r*sphere.r)/3.;
cospsi = sin(d2r*latp)*sin(d2r*sphere.latc) + cos(d2r*latp)*
cos(d2r*sphere.latc)*cos(d2r*(lonp - sphere.lonc));
l_sqr = rp*rp + sphere.rc*sphere.rc - 2*rp*sphere.rc*cospsi;
deltaz = sphere.rc*cospsi - rp;
return G*SI2EOTVOS*mass*(3*deltaz*deltaz - l_sqr)/pow(l_sqr, 2.5);
}

267
lib/grav_sphere.h Normal file
View File

@@ -0,0 +1,267 @@
/*
Functions that calculate the gravitational potential and its first and second
derivatives for the sphere in spherical coordinates.
The position of the sphere and computation point are in spherical coordinates.
The derivatives of the potential are made with respect to the local coordinate
system x->North, y->East, z->out. So it would be normal for a sphere of
positive density to have negative gz.
Used the generic formula for gravity gradient computation of tesseroids by
Grombein et al. (2010).
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.
*/
#ifndef _TESSEROIDS_GRAV_SPHERE_H_
#define _TESSEROIDS_GRAV_SPHERE_H_
/* Needed for definition of SPHERE */
#include "geometry.h"
/** Calculates potential caused by a sphere.
\f[
V(r_p,\phi_p,\lambda_p) = \frac{G M}{\ell}
\f]
The position of the sphere and computation point should be in spherical
coordinates.
<b>Input and output values in SI units and degrees</b>
@param sphere data structure describing the sphere
@param lonp longitude of the computation point P
@param latp latitude of the computation point P
@param rp radial coordinate of the computation point P
@return field calculated at P
*/
extern double sphere_pot(SPHERE sphere, double lonp, double latp, double rp);
/** Calculates gx caused by a sphere (Grombein et al., 2010).
\f[
g_x(r_p,\phi_p,\lambda_p) = G M \frac{r_c K_{\phi}}{\ell^3}
\f]
The position of the sphere and computation point should be in spherical
coordinates.
The derivatives of the potential are made with respect to the local coordinate
system <b>x->North, y->East, z->out</b>
<b>Input values in SI units and <b>degrees</b> and returns values in mGal!</b>
@param sphere data structure describing the sphere
@param lonp longitude of the computation point P
@param latp latitude of the computation point P
@param rp radial coordinate of the computation point P
@return field calculated at P
*/
extern double sphere_gx(SPHERE sphere, double lonp, double latp, double rp);
/** Calculates gy caused by a sphere (Grombein et al., 2010).
\f[
g_y(r_p,\phi_p,\lambda_p) = G M \frac{r_c\cos\phi_c\sin(\phi_c-\phi_p)}{\ell^3}
\f]
The position of the sphere and computation point should be in spherical
coordinates.
The derivatives of the potential are made with respect to the local coordinate
system <b>x->North, y->East, z->out</b>
<b>Input values in SI units and <b>degrees</b> and returns values in mGal!</b>
@param sphere data structure describing the sphere
@param lonp longitude of the computation point P
@param latp latitude of the computation point P
@param rp radial coordinate of the computation point P
@return field calculated at P
*/
extern double sphere_gy(SPHERE sphere, double lonp, double latp, double rp);
/** Calculates gz caused by a sphere (Grombein et al., 2010).
\f[
g_z(r_p,\phi_p,\lambda_p) = G M \frac{r_c\cos\psi - r_p}{\ell^3}
\f]
The position of the sphere and computation point should be in spherical
coordinates.
The derivatives of the potential are made with respect to the local coordinate
system <b>x->North, y->East, z->out</b>
<b>Input values in SI units and <b>degrees</b> and returns values in mGal!</b>
@param sphere data structure describing the sphere
@param lonp longitude of the computation point P
@param latp latitude of the computation point P
@param rp radial coordinate of the computation point P
@return field calculated at P
*/
extern double sphere_gz(SPHERE sphere, double lonp, double latp, double rp);
/** Calculates gxx caused by a sphere (Grombein et al., 2010).
\f[
g_{xx}(r_p,\phi_p,\lambda_p) = G M \frac{3(r_c K_{\phi})^2 - \ell^2}{\ell^5}
\f]
The position of the sphere and computation point are in spherical coordinates.
The derivatives of the potential are made with respect to the local coordinate
system <b>x->North, y->East, z->out</b>
<b>Input values in SI units and <b>degrees</b> and returns values in Eotvos!</b>
@param sphere data structure describing the sphere
@param lonp longitude of the computation point P
@param latp latitude of the computation point P
@param rp radial coordinate of the computation point P
@return field calculated at P
*/
extern double sphere_gxx(SPHERE sphere, double lonp, double latp, double rp);
/** Calculates gxy caused by a sphere (Grombein et al., 2010).
\f[
g_{xy}(r_p,\phi_p,\lambda_p) = G M \frac{3r_c^2 K_{\phi}\cos\phi_c
\sin(\lambda_c - \lambda_p)}{\ell^5}
\f]
The position of the sphere and computation point are in spherical coordinates.
The derivatives of the potential are made with respect to the local coordinate
system <b>x->North, y->East, z->out</b>
<b>Input values in SI units and <b>degrees</b> and returns values in Eotvos!</b>
@param sphere data structure describing the sphere
@param lonp longitude of the computation point P
@param latp latitude of the computation point P
@param rp radial coordinate of the computation point P
@return field calculated at P
*/
extern double sphere_gxy(SPHERE sphere, double lonp, double latp, double rp);
/** Calculates gxz caused by a sphere (Grombein et al., 2010).
\f[
g_{xz}(r_p,\phi_p,\lambda_p) = G M \frac{3 r_c K_{\phi}(r_c \cos\psi - r_p)}
{\ell^5}
\f]
The position of the sphere and computation point are in spherical coordinates.
The derivatives of the potential are made with respect to the local coordinate
system <b>x->North, y->East, z->out</b>
<b>Input values in SI units and <b>degrees</b> and returns values in Eotvos!</b>
@param sphere data structure describing the sphere
@param lonp longitude of the computation point P
@param latp latitude of the computation point P
@param rp radial coordinate of the computation point P
@return field calculated at P
*/
extern double sphere_gxz(SPHERE sphere, double lonp, double latp, double rp);
/** Calculates gyy caused by a sphere (Grombein et al., 2010).
\f[
g_{yy}(r_p,\phi_p,\lambda_p) = G M \frac{3(r_c\cos\phi_c
\sin(\lambda_c - \lambda_p))^2 - \ell^2}{\ell^5}
\f]
The position of the sphere and computation point are in spherical coordinates.
The derivatives of the potential are made with respect to the local coordinate
system <b>x->North, y->East, z->out</b>
<b>Input values in SI units and <b>degrees</b> and returns values in Eotvos!</b>
@param sphere data structure describing the sphere
@param lonp longitude of the computation point P
@param latp latitude of the computation point P
@param rp radial coordinate of the computation point P
@return field calculated at P
*/
extern double sphere_gyy(SPHERE sphere, double lonp, double latp, double rp);
/** Calculates gyz caused by a sphere (Grombein et al., 2010).
\f[
g_{yz}(r_p,\phi_p,\lambda_p) = G M \frac{3 r_c \cos\phi_c \sin(\lambda_c -
\lambda_p)(r_c\cos\psi - r_p)}{\ell^5}
\f]
The position of the sphere and computation point are in spherical coordinates.
The derivatives of the potential are made with respect to the local coordinate
system <b>x->North, y->East, z->out</b>
<b>Input values in SI units and <b>degrees</b> and returns values in Eotvos!</b>
@param sphere data structure describing the sphere
@param lonp longitude of the computation point P
@param latp latitude of the computation point P
@param rp radial coordinate of the computation point P
@return field calculated at P
*/
extern double sphere_gyz(SPHERE sphere, double lonp, double latp, double rp);
/** Calculates gzz caused by a sphere (Grombein et al., 2010).
\f[
g_{zz}(r_p,\phi_p,\lambda_p) = G M \frac{3(r_c\cos\psi-r_p)^2 - \ell^2}{\ell^5}
\f]
The position of the sphere and computation point are in spherical coordinates.
The derivatives of the potential are made with respect to the local coordinate
system <b>x->North, y->East, z->out</b>
<b>Input values in SI units and <b>degrees</b> and returns values in Eotvos!</b>
@param sphere data structure describing the sphere
@param lonp longitude of the computation point P
@param latp latitude of the computation point P
@param rp radial coordinate of the computation point P
@return field calculated at P
*/
extern double sphere_gzz(SPHERE sphere, double lonp, double latp, double rp);
#endif

580
lib/grav_tess.c Normal file
View File

@@ -0,0 +1,580 @@
/*
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;
}

501
lib/grav_tess.h Normal file
View File

@@ -0,0 +1,501 @@
/*
Functions that calculate the gravitational potential and its first and second
derivatives for the tesseroid.
The gravity gradients can be calculated using the general formula of
Grombein et al. (2010).
The integrals are solved using the Gauss-Legendre Quadrature rule
(Asgharzadeh et al., 2007).
The derivatives of the potential are made with respect to the local coordinate
system x->North, y->East, z->Up (away from center of the Earth).
To maintain the standard convention, only for component gz the z axis is
inverted, so a positive density results in positive gz.
Example
-------
To calculate the gzz component due to a tesseroid on a regular grid:
#include <stdio.h>
#include "glq.h"r
#include "constants.h"
#include "grav_tess.h"
int main()
{
TESSEROID tess = {1000, 44, 46, -1, 1, MEAN_EARTH_RADIUS - 100000,
MEAN_EARTH_RADIUS};
GLQ *glqlon, *glqlat, *glqr;
double lon, lat, r = MEAN_EARTH_RADIUS + 1500000, res;
int order = 8;
glqlon = glq_new(order, tess.w, tess.e);
glqlat = glq_new(order, tess.s, tess.n);
glqr = glq_new(order, tess.r1, tess.r2);
for(lat = 20; lat <= 70; lat += 0.5)
{
for(lon = -25; lon <= 25; lon += 0.5)
{
res = tess_gzz(tess, lon, lat, r, *glqlon, *glqlat, *glqr);
printf("%g %g %g\n", lon, lat, res);
}
}
glq_free(glqlon);
glq_free(glqlat);
glq_free(glqr);
return 0;
}
References
----------
Asgharzadeh, M.F., von Frese, R.R.B., Kim, H.R., Leftwich, T.E. & Kim, J.W.
(2007): Spherical prism gravity effects by Gauss-Legendre quadrature integration.
Geophysical Journal International, 169, 1-11.
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.
*/
#ifndef _TESSEROIDS_GRAV_TESS_H_
#define _TESSEROIDS_GRAV_TESS_H_
/* Needed for definition of TESSEROID */
#include "geometry.h"
/* Needed for definition of GLQ */
#include "glq.h"
/** Calculates the field of a tesseroid model at a given point.
Uses a function pointer to call one of the apropriate field calculating
functions:
- tess_gx()
- tess_gy()
- tess_gz()
- tess_gxx()
- tess_gxy()
- tess_gxz()
- tess_gyy()
- tess_gyz()
- tess_gzz()
To pass a function pointer to a function use something like:
\verbatim
calc_tess_model(my_model, 10, 0, 10, 1, glqlon, glqlat, glqr, &tess_gx);
\endverbatim
This would calculate the gx effect of the model my_model with 10 tesseroids
at lon=0 lat=10 r=1.
Will re-use the same GLQ structures, and therefore the <b>same order, for all
the tesseroids</b>.
@param model TESSEROID array defining the model
@param size number of tesseroids in the model
@param lonp longitude of the computation point P
@param latp latitude of the computation point P
@param rp radial coordinate of the computation point P
@param glq_lon pointer to GLQ structure used for the longitudinal integration
@param glq_lat pointer to GLQ structure used for the latitudinal integration
@param glq_r pointer to GLQ structure used for the radial integration
@param field pointer to one of the field calculating functions
@return the sum of the fields of all the tesseroids in the model
*/
extern 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));
/** Adaptatively calculate the field of a tesseroid model at a given point by
splitting the tesseroids if necessary to maintain GLQ stability.
See calc_tess_model() for more details.
Will re-use the same GLQ structures, and therefore the <b>same order, for all
the tesseroids</b>.
@param model TESSEROID array defining the model
@param size number of tesseroids in the model
@param lonp longitude of the computation point P
@param latp latitude of the computation point P
@param rp radial coordinate of the computation point P
@param glq_lon pointer to GLQ structure used for the longitudinal integration
@param glq_lat pointer to GLQ structure used for the latitudinal integration
@param glq_r pointer to GLQ structure used for the radial integration
@param field pointer to one of the field calculating functions
@param ratio distance-to-size ratio for doing adaptative resizing
@return the sum of the fields of all the tesseroids in the model
*/
extern 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);
/** Calculates potential caused by a tesseroid.
\f[
V(r_p,\phi_p,\lambda_p) = G \rho \displaystyle\int_{\lambda_1}^{\lambda_2}
\displaystyle\int_{\phi_1}^{\phi_2} \displaystyle\int_{r_1}^{r_2}
\frac{1}{\ell}\kappa \ d r' d \phi' d \lambda'
\f]
<b>Input and output values in SI units and degrees</b>!
Use function glq_new() to create the GLQ parameters required. The integration
limits should be set to:
- glq_lon: lower = tess.w and upper = tess.e (in degrees)
- glq_lat: lower = tess.s and upper = tess.n (in degrees)
- glq_r: lower = tess.r1 and upper = tess.r2
@param tess data structure describing the tesseroid
@param lonp longitude of the computation point P
@param latp latitude of the computation point P
@param rp radial coordinate of the computation point P
@param glq_lon GLQ structure with the nodes, weights and integration limits set
for the longitudinal integration
@param glq_lat GLQ structure with the nodes, weights and integration limits set
for the latitudinal integration
@param glq_r GLQ structure with the nodes, weights and integration limits set
for the radial integration
@return field calculated at P
*/
extern double tess_pot(TESSEROID tess, double lonp, double latp, double rp,
GLQ glq_lon, GLQ glq_lat, GLQ glq_r);
/** Calculates gx caused by a tesseroid (Grombein et al., 2010).
\f[
g_x(r_p,\phi_p,\lambda_p) = G \rho \displaystyle\int_{\lambda_1}^{\lambda_2}
\displaystyle\int_{\phi_1}^{\phi_2} \displaystyle\int_{r_1}^{r_2}
\frac{r'K_{\phi}}{\ell^3}\kappa \ d r' d \phi' d \lambda'
\f]
The derivatives of the potential are made with respect to the local coordinate
system <b>x->North, y->East, z->out</b>
<b>Input values in SI units and <b>degrees</b> and returns values in mGal!</b>
Use function glq_new() to create the GLQ parameters required. The integration
limits should be set to:
- glq_lon: lower = tess.w and upper = tess.e (in degrees)
- glq_lat: lower = tess.s and upper = tess.n (in degrees)
- glq_r: lower = tess.r1 and upper = tess.r2
@param tess data structure describing the tesseroid
@param lonp longitude of the computation point P
@param latp latitude of the computation point P
@param rp radial coordinate of the computation point P
@param glq_lon GLQ structure with the nodes, weights and integration limits set
for the longitudinal integration
@param glq_lat GLQ structure with the nodes, weights and integration limits set
for the latitudinal integration
@param glq_r GLQ structure with the nodes, weights and integration limits set
for the radial integration
@return field calculated at P
*/
extern double tess_gx(TESSEROID tess, double lonp, double latp, double rp,
GLQ glq_lon, GLQ glq_lat, GLQ glq_r);
/** Calculates gy caused by a tesseroid (Grombein et al., 2010).
\f[
g_y(r_p,\phi_p,\lambda_p) = G \rho \displaystyle\int_{\lambda_1}^{\lambda_2}
\displaystyle\int_{\phi_1}^{\phi_2} \displaystyle\int_{r_1}^{r_2}
\frac{r'\cos\phi'\sin(\lambda'-\lambda)}{\ell^3}\kappa
\ d r' d \phi' d \lambda'
\f]
The derivatives of the potential are made with respect to the local coordinate
system <b>x->North, y->East, z->out</b>
<b>Input values in SI units and <b>degrees</b> and returns values in mGal!</b>
Use function glq_new() to create the GLQ parameters required. The integration
limits should be set to:
- glq_lon: lower = tess.w and upper = tess.e (in degrees)
- glq_lat: lower = tess.s and upper = tess.n (in degrees)
- glq_r: lower = tess.r1 and upper = tess.r2
@param tess data structure describing the tesseroid
@param lonp longitude of the computation point P
@param latp latitude of the computation point P
@param rp radial coordinate of the computation point P
@param glq_lon GLQ structure with the nodes, weights and integration limits set
for the longitudinal integration
@param glq_lat GLQ structure with the nodes, weights and integration limits set
for the latitudinal integration
@param glq_r GLQ structure with the nodes, weights and integration limits set
for the radial integration
@return field calculated at P
*/
extern double tess_gy(TESSEROID tess, double lonp, double latp, double rp,
GLQ glq_lon, GLQ glq_lat, GLQ glq_r);
/** Calculates gz caused by a tesseroid (Grombein et al., 2010).
\f[
g_z(r_p,\phi_p,\lambda_p) = G \rho \displaystyle\int_{\lambda_1}^{\lambda_2}
\displaystyle\int_{\phi_1}^{\phi_2} \displaystyle\int_{r_1}^{r_2}
\frac{r'\cos\psi - r_p}{\ell^3}\kappa \ d r' d \phi' d \lambda'
\f]
The derivatives of the potential are made with respect to the local coordinate
system <b>x->North, y->East, z->out</b>
<b>Input values in SI units and <b>degrees</b> and returns values in mGal!</b>
Use function glq_new() to create the GLQ parameters required. The integration
limits should be set to:
- glq_lon: lower = tess.w and upper = tess.e (in degrees)
- glq_lat: lower = tess.s and upper = tess.n (in degrees)
- glq_r: lower = tess.r1 and upper = tess.r2
@param tess data structure describing the tesseroid
@param lonp longitude of the computation point P
@param latp latitude of the computation point P
@param rp radial coordinate of the computation point P
@param glq_lon GLQ structure with the nodes, weights and integration limits set
for the longitudinal integration
@param glq_lat GLQ structure with the nodes, weights and integration limits set
for the latitudinal integration
@param glq_r GLQ structure with the nodes, weights and integration limits set
for the radial integration
@return field calculated at P
*/
extern double tess_gz(TESSEROID tess, double lonp, double latp, double rp,
GLQ glq_lon, GLQ glq_lat, GLQ glq_r);
/** Calculates gxx caused by a tesseroid (Grombein et al., 2010).
\f[
g_{xx}(r_p,\phi_p,\lambda_p) = G \rho \displaystyle\int_{\lambda_1}^{\lambda_2}
\displaystyle\int_{\phi_1}^{\phi_2} \displaystyle\int_{r_1}^{r_2}
\frac{3(r' K_{\phi})^2 - \ell^2}{\ell^5}\kappa \ d r' d \phi' d \lambda'
\f]
The derivatives of the potential are made with respect to the local coordinate
system <b>x->North, y->East, z->out</b>
<b>Input values in SI units and <b>degrees</b> and returns values in Eotvos!</b>
Use function glq_new() to create the GLQ parameters required. The integration
limits should be set to:
- glq_lon: lower = tess.w and upper = tess.e (in degrees)
- glq_lat: lower = tess.s and upper = tess.n (in degrees)
- glq_r: lower = tess.r1 and upper = tess.r2
@param tess data structure describing the tesseroid
@param lonp longitude of the computation point P
@param latp latitude of the computation point P
@param rp radial coordinate of the computation point P
@param glq_lon GLQ structure with the nodes, weights and integration limits set
for the longitudinal integration
@param glq_lat GLQ structure with the nodes, weights and integration limits set
for the latitudinal integration
@param glq_r GLQ structure with the nodes, weights and integration limits set
for the radial integration
@return field calculated at P
*/
extern double tess_gxx(TESSEROID tess, double lonp, double latp, double rp,
GLQ glq_lon, GLQ glq_lat, GLQ glq_r);
/** Calculates gxy caused by a tesseroid (Grombein et al., 2010).
\f[
g_{xy}(r_p,\phi_p,\lambda_p) = G \rho \displaystyle\int_{\lambda_1}^{\lambda_2}
\displaystyle\int_{\phi_1}^{\phi_2} \displaystyle\int_{r_1}^{r_2}
\frac{3{r'}^2 K_{\phi}\cos\phi'\sin(\lambda' - \lambda_p)}{\ell^5}
\kappa \ d r' d \phi' d \lambda'
\f]
The derivatives of the potential are made with respect to the local coordinate
system <b>x->North, y->East, z->out</b>
<b>Input values in SI units and <b>degrees</b> and returns values in Eotvos!</b>
Use function glq_new() to create the GLQ parameters required. The integration
limits should be set to:
- glq_lon: lower = tess.w and upper = tess.e (in degrees)
- glq_lat: lower = tess.s and upper = tess.n (in degrees)
- glq_r: lower = tess.r1 and upper = tess.r2
@param tess data structure describing the tesseroid
@param lonp longitude of the computation point P
@param latp latitude of the computation point P
@param rp radial coordinate of the computation point P
@param glq_lon GLQ structure with the nodes, weights and integration limits set
for the longitudinal integration
@param glq_lat GLQ structure with the nodes, weights and integration limits set
for the latitudinal integration
@param glq_r GLQ structure with the nodes, weights and integration limits set
for the radial integration
@return field calculated at P
*/
extern double tess_gxy(TESSEROID tess, double lonp, double latp, double rp,
GLQ glq_lon, GLQ glq_lat, GLQ glq_r);
/** Calculates gxz caused by a tesseroid (Grombein et al., 2010).
\f[
g_{xz}(r_p,\phi_p,\lambda_p) = G \rho \displaystyle\int_{\lambda_1}^{\lambda_2}
\displaystyle\int_{\phi_1}^{\phi_2} \displaystyle\int_{r_1}^{r_2}
\frac{3 r' K_{\phi}(r' \cos\psi - r_p)}{\ell^5}\kappa
\ d r' d \phi' d \lambda'
\f]
The derivatives of the potential are made with respect to the local coordinate
system <b>x->North, y->East, z->out</b>
<b>Input values in SI units and <b>degrees</b> and returns values in Eotvos!</b>
Use function glq_new() to create the GLQ parameters required. The integration
limits should be set to:
- glq_lon: lower = tess.w and upper = tess.e (in degrees)
- glq_lat: lower = tess.s and upper = tess.n (in degrees)
- glq_r: lower = tess.r1 and upper = tess.r2
@param tess data structure describing the tesseroid
@param lonp longitude of the computation point P
@param latp latitude of the computation point P
@param rp radial coordinate of the computation point P
@param glq_lon GLQ structure with the nodes, weights and integration limits set
for the longitudinal integration
@param glq_lat GLQ structure with the nodes, weights and integration limits set
for the latitudinal integration
@param glq_r GLQ structure with the nodes, weights and integration limits set
for the radial integration
@return field calculated at P
*/
extern double tess_gxz(TESSEROID tess, double lonp, double latp, double rp,
GLQ glq_lon, GLQ glq_lat, GLQ glq_r);
/** Calculates gyy caused by a tesseroid (Grombein et al., 2010).
\f[
g_{yy}(r_p,\phi_p,\lambda_p) = G \rho \displaystyle\int_{\lambda_1}^{\lambda_2}
\displaystyle\int_{\phi_1}^{\phi_2} \displaystyle\int_{r_1}^{r_2}
\frac{3(r'\cos\phi'\sin(\lambda' - \lambda_p))^2 - \ell^2}{\ell^5}
\kappa \ d r' d \phi' d \lambda'
\f]
The derivatives of the potential are made with respect to the local coordinate
system <b>x->North, y->East, z->out</b>
<b>Input values in SI units and <b>degrees</b> and returns values in Eotvos!</b>
Use function glq_new() to create the GLQ parameters required. The integration
limits should be set to:
- glq_lon: lower = tess.w and upper = tess.e (in degrees)
- glq_lat: lower = tess.s and upper = tess.n (in degrees)
- glq_r: lower = tess.r1 and upper = tess.r2
@param tess data structure describing the tesseroid
@param lonp longitude of the computation point P
@param latp latitude of the computation point P
@param rp radial coordinate of the computation point P
@param glq_lon GLQ structure with the nodes, weights and integration limits set
for the longitudinal integration
@param glq_lat GLQ structure with the nodes, weights and integration limits set
for the latitudinal integration
@param glq_r GLQ structure with the nodes, weights and integration limits set
for the radial integration
@return field calculated at P
*/
extern double tess_gyy(TESSEROID tess, double lonp, double latp, double rp,
GLQ glq_lon, GLQ glq_lat, GLQ glq_r);
/** Calculates gyz caused by a tesseroid (Grombein et al., 2010).
\f[
g_{yz}(r_p,\phi_p,\lambda_p) = G \rho \displaystyle\int_{\lambda_1}^{\lambda_2}
\displaystyle\int_{\phi_1}^{\phi_2} \displaystyle\int_{r_1}^{r_2}
\frac{3 r' \cos\phi' \sin(\lambda' - \lambda_p)(r'\cos\psi - r_p)}{\ell^5}
\kappa \ d r' d \phi' d \lambda'
\f]
The derivatives of the potential are made with respect to the local coordinate
system <b>x->North, y->East, z->out</b>
<b>Input values in SI units and <b>degrees</b> and returns values in Eotvos!</b>
Use function glq_new() to create the GLQ parameters required. The integration
limits should be set to:
- glq_lon: lower = tess.w and upper = tess.e (in degrees)
- glq_lat: lower = tess.s and upper = tess.n (in degrees)
- glq_r: lower = tess.r1 and upper = tess.r2
@param tess data structure describing the tesseroid
@param lonp longitude of the computation point P
@param latp latitude of the computation point P
@param rp radial coordinate of the computation point P
@param glq_lon GLQ structure with the nodes, weights and integration limits set
for the longitudinal integration
@param glq_lat GLQ structure with the nodes, weights and integration limits set
for the latitudinal integration
@param glq_r GLQ structure with the nodes, weights and integration limits set
for the radial integration
@return field calculated at P
*/
extern double tess_gyz(TESSEROID tess, double lonp, double latp, double rp,
GLQ glq_lon, GLQ glq_lat, GLQ glq_r);
/** Calculates gzz caused by a tesseroid (Grombein et al., 2010).
\f[
g_{zz}(r_p,\phi_p,\lambda_p) = G \rho \displaystyle\int_{\lambda_1}^{\lambda_2}
\displaystyle\int_{\phi_1}^{\phi_2} \displaystyle\int_{r_1}^{r_2}
\frac{3(r'\cos\psi-r_p)^2 - \ell^2}{\ell^5}\kappa \ d r' d \phi' d \lambda'
\f]
The derivatives of the potential are made with respect to the local coordinate
system <b>x->North, y->East, z->out</b>
<b>Input values in SI units and <b>degrees</b> and returns values in Eotvos!</b>
Use function glq_new() to create the GLQ parameters required. The integration
limits should be set to:
- glq_lon: lower = tess.w and upper = tess.e (in degrees)
- glq_lat: lower = tess.s and upper = tess.n (in degrees)
- glq_r: lower = tess.r1 and upper = tess.r2
@param tess data structure describing the tesseroid
@param lonp longitude of the computation point P
@param latp latitude of the computation point P
@param rp radial coordinate of the computation point P
@param glq_lon GLQ structure with the nodes, weights and integration limits set
for the longitudinal integration
@param glq_lat GLQ structure with the nodes, weights and integration limits set
for the latitudinal integration
@param glq_r GLQ structure with the nodes, weights and integration limits set
for the radial integration
@return field calculated at P
*/
extern double tess_gzz(TESSEROID tess, double lonp, double latp, double rp,
GLQ glq_lon, GLQ glq_lat, GLQ glq_r);
#endif

110
lib/logger.c Normal file
View File

@@ -0,0 +1,110 @@
/*
Functions to set up logging.
*/
#include <stdio.h>
#include <stdarg.h>
#include <time.h>
#include "logger.h"
/* Initialize the logger so that it doesn't print by default */
LOGGER logger = {100, 0, 100, NULL};
/* Setup logging to stderr.*/
void log_init(int level)
{
logger.level = level;
}
/* Set logging to a file. */
void log_tofile(FILE *logfile, int level)
{
logger.filelogging = 1;
logger.logfile = logfile;
logger.file_level = level;
}
/* Log a message at debug level */
/* These messages are always printed to stderr and the log file */
void log_debug(const char *fmt, ...)
{
char msg[10000];
va_list args;
va_start(args, fmt);
vsprintf(msg, fmt, args);
va_end(args);
fprintf(stderr, "DEBUG: %s\n", msg);
if(logger.filelogging)
{
fprintf(logger.logfile, "DEBUG: %s\n", msg);
}
}
/* Log a message at info level */
void log_info(const char *fmt, ...)
{
char msg[10000];
va_list args;
va_start(args, fmt);
vsprintf(msg, fmt, args);
va_end(args);
if(logger.level <= LOG_INFO)
{
fprintf(stderr, "%s\n", msg);
}
if(logger.filelogging && logger.file_level <= LOG_INFO)
{
fprintf(logger.logfile, "%s\n", msg);
}
}
/* Log a message at warning level */
void log_warning(const char *fmt, ...)
{
char msg[10000];
va_list args;
va_start(args, fmt);
vsprintf(msg, fmt, args);
va_end(args);
if(logger.level <= LOG_WARNING)
{
fprintf(stderr, "WARNING: %s\n", msg);
}
if(logger.filelogging && logger.file_level <= LOG_WARNING)
{
fprintf(logger.logfile, "WARNING: %s\n", msg);
}
}
/* Log a message at error level */
void log_error(const char *fmt, ...)
{
char msg[10000];
va_list args;
va_start(args, fmt);
vsprintf(msg, fmt, args);
va_end(args);
if(logger.level <= LOG_ERROR)
{
fprintf(stderr, "\nERROR: %s\n\n", msg);
}
if(logger.filelogging && logger.file_level <= LOG_ERROR)
{
fprintf(logger.logfile, "\nERROR: %s\n\n", msg);
}
}

166
lib/logger.h Normal file
View File

@@ -0,0 +1,166 @@
/*
Functions to set up logging.
Examples
--------
Logging to stderr:
#include "logger.h"
void my_func(){
log_info("From my_func!\n");
}
int main(){
// Enable logging to stderr in debug level
// will only print messages of level DEBUG or higher
log_init(LOG_DEBUG);
log_debug("debug line. The code is %d", LOG_DEBUG);
log_info("info line. The code is %d", LOG_INFO);
log_warning("warning line. The code is %d", LOG_WARNING);
log_error("error line. The code is %d", LOG_ERROR);
my_func();
return 0;
}
will print:
DEBUG: debug line. The code is 0
info line. The code is 1
WARNING: warning line. The code is 2
ERROR: error line. The code is 3
From my_func!
If function log_init() is not called than logging to stderr is disabled and no
messages will be printed.
Logging to a file:
#include <stdio.h>
#include "logger.h"
void my_func(){
log_info("From my_func!\n");
log_debug("Should not appear in log file\n");
}
int main(){
// Enable logging to file "log.txt" in info level
// will not print DEBUG level messages
// since log_init was not called, there is no logging to stderr
FILE *logfile = fopen("log.txt", "w");
log_tofile(logfile, LOG_INFO);
log_debug("debug line. The code is %d", LOG_DEBUG);
log_info("info line. The code is %d", LOG_INFO);
log_warning("warning line. The code is %d", LOG_WARNING);
log_error("error line. The code is %d", LOG_ERROR);
my_func();
return 0;
}
File log.txt will look like:
info line. The code is 1
WARNING: warning line. The code is 2
ERROR: error line. The code is 3
From my_func!
Note that you can combine loggin to stderr and to a file with different
levels in the same program.
*/
#ifndef _TESSEROIDS_LOGGER_H_
#define _TESSEROIDS_LOGGER_H_
/* Needed for definition of FILE */
#include <stdio.h>
/** Logging level for debug messages */
#define LOG_DEBUG 1
/** Logging level for general information */
#define LOG_INFO 2
/** Logging level for warning messages */
#define LOG_WARNING 3
/** Logging level for error messages */
#define LOG_ERROR 4
/** Keep the information on the global logger */
typedef struct logger_struct
{
int level; /**< level of logging */
int filelogging; /**< flag to know wether loggint to a file is enabled */
int file_level; /**< logging level for the file */
FILE *logfile; /**< file to log to */
} LOGGER;
/** Global logger struct. Only declare in the main program! */
extern LOGGER logger;
/** Setup logging to stderr.
@param level level of logging to be made. Can be one of:
- LOG_DEBUG
- LOG_INFO
- LOG_WARNING
- LOG_ERROR
*/
extern void log_init(int level);
/** Set logging to a file.
@param logfile FILE pointer to the already open file to log to.
@param level level of logging to be made to the file. Can be one of:
- LOG_DEBUG
- LOG_INFO
- LOG_WARNING
- LOG_ERROR
*/
extern void log_tofile(FILE *logfile, int level);
/** Log a message at debug level.
Pass parameters in the same format as printf()
Prints a newline at the end.
*/
extern void log_debug(const char *fmt, ...);
/** Log a message at info level.
Pass parameters in the same format as printf()
Does not print "INFO: " in front of the message when logging
Prints a newline at the end.
*/
extern void log_info(const char *fmt, ...);
/** Log a message at warning level.
Pass parameters in the same format as printf()
Prints a newline at the end.
*/
extern void log_warning(const char *fmt, ...);
/** Log a message at error level.
Pass parameters in the same format as printf()
Prints a newline at the end.
*/
extern void log_error(const char *fmt, ...);
#endif

1410
lib/parsers.c Normal file

File diff suppressed because it is too large Load Diff

316
lib/parsers.h Normal file
View File

@@ -0,0 +1,316 @@
/*
Input and output parsing tools.
*/
#ifndef _TESSEROIDS_PARSERS_H_
#define _TESSEROIDS_PARSERS_H_
/* Needed for definition of TESSEROID and PRISM */
#include "geometry.h"
/* Need for the definition of FILE */
#include <stdio.h>
/** Store basic input arguments and option flags */
typedef struct basic_args
{
char *inputfname; /**< name of the input file */
int verbose; /**< flag to indicate if verbose printing is enabled */
int logtofile; /**< flag to indicate if logging to a file is enabled */
char *logfname; /**< name of the log file */
} BASIC_ARGS;
/** Store input arguments and option flags for tessmass program */
typedef struct tessmass_args
{
char *inputfname; /**< name of the input file */
int verbose; /**< flag to indicate if verbose printing is enabled */
int logtofile; /**< flag to indicate if logging to a file is enabled */
char *logfname; /**< name of the log file */
int use_range; /**< flag to indicate wether to use a density range or not */
double low_dens; /**< lower bound for density range */
double high_dens; /**< upper bound for density range */
} TESSMASS_ARGS;
/** Store input arguments and option flags for tess2prism program */
typedef struct tess2prism_args
{
char *inputfname; /**< name of the input file */
int verbose; /**< flag to indicate if verbose printing is enabled */
int logtofile; /**< flag to indicate if logging to a file is enabled */
char *logfname; /**< name of the log file */
int flatten; /**< flag to indicate wether to use a flattened tesseroid or
a prism in spherical coordinates */
} TESS2PRISM_ARGS;
/** Store input arguments and option flags for tessmodgen program */
typedef struct tessmodgen_args
{
int verbose; /**< flag to indicate if verbose printing is enabled */
int logtofile; /**< flag to indicate if logging to a file is enabled */
char *logfname; /**< name of the log file */
double dlon; /**< grid spacing in longitude */
double dlat; /**< grid spacing in latitude */
double ref; /**< depth of the reference level */
double dens; /**< density of the tesseroids */
int fix_density; /**< flag to tell wether using value passed by -d */
} TESSMODGEN_ARGS;
/** Store input arguments and option flags for tesslayers program */
typedef struct tesslayers_args
{
int verbose; /**< flag to indicate if verbose printing is enabled */
int logtofile; /**< flag to indicate if logging to a file is enabled */
char *logfname; /**< name of the log file */
double dlon; /**< grid spacing in longitude */
double dlat; /**< grid spacing in latitude */
} TESSLAYERS_ARGS;
/** Store input arguments and option flags for tessg* programs */
typedef struct tessg_args
{
int lon_order; /**< glq order in longitude integration */
int lat_order; /**< glq order in latitude integration */
int r_order; /**< glq order in radial integration */
char *modelfname; /**< name of the file with the tesseroid model */
int verbose; /**< flag to indicate if verbose printing is enabled */
int logtofile; /**< flag to indicate if logging to a file is enabled */
char *logfname; /**< name of the log file */
int adaptative; /**< flat to indicate wether to use the adaptative size
of tesseroid algorithm */
double ratio; /**< distance-size ratio used for recusive division */
} TESSG_ARGS;
/** Store input arguments and option flags for tessgrd program */
typedef struct tessgrd_args
{
double w; /**< western border of the grid */
double e; /**< eastern border of the grid */
double s; /**< southern border of the grid */
double n; /**< northern border of the grid */
int nlon; /**< number of grid points in the longitudinal direction */
int nlat; /**< number of grid points in the latitudinal direction */
double height; /**< height above geoid of the grid */
int verbose; /**< flag to indicate if verbose printing is enabled */
int logtofile; /**< flag to indicate if logging to a file is enabled */
char *logfname; /**< name of the log file */
} TESSGRD_ARGS;
/** Parse basic command line arguments for programs
Basic arguments are: -h (for help msg), -v (for verbose), -l (for log file),
--version and an input file.
@param argc number of command line arguments
@param argv command line arguments
@param progname name of the specific program
@param args to return the parsed arguments
@param print_help pointer to a function that prints the help message for the
program
@return Return code:
- 0: if all went well
- 1: if there were bad arguments and program should exit
- 2: if printed help or version info and program should exit
- 3: if input file was missing (doesn't log an error)
*/
extern int parse_basic_args(int argc, char **argv, const char *progname,
BASIC_ARGS *args, void (*print_help)(void));
/** Parse command line arguments for tessmass program
@param argc number of command line arguments
@param argv command line arguments
@param progname name of the program
@param args to return the parsed arguments
@param print_help pointer to a function that prints the help message for the
program
@return Return code:
- 0: if all went well
- 1: if there were bad arguments and program should exit
- 2: if printed help or version info and program should exit
- 3: if input file was missing (doesn't log an error)
*/
extern int parse_tessmass_args(int argc, char **argv, const char *progname,
TESSMASS_ARGS *args, void (*print_help)(void));
/** Parse command line arguments for tess2prism program
@param argc number of command line arguments
@param argv command line arguments
@param progname name of the program
@param args to return the parsed arguments
@param print_help pointer to a function that prints the help message for the
program
@return Return code:
- 0: if all went well
- 1: if there were bad arguments and program should exit
- 2: if printed help or version info and program should exit
- 3: if input file was missing (doesn't log an error)
*/
extern int parse_tess2prism_args(int argc, char **argv, const char *progname,
TESS2PRISM_ARGS *args, void (*print_help)(void));
/** Parse command line arguments for tessmodgen program
@param argc number of command line arguments
@param argv command line arguments
@param progname name of the program
@param args to return the parsed arguments
@param print_help pointer to a function that prints the help message for the
program
@return Return code:
- 0: if all went well
- 1: if there were bad arguments and program should exit
- 2: if printed help or version info and program should exit
*/
extern int parse_tessmodgen_args(int argc, char **argv, const char *progname,
TESSMODGEN_ARGS *args, void (*print_help)(void));
/** Parse command line arguments for tesslayers program
@param argc number of command line arguments
@param argv command line arguments
@param progname name of the program
@param args to return the parsed arguments
@param print_help pointer to a function that prints the help message for the
program
@return Return code:
- 0: if all went well
- 1: if there were bad arguments and program should exit
- 2: if printed help or version info and program should exit
*/
extern int parse_tesslayers_args(int argc, char **argv, const char *progname,
TESSLAYERS_ARGS *args, void (*print_help)(void));
/** Parse command line arguments for tessg* programs
logs the bad argument warnings using logger.h
@param argc number of command line arguments
@param argv command line arguments
@param progname name of the specific program
@param args to return the parsed arguments
@return Return code:
- 0: if all went well
- 1: if there were bad arguments and program should exit
- 2: if printed help or version info and program should exit
*/
extern int parse_tessg_args(int argc, char **argv, const char *progname,
TESSG_ARGS *args, void (*print_help)(const char *));
/** Parse command line arguments for tessgrd program
logs the bad argument warnings using logger.h
@param argc number of command line arguments
@param argv command line arguments
@param args to return the parsed arguments
@return Return code:
- 0: if all went well
- 1: if there were bad arguments and program should exit
- 2: if printed help or version info and program should exit
*/
extern int parse_tessgrd_args(int argc, char **argv, TESSGRD_ARGS *args,
void (*print_help)(void));
/** Strip trailing spaces and newlines from the end of a string
Done IN PLACE!
@param str string to strip
*/
extern void strstrip(char *str);
/** Read a single tesseroid from a string
@param str string with the tesseroid parameters
@param tess used to return the read tesseroid
@return 0 if all went well, 1 if failed to read.
*/
extern int gets_tess(const char *str, TESSEROID *tess);
/** Read tesseroids from an open file and store them in an array.
Allocates memory. Don't forget to free 'model'!
@param modelfile open FILE for reading with the tesseroid model
@param size used to return the size of the model read
@return pointer to array with the model. NULL if there was an error
*/
extern TESSEROID * read_tess_model(FILE *modelfile, int *size);
/** Read a single rectangular prism from a string
@param str string with the tesseroid parameters
@param prism used to return the read prism
@return 0 if all went well, 1 if failed to read.
*/
extern int gets_prism(const char *str, PRISM *prism);
/** Read a single rectangular prism and the spherical coordinates of its top
from a string
@param str string with the tesseroid parameters
@param prism used to return the read prism
@return 0 if all went well, 1 if failed to read.
*/
extern int gets_prism_sph(const char *str, PRISM *prism);
/** Read rectangular prisms from an open file and store them in an array.
Allocates memory. Don't forget to free 'model'!
@param modelfile open FILE for reading with the model
@param pos if not 0 (true) will read the spherical coordinates of the top as
well
@param size used to return the size of the model read
@return pointer to array with the model. NULL if there was an error
*/
extern PRISM * read_prism_model(FILE *modelfile, int pos, int *size);
/** Read the coordinates, height, thickness and densities of the layers and
convert it to tesseroids.
@param str string with the coordinates and layer parameters
@param dlon the size of the tesseroid in the longitudinal direction
@param dlat the size of the tesseroid in the latitudinal direction
@param tessbuff buffer used to return the tesseroids corresponding to the layer
@param buffsize the size of the buffer
@return the number of layers read and converted, -1 if there was an error
*/
extern int gets_layers(const char *str, double dlon, double dlat,
TESSEROID *tessbuff, int buffsize);
#endif

241
lib/prismg_main.c Normal file
View File

@@ -0,0 +1,241 @@
/*
Generic main function for the prismg* programs.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "logger.h"
#include "version.h"
#include "grav_prism.h"
#include "geometry.h"
#include "parsers.h"
#include "prismg_main.h"
char global_progname[100];
/* Print the help message */
void print_help()
{
printf("Usage: %s MODELFILE [OPTIONS]\n\n", global_progname);
if(strcmp(global_progname + 5, "pot") == 0)
{
printf("Calculate the potential due to a rectangular prism model on\n");
}
else
{
printf("Calculate the %s component due to a rectangular prism model on\n",
global_progname + 5);
}
printf("specified observation points using Cartesian coordinates.\n\n");
printf("All input units are SI! Output is SI, mGal or Eotvos.\n\n");
printf("Coordinate system:\n");
printf(" The coordinate system for the prism is x->North, y->East\n");
printf(" and z->Down\n\n");
printf("Input:\n");
printf(" Computation points passed through standard input (stdin).\n");
printf(" Reads 3 or more values per line and inteprets the first 3 as:\n");
printf(" Easting(y) Northing(x) height \n");
printf(" (the coordinates of a computation point in meters).\n");
printf(" Other values in the line are ignored.\n");
printf(" Lines that start with # are ignored as comments.\n");
printf(" Lines should be no longer than 10000 (ten thousand) characters.");
printf(" \n\n");
printf("Output:\n");
printf(" Printed to standard output (stdout) in the form:\n");
printf(" y x height ... result\n");
printf(" ... represents any values that were read from input and\n");
printf(" ignored. In other words, the result is appended to the last\n");
printf(" column of the input. Use this to pipe prism* programs\n");
printf(" together.\n\n");
printf(" Comments about the provenance of the data are inserted into\n");
printf(" the top of the output\n\n");
printf("MODELFILE: File containing the prism model\n");
printf(" * Each prism is specified by the values of its borders\n");
printf(" and density\n");
printf(" * The file should contain one prism per line\n");
printf(" * If a line starts with # it will be considered a comment and\n");
printf(" will be ignored.\n");
printf(" * Each line should have the following column format:\n");
printf(" X1 X2 Y1 Y2 Z1 Z2 Density\n\n");
printf("Options:\n");
printf(" -h Print instructions.\n");
printf(" --version Print version and license information.\n");
printf(" -v Enable verbose printing to stderr.\n");
printf(" -lFILENAME Print log messages to file FILENAME.\n");
print_copyright();
}
/* Run the main for a generic prismg* program */
int run_prismg_main(int argc, char **argv, const char *progname,
double (*field)(PRISM, double, double, double))
{
BASIC_ARGS args;
PRISM *model;
int modelsize, rc, line, points = 0, error_exit = 0, bad_input = 0, i;
char buff[10000];
double x, y, height, res;
FILE *logfile = NULL, *modelfile = NULL;
time_t rawtime;
clock_t tstart;
struct tm * timeinfo;
log_init(LOG_INFO);
strcpy(global_progname, progname);
rc = parse_basic_args(argc, argv, progname, &args, &print_help);
if(rc == 3)
{
log_error("%s: missing input file", progname);
log_warning("Terminating due to bad input");
log_warning("Try '%s -h' for instructions", progname);
return 1;
}
if(rc == 2)
{
return 0;
}
if(rc == 1)
{
log_warning("Terminating due to bad input");
log_warning("Try '%s -h' for instructions", progname);
return 1;
}
/* Set the appropriate logging level and log to file if necessary */
if(!args.verbose)
{
log_init(LOG_WARNING);
}
if(args.logtofile)
{
logfile = fopen(args.logfname, "w");
if(logfile == NULL)
{
log_error("unable to create log file %s", args.logfname);
log_warning("Terminating due to bad input");
log_warning("Try '%s -h' for instructions", progname);
return 1;
}
log_tofile(logfile, LOG_INFO);
}
/* Print standard verbose */
log_info("%s (Tesseroids project) %s", progname, tesseroids_version);
time(&rawtime);
timeinfo = localtime(&rawtime);
log_info("(local time) %s", asctime(timeinfo));
/* Read the model file */
log_info("Reading prism model from file %s", args.inputfname);
modelfile = fopen(args.inputfname, "r");
if(modelfile == NULL)
{
log_error("failed to open model file %s", args.inputfname);
log_warning("Terminating due to bad input");
log_warning("Try '%s -h' for instructions", progname);
if(args.logtofile)
fclose(logfile);
return 1;
}
model = read_prism_model(modelfile, 0, &modelsize);
fclose(modelfile);
if(modelsize == 0)
{
log_error("prism file %s is empty", args.inputfname);
log_warning("Terminating due to bad input");
log_warning("Try '%s -h' for instructions", progname);
if(args.logtofile)
fclose(logfile);
return 1;
}
if(model == NULL)
{
log_error("failed to read model from file %s", args.inputfname);
log_warning("Terminating due to bad input");
log_warning("Try '%s -h' for instructions", progname);
if(args.logtofile)
fclose(logfile);
return 1;
}
log_info("Total of %d prism(s) read", modelsize);
/* Print a header on the output with provenance information */
if(strcmp(progname + 5, "pot") == 0)
{
printf("# Potential calculated with %s %s:\n", progname,
tesseroids_version);
}
else
{
printf("# %s component calculated with %s %s:\n", progname+5, progname,
tesseroids_version);
}
printf("# local time: %s", asctime(timeinfo));
printf("# model file: %s (%d prisms)\n", args.inputfname, modelsize);
/* Read each computation point from stdin and calculate */
log_info("Calculating (this may take a while)...");
tstart = clock();
for(line = 1; !feof(stdin); line++)
{
if(fgets(buff, 10000, stdin) == NULL)
{
if(ferror(stdin))
{
log_error("problem encountered reading line %d", line);
error_exit = 1;
break;
}
}
else
{
/* Check for comments and blank lines */
if(buff[0] == '#' || buff[0] == '\r' || buff[0] == '\n')
{
printf("%s", buff);
continue;
}
if(sscanf(buff, "%lf %lf %lf", &y, &x, &height) != 3)
{
log_warning("bad/invalid computation point at line %d", line);
log_warning("skipping this line and continuing");
bad_input++;
continue;
}
/* Need to remove \n and \r from end of buff first to print the
result in the end */
strstrip(buff);
for(res = 0, i = 0; i < modelsize; i++)
{
res += field(model[i], x, y, -height);
}
printf("%s %.15g\n", buff, res);
points++;
}
}
if(bad_input)
{
log_warning("Encountered %d bad computation points which were skipped",
bad_input);
}
if(error_exit)
{
log_warning("Terminating due to error in input");
log_warning("Try '%s -h' for instructions", progname);
}
else
{
log_info("Calculated on %d points in %.5g seconds", points,
(double)(clock() - tstart)/CLOCKS_PER_SEC);
}
/* Clean up */
free(model);
log_info("Done");
if(args.logtofile)
fclose(logfile);
return 0;
}

31
lib/prismg_main.h Normal file
View File

@@ -0,0 +1,31 @@
/*
Generic main function for the prismg* programs.
*/
#ifndef _TESSEROIDS_PRISMG_MAIN_H_
#define _TESSEROIDS_PRISMG_MAIN_H_
/* For the definitions of PRISM */
#include "geometry.h"
/** Print the help message
*/
extern void print_help();
/** Run the main for a generic prismg* program
@param argc number of command line arguments
@param argv command line arguments
@param progname name of the specific program
@param field pointer to function that calculates the field of a single prism
@return 0 is all went well. 1 if failed.
*/
extern int run_prismg_main(int argc, char **argv, const char *progname,
double (*field)(PRISM, double, double, double));
#endif

292
lib/tessg_main.c Normal file
View File

@@ -0,0 +1,292 @@
/*
Generic main function for the tessg* programs.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "logger.h"
#include "version.h"
#include "grav_tess.h"
#include "glq.h"
#include "constants.h"
#include "geometry.h"
#include "parsers.h"
#include "tessg_main.h"
/* Print the help message for tessg* programs */
void print_tessg_help(const char *progname)
{
printf("Usage: %s MODELFILE [OPTIONS]\n\n", progname);
if(strcmp(progname + 4, "pot") == 0)
{
printf("Calculate the potential due to a tesseroid model on\n");
}
else
{
printf("Calculate the %s component due to a tesseroid model on\n",
progname + 4);
}
printf("specified observation points.\n\n");
printf("Values are calculated in the local coordinate system of the\n");
printf("observation point: x-> North y-> East z-> Up (away from the\n");
printf("center of the Earth).\n");
printf("In order to maintain mainstream convention, component gz is\n");
printf("calculated with z-> Down.\n\n");
printf("All units either SI or degrees!\n\n");
printf("The computation of the gravitational effect of the tesseroids\n");
printf("is done using the Gauss-Legendre Quadrature (GLQ) numerical\n");
printf("integration method.\n\n");
printf("WARNING: Avoid computing directly on top or inside the\n");
printf(" tesseroids! This will break the GLQ and the formulas!\n");
printf("\n");
printf("Input:\n");
printf(" Computation points passed through standard input (stdin).\n");
printf(" Reads 3 or more values per line and inteprets the first 3 as:\n");
printf(" longitude, latitude and height\n");
printf(" of a computation points. Height should be in meters.\n");
printf(" Othervalues in the line are ignored.\n");
printf(" Lines that start with # are ignored as comments.\n");
printf(" Lines should be no longer than 10000 (ten thousand) characters.");
printf("\n\n");
printf("Output:\n");
printf(" Printed to standard output (stdout) in the form:\n");
printf(" lon lat height ... result\n");
printf(" ... represents any values that were read from input and\n");
printf(" ignored. In other words, the result is appended to the last\n");
printf(" column of the input. Use this to pipe tessg* programs\n");
printf(" together.\n");
printf(" * Comments about the provenance of the data are inserted into\n");
printf(" the top of the output\n\n");
printf("MODELFILE: File containing the tesseroid model\n");
printf(" * Each tesseroid is specified by the values of its borders\n");
printf(" and density\n");
printf(" * The file should contain one tesseroid per line\n");
printf(" * Each line should have the following column format:\n");
printf(" West East South North Top Bottom Density\n");
printf(" * Top and Bottom should be read as 'height to top' and \n");
printf(" 'height to bottom' from the mean Earth radius. Use negative\n");
printf(" values if bellow the surface, for example when modeling\n");
printf(" deep structures, and positive if above the surface, for\n");
printf(" example when modeling topography.\n");
printf(" * If a line starts with # it will be considered a comment and\n");
printf(" will be ignored.\n\n");
printf("Options:\n");
printf(" -a Disable the automatic subdividing of\n");
printf(" tesseroids. Subdividing is done to ensure the\n");
printf(" GLQ gives accurate results. ONLY USE THIS\n");
printf(" OPTION IF YOU KNOW WHAT YOU ARE DOING!\n");
printf(" -tRATIO Use a custom distance-size ratio for the\n");
printf(" automatic subdivision of tesseroids. ONLY USE\n");
printf(" THIS OPTION IF YOU KNOW WHAT YOU ARE DOING!\n");
printf(" -oOLON/OLAT/OR GLQ order to use in the longitudinal,\n");
printf(" latitudinal and radial integrations,\n");
printf(" respectively. Defaults to 2/2/2.\n");
printf(" Subdividing of tesseroids works best with the\n");
printf(" default order.\n");
printf(" -h Print instructions.\n");
printf(" --version Print version and license information.\n");
printf(" -v Enable verbose printing to stderr.\n");
printf(" -lFILENAME Print log messages to file FILENAME.\n");
print_copyright();
}
/* Run the main for a generic tessg* program */
int run_tessg_main(int argc, char **argv, const char *progname,
double (*field)(TESSEROID, double, double, double, GLQ, GLQ, GLQ),
double ratio)
{
TESSG_ARGS args;
GLQ *glq_lon, *glq_lat, *glq_r;
TESSEROID *model;
int modelsize, rc, line, points = 0, error_exit = 0, bad_input = 0;
char buff[10000];
double lon, lat, height, res;
FILE *logfile = NULL, *modelfile = NULL;
time_t rawtime;
clock_t tstart;
struct tm * timeinfo;
log_init(LOG_INFO);
rc = parse_tessg_args(argc, argv, progname, &args, &print_tessg_help);
if(rc == 2)
{
return 0;
}
if(rc == 1)
{
log_warning("Terminating due to bad input");
log_warning("Try '%s -h' for instructions", progname);
return 1;
}
/* Set the appropriate logging level and log to file if necessary */
if(!args.verbose)
{
log_init(LOG_WARNING);
}
if(args.logtofile)
{
logfile = fopen(args.logfname, "w");
if(logfile == NULL)
{
log_error("unable to create log file %s", args.logfname);
log_warning("Terminating due to bad input");
log_warning("Try '%s -h' for instructions", progname);
return 1;
}
log_tofile(logfile, LOG_DEBUG);
}
/* Check if a custom distance-size ratio is given */
if(args.ratio != 0)
{
ratio = args.ratio;
}
/* Print standard verbose */
log_info("%s (Tesseroids project) %s", progname, tesseroids_version);
time(&rawtime);
timeinfo = localtime(&rawtime);
log_info("(local time) %s", asctime(timeinfo));
log_info("Use recursive division of tesseroids: %s",
args.adaptative ? "True" : "False");
log_info("Distance-size ratio for recusive division: %g", ratio);
/* Make the necessary GLQ structures */
log_info("Using GLQ orders: %d lon / %d lat / %d r", args.lon_order,
args.lat_order, args.r_order);
glq_lon = glq_new(args.lon_order, -1, 1);
glq_lat = glq_new(args.lat_order, -1, 1);
glq_r = glq_new(args.r_order, -1, 1);
if(glq_lon == NULL || glq_lat == NULL || glq_r == NULL)
{
log_error("failed to create required GLQ structures");
log_warning("Terminating due to bad input");
log_warning("Try '%s -h' for instructions", progname);
if(args.logtofile)
fclose(logfile);
return 1;
}
/* Read the tesseroid model file */
log_info("Reading tesseroid model from file %s", args.modelfname);
modelfile = fopen(args.modelfname, "r");
if(modelfile == NULL)
{
log_error("failed to open model file %s", args.modelfname);
log_warning("Terminating due to bad input");
log_warning("Try '%s -h' for instructions", progname);
if(args.logtofile)
fclose(logfile);
return 1;
}
model = read_tess_model(modelfile, &modelsize);
fclose(modelfile);
if(modelsize == 0)
{
log_error("tesseroid file %s is empty", args.modelfname);
log_warning("Terminating due to bad input");
log_warning("Try '%s -h' for instructions", progname);
if(args.logtofile)
fclose(logfile);
return 1;
}
if(model == NULL)
{
log_error("failed to read model from file %s", args.modelfname);
log_warning("Terminating due to bad input");
log_warning("Try '%s -h' for instructions", progname);
if(args.logtofile)
fclose(logfile);
return 1;
}
log_info("Total of %d tesseroid(s) read", modelsize);
/* Print a header on the output with provenance information */
if(strcmp(progname + 4, "pot") == 0)
{
printf("# Potential calculated with %s %s:\n", progname,
tesseroids_version);
}
else
{
printf("# %s component calculated with %s %s:\n", progname+4, progname,
tesseroids_version);
}
printf("# local time: %s", asctime(timeinfo));
printf("# model file: %s (%d tesseroids)\n", args.modelfname, modelsize);
printf("# GLQ order: %d lon / %d lat / %d r\n", args.lon_order,
args.lat_order, args.r_order);
printf("# Use recursive division of tesseroids: %s\n",
args.adaptative ? "True" : "False");
printf("# Distance-size ratio for recusive division: %g\n", ratio);
/* Read each computation point from stdin and calculate */
log_info("Calculating (this may take a while)...");
tstart = clock();
for(line = 1; fgets(buff, 10000, stdin) != NULL; line++)
{
/* Check for comments and blank lines */
if(buff[0] == '#' || buff[0] == '\r' || buff[0] == '\n')
{
printf("%s", buff);
continue;
}
/* Need to remove \n and \r from end of buff first to print the
result in the end */
strstrip(buff);
if(sscanf(buff, "%lf %lf %lf", &lon, &lat, &height) != 3)
{
log_warning("bad/invalid computation point at line %d:", line);
log_warning(" '%s'", buff);
log_warning("skipping this line and continuing");
bad_input++;
continue;
}
if(args.adaptative)
{
res = calc_tess_model_adapt(model, modelsize, lon, lat,
height + MEAN_EARTH_RADIUS, glq_lon,
glq_lat, glq_r, field, ratio);
}
else
{
res = calc_tess_model(model, modelsize, lon, lat,
height + MEAN_EARTH_RADIUS, glq_lon,
glq_lat, glq_r, field);
}
printf("%s %.15g\n", buff, res);
points++;
}
if(bad_input)
{
log_warning("Encountered %d bad computation points which were skipped",
bad_input);
}
if(error_exit)
{
log_warning("Terminating due to error in input");
log_warning("Try '%s -h' for instructions", progname);
}
else
{
log_info("Calculated on %d points in %.5g seconds", points,
(double)(clock() - tstart)/CLOCKS_PER_SEC);
}
/* Clean up */
free(model);
glq_free(glq_lon);
glq_free(glq_lat);
glq_free(glq_r);
log_info("Done");
if(args.logtofile)
fclose(logfile);
return 0;
}

36
lib/tessg_main.h Normal file
View File

@@ -0,0 +1,36 @@
/*
Generic main function for the tessg* programs.
*/
#ifndef _TESSEROIDS_TESSG_MAIN_H_
#define _TESSEROIDS_TESSG_MAIN_H_
/* For the definitions of GLQ and TESSEROID */
#include "glq.h"
#include "geometry.h"
/** Print the help message for tessg* programs
@param progname name of the specific tessg* program
*/
extern void print_tessg_help(const char *progname);
/** Run the main for a generic tessg* program
@param argc number of command line arguments
@param argv command line arguments
@param progname name of the specific program
@param field pointer to function that calculates the field of a single tesseroid
@param ratio distance-to-size ratio for doing adaptative resizing
@return 0 is all went well. 1 if failed.
*/
extern int run_tessg_main(int argc, char **argv, const char *progname,
double (*field)(TESSEROID, double, double, double, GLQ, GLQ, GLQ),
double ratio);
#endif

32
lib/version.c Normal file
View File

@@ -0,0 +1,32 @@
#include "version.h"
/*
* The following definitions are copied from the original version.template file.
* And the version number is set to tesseroids-1.6 directly.
*
* By Yi Zhang. 2021-05-05
*/
/* Current project version number */
const char tesseroids_version[] = "tesseroids-1.6";
/* Print version number*/
void print_version(const char* version_num)
{
printf("%s", version_num);
}
/* Print a copyright notice */
void print_copyright()
{
printf("\nPart of the Tesseroids package (v%s).\n", tesseroids_version);
printf("\nProject site: <http://www.leouieda.com/tesseroids/>\n");
printf("Report bugs at: ");
printf("<https://github.com/leouieda/tesseroids/issues>\n");
printf("\nCopyright (C) 2011-$YEAR, Leonardo Uieda.\n");
printf("This software is distributed under the terms of the BSD License:\n");
printf("<http://tesseroids.readthedocs.org/en/latest/license.html>\n");
printf("This is free software: ");
printf("you are free to change and redistribute it.\n");
printf("There is NO WARRANTY, to the extent permitted by law.\n");
}

19
lib/version.h Normal file
View File

@@ -0,0 +1,19 @@
/*
Hold the version number of the project.
*/
#ifndef _TESSEROIDS_VERSION_H_
#define _TESSEROIDS_VERSION_H_
#include "stdio.h"
/** Current project version number */
extern const char tesseroids_version[];
/** Print version number */
extern void print_version(const char* version_num);
/** Print version number */
extern void print_copyright();
#endif // _TESSEROIDS_VERSION_H_