/*
 * -----------------------------------------------------------------
 * Programmer(s): Daniel R. Reynolds @ SMU,
 *      Alan C. Hindmarsh, Radu Serban and Aaron Collier @ LLNL
 * -----------------------------------------------------------------
 * SUNDIALS Copyright Start
 * Copyright (c) 2002-2019, Lawrence Livermore National Security
 * and Southern Methodist University.
 * All rights reserved.
 *
 * See the top-level LICENSE and NOTICE files for details.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 * SUNDIALS Copyright End
 * -----------------------------------------------------------------
 * This is the header file for the IDABBDPRE module, for a
 * band-block-diagonal preconditioner, i.e. a block-diagonal
 * matrix with banded blocks, for use with IDAS and the
 * IDASpils solver interface.
 * -----------------------------------------------------------------
 */

#ifndef _IDASBBDPRE_H
#define _IDASBBDPRE_H

#include <sundials/sundials_nvector.h>

#ifdef __cplusplus     /* wrapper to enable C++ usage */
extern "C" {
#endif

/*=================================================================
  PART I - forward problems
  =================================================================*/

/*-----------------------------------------------------------------
  Summary:
 
  These routines provide a preconditioner matrix that is
  block-diagonal with banded blocks. The blocking corresponds
  to the distribution of the dependent variable vector y among
  the processors. Each preconditioner block is generated from
  the Jacobian of the local part (on the current processor) of a
  given function G(t,y,y') approximating F(t,y,y'). The blocks
  are generated by a difference quotient scheme on each processor
  independently. This scheme utilizes an assumed banded structure
  with given half-bandwidths, mudq and mldq. However, the banded
  Jacobian block kept by the scheme has half-bandwiths mukeep and
  mlkeep, which may be smaller.
 
  The user's calling program should have the following form:
 
    #include <idas/idas_bbdpre.h>
    #include <idas/idas_spils.h>
    #include <nvector_parallel.h>
    ...
    void *ida_mem;
    ...
    y0  = N_VNew_Parallel(...);
    yp0 = N_VNew_Parallel(...);
    ...
    SUNLinearSolver LS = SUNSPBCGS(y0, pretype, maxl);
      -or-
    SUNLinearSolver LS = SUNSPFGMR(y0, pretype, maxl);
      -or-
    SUNLinearSolver LS = SUNSPGMR(y0, pretype, maxl);
      -or-
    SUNLinearSolver LS = SUNSPTFQMR(y0, pretype, maxl);
      -or-
    SUNLinearSolver LS = SUNPCG(y0, pretype, maxl);
    ...
    ida_mem = IDACreate(...);
    ier = IDAInit(...);
    ...
    flag = IDASpilsSetLinearSolver(ida_mem, LS);
    ...
    flag = IDABBDPrecInit(ida_mem, Nlocal, mudq, mldq,
                          mukeep, mlkeep, dq_rel_yy, Gres, Gcomm);
    ...
    ier = IDASolve(...);
    ...
    IDAFree(&ida_mem);
    ...
    SUNLinSolFree(LS);
    ...
    N_VDestroy(y0);
    N_VDestroy(yp0);
    ...
 
  The user-supplied routines required are:
 
    res  is the function F(t,y,y') defining the DAE system to
    be solved:  F(t,y,y') = 0.
 
    Gres  is the function defining a local approximation
    G(t,y,y') to F, for the purposes of the preconditioner.
 
    Gcomm  is the function performing communication needed
    for Glocal.
 
  Notes:
 
  1) This header file is included by the user for the definition
     of the IBBDPrecData type and for needed function prototypes.
 
  2) The IDABBDPrecInit call includes half-bandwidths mudq and
     mldq to be used in the approximate Jacobian. They need
     not be the true half-bandwidths of the Jacobian of the
     local block of G, when smaller values may provide a greater
     efficiency. Similarly, mukeep and mlkeep, specifying the
     bandwidth kept for the approximate Jacobian, need not be
     the true half-bandwidths. Also, mukeep, mlkeep, mudq, and
     mldq need not be the same on every processor.
 
  3) The actual name of the user's res function is passed to
     IDAInit, and the names of the user's Gres and Gcomm
     functions are passed to IDABBDPrecInit.        
 
  4) The pointer to the user-defined data block user_data, which
     is set through IDASetUserData is also available to the user
     in glocal and gcomm.
 
  5) Optional outputs specific to this module are available by
     way of routines listed below. These include work space sizes
     and the cumulative number of glocal calls. The costs
     associated with this module also include nsetups banded LU
     factorizations, nsetups gcomm calls, and nps banded
     backsolve calls, where nsetups and nps are integrator
     optional outputs.
  -----------------------------------------------------------------*/

  
/*-----------------------------------------------------------------
  Type : IDABBDLocalFn

  The user must supply a function G(t,y,y') which approximates
  the function F for the system F(t,y,y') = 0, and which is
  computed locally (without interprocess communication).
  (The case where G is mathematically identical to F is allowed.)
  The implementation of this function must have type IDABBDLocalFn.
 
  This function takes as input the independent variable value tt,
  the current solution vector yy, the current solution
  derivative vector yp, and a pointer to the user-defined data
  block user_data. It is to compute the local part of G(t,y,y')
  and store it in the vector gval. (Providing memory for yy and
  gval is handled within this preconditioner module.) It is
  expected that this routine will save communicated data in work
  space defined by the user, and made available to the
  preconditioner function for the problem. The user_data
  parameter is the same as that passed by the user to the
  IDAMalloc routine.
 
  An IDABBDLocalFn Gres is to return an int, defined in the same
  way as for the residual function: 0 (success), +1 or -1 (fail).
  -----------------------------------------------------------------*/
typedef int (*IDABBDLocalFn)(sunindextype Nlocal, realtype tt,
			     N_Vector yy, N_Vector yp, N_Vector gval,
			     void *user_data);

/*-----------------------------------------------------------------
  Type : IDABBDCommFn
  -----------------------------------------------------------------
  The user may supply a function of type IDABBDCommFn which
  performs all interprocess communication necessary to
  evaluate the approximate system function described above.
 
  This function takes as input the solution vectors yy and yp,
  and a pointer to the user-defined data block user_data. The
  user_data parameter is the same as that passed by the user to
  the IDASetUserData routine.
 
  The IDABBDCommFn Gcomm is expected to save communicated data in
  space defined with the structure *user_data.
 
  A IDABBDCommFn Gcomm returns an int value equal to 0 (success),
  > 0 (recoverable error), or < 0 (unrecoverable error).
 
  Each call to the IDABBDCommFn is preceded by a call to the system
  function res with the same vectors yy and yp. Thus the
  IDABBDCommFn gcomm can omit any communications done by res if
  relevant to the evaluation of the local function glocal.
  A NULL communication function can be passed to IDABBDPrecInit
  if all necessary communication was done by res.
  -----------------------------------------------------------------*/
typedef int (*IDABBDCommFn)(sunindextype Nlocal, realtype tt,
			    N_Vector yy, N_Vector yp, void *user_data);

/*-----------------------------------------------------------------
  Function : IDABBDPrecInit

  IDABBDPrecInit allocates and initializes the BBD preconditioner.
 
  The parameters of IDABBDPrecInit are as follows:
 
  ida_mem  is a pointer to the memory blockreturned by IDACreate.
 
  Nlocal  is the length of the local block of the vectors yy etc.
          on the current processor.
 
  mudq, mldq  are the upper and lower half-bandwidths to be used
              in the computation of the local Jacobian blocks.
 
  mukeep, mlkeep are the upper and lower half-bandwidths to be
                 used in saving the Jacobian elements in the local
                 block of the preconditioner matrix PP.
 
  dq_rel_yy is an optional input. It is the relative increment
            to be used in the difference quotient routine for
            Jacobian calculation in the preconditioner. The
            default is sqrt(unit roundoff), and specified by
            passing dq_rel_yy = 0.
 
  Gres    is the name of the user-supplied function G(t,y,y')
          that approximates F and whose local Jacobian blocks
          are to form the preconditioner.
 
  Gcomm   is the name of the user-defined function that performs
          necessary interprocess communication for the
          execution of glocal.
 
  The return value of IDABBDPrecInit is one of:
    IDASPILS_SUCCESS if no errors occurred
    IDASPILS_MEM_NULL if the integrator memory is NULL
    IDASPILS_LMEM_NULL if the linear solver memory is NULL
    IDASPILS_ILL_INPUT if an input has an illegal value
    IDASPILS_MEM_FAIL if a memory allocation request failed
  -----------------------------------------------------------------*/
SUNDIALS_EXPORT int IDABBDPrecInit(void *ida_mem,
                                   sunindextype Nlocal,
                                   sunindextype mudq,
                                   sunindextype mldq,
                                   sunindextype mukeep,
                                   sunindextype mlkeep,
                                   realtype dq_rel_yy,
                                   IDABBDLocalFn Gres,
                                   IDABBDCommFn Gcomm);

/*-----------------------------------------------------------------
  Function : IDABBDPrecReInit

  IDABBDPrecReInit reinitializes the IDABBDPRE module when
  solving a sequence of problems of the same size with
  IDASPILS/IDABBDPRE provided there is no change in Nlocal, mukeep, 
  or mlkeep. After solving one problem, and after calling IDAReInit 
  to reinitialize the integrator for a subsequent problem, call 
  IDABBDPrecReInit.
 
  All arguments have the same names and meanings as those
  of IDABBDPrecInit.
 
  The return value of IDABBDPrecReInit is one of:
    IDASPILS_SUCCESS if no errors occurred
    IDASPILS_MEM_NULL if the integrator memory is NULL
    IDASPILS_LMEM_NULL if the linear solver memory is NULL
    IDASPILS_PMEM_NULL if the preconditioner memory is NULL
  -----------------------------------------------------------------*/
SUNDIALS_EXPORT int IDABBDPrecReInit(void *ida_mem,
                                     sunindextype mudq, 
				     sunindextype mldq,
				     realtype dq_rel_yy);

/*-----------------------------------------------------------------
  Optional outputs for IDABBDPRE

  IDABBDPrecGetWorkSpace returns the real and integer work space
                         for IDABBDPRE.
  IDABBDPrecGetNumGfnEvals returns the number of calls to the
                           user Gres function.
  
  The return value of IDABBDPrecGet* is one of:
    IDASPILS_SUCCESS if no errors occurred
    IDASPILS_MEM_NULL if the integrator memory is NULL
    IDASPILS_LMEM_NULL if the linear solver memory is NULL
    IDASPILS_PMEM_NULL if the preconditioner memory is NULL
  -----------------------------------------------------------------*/
SUNDIALS_EXPORT int IDABBDPrecGetWorkSpace(void *ida_mem, 
                                           long int *lenrwBBDP,
                                           long int *leniwBBDP);
SUNDIALS_EXPORT int IDABBDPrecGetNumGfnEvals(void *ida_mem,
                                             long int *ngevalsBBDP);


/*=================================================================
  PART II - backward problems
  =================================================================*/

/*-----------------------------------------------------------------
  Types: IDALocalFnB and IDACommFnB

  Local approximation function and inter-process communication
  function for the BBD preconditioner on the backward phase.
  -----------------------------------------------------------------*/
typedef int (*IDABBDLocalFnB)(sunindextype NlocalB, realtype tt,
			      N_Vector yy, N_Vector yp,
                              N_Vector yyB, N_Vector ypB,
                              N_Vector gvalB, void *user_dataB);
typedef int (*IDABBDCommFnB)(sunindextype NlocalB, realtype tt,
			     N_Vector yy, N_Vector yp,
			     N_Vector yyB, N_Vector ypB,
			     void *user_dataB);

/*-----------------------------------------------------------------
  Functions: IDABBDPrecInitB, IDABBDPrecReInit

  Interface functions for the IDABBDPRE preconditioner to be used
  on the backward phase.
  The 'which' argument is the int returned by IDACreateB.
  -----------------------------------------------------------------*/
SUNDIALS_EXPORT int IDABBDPrecInitB(void *ida_mem, int which,
                                    sunindextype NlocalB,
                                    sunindextype mudqB,
                                    sunindextype mldqB,
                                    sunindextype mukeepB,
                                    sunindextype mlkeepB,
                                    realtype dq_rel_yyB,
                                    IDABBDLocalFnB GresB,
                                    IDABBDCommFnB GcommB);
SUNDIALS_EXPORT int IDABBDPrecReInitB(void *ida_mem, int which, 
                                      sunindextype mudqB,
                                      sunindextype mldqB,
                                      realtype dq_rel_yyB);

#ifdef __cplusplus
}
#endif

#endif
