cantera/ext/cvode/source/cvbandpre.c

318 lines
12 KiB
C

/******************************************************************
* *
* File : cvbandpre.c *
* Programmers : Michael Wittman and Alan C. Hindmarsh @ LLNL *
* Version of : 23 March 2000 *
*----------------------------------------------------------------*
* This file contains implementations of the banded difference *
* quotient Jacobian-based preconditioner and solver routines for *
* use with CVSpgmr. *
* *
******************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include "cvbandpre.h"
#include "cvode.h"
#include "llnltyps.h"
#include "nvector.h"
#include "llnlmath.h"
#include "band.h"
#define MIN_INC_MULT RCONST(1000.0)
#define ZERO RCONST(0.0)
#define ONE RCONST(1.0)
/* Prototype for difference quotient Jacobian calculation routine */
static void CVBandPDQJac(integer N, integer mupper, integer mlower, BandMat J,
RhsFn f, void *f_data, real tn, N_Vector y,
N_Vector fy, N_Vector ewt, real h, real uround,
N_Vector ftemp, N_Vector ytemp);
/********************** Malloc and Free Functions **********************/
CVBandPreData CVBandPreAlloc(integer N, RhsFn f, void *f_data,
integer mu, integer ml)
{
CVBandPreData pdata;
integer mup, mlp, storagemu;
pdata = (CVBandPreData) malloc(sizeof *pdata); /* Allocate data memory */
if (pdata == NULL) return(NULL);
/* Load pointers and bandwidths into pdata block. */
pdata->f = f;
pdata->f_data = f_data;
pdata->mu = mup = MIN( N-1, MAX(0,mu) );
pdata->ml = mlp = MIN( N-1, MAX(0,ml) );
/* Allocate memory for saved banded Jacobian approximation. */
pdata->savedJ = BandAllocMat(N, mup, mlp, mup);
if (pdata->savedJ == NULL) {
free(pdata);
return(NULL);
}
/* Allocate memory for banded preconditioner. */
storagemu = MIN( N-1, mup + mlp);
pdata->savedP = BandAllocMat(N, mup, mlp, storagemu);
if (pdata->savedP == NULL) {
BandFreeMat(pdata->savedJ);
free(pdata);
return(NULL);
}
/* Allocate memory for pivot array. */
pdata->pivots = BandAllocPiv(N);
if (pdata->savedJ == NULL) {
BandFreeMat(pdata->savedP);
BandFreeMat(pdata->savedJ);
free(pdata);
return(NULL);
}
return(pdata);
}
void CVBandPreFree(CVBandPreData pdata)
{
BandFreeMat(pdata->savedJ);
BandFreeMat(pdata->savedP);
BandFreePiv(pdata->pivots);
free(pdata);
}
/***************** Preconditioner setup and solve functions *******/
/* Readability Replacements */
#define f (pdata->f)
#define f_data (pdata->f_data)
#define mu (pdata->mu)
#define ml (pdata->ml)
#define pivots (pdata->pivots)
#define savedJ (pdata->savedJ)
#define savedP (pdata->savedP)
/* Preconditioner setup routine CVBandPrecond. */
/******************************************************************
* Together CVBandPrecond and CVBandPSolve use a banded *
* difference quotient Jacobian to create a preconditioner. *
* CVBandPrecond calculates a new J, if necessary, then *
* calculates P = I - gamma*J, and does an LU factorization of P. *
* *
* The parameters of CVBandPrecond are as follows: *
* *
* N is the length of all vector arguments. *
* *
* t is the current value of the independent variable. *
* *
* y is the current value of the dependent variable vector, *
* namely the predicted value of y(t). *
* *
* fy is the vector f(t,y). *
* *
* jok is an input flag indicating whether Jacobian-related *
* data needs to be recomputed, as follows: *
* jok == FALSE means recompute Jacobian-related data *
* from scratch. *
* jok == TRUE means that Jacobian data from the *
* previous Precond call will be reused *
* (with the current value of gamma). *
* A CVBandPrecond call with jok == TRUE should only *
* occur after a call with jok == FALSE. *
* *
* jcurPtr is a pointer to an output integer flag which is *
* set by CVBandPrecond as follows: *
* *jcurPtr = TRUE if Jacobian data was recomputed. *
* *jcurPtr = FALSE if Jacobian data was not recomputed,*
* but saved data was reused. *
* *
* gamma is the scalar appearing in the Newton matrix. *
* *
* ewt is the error weight vector. *
* *
* h is a tentative step size in t. *
* *
* uround is the machine unit roundoff. *
* *
* nfePtr is a pointer to the memory location containing the *
* CVODE problem data nfe = number of calls to f. *
* The routine calls f a total of ml+mu+1 times, so *
* it increments *nfePtr by ml+mu+1. *
* *
* bp_data is a pointer to preconditoner data - the same as the *
* bp_data parameter passed to CVSpgmr. *
* *
* vtemp1, vtemp2, and vtemp3 are pointers to memory allocated *
* for vectors of length N for work space. This *
* routine uses only vtemp1 and vtemp2. *
* *
* *
* The value to be returned by the CVBandPrecond function is *
* 0 if successful, or *
* 1 if the band factorization failed. *
******************************************************************/
int CVBandPrecond(integer N, real t, N_Vector y, N_Vector fy,
boole jok, boole *jcurPtr, real gamma,
N_Vector ewt, real h, real uround,
long int *nfePtr, void *bp_data,
N_Vector vtemp1, N_Vector vtemp2,
N_Vector vtemp3)
{
integer ier;
CVBandPreData pdata;
/* Assume matrix and pivots have already been allocated. */
pdata = (CVBandPreData) bp_data;
if (jok) {
/* If jok = TRUE, use saved copy of J. */
*jcurPtr = FALSE;
BandCopy(savedJ, savedP, mu, ml);
} else {
/* If jok = FALSE, call CVBandPDQJac for new J value. */
*jcurPtr = TRUE;
BandZero(savedJ);
CVBandPDQJac(N, mu, ml, savedJ, f, f_data, t, y, fy, ewt,
h, uround, vtemp1, vtemp2);
BandCopy(savedJ, savedP, mu, ml);
*nfePtr += MIN( N, ml + mu + 1 );
}
/* Scale and add I to get savedP = I - gamma*J. */
BandScale(-gamma, savedP);
BandAddI(savedP);
/* Do LU factorization of matrix. */
ier = BandFactor(savedP, pivots);
/* Return 0 if the LU was complete; otherwise return 1. */
if (ier > 0) return(1);
return(0);
}
/* Preconditioner solve routine CVBandPSolve */
/******************************************************************
* CVBandPSolve solves a linear system P z = r, where P is the *
* matrix computed by CVBandPrecond. *
* *
* The parameters of CVBandPSolve used here are as follows: *
* *
* r is the right-hand side vector of the linear system. *
* *
* bp_data is a pointer to preconditioner data - the same as the *
* bp_data parameter passed to CVSpgmr. *
* *
* z is the output vector computed by CVBandPSolve. *
* *
* The value returned by the CVBandPSolve function is always 0, *
* indicating success. *
* *
******************************************************************/
int CVBandPSolve(integer N, real t, N_Vector y, N_Vector fy,
N_Vector vtemp, real gamma, N_Vector ewt,
real delta, long int *nfePtr, N_Vector r,
int lr, void *bp_data, N_Vector z)
{
CVBandPreData pdata;
/* Assume matrix and pivots have already been allocated. */
pdata = (CVBandPreData) bp_data;
/* Copy r to z. */
N_VScale(ONE, r, z);
/* Do band backsolve on the vector z. */
BandBacksolve(savedP, pivots, z);
return(0);
}
#undef f
#undef f_data
#undef mu
#undef ml
#undef pivots
#undef savedJ
#undef savedP
/*************** CVBandPDQJac ****************************************
This routine generates a banded difference quotient approximation to
the Jacobian of f(t,y). It assumes that a band matrix of type
BandMat is stored column-wise, and that elements within each column
are contiguous. This makes it possible to get the address of a column
of J via the macro BAND_COL and to write a simple for loop to set
each of the elements of a column in succession.
**********************************************************************/
static void CVBandPDQJac(integer N, integer mupper, integer mlower, BandMat J,
RhsFn f, void *f_data, real tn, N_Vector y,
N_Vector fy, N_Vector ewt, real h, real uround,
N_Vector ftemp, N_Vector ytemp)
{
real fnorm, minInc, inc, inc_inv, srur;
integer group, i, j, width, ngroups, i1, i2;
real *col_j, *ewt_data, *fy_data, *ftemp_data, *y_data, *ytemp_data;
/* Obtain pointers to the data for ewt, fy, ftemp, y, ytemp. */
ewt_data = N_VDATA(ewt);
fy_data = N_VDATA(fy);
ftemp_data = N_VDATA(ftemp);
y_data = N_VDATA(y);
ytemp_data = N_VDATA(ytemp);
/* Load ytemp with y = predicted y vector. */
N_VScale(ONE, y, ytemp);
/* Set minimum increment based on uround and norm of f. */
srur = RSqrt(uround);
fnorm = N_VWrmsNorm(fy, ewt);
minInc = (fnorm != ZERO) ?
(MIN_INC_MULT * ABS(h) * uround * N * fnorm) : ONE;
/* Set bandwidth and number of column groups for band differencing. */
width = mlower + mupper + 1;
ngroups = MIN(width, N);
for (group = 1; group <= ngroups; group++) {
/* Increment all y_j in group. */
for(j = group-1; j < N; j += width) {
inc = MAX(srur*ABS(y_data[j]), minInc/ewt_data[j]);
ytemp_data[j] += inc;
}
/* Evaluate f with incremented y. */
f(N, tn, ytemp, ftemp, f_data);
/* Restore ytemp, then form and load difference quotients. */
for (j = group-1; j < N; j += width) {
ytemp_data[j] = y_data[j];
col_j = BAND_COL(J,j);
inc = MAX(srur*ABS(y_data[j]), minInc/ewt_data[j]);
inc_inv = ONE/inc;
i1 = MAX(0, j-mupper);
i2 = MIN(j+mlower, N-1);
for (i=i1; i <= i2; i++)
BAND_COL_ELEM(col_j,i,j) =
inc_inv * (ftemp_data[i] - fy_data[i]);
}
}
}