Blame integration/romberg.c

Packit 67cb25
/* integration/romberg.c
Packit 67cb25
 * 
Packit 67cb25
 * Copyright (C) 2018 Patrick Alken
Packit 67cb25
 * 
Packit 67cb25
 * This program is free software; you can redistribute it and/or modify
Packit 67cb25
 * it under the terms of the GNU General Public License as published by
Packit 67cb25
 * the Free Software Foundation; either version 3 of the License, or (at
Packit 67cb25
 * your option) any later version.
Packit 67cb25
 * 
Packit 67cb25
 * This program is distributed in the hope that it will be useful, but
Packit 67cb25
 * WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 67cb25
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 67cb25
 * General Public License for more details.
Packit 67cb25
 * 
Packit 67cb25
 * You should have received a copy of the GNU General Public License
Packit 67cb25
 * along with this program; if not, write to the Free Software
Packit 67cb25
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Packit 67cb25
 */
Packit 67cb25
Packit 67cb25
/* the code in this module performs Romberg integration */
Packit 67cb25
Packit 67cb25
#include <config.h>
Packit 67cb25
#include <stdlib.h>
Packit 67cb25
#include <gsl/gsl_math.h>
Packit 67cb25
#include <gsl/gsl_errno.h>
Packit 67cb25
#include <gsl/gsl_integration.h>
Packit 67cb25
Packit 67cb25
#define ROMBERG_PRINT_ROW(i, r) \
Packit 67cb25
  do { \
Packit 67cb25
    size_t jj; \
Packit 67cb25
    fprintf(stderr, "R[%zu] = ", i); \
Packit 67cb25
    for (jj = 0; jj <= i; ++jj) \
Packit 67cb25
      fprintf(stderr, "%.8e ", r[jj]); \
Packit 67cb25
    fprintf(stderr, "\n"); \
Packit 67cb25
  } while (0)
Packit 67cb25
Packit 67cb25
gsl_integration_romberg_workspace *
Packit 67cb25
gsl_integration_romberg_alloc(const size_t n)
Packit 67cb25
{
Packit 67cb25
  gsl_integration_romberg_workspace *w;
Packit 67cb25
Packit 67cb25
  /* check inputs */
Packit 67cb25
  if (n < 1)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR_VAL ("workspace size n must be at least 1", GSL_EDOM, 0);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  w = calloc(1, sizeof(gsl_integration_romberg_workspace));
Packit 67cb25
  if (w == NULL)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR_VAL ("unable to allocate workspace", GSL_ENOMEM, 0);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  /* ceiling on n, since the number of points is 2^n + 1 */
Packit 67cb25
  w->n = GSL_MIN(n, 30);
Packit 67cb25
Packit 67cb25
  w->work1 = malloc(w->n * sizeof(double));
Packit 67cb25
  if (w->work1 == NULL)
Packit 67cb25
    {
Packit 67cb25
      gsl_integration_romberg_free(w);
Packit 67cb25
      GSL_ERROR_VAL ("unable to allocate previous row", GSL_ENOMEM, 0);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  w->work2 = malloc(w->n * sizeof(double));
Packit 67cb25
  if (w->work2 == NULL)
Packit 67cb25
    {
Packit 67cb25
      gsl_integration_romberg_free(w);
Packit 67cb25
      GSL_ERROR_VAL ("unable to allocate current row", GSL_ENOMEM, 0);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  return w;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
void
Packit 67cb25
gsl_integration_romberg_free(gsl_integration_romberg_workspace * w)
Packit 67cb25
{
Packit 67cb25
  if (w->work1)
Packit 67cb25
    free(w->work1);
Packit 67cb25
Packit 67cb25
  if (w->work2)
Packit 67cb25
    free(w->work2);
Packit 67cb25
Packit 67cb25
  free(w);
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_integration_romberg(const gsl_function * f, const double a, const double b,
Packit 67cb25
                        const double epsabs, const double epsrel, double * result,
Packit 67cb25
                        size_t * neval, gsl_integration_romberg_workspace * w)
Packit 67cb25
{
Packit 67cb25
  if (epsabs < 0.0)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("epsabs must be non-negative", GSL_EDOM);
Packit 67cb25
    }
Packit 67cb25
  else if (epsrel < 0.0)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("epsrel must be non-negative", GSL_EDOM);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      const size_t n = w->n;
Packit 67cb25
      double *Rp = &(w->work1[0]); /* previous row */
Packit 67cb25
      double *Rc = &(w->work2[0]); /* current row */
Packit 67cb25
      double *Rtmp;
Packit 67cb25
      double h = 0.5 * (b - a);    /* step size */
Packit 67cb25
      size_t i;
Packit 67cb25
Packit 67cb25
      /* R(0,0) */
Packit 67cb25
      Rp[0] = h * (GSL_FN_EVAL(f, a) + GSL_FN_EVAL(f, b));
Packit 67cb25
      *neval = 2;
Packit 67cb25
Packit 67cb25
      /*ROMBERG_PRINT_ROW((size_t) 0, Rp);*/
Packit 67cb25
Packit 67cb25
      for (i = 1; i < n; ++i)
Packit 67cb25
        {
Packit 67cb25
          size_t j;
Packit 67cb25
          double sum = 0.0;
Packit 67cb25
          double err;
Packit 67cb25
          double four_j;         /* 4^j */
Packit 67cb25
          size_t two_i = 1 << i; /* 2^i */
Packit 67cb25
Packit 67cb25
          for (j = 1; j < two_i; j += 2)
Packit 67cb25
            {
Packit 67cb25
              sum += GSL_FN_EVAL(f, a + j * h);
Packit 67cb25
              ++(*neval);
Packit 67cb25
            }
Packit 67cb25
Packit 67cb25
          /* R(i,0) */
Packit 67cb25
          Rc[0] = sum * h + 0.5 * Rp[0];
Packit 67cb25
Packit 67cb25
          four_j = 4.0;
Packit 67cb25
          for (j = 1; j <= i; ++j)
Packit 67cb25
            {
Packit 67cb25
              Rc[j] = (four_j * Rc[j - 1] - Rp[j - 1]) / (four_j - 1.0);
Packit 67cb25
              four_j *= 4.0;
Packit 67cb25
            }
Packit 67cb25
Packit 67cb25
          /*ROMBERG_PRINT_ROW(i, Rc);*/
Packit 67cb25
Packit 67cb25
          /*
Packit 67cb25
           * compute difference between current and previous result and
Packit 67cb25
           * check for convergence
Packit 67cb25
           */
Packit 67cb25
          err = fabs(Rc[i] - Rp[i - 1]);
Packit 67cb25
          if ((err < epsabs) || (err < epsrel * fabs(Rc[i])))
Packit 67cb25
            {
Packit 67cb25
              *result = Rc[i];
Packit 67cb25
              return GSL_SUCCESS;
Packit 67cb25
            }
Packit 67cb25
Packit 67cb25
          /* swap Rp and Rc */
Packit 67cb25
          Rtmp = Rp;
Packit 67cb25
          Rp = Rc;
Packit 67cb25
          Rc = Rtmp;
Packit 67cb25
Packit 67cb25
          h *= 0.5;
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      /* did not converge - return best guess */
Packit 67cb25
      *result = Rp[n - 1];
Packit 67cb25
Packit 67cb25
      return GSL_EMAXITER;
Packit 67cb25
    }
Packit 67cb25
}