Actual source code: bdquadratic.c

  1: #ifdef PETSC_RCS_HEADER
  2: static char vcid[] = "$Id: bdquadratic.c,v 1.4 2000/01/31 17:56:20 knepley Exp $";
  3: #endif

  5: /*
  6:    Defines piecewise quadratic function space on a two dimensional 
  7:    grid. Suitable for finite element type discretization of a PDE.
  8: */

 10: #include "src/grid/discretization/discimpl.h"         /*I "discretization.h" I*/
 11: #include "src/mesh/impls/triangular/triimpl.h"

 13: /* For precomputed integrals, the table is structured as follows:

 15:      precompInt[op,i,j] = int_{SE} <op phi^i(xi), phi^j(xi)> |J^{-1}|

 17:    where we recall that |J| is a constant for linear affine maps,
 18:    and the map of any triangle to the standard element is linear.
 19:    The numbering of the nodes in the standard element is

 21:                  1--3--2
 22: */

 24: static int BoundaryDiscDestroy_Triangular_2D_Quadratic(Discretization disc) {
 26:   return(0);
 27: }

 29: static int BoundaryDiscView_Triangular_2D_Quadratic_File(Discretization disc, PetscViewer viewer) {
 31:   PetscViewerASCIIPrintf(viewer, "Quadratic boundary discretizationn");
 32:   PetscViewerASCIIPrintf(viewer, "    %d shape functions per componentn", disc->funcs);
 33:   PetscViewerASCIIPrintf(viewer, "    %d registered operatorsn", disc->numOps);
 34:   return(0);
 35: }

 37: static int BoundaryDiscView_Triangular_2D_Quadratic(Discretization disc, PetscViewer viewer) {
 38:   PetscTruth isascii;
 39:   int        ierr;

 42:   PetscTypeCompare((PetscObject) viewer, PETSC_VIEWER_ASCII, &isascii);
 43:   if (isascii == PETSC_TRUE) {
 44:     BoundaryDiscView_Triangular_2D_Quadratic_File(disc, viewer);
 45:   }
 46:   return(0);
 47: }

 49: static int BoundaryDiscEvaluateFunctionGalerkin_Triangular_2D_Quadratic(Discretization disc, Mesh mesh, PointFunction f,
 50:                                                                         PetscScalar alpha, int edge, PetscScalar *array, void *ctx)
 51: {
 52:   Mesh_Triangular *tri            = (Mesh_Triangular *) mesh->data;
 53:   EdgeContext     *bdCtx          = (EdgeContext *)         ctx;
 54:   void            *uCtx           = bdCtx->ctx;
 55:   double          *nodes          = tri->nodes;
 56:   int             *edges          = tri->edges;
 57:   int              comp           = disc->comp;                 /* The number of components in this field */
 58:   int              funcs          = disc->funcs;                /* The number of shape functions per component */
 59:   PetscScalar     *funcVal        = disc->funcVal;              /* Function value at a quadrature point */
 60:   int              numQuadPoints  = disc->numQuadPoints;        /* Number of points used for Gaussian quadrature */
 61:   double          *quadWeights    = disc->quadWeights;          /* Weights in the standard element for Gaussian quadrature */
 62:   double          *quadShapeFuncs = disc->quadShapeFuncs;       /* Shape function evaluated at quadrature points */
 63:   double          *quadShapeFuncDers = disc->quadShapeFuncDers; /* Shape function derivatives at quadrature points */
 64:   double           jac;                                         /* |J| for map to standard element */
 65:   double           x, y;                                        /* The integration point */
 66:   double           dxxi;                                        /* PartDer{x}{xi}  */
 67:   double           dyxi;                                        /* PartDer{y}{eta} */
 68:   double           coords[3*2];
 69:   int              rank;
 70:   int              i, j, k, p, func;
 71: #ifdef PETSC_USE_BOPT_g
 72:   PetscTruth       opt;
 73: #endif
 74:   int              ierr;

 77:   MPI_Comm_rank(disc->comm, &rank);

 79:   /* For dummy collective calls */
 80:   if (array == PETSC_NULL) {
 81:     for(p = 0; p < numQuadPoints; p++) {
 82:       (*f)(0, 0, PETSC_NULL, PETSC_NULL, PETSC_NULL, PETSC_NULL, ctx);
 83:     }
 84:     return(0);
 85:   }

 87: #ifdef PETSC_USE_BOPT_g
 88:   if ((edge < 0) || (edge >= mesh->numEdges)) SETERRQ(PETSC_ERR_ARG_OUTOFRANGE, "Invalid edge");
 89: #endif
 90:   /* Calculate the determinant of the inverse Jacobian of the map to the standard element */
 91:   if (mesh->isPeriodic == PETSC_TRUE) {
 92:     coords[0*2]   = nodes[edges[edge*2+0]*2];
 93:     coords[0*2+1] = nodes[edges[edge*2+0]*2+1];
 94:     coords[1*2+0] = MeshPeriodicRelativeX(mesh, nodes[edges[edge*2+1]*2],   coords[0*2+0]);
 95:     coords[1*2+1] = MeshPeriodicRelativeY(mesh, nodes[edges[edge*2+1]*2+1], coords[0*2+1]);
 96:     coords[2*2+0] = MeshPeriodicRelativeX(mesh, nodes[bdCtx->midnode *2],   coords[0*2+0]);
 97:     coords[2*2+1] = MeshPeriodicRelativeY(mesh, nodes[bdCtx->midnode *2+1], coords[0*2+1]);
 98:   } else {
 99:     coords[0*2]   = nodes[edges[edge*2+0]*2];
100:     coords[0*2+1] = nodes[edges[edge*2+0]*2+1];
101:     coords[1*2]   = nodes[edges[edge*2+1]*2];
102:     coords[1*2+1] = nodes[edges[edge*2+1]*2+1];
103:     coords[2*2]   = nodes[bdCtx->midnode *2];
104:     coords[2*2+1] = nodes[bdCtx->midnode *2+1];
105:   }
106: #ifdef PETSC_USE_BOPT_g
107:   PetscOptionsHasName(PETSC_NULL, "-trace_assembly", &opt);
108:   if (opt == PETSC_TRUE) {
109:     PetscPrintf(PETSC_COMM_SELF, "[%d]edge: %d x1: %g y1: %g x2: %g y2: %g x3: %g y3: %gn",
110:                 rank, edge, coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]);
111:   }
112: #endif

114:   /* Calculate element vector entries by Gaussian quadrature */
115:   for(p = 0; p < numQuadPoints; p++)
116:   {
117:     /* x                    = sum^{funcs}_{f=1} x_f phi^f(p)
118:        PartDer{x}{xi}(p)  = sum^{funcs}_{f=1} x_f PartDer{phi^f(p)}{xi}
119:        y                    = sum^{funcs}_{f=1} y_f phi^f(p)
120:        PartDer{y}{xi}(p)  = sum^{funcs}_{f=1} y_f PartDer{phi^f(p)}{xi} */
121:     x    = 0.0; y    = 0.0;
122:     dxxi = 0.0; dyxi = 0.0;
123:     for(func = 0; func < funcs; func++)
124:     {
125:       x    += coords[func*2]  *quadShapeFuncs[p*funcs+func];
126:       dxxi += coords[func*2]  *quadShapeFuncDers[p*funcs+func];
127:       y    += coords[func*2+1]*quadShapeFuncs[p*funcs+func];
128:       dyxi += coords[func*2+1]*quadShapeFuncDers[p*funcs+func];
129:     }
130:     if (mesh->isPeriodic == PETSC_TRUE) {
131:       x = MeshPeriodicX(mesh, x);
132:       y = MeshPeriodicY(mesh, y);
133:     }
134:     jac = sqrt(dxxi*dxxi + dyxi*dyxi);
135:     if (jac < 1.0e-14) {
136:       PetscPrintf(PETSC_COMM_SELF, "[%d]p: %d x1: %g y1: %g x2: %g y2: %g x3: %g y3: %gn",
137:                   rank, p, coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]);
138:       SETERRQ(PETSC_ERR_DISC_SING_JAC, "Singular Jacobian");
139:     }
140:     (*f)(1, comp, &x, &y, PETSC_NULL, funcVal, uCtx);
141: #ifdef PETSC_USE_BOPT_g
142:     PetscOptionsHasName(PETSC_NULL, "-trace_assembly", &opt);
143:     if (opt == PETSC_TRUE) {
144:       PetscPrintf(PETSC_COMM_SELF, "[%d]p:%d jac: %g", rank, p, jac);
145:       for(j = 0; j < comp; j++)
146:         PetscPrintf(PETSC_COMM_SELF, " func[%d]: %g", j, PetscRealPart(funcVal[j]));
147:       PetscPrintf(PETSC_COMM_SELF, "n");
148:     }
149: #endif

151:     for(i = 0, k = 0; i < funcs; i++) {
152:       for(j = 0; j < comp; j++, k++) {
153:         array[k] += alpha*funcVal[j]*quadShapeFuncs[p*funcs+i]*jac*quadWeights[p];
154: #ifdef PETSC_USE_BOPT_g
155:         PetscOptionsHasName(PETSC_NULL, "-trace_assembly", &opt);
156:         if (opt == PETSC_TRUE) {
157:           PetscPrintf(PETSC_COMM_SELF, "[%d]  array[%d]: %gn", rank, k, PetscRealPart(array[k]));
158:         }
159: #endif
160:       }
161:     }
162:   }
163:   PetscLogFlops((4 + 8*funcs + 5*funcs*comp) * numQuadPoints);
164:   return(0);
165: }

167: static int BoundaryDiscEvaluateOperatorGalerkin_Triangular_2D_Quadratic(Discretization disc, Mesh mesh, int elemSize,
168:                                                                      int rowStart, int colStart, int op, PetscScalar alpha,
169:                                                                      int edge, PetscScalar *field, PetscScalar *mat, void *ctx)
170: {
171:   Mesh_Triangular *tri        = (Mesh_Triangular *) mesh->data;
172:   EdgeContext     *bdCtx      = (EdgeContext *)         ctx;
173:   void            *uCtx       = bdCtx->ctx;
174:   double          *nodes      = tri->nodes;
175:   int             *edges      = tri->edges;
176:   Operator         oper       = disc->operators[op]; /* The operator to discretize */
177:   Discretization   test       = oper->test;          /* The space of test functions */
178:   int              rowSize    = test->size;          /* The number of shape functions per element */
179:   int              colSize    = disc->size;          /* The number of test  functions per element */
180:   OperatorFunction opFunc     = oper->opFunc;        /* Integrals of operators which depend on J */
181:   double           coords[MAX_CORNERS*2];            /* Coordinates of the element */
182:   int              ierr;

185: #ifdef PETSC_USE_BOPT_g
186:   /* Check for valid operator */
187:   if ((op < 0) || (op >= disc->numOps) || (!disc->operators[op])) SETERRQ(PETSC_ERR_ARG_WRONG, "Invalid operator");
188: #endif

190:   if (opFunc == PETSC_NULL) SETERRQ(PETSC_ERR_ARG_CORRUPT, "Invalid function");
191:   if (mesh->isPeriodic == PETSC_TRUE) {
192:     coords[0*2]   = nodes[edges[edge*2+0]*2];
193:     coords[0*2+1] = nodes[edges[edge*2+0]*2+1];
194:     coords[1*2+0] = MeshPeriodicRelativeX(mesh, nodes[edges[edge*2+1]*2],   coords[0*2+0]);
195:     coords[1*2+1] = MeshPeriodicRelativeY(mesh, nodes[edges[edge*2+1]*2+1], coords[0*2+1]);
196:     coords[2*2+0] = MeshPeriodicRelativeX(mesh, nodes[bdCtx->midnode *2],   coords[0*2+0]);
197:     coords[2*2+1] = MeshPeriodicRelativeY(mesh, nodes[bdCtx->midnode *2+1], coords[0*2+1]);
198:   } else {
199:     coords[0*2]   = nodes[edges[edge*2+0]*2];
200:     coords[0*2+1] = nodes[edges[edge*2+0]*2+1];
201:     coords[1*2]   = nodes[edges[edge*2+1]*2];
202:     coords[1*2+1] = nodes[edges[edge*2+1]*2+1];
203:     coords[2*2]   = nodes[bdCtx->midnode *2];
204:     coords[2*2+1] = nodes[bdCtx->midnode *2+1];
205:   }

207:   (*opFunc)(disc, test, rowSize, colSize, rowStart, colStart, elemSize, coords, alpha, field, mat, uCtx);
208: 

210:   return(0);
211: }

213: static int BoundaryDiscEvaluateALEOperatorGalerkin_Triangular_2D_Quadratic(Discretization disc, Mesh mesh, int elemSize,
214:                                                                         int rowStart, int colStart, int op, PetscScalar alpha,
215:                                                                         int elem, PetscScalar *field, PetscScalar *ALEfield, PetscScalar *mat,
216:                                                                         void *ctx)
217: {
218:   SETERRQ(PETSC_ERR_SUP, " ");
219: }

221: static int BoundaryDiscEvaluateNonlinearOperatorGalerkin_Triangular_2D_Quadratic(Discretization disc, Mesh mesh,
222:                                                                                  NonlinearOperator f, PetscScalar alpha, int elem,
223:                                                                                  int numArgs, PetscScalar **field, PetscScalar *vec, void *ctx)
224: {
225:   SETERRQ(PETSC_ERR_SUP, " ");
226: }

228: static int BoundaryDiscEvaluateNonlinearALEOperatorGalerkin_Triangular_2D_Quadratic(Discretization disc, Mesh mesh,
229:                                                                                     NonlinearOperator f, PetscScalar alpha, int elem,
230:                                                                                     int numArgs, PetscScalar **field, PetscScalar *ALEfield,
231:                                                                                     PetscScalar *vec, void *ctx)
232: {
233:   SETERRQ(PETSC_ERR_SUP, " ");
234: }

236: int BoundaryIdentity_Triangular_2D_Quadratic(Discretization disc, Discretization test, int rowSize, int colSize,
237:                                              int globalRowStart, int globalColStart, int globalSize, double *coords,
238:                                              PetscScalar alpha, PetscScalar *field, PetscScalar *array, void *ctx)
239: {
240:   int     numQuadPoints     = disc->numQuadPoints;     /* Number of points used for Gaussian quadrature */
241:   double *quadWeights       = disc->quadWeights;       /* Weights in the standard element for Gaussian quadrature */
242:   double *quadShapeFuncs    = disc->quadShapeFuncs;    /* Shape functions evaluated at quadrature points */
243:   double *quadTestFuncs     = test->quadShapeFuncs;    /* Test  functions evaluated at quadrature points */
244:   double *quadShapeFuncDers = disc->quadShapeFuncDers; /* Shape function derivatives evaluated at quadrature points */
245:   int     comp              = disc->comp;              /* The number of field components */
246:   int     tcomp             = test->comp;              /* The number of field components */
247:   int     funcs             = disc->funcs;             /* The number of shape functions */
248:   int     tfuncs            = test->funcs;             /* The number of test functions */
249:   double  dxxi;                                        /* PartDer{x}{xi}  */
250:   double  dyxi;                                        /* PartDer{y}{xi}  */
251:   double  jac;                                         /* |J| for map to standard element */
252:   int     i, j, c, f, p;

255: #ifdef PETSC_USE_BOPT_g
256:   if (comp != tcomp) SETERRQ(PETSC_ERR_ARG_INCOMP, "Shape and test fields must have the same number of components");
257: #endif
258:   /* Calculate element matrix entries by Gaussian quadrature */
259:   for(p = 0; p < numQuadPoints; p++) {
260:     /* PartDer{x}{xi}(p)  = sum^{funcs}_{f=1} x_f PartDer{phi^f(p)}{xi}
261:        PartDer{y}{xi}(p)  = sum^{funcs}_{f=1} y_f PartDer{phi^f(p)}{xi} */
262:     dxxi = 0.0; dyxi = 0.0;
263:     for(f = 0; f < funcs; f++) {
264:       dxxi += coords[f*2]  *quadShapeFuncDers[p*funcs+f];
265:       dyxi += coords[f*2+1]*quadShapeFuncDers[p*funcs+f];
266:     }
267:     jac = sqrt(dxxi*dxxi + dyxi*dyxi);
268: #ifdef PETSC_USE_BOPT_g
269:     if (jac < 1.0e-14) {
270:       PetscPrintf(PETSC_COMM_SELF, "p: %d x1: %g y1: %g x2: %g y2: %g x3: %g y3: %gn",
271:                   p, coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]);
272:       SETERRQ(PETSC_ERR_DISC_SING_JAC, "Singular Jacobian");
273:     }
274: #endif

276:     for(i = 0; i < tfuncs; i++) {
277:       for(j = 0; j < funcs; j++) {
278:         for(c = 0; c < comp; c++) {
279:           array[(i*tcomp+c+globalRowStart)*globalSize + j*comp+c+globalColStart] +=
280:             alpha*quadTestFuncs[p*tfuncs+i]*quadShapeFuncs[p*funcs+j]*jac*quadWeights[p];
281:         }
282:       }
283:     }
284:   }
285:   PetscLogFlops((4*funcs + 4 + 5*funcs*funcs*comp) * numQuadPoints);

287:   return(0);
288: }

290: int BoundaryDiscInterpolateField_Triangular_2D_Quadratic(Discretization disc, Mesh oldMesh, int elem, double x, double y, double z,
291:                                                       PetscScalar *oldFieldVal, PetscScalar *newFieldVal, InterpolationType type)
292: {
293:   SETERRQ(PETSC_ERR_SUP, " ");
294: }

296: int BoundaryDiscInterpolateElementVec_Triangular_2D_Quadratic(Discretization disc, ElementVec vec,
297:                                                               Discretization newDisc, ElementVec newVec)
298: {
299:   SETERRQ(PETSC_ERR_SUP, " ");
300: }

302: /*
303:   BoundaryDiscSetupQuadrature_Triangular_2D_Linear - Setup Gaussian quadrature with a 5 point integration rule

305:   Input Parameter:
306: . disc - The Discretization
307: */
308: int BoundaryDiscSetupQuadrature_Triangular_2D_Quadratic(Discretization disc) {
309:   int dim   = disc->dim;
310:   int funcs = disc->funcs;
311:   int p;

315:   disc->numQuadPoints = 5;
316:   PetscMalloc(disc->numQuadPoints*dim       * sizeof(double), &disc->quadPoints);
317:   PetscMalloc(disc->numQuadPoints           * sizeof(double), &disc->quadWeights);
318:   PetscMalloc(disc->numQuadPoints*funcs     * sizeof(double), &disc->quadShapeFuncs);
319:   PetscMalloc(disc->numQuadPoints*funcs*dim * sizeof(double), &disc->quadShapeFuncDers);
320:   PetscLogObjectMemory(disc, (disc->numQuadPoints*(funcs*(dim+1) + dim+1)) * sizeof(double));
321:   disc->quadPoints[0]  = 0.0469101;
322:   disc->quadWeights[0] = 0.118463;
323:   disc->quadPoints[1]  = 0.230765;
324:   disc->quadWeights[1] = 0.239314;
325:   disc->quadPoints[2]  = 0.500000;
326:   disc->quadWeights[2] = 0.284444;
327:   disc->quadPoints[3]  = 0.769235;
328:   disc->quadWeights[3] = 0.239314;
329:   disc->quadPoints[4]  = 0.953090;
330:   disc->quadWeights[4] = 0.118463;
331:   for(p = 0; p < disc->numQuadPoints; p++) {
332:     /* phi^0: (1 - xi) (1 - 2 xi) */
333:     disc->quadShapeFuncs[p*funcs+0]    = (1.0 - disc->quadPoints[p])*(1.0 - 2.0*disc->quadPoints[p]);
334:     disc->quadShapeFuncDers[p*funcs+0] =  4.0*disc->quadPoints[p] - 3.0;
335:     /* phi^1: -xi (1 - 2 xi) */
336:     disc->quadShapeFuncs[p*funcs+1]    = -disc->quadPoints[p]*(1.0 - 2.0*disc->quadPoints[p]);
337:     disc->quadShapeFuncDers[p*funcs+1] =  4.0*disc->quadPoints[p] - 1.0;
338:     /* phi^2: 4 xi (1 - xi) */
339:     disc->quadShapeFuncs[p*funcs+2]    =  4.0*disc->quadPoints[p]*(1.0 - disc->quadPoints[p]);
340:     disc->quadShapeFuncDers[p*funcs+2] =  4.0 - 8.0*disc->quadPoints[p];
341:   }
342:   return(0);
343: }

345: /*
346:   BoundaryDiscSetupOperators_Triangular_2D_Quadratic - Setup the default operators

348:   Input Parameter:
349: . disc - The Discretization
350: */
351: int BoundaryDiscSetupOperators_Triangular_2D_Quadratic(Discretization disc) {
352:   int newOp;

356:   /* The Identity operator I -- the matrix is symmetric */
357:   DiscretizationRegisterOperator(disc, BoundaryIdentity_Triangular_2D_Quadratic, &newOp);
358:   if (newOp != IDENTITY) SETERRQ1(PETSC_ERR_ARG_WRONGSTATE, "Default operator %d not setup correctly", IDENTITY);
359:   /* The Laplacian operator Delta -- the matrix is symmetric */
360:   DiscretizationRegisterOperator(disc, PETSC_NULL, &newOp);
361:   if (newOp != LAPLACIAN) SETERRQ1(PETSC_ERR_ARG_WRONGSTATE, "Default operator %d not setup correctly", LAPLACIAN);
362:   /* The Gradient operator nabla -- the matrix is rectangular */
363:   DiscretizationRegisterOperator(disc, PETSC_NULL, &newOp);
364:   if (newOp != GRADIENT) SETERRQ1(PETSC_ERR_ARG_WRONGSTATE, "Default operator %d not setup correctly", GRADIENT);
365:   /* The Divergence operator nablacdot -- the matrix is rectangular */
366:   DiscretizationRegisterOperator(disc, PETSC_NULL, &newOp);
367:   if (newOp != DIVERGENCE) SETERRQ1(PETSC_ERR_ARG_WRONGSTATE, "Default operator %d not setup correctly", DIVERGENCE);
368:   /* The weighted Laplacian operator -- the matrix is symmetric */
369:   DiscretizationRegisterOperator(disc, PETSC_NULL, &newOp);
370:   if (newOp != WEIGHTED_LAP) SETERRQ1(PETSC_ERR_ARG_WRONGSTATE, "Default operator %d not setup correctly", WEIGHTED_LAP);
371:   return(0);
372: }

374: static struct _DiscretizationOps DOps = {PETSC_NULL/* DiscretizationSetup */,
375:                                          BoundaryDiscSetupOperators_Triangular_2D_Quadratic,
376:                                          PETSC_NULL/* DiscretizationSetFromOptions */,
377:                                          BoundaryDiscView_Triangular_2D_Quadratic,
378:                                          BoundaryDiscDestroy_Triangular_2D_Quadratic,
379:                                          BoundaryDiscEvaluateFunctionGalerkin_Triangular_2D_Quadratic,
380:                                          BoundaryDiscEvaluateOperatorGalerkin_Triangular_2D_Quadratic,
381:                                          BoundaryDiscEvaluateALEOperatorGalerkin_Triangular_2D_Quadratic,
382:                                          BoundaryDiscEvaluateNonlinearOperatorGalerkin_Triangular_2D_Quadratic,
383:                                          BoundaryDiscEvaluateNonlinearALEOperatorGalerkin_Triangular_2D_Quadratic,
384:                                          BoundaryDiscInterpolateField_Triangular_2D_Quadratic,
385:                                          BoundaryDiscInterpolateElementVec_Triangular_2D_Quadratic};

387: EXTERN_C_BEGIN
388: int BoundaryDiscCreate_Triangular_2D_Quadratic(Discretization disc) {
389:   int sarg;

393:   if (disc->comp <= 0) {
394:     SETERRQ(PETSC_ERR_ARG_WRONG, "Discretization must have at least 1 component. Call DiscretizationSetNumComponents() to set this.");
395:   }
396:   PetscMemcpy(disc->ops, &DOps, sizeof(struct _DiscretizationOps));
397:   disc->dim   = 2;
398:   disc->funcs = 3;
399:   disc->size  = disc->funcs*disc->comp;

401:   DiscretizationSetupDefaultOperators(disc);
402:   BoundaryDiscSetupQuadrature_Triangular_2D_Quadratic(disc);

404:   /* Storage */
405:   PetscMalloc(disc->comp * sizeof(PetscScalar),   &disc->funcVal);
406:   PetscMalloc(2          * sizeof(PetscScalar *), &disc->fieldVal);
407:   for(sarg = 0; sarg < 2; sarg++) {
408:     PetscMalloc(disc->comp*(disc->dim+1) * sizeof(PetscScalar), &disc->fieldVal[sarg]);
409:   }
410:   return(0);
411: }
412: EXTERN_C_END