gctl/dep/magnetic_tesseroids/lib/glq.cpp
2024-09-10 15:45:07 +08:00

266 lines
6.3 KiB
C++
Executable File

/*
Functions for implementing a Gauss-Legendre Quadrature numerical integration.
*/
#include <stdlib.h>
#include <math.h>
#include "constants.h"
#include "logger.h"
#include "glq.h"
/* 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;
}
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);
}
/* 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. */
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;
}