/* nag_pde_parab_1d_fd (d03pcc) Example Program.
 *
 * Copyright 2014 Numerical Algorithms Group.
 *
 * Mark 7, 2001.
 * Mark 7b revised, 2004.
 * Mark 23 revised, 2011.
 */

#include <stdio.h>
#include <string.h>
#include <math.h>
#include <nag.h>
#include <nag_stdlib.h>
#include <nagd03.h>
#include <nagx01.h>

#ifdef __cplusplus
extern "C" {
#endif
static void NAG_CALL pdedef(Integer, double, double, const double[],
                            const double[], double[], double[], double[],
                            Integer *, Nag_Comm *);
static void NAG_CALL bndary(Integer, double, const double[], const double[],
                            Integer, double[], double[], Integer *, Nag_Comm *);
static int NAG_CALL uinit(double *, double *, Integer, Integer, double);
#ifdef __cplusplus
}
#endif


int main(void)
{
  const Integer npts = 20, npde = 2, neqn = npts*npde, intpts = 6, itype = 1;
  const Integer nwk = (10+6*npde)*neqn, lisave = neqn+24;
  const Integer lrsave = nwk+(21+3*npde)*npde+7*npts+54;
  static double ruser[2] = {-1.0, -1.0};
  Integer       exit_status = 0, i, ind, it, itask, itrace, m;
  double        acc, alpha, hx, piby2, tout, ts;
  double        xout[6] = { 0., .4, .6, .8, .9, 1. };
  double        *rsave = 0, *u = 0, *uout = 0, *x = 0;
  Integer       *isave = 0;
  NagError      fail;
  Nag_Comm      comm;
  Nag_D03_Save  saved;

  INIT_FAIL(fail);

  printf("nag_pde_parab_1d_fd (d03pcc) Example Program Results\n\n");

  /* For communication with user-supplied functions: */
  comm.user = ruser;

  /* Allocate memory */
  if (!(rsave = NAG_ALLOC(lrsave, double)) ||
      !(u = NAG_ALLOC(npde*npts, double)) ||
      !(uout = NAG_ALLOC(npde*intpts*itype, double)) ||
      !(x = NAG_ALLOC(npts, double)) ||
      !(isave = NAG_ALLOC(lisave, Integer)))
    {
      printf("Allocation failure\n");
      exit_status = 1;
      goto END;
    }

  acc = 0.001;
  m = 1;
  itrace = 0;
  alpha = 1.0;
  comm.p = (Pointer)&alpha;
  ind = 0;
  itask = 1;

  /* Set spatial mesh points */

  piby2 = 0.5*nag_pi;
  hx = piby2/((double)(npts-1));
  x[0] = 0.0;
  x[npts-1] = 1.0;
  for (i = 1; i < npts-1; ++i)  x[i] = sin(hx*i);

  /* Set initial conditions */

  ts = 0.0;
  tout = 1e-5;

  printf("Accuracy requirement  = %12.5f\n", acc);
  printf("Parameter alpha       = %10.3f\n\n", alpha);
  printf("   t  /  x  ");

  for (i = 0; i < intpts; ++i) printf("%8.4f", xout[i]);
  printf("\n");

  /* Set the initial values */

  uinit(u, x, npde, npts, alpha);
  for (it = 0; it < 5; ++it)
    {
      tout *= 10.0;

      /* Solve for next iteration step using
       * nag_pde_parab_1d_fd (d03pcc).
       * General system of parabolic PDEs, method of lines, finite
       * differences, one space variable
       */
      nag_pde_parab_1d_fd(npde, m, &ts, tout, pdedef, bndary, u, npts, x, acc,
                          rsave, lrsave, isave, lisave, itask, itrace, 0, &ind,
                          &comm, &saved, &fail);

      if (fail.code != NE_NOERROR)
        {
          printf("Error from nag_pde_parab_1d_fd (d03pcc).\n%s\n",
                  fail.message);
          exit_status = 1;
          goto END;
        }

      /* Interpolate at required spatial points using
       * nag_pde_interp_1d_fd (d03pzc).
       * PDEs, spatial interpolation fo use with the suite of routines
       * nag_pde_parab_1d (d03p).
       */
      nag_pde_interp_1d_fd(npde, m, u, npts, x, xout, intpts, 1, uout, &fail);

      if (fail.code != NE_NOERROR)
        {
          printf("Error from nag_pde_interp_1d_fd (d03pzc).\n%s\n",
                 fail.message);
          exit_status = 1;
          goto END;
        }

      printf("\n %6.4f u(1)", tout);
      for (i = 0; i < intpts; ++i) printf("%8.4f", uout[npde*i]);

      printf("\n %6s u(2)","");
      for (i = 0; i < intpts; ++i) printf("%8.4f", uout[npde*i+1]);
      printf("\n");
    }


  /* Print integration statistics */

  printf("\n %-55s%4ld\n", "Number of integration steps in time",
         isave[0]);
  printf(" %-55s%4ld\n", "Number of residual evaluations of"
         " resulting ODE system", isave[1]);
  printf(" %-55s%4ld\n", "Number of Jacobian evaluations", isave[2]);
  printf(" %-55s%4ld\n", "Number of iterations of nonlinear solver",
         isave[4]);

 END:
  NAG_FREE(rsave);
  NAG_FREE(u);
  NAG_FREE(uout);
  NAG_FREE(x);
  NAG_FREE(isave);

  return exit_status;
}

static int NAG_CALL uinit(double *u, double *x, Integer npde, Integer npts,
                          double alpha)
{
  Integer i;

  /* Intial conditions for u1 */
  for (i = 0; i < npts; ++i)  u[i*npde] = alpha*2.0*x[i];
  /* Intial conditions for u2 */
  for (i = 0; i < npts; ++i)  u[i*npde+1] = 1.0;

  return 0;
}


static void NAG_CALL pdedef(Integer npde, double t, double x, const double u[],
                            const double ux[], double p[], double q[],
                            double r[], Integer *ires, Nag_Comm *comm)
{
  /* PDE coefficients */

  double *alpha = (double *) comm->p;

  if (comm->user[0] == -1.0)
    {
      printf("(User-supplied callback pdedef, first invocation.)\n");
      comm->user[0] = 0.0;
    }
  /* Coefficients on first PDE */
  q[0] = *alpha*4.0*(u[1]+x*ux[1]);
  r[0] = x*ux[0];
  p[0] = 0.0;
  p[npde] = 0.0;
  /* Coefficients on first PDE */
  q[1] = 0.0;
  r[1] = ux[1] - u[0] * u[1];
  p[1] = 0.0;
  p[1+npde] = 1.0-x*x;
  return;
}

static void NAG_CALL bndary(Integer npde, double t, const double u[],
                            const double ux[], Integer ibnd, double beta[],
                            double gamma[], Integer *ires, Nag_Comm *comm)
{
  /* Boundary conditions */

  if (comm->user[1] == -1.0)
    {
      printf("(User-supplied callback bndary, first invocation.)\n");
      comm->user[1] = 0.0;
    }
  if (ibnd == 0)
    {
      /* u[0] = 0 */
      beta[0] = 0.0;
      gamma[0] = u[0];
      /* ux[1] = 0 ==> 1.0*r[1] = ux[1] - u[0]*u[1] = -u[0]*u[1] */ 
      beta[1] = 1.0;
      gamma[1] = -u[0]*u[1];
    }
  else
    {
      /* d(x*u[0])/dx = x*ux[0] + u[0] = 0 */
      beta[0] = 1.0;
      gamma[0] = -u[0];
      /* u[1] = 0 */
      beta[1] = 0.0;
      gamma[1] = u[1];
    }
  return;
}