/* nag_pde_parab_1d_fd (d03pcc) Example Program.
 *
 * NAGPRODCODE Version.
 *
 * Copyright 2016 Numerical Algorithms Group.
 *
 * Mark 26, 2016.
 */

#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%4" NAG_IFMT "\n", "Number of integration steps in time",
         isave[0]);
  printf(" %-55s%4" NAG_IFMT "\n", "Number of residual evaluations of"
         " resulting ODE system", isave[1]);
  printf(" %-55s%4" NAG_IFMT "\n", "Number of Jacobian evaluations",
         isave[2]);
  printf(" %-55s%4" NAG_IFMT "\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;
}