Date: 2022-03-04T17:38:27+00:00

Actual source code: plexfem.c

      1: #include <petsc/private/dmpleximpl.h>
      2: #include <petscsf.h>

      4: #include <petscblaslapack.h>
      5: #include <petsc/private/hashsetij.h>
      6: #include <petsc/private/petscfeimpl.h>
      7: #include <petsc/private/petscfvimpl.h>

      9: static PetscErrorCode DMPlexConvertPlex(DM dm, DM *plex, PetscBool copy)
     10: {
     11:   PetscBool      isPlex;

     15:   PetscObjectTypeCompare((PetscObject) dm, DMPLEX, &isPlex);
     16:   if (isPlex) {
     17:     *plex = dm;
     18:     PetscObjectReference((PetscObject) dm);
     19:   } else {
     20:     PetscObjectQuery((PetscObject) dm, "dm_plex", (PetscObject *) plex);
     21:     if (!*plex) {
     22:       DMConvert(dm, DMPLEX, plex);
     23:       PetscObjectCompose((PetscObject) dm, "dm_plex", (PetscObject) *plex);
     24:       if (copy) {
     25:         DMSubDomainHookLink link;

     27:         DMCopyAuxiliaryVec(dm, *plex);
     28:         /* Run the subdomain hook (this will copy the DMSNES/DMTS) */
     29:         for (link = dm->subdomainhook; link; link = link->next) {
     30:           if (link->ddhook) {(*link->ddhook)(dm, *plex, link->ctx);}
     31:         }
     32:       }
     33:     } else {
     34:       PetscObjectReference((PetscObject) *plex);
     35:     }
     36:   }
     37:   return(0);
     38: }

     40: static PetscErrorCode PetscContainerUserDestroy_PetscFEGeom (void *ctx)
     41: {
     42:   PetscFEGeom *geom = (PetscFEGeom *) ctx;

     46:   PetscFEGeomDestroy(&geom);
     47:   return(0);
     48: }

     50: static PetscErrorCode DMPlexGetFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom)
     51: {
     52:   char            composeStr[33] = {0};
     53:   PetscObjectId   id;
     54:   PetscContainer  container;
     55:   PetscErrorCode  ierr;

     58:   PetscObjectGetId((PetscObject)quad,&id);
     59:   PetscSNPrintf(composeStr, 32, "DMPlexGetFEGeom_%x\n", id);
     60:   PetscObjectQuery((PetscObject) pointIS, composeStr, (PetscObject *) &container);
     61:   if (container) {
     62:     PetscContainerGetPointer(container, (void **) geom);
     63:   } else {
     64:     DMFieldCreateFEGeom(coordField, pointIS, quad, faceData, geom);
     65:     PetscContainerCreate(PETSC_COMM_SELF,&container);
     66:     PetscContainerSetPointer(container, (void *) *geom);
     67:     PetscContainerSetUserDestroy(container, PetscContainerUserDestroy_PetscFEGeom);
     68:     PetscObjectCompose((PetscObject) pointIS, composeStr, (PetscObject) container);
     69:     PetscContainerDestroy(&container);
     70:   }
     71:   return(0);
     72: }

     74: static PetscErrorCode DMPlexRestoreFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom)
     75: {
     77:   *geom = NULL;
     78:   return(0);
     79: }

     81: /*@
     82:   DMPlexGetScale - Get the scale for the specified fundamental unit

     84:   Not collective

     86:   Input Parameters:
     87: + dm   - the DM
     88: - unit - The SI unit

     90:   Output Parameter:
     91: . scale - The value used to scale all quantities with this unit

     93:   Level: advanced

     95: .seealso: DMPlexSetScale(), PetscUnit
     96: @*/
     97: PetscErrorCode DMPlexGetScale(DM dm, PetscUnit unit, PetscReal *scale)
     98: {
     99:   DM_Plex *mesh = (DM_Plex*) dm->data;

    104:   *scale = mesh->scale[unit];
    105:   return(0);
    106: }

    108: /*@
    109:   DMPlexSetScale - Set the scale for the specified fundamental unit

    111:   Not collective

    113:   Input Parameters:
    114: + dm   - the DM
    115: . unit - The SI unit
    116: - scale - The value used to scale all quantities with this unit

    118:   Level: advanced

    120: .seealso: DMPlexGetScale(), PetscUnit
    121: @*/
    122: PetscErrorCode DMPlexSetScale(DM dm, PetscUnit unit, PetscReal scale)
    123: {
    124:   DM_Plex *mesh = (DM_Plex*) dm->data;

    128:   mesh->scale[unit] = scale;
    129:   return(0);
    130: }

    132: static PetscErrorCode DMPlexProjectRigidBody_Private(PetscInt dim, PetscReal t, const PetscReal X[], PetscInt Nc, PetscScalar *mode, void *ctx)
    133: {
    134:   const PetscInt eps[3][3][3] = {{{0, 0, 0}, {0, 0, 1}, {0, -1, 0}}, {{0, 0, -1}, {0, 0, 0}, {1, 0, 0}}, {{0, 1, 0}, {-1, 0, 0}, {0, 0, 0}}};
    135:   PetscInt *ctxInt  = (PetscInt *) ctx;
    136:   PetscInt  dim2    = ctxInt[0];
    137:   PetscInt  d       = ctxInt[1];
    138:   PetscInt  i, j, k = dim > 2 ? d - dim : d;

    141:   if (dim != dim2) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Input dimension %D does not match context dimension %D", dim, dim2);
    142:   for (i = 0; i < dim; i++) mode[i] = 0.;
    143:   if (d < dim) {
    144:     mode[d] = 1.; /* Translation along axis d */
    145:   } else {
    146:     for (i = 0; i < dim; i++) {
    147:       for (j = 0; j < dim; j++) {
    148:         mode[j] += eps[i][j][k]*X[i]; /* Rotation about axis d */
    149:       }
    150:     }
    151:   }
    152:   return(0);
    153: }

    155: /*@
    156:   DMPlexCreateRigidBody - For the default global section, create rigid body modes by function space interpolation

    158:   Collective on dm

    160:   Input Parameters:
    161: + dm - the DM
    162: - field - The field number for the rigid body space, or 0 for the default

    164:   Output Parameter:
    165: . sp - the null space

    167:   Note: This is necessary to provide a suitable coarse space for algebraic multigrid

    169:   Level: advanced

    171: .seealso: MatNullSpaceCreate(), PCGAMG
    172: @*/
    173: PetscErrorCode DMPlexCreateRigidBody(DM dm, PetscInt field, MatNullSpace *sp)
    174: {
    175:   PetscErrorCode (**func)(PetscInt, PetscReal, const PetscReal *, PetscInt, PetscScalar *, void *);
    176:   MPI_Comm          comm;
    177:   Vec               mode[6];
    178:   PetscSection      section, globalSection;
    179:   PetscInt          dim, dimEmbed, Nf, n, m, mmin, d, i, j;
    180:   PetscErrorCode    ierr;

    183:   PetscObjectGetComm((PetscObject) dm, &comm);
    184:   DMGetDimension(dm, &dim);
    185:   DMGetCoordinateDim(dm, &dimEmbed);
    186:   DMGetNumFields(dm, &Nf);
    187:   if (Nf && (field < 0 || field >= Nf)) SETERRQ2(comm, PETSC_ERR_ARG_OUTOFRANGE, "Field %D is not in [0, Nf)", field, Nf);
    188:   if (dim == 1 && Nf < 2) {
    189:     MatNullSpaceCreate(comm, PETSC_TRUE, 0, NULL, sp);
    190:     return(0);
    191:   }
    192:   DMGetLocalSection(dm, &section);
    193:   DMGetGlobalSection(dm, &globalSection);
    194:   PetscSectionGetConstrainedStorageSize(globalSection, &n);
    195:   PetscCalloc1(Nf, &func);
    196:   m    = (dim*(dim+1))/2;
    197:   VecCreate(comm, &mode[0]);
    198:   VecSetSizes(mode[0], n, PETSC_DETERMINE);
    199:   VecSetUp(mode[0]);
    200:   VecGetSize(mode[0], &n);
    201:   mmin = PetscMin(m, n);
    202:   func[field] = DMPlexProjectRigidBody_Private;
    203:   for (i = 1; i < m; ++i) {VecDuplicate(mode[0], &mode[i]);}
    204:   for (d = 0; d < m; d++) {
    205:     PetscInt ctx[2];
    206:     void    *voidctx = (void *) (&ctx[0]);

    208:     ctx[0] = dimEmbed;
    209:     ctx[1] = d;
    210:     DMProjectFunction(dm, 0.0, func, &voidctx, INSERT_VALUES, mode[d]);
    211:   }
    212:   /* Orthonormalize system */
    213:   for (i = 0; i < mmin; ++i) {
    214:     PetscScalar dots[6];

    216:     VecNormalize(mode[i], NULL);
    217:     VecMDot(mode[i], mmin-i-1, mode+i+1, dots+i+1);
    218:     for (j = i+1; j < mmin; ++j) {
    219:       dots[j] *= -1.0;
    220:       VecAXPY(mode[j], dots[j], mode[i]);
    221:     }
    222:   }
    223:   MatNullSpaceCreate(comm, PETSC_FALSE, mmin, mode, sp);
    224:   for (i = 0; i < m; ++i) {VecDestroy(&mode[i]);}
    225:   PetscFree(func);
    226:   return(0);
    227: }

    229: /*@
    230:   DMPlexCreateRigidBodies - For the default global section, create rigid body modes by function space interpolation

    232:   Collective on dm

    234:   Input Parameters:
    235: + dm    - the DM
    236: . nb    - The number of bodies
    237: . label - The DMLabel marking each domain
    238: . nids  - The number of ids per body
    239: - ids   - An array of the label ids in sequence for each domain

    241:   Output Parameter:
    242: . sp - the null space

    244:   Note: This is necessary to provide a suitable coarse space for algebraic multigrid

    246:   Level: advanced

    248: .seealso: MatNullSpaceCreate()
    249: @*/
    250: PetscErrorCode DMPlexCreateRigidBodies(DM dm, PetscInt nb, DMLabel label, const PetscInt nids[], const PetscInt ids[], MatNullSpace *sp)
    251: {
    252:   MPI_Comm       comm;
    253:   PetscSection   section, globalSection;
    254:   Vec           *mode;
    255:   PetscScalar   *dots;
    256:   PetscInt       dim, dimEmbed, n, m, b, d, i, j, off;

    260:   PetscObjectGetComm((PetscObject)dm,&comm);
    261:   DMGetDimension(dm, &dim);
    262:   DMGetCoordinateDim(dm, &dimEmbed);
    263:   DMGetLocalSection(dm, &section);
    264:   DMGetGlobalSection(dm, &globalSection);
    265:   PetscSectionGetConstrainedStorageSize(globalSection, &n);
    266:   m    = nb * (dim*(dim+1))/2;
    267:   PetscMalloc2(m, &mode, m, &dots);
    268:   VecCreate(comm, &mode[0]);
    269:   VecSetSizes(mode[0], n, PETSC_DETERMINE);
    270:   VecSetUp(mode[0]);
    271:   for (i = 1; i < m; ++i) {VecDuplicate(mode[0], &mode[i]);}
    272:   for (b = 0, off = 0; b < nb; ++b) {
    273:     for (d = 0; d < m/nb; ++d) {
    274:       PetscInt         ctx[2];
    275:       PetscErrorCode (*func)(PetscInt, PetscReal, const PetscReal *, PetscInt, PetscScalar *, void *) = DMPlexProjectRigidBody_Private;
    276:       void            *voidctx = (void *) (&ctx[0]);

    278:       ctx[0] = dimEmbed;
    279:       ctx[1] = d;
    280:       DMProjectFunctionLabel(dm, 0.0, label, nids[b], &ids[off], 0, NULL, &func, &voidctx, INSERT_VALUES, mode[d]);
    281:       off   += nids[b];
    282:     }
    283:   }
    284:   /* Orthonormalize system */
    285:   for (i = 0; i < m; ++i) {
    286:     PetscScalar dots[6];

    288:     VecNormalize(mode[i], NULL);
    289:     VecMDot(mode[i], m-i-1, mode+i+1, dots+i+1);
    290:     for (j = i+1; j < m; ++j) {
    291:       dots[j] *= -1.0;
    292:       VecAXPY(mode[j], dots[j], mode[i]);
    293:     }
    294:   }
    295:   MatNullSpaceCreate(comm, PETSC_FALSE, m, mode, sp);
    296:   for (i = 0; i< m; ++i) {VecDestroy(&mode[i]);}
    297:   PetscFree2(mode, dots);
    298:   return(0);
    299: }

    301: /*@
    302:   DMPlexSetMaxProjectionHeight - In DMPlexProjectXXXLocal() functions, the projected values of a basis function's dofs
    303:   are computed by associating the basis function with one of the mesh points in its transitively-closed support, and
    304:   evaluating the dual space basis of that point.  A basis function is associated with the point in its
    305:   transitively-closed support whose mesh height is highest (w.r.t. DAG height), but not greater than the maximum
    306:   projection height, which is set with this function.  By default, the maximum projection height is zero, which means
    307:   that only mesh cells are used to project basis functions.  A height of one, for example, evaluates a cell-interior
    308:   basis functions using its cells dual space basis, but all other basis functions with the dual space basis of a face.

    310:   Input Parameters:
    311: + dm - the DMPlex object
    312: - height - the maximum projection height >= 0

    314:   Level: advanced

    316: .seealso: DMPlexGetMaxProjectionHeight(), DMProjectFunctionLocal(), DMProjectFunctionLabelLocal()
    317: @*/
    318: PetscErrorCode DMPlexSetMaxProjectionHeight(DM dm, PetscInt height)
    319: {
    320:   DM_Plex *plex = (DM_Plex *) dm->data;

    324:   plex->maxProjectionHeight = height;
    325:   return(0);
    326: }

    328: /*@
    329:   DMPlexGetMaxProjectionHeight - Get the maximum height (w.r.t. DAG) of mesh points used to evaluate dual bases in
    330:   DMPlexProjectXXXLocal() functions.

    332:   Input Parameters:
    333: . dm - the DMPlex object

    335:   Output Parameters:
    336: . height - the maximum projection height

    338:   Level: intermediate

    340: .seealso: DMPlexSetMaxProjectionHeight(), DMProjectFunctionLocal(), DMProjectFunctionLabelLocal()
    341: @*/
    342: PetscErrorCode DMPlexGetMaxProjectionHeight(DM dm, PetscInt *height)
    343: {
    344:   DM_Plex *plex = (DM_Plex *) dm->data;

    348:   *height = plex->maxProjectionHeight;
    349:   return(0);
    350: }

    352: typedef struct {
    353:   PetscReal    alpha; /* The first Euler angle, and in 2D the only one */
    354:   PetscReal    beta;  /* The second Euler angle */
    355:   PetscReal    gamma; /* The third Euler angle */
    356:   PetscInt     dim;   /* The dimension of R */
    357:   PetscScalar *R;     /* The rotation matrix, transforming a vector in the local basis to the global basis */
    358:   PetscScalar *RT;    /* The transposed rotation matrix, transforming a vector in the global basis to the local basis */
    359: } RotCtx;

    361: /*
    362:   Note: Following https://en.wikipedia.org/wiki/Euler_angles, we will specify Euler angles by extrinsic rotations, meaning that
    363:   we rotate with respect to a fixed initial coordinate system, the local basis (x-y-z). The global basis (X-Y-Z) is reached as follows:
    364:   $ The XYZ system rotates about the z axis by alpha. The X axis is now at angle alpha with respect to the x axis.
    365:   $ The XYZ system rotates again about the x axis by beta. The Z axis is now at angle beta with respect to the z axis.
    366:   $ The XYZ system rotates a third time about the z axis by gamma.
    367: */
    368: static PetscErrorCode DMPlexBasisTransformSetUp_Rotation_Internal(DM dm, void *ctx)
    369: {
    370:   RotCtx        *rc  = (RotCtx *) ctx;
    371:   PetscInt       dim = rc->dim;
    372:   PetscReal      c1, s1, c2, s2, c3, s3;

    376:   PetscMalloc2(PetscSqr(dim), &rc->R, PetscSqr(dim), &rc->RT);
    377:   switch (dim) {
    378:   case 2:
    379:     c1 = PetscCosReal(rc->alpha);s1 = PetscSinReal(rc->alpha);
    380:     rc->R[0] =  c1;rc->R[1] = s1;
    381:     rc->R[2] = -s1;rc->R[3] = c1;
    382:     PetscArraycpy(rc->RT, rc->R, PetscSqr(dim));
    383:     DMPlex_Transpose2D_Internal(rc->RT);
    384:     break;
    385:   case 3:
    386:     c1 = PetscCosReal(rc->alpha);s1 = PetscSinReal(rc->alpha);
    387:     c2 = PetscCosReal(rc->beta); s2 = PetscSinReal(rc->beta);
    388:     c3 = PetscCosReal(rc->gamma);s3 = PetscSinReal(rc->gamma);
    389:     rc->R[0] =  c1*c3 - c2*s1*s3;rc->R[1] =  c3*s1    + c1*c2*s3;rc->R[2] = s2*s3;
    390:     rc->R[3] = -c1*s3 - c2*c3*s1;rc->R[4] =  c1*c2*c3 - s1*s3;   rc->R[5] = c3*s2;
    391:     rc->R[6] =  s1*s2;           rc->R[7] = -c1*s2;              rc->R[8] = c2;
    392:     PetscArraycpy(rc->RT, rc->R, PetscSqr(dim));
    393:     DMPlex_Transpose3D_Internal(rc->RT);
    394:     break;
    395:   default: SETERRQ1(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_OUTOFRANGE, "Dimension %D not supported", dim);
    396:   }
    397:   return(0);
    398: }

    400: static PetscErrorCode DMPlexBasisTransformDestroy_Rotation_Internal(DM dm, void *ctx)
    401: {
    402:   RotCtx        *rc = (RotCtx *) ctx;

    406:   PetscFree2(rc->R, rc->RT);
    407:   PetscFree(rc);
    408:   return(0);
    409: }

    411: static PetscErrorCode DMPlexBasisTransformGetMatrix_Rotation_Internal(DM dm, const PetscReal x[], PetscBool l2g, const PetscScalar **A, void *ctx)
    412: {
    413:   RotCtx *rc = (RotCtx *) ctx;

    417:   if (l2g) {*A = rc->R;}
    418:   else     {*A = rc->RT;}
    419:   return(0);
    420: }

    422: PetscErrorCode DMPlexBasisTransformApplyReal_Internal(DM dm, const PetscReal x[], PetscBool l2g, PetscInt dim, const PetscReal *y, PetscReal *z, void *ctx)
    423: {

    427:   #if defined(PETSC_USE_COMPLEX)
    428:   switch (dim) {
    429:     case 2:
    430:     {
    431:       PetscScalar yt[2] = {y[0], y[1]}, zt[2] = {0.0,0.0};

    433:       DMPlexBasisTransformApply_Internal(dm, x, l2g, dim, yt, zt, ctx);
    434:       z[0] = PetscRealPart(zt[0]); z[1] = PetscRealPart(zt[1]);
    435:     }
    436:     break;
    437:     case 3:
    438:     {
    439:       PetscScalar yt[3] = {y[0], y[1], y[2]}, zt[3] = {0.0,0.0,0.0};

    441:       DMPlexBasisTransformApply_Internal(dm, x, l2g, dim, yt, zt, ctx);
    442:       z[0] = PetscRealPart(zt[0]); z[1] = PetscRealPart(zt[1]); z[2] = PetscRealPart(zt[2]);
    443:     }
    444:     break;
    445:   }
    446:   #else
    447:   DMPlexBasisTransformApply_Internal(dm, x, l2g, dim, y, z, ctx);
    448:   #endif
    449:   return(0);
    450: }

    452: PetscErrorCode DMPlexBasisTransformApply_Internal(DM dm, const PetscReal x[], PetscBool l2g, PetscInt dim, const PetscScalar *y, PetscScalar *z, void *ctx)
    453: {
    454:   const PetscScalar *A;
    455:   PetscErrorCode     ierr;

    458:   (*dm->transformGetMatrix)(dm, x, l2g, &A, ctx);
    459:   switch (dim) {
    460:   case 2: DMPlex_Mult2D_Internal(A, 1, y, z);break;
    461:   case 3: DMPlex_Mult3D_Internal(A, 1, y, z);break;
    462:   }
    463:   return(0);
    464: }

    466: static PetscErrorCode DMPlexBasisTransformField_Internal(DM dm, DM tdm, Vec tv, PetscInt p, PetscInt f, PetscBool l2g, PetscScalar *a)
    467: {
    468:   PetscSection       ts;
    469:   const PetscScalar *ta, *tva;
    470:   PetscInt           dof;
    471:   PetscErrorCode     ierr;

    474:   DMGetLocalSection(tdm, &ts);
    475:   PetscSectionGetFieldDof(ts, p, f, &dof);
    476:   VecGetArrayRead(tv, &ta);
    477:   DMPlexPointLocalFieldRead(tdm, p, f, ta, &tva);
    478:   if (l2g) {
    479:     switch (dof) {
    480:     case 4: DMPlex_Mult2D_Internal(tva, 1, a, a);break;
    481:     case 9: DMPlex_Mult3D_Internal(tva, 1, a, a);break;
    482:     }
    483:   } else {
    484:     switch (dof) {
    485:     case 4: DMPlex_MultTranspose2D_Internal(tva, 1, a, a);break;
    486:     case 9: DMPlex_MultTranspose3D_Internal(tva, 1, a, a);break;
    487:     }
    488:   }
    489:   VecRestoreArrayRead(tv, &ta);
    490:   return(0);
    491: }

    493: static PetscErrorCode DMPlexBasisTransformFieldTensor_Internal(DM dm, DM tdm, Vec tv, PetscInt pf, PetscInt f, PetscInt pg, PetscInt g, PetscBool l2g, PetscInt lda, PetscScalar *a)
    494: {
    495:   PetscSection       s, ts;
    496:   const PetscScalar *ta, *tvaf, *tvag;
    497:   PetscInt           fdof, gdof, fpdof, gpdof;
    498:   PetscErrorCode     ierr;

    501:   DMGetLocalSection(dm, &s);
    502:   DMGetLocalSection(tdm, &ts);
    503:   PetscSectionGetFieldDof(s, pf, f, &fpdof);
    504:   PetscSectionGetFieldDof(s, pg, g, &gpdof);
    505:   PetscSectionGetFieldDof(ts, pf, f, &fdof);
    506:   PetscSectionGetFieldDof(ts, pg, g, &gdof);
    507:   VecGetArrayRead(tv, &ta);
    508:   DMPlexPointLocalFieldRead(tdm, pf, f, ta, &tvaf);
    509:   DMPlexPointLocalFieldRead(tdm, pg, g, ta, &tvag);
    510:   if (l2g) {
    511:     switch (fdof) {
    512:     case 4: DMPlex_MatMult2D_Internal(tvaf, gpdof, lda, a, a);break;
    513:     case 9: DMPlex_MatMult3D_Internal(tvaf, gpdof, lda, a, a);break;
    514:     }
    515:     switch (gdof) {
    516:     case 4: DMPlex_MatMultTransposeLeft2D_Internal(tvag, fpdof, lda, a, a);break;
    517:     case 9: DMPlex_MatMultTransposeLeft3D_Internal(tvag, fpdof, lda, a, a);break;
    518:     }
    519:   } else {
    520:     switch (fdof) {
    521:     case 4: DMPlex_MatMultTranspose2D_Internal(tvaf, gpdof, lda, a, a);break;
    522:     case 9: DMPlex_MatMultTranspose3D_Internal(tvaf, gpdof, lda, a, a);break;
    523:     }
    524:     switch (gdof) {
    525:     case 4: DMPlex_MatMultLeft2D_Internal(tvag, fpdof, lda, a, a);break;
    526:     case 9: DMPlex_MatMultLeft3D_Internal(tvag, fpdof, lda, a, a);break;
    527:     }
    528:   }
    529:   VecRestoreArrayRead(tv, &ta);
    530:   return(0);
    531: }

    533: PetscErrorCode DMPlexBasisTransformPoint_Internal(DM dm, DM tdm, Vec tv, PetscInt p, PetscBool fieldActive[], PetscBool l2g, PetscScalar *a)
    534: {
    535:   PetscSection    s;
    536:   PetscSection    clSection;
    537:   IS              clPoints;
    538:   const PetscInt *clp;
    539:   PetscInt       *points = NULL;
    540:   PetscInt        Nf, f, Np, cp, dof, d = 0;
    541:   PetscErrorCode  ierr;

    544:   DMGetLocalSection(dm, &s);
    545:   PetscSectionGetNumFields(s, &Nf);
    546:   DMPlexGetCompressedClosure(dm, s, p, &Np, &points, &clSection, &clPoints, &clp);
    547:   for (f = 0; f < Nf; ++f) {
    548:     for (cp = 0; cp < Np*2; cp += 2) {
    549:       PetscSectionGetFieldDof(s, points[cp], f, &dof);
    550:       if (!dof) continue;
    551:       if (fieldActive[f]) {DMPlexBasisTransformField_Internal(dm, tdm, tv, points[cp], f, l2g, &a[d]);}
    552:       d += dof;
    553:     }
    554:   }
    555:   DMPlexRestoreCompressedClosure(dm, s, p, &Np, &points, &clSection, &clPoints, &clp);
    556:   return(0);
    557: }

    559: PetscErrorCode DMPlexBasisTransformPointTensor_Internal(DM dm, DM tdm, Vec tv, PetscInt p, PetscBool l2g, PetscInt lda, PetscScalar *a)
    560: {
    561:   PetscSection    s;
    562:   PetscSection    clSection;
    563:   IS              clPoints;
    564:   const PetscInt *clp;
    565:   PetscInt       *points = NULL;
    566:   PetscInt        Nf, f, g, Np, cpf, cpg, fdof, gdof, r, c = 0;
    567:   PetscErrorCode  ierr;

    570:   DMGetLocalSection(dm, &s);
    571:   PetscSectionGetNumFields(s, &Nf);
    572:   DMPlexGetCompressedClosure(dm, s, p, &Np, &points, &clSection, &clPoints, &clp);
    573:   for (f = 0, r = 0; f < Nf; ++f) {
    574:     for (cpf = 0; cpf < Np*2; cpf += 2) {
    575:       PetscSectionGetFieldDof(s, points[cpf], f, &fdof);
    576:       for (g = 0, c = 0; g < Nf; ++g) {
    577:         for (cpg = 0; cpg < Np*2; cpg += 2) {
    578:           PetscSectionGetFieldDof(s, points[cpg], g, &gdof);
    579:           DMPlexBasisTransformFieldTensor_Internal(dm, tdm, tv, points[cpf], f, points[cpg], g, l2g, lda, &a[r*lda+c]);
    580:           c += gdof;
    581:         }
    582:       }
    583:       if (c != lda) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid number of columns %D should be %D", c, lda);
    584:       r += fdof;
    585:     }
    586:   }
    587:   if (r != lda) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid number of rows %D should be %D", c, lda);
    588:   DMPlexRestoreCompressedClosure(dm, s, p, &Np, &points, &clSection, &clPoints, &clp);
    589:   return(0);
    590: }

    592: static PetscErrorCode DMPlexBasisTransform_Internal(DM dm, Vec lv, PetscBool l2g)
    593: {
    594:   DM                 tdm;
    595:   Vec                tv;
    596:   PetscSection       ts, s;
    597:   const PetscScalar *ta;
    598:   PetscScalar       *a, *va;
    599:   PetscInt           pStart, pEnd, p, Nf, f;
    600:   PetscErrorCode     ierr;

    603:   DMGetBasisTransformDM_Internal(dm, &tdm);
    604:   DMGetBasisTransformVec_Internal(dm, &tv);
    605:   DMGetLocalSection(tdm, &ts);
    606:   DMGetLocalSection(dm, &s);
    607:   PetscSectionGetChart(s, &pStart, &pEnd);
    608:   PetscSectionGetNumFields(s, &Nf);
    609:   VecGetArray(lv, &a);
    610:   VecGetArrayRead(tv, &ta);
    611:   for (p = pStart; p < pEnd; ++p) {
    612:     for (f = 0; f < Nf; ++f) {
    613:       DMPlexPointLocalFieldRef(dm, p, f, a, &va);
    614:       DMPlexBasisTransformField_Internal(dm, tdm, tv, p, f, l2g, va);
    615:     }
    616:   }
    617:   VecRestoreArray(lv, &a);
    618:   VecRestoreArrayRead(tv, &ta);
    619:   return(0);
    620: }

    622: /*@
    623:   DMPlexGlobalToLocalBasis - Transform the values in the given local vector from the global basis to the local basis

    625:   Input Parameters:
    626: + dm - The DM
    627: - lv - A local vector with values in the global basis

    629:   Output Parameters:
    630: . lv - A local vector with values in the local basis

    632:   Note: This method is only intended to be called inside DMGlobalToLocal(). It is unlikely that a user will have a local vector full of coefficients for the global basis unless they are reimplementing GlobalToLocal.

    634:   Level: developer

    636: .seealso: DMPlexLocalToGlobalBasis(), DMGetLocalSection(), DMPlexCreateBasisRotation()
    637: @*/
    638: PetscErrorCode DMPlexGlobalToLocalBasis(DM dm, Vec lv)
    639: {

    645:   DMPlexBasisTransform_Internal(dm, lv, PETSC_FALSE);
    646:   return(0);
    647: }

    649: /*@
    650:   DMPlexLocalToGlobalBasis - Transform the values in the given local vector from the local basis to the global basis

    652:   Input Parameters:
    653: + dm - The DM
    654: - lv - A local vector with values in the local basis

    656:   Output Parameters:
    657: . lv - A local vector with values in the global basis

    659:   Note: This method is only intended to be called inside DMGlobalToLocal(). It is unlikely that a user would want a local vector full of coefficients for the global basis unless they are reimplementing GlobalToLocal.

    661:   Level: developer

    663: .seealso: DMPlexGlobalToLocalBasis(), DMGetLocalSection(), DMPlexCreateBasisRotation()
    664: @*/
    665: PetscErrorCode DMPlexLocalToGlobalBasis(DM dm, Vec lv)
    666: {

    672:   DMPlexBasisTransform_Internal(dm, lv, PETSC_TRUE);
    673:   return(0);
    674: }

    676: /*@
    677:   DMPlexCreateBasisRotation - Create an internal transformation from the global basis, used to specify boundary conditions
    678:     and global solutions, to a local basis, appropriate for discretization integrals and assembly.

    680:   Input Parameters:
    681: + dm    - The DM
    682: . alpha - The first Euler angle, and in 2D the only one
    683: . beta  - The second Euler angle
    684: - gamma - The third Euler angle

    686:   Note: Following https://en.wikipedia.org/wiki/Euler_angles, we will specify Euler angles by extrinsic rotations, meaning that
    687:   we rotate with respect to a fixed initial coordinate system, the local basis (x-y-z). The global basis (X-Y-Z) is reached as follows:
    688:   $ The XYZ system rotates about the z axis by alpha. The X axis is now at angle alpha with respect to the x axis.
    689:   $ The XYZ system rotates again about the x axis by beta. The Z axis is now at angle beta with respect to the z axis.
    690:   $ The XYZ system rotates a third time about the z axis by gamma.

    692:   Level: developer

    694: .seealso: DMPlexGlobalToLocalBasis(), DMPlexLocalToGlobalBasis()
    695: @*/
    696: PetscErrorCode DMPlexCreateBasisRotation(DM dm, PetscReal alpha, PetscReal beta, PetscReal gamma)
    697: {
    698:   RotCtx        *rc;
    699:   PetscInt       cdim;

    703:   DMGetCoordinateDim(dm, &cdim);
    704:   PetscMalloc1(1, &rc);
    705:   dm->transformCtx       = rc;
    706:   dm->transformSetUp     = DMPlexBasisTransformSetUp_Rotation_Internal;
    707:   dm->transformDestroy   = DMPlexBasisTransformDestroy_Rotation_Internal;
    708:   dm->transformGetMatrix = DMPlexBasisTransformGetMatrix_Rotation_Internal;
    709:   rc->dim   = cdim;
    710:   rc->alpha = alpha;
    711:   rc->beta  = beta;
    712:   rc->gamma = gamma;
    713:   (*dm->transformSetUp)(dm, dm->transformCtx);
    714:   DMConstructBasisTransform_Internal(dm);
    715:   return(0);
    716: }

    718: /*@C
    719:   DMPlexInsertBoundaryValuesEssential - Insert boundary values into a local vector using a function of the coordinates

    721:   Input Parameters:
    722: + dm     - The DM, with a PetscDS that matches the problem being constrained
    723: . time   - The time
    724: . field  - The field to constrain
    725: . Nc     - The number of constrained field components, or 0 for all components
    726: . comps  - An array of constrained component numbers, or NULL for all components
    727: . label  - The DMLabel defining constrained points
    728: . numids - The number of DMLabel ids for constrained points
    729: . ids    - An array of ids for constrained points
    730: . func   - A pointwise function giving boundary values
    731: - ctx    - An optional user context for bcFunc

    733:   Output Parameter:
    734: . locX   - A local vector to receives the boundary values

    736:   Level: developer

    738: .seealso: DMPlexInsertBoundaryValuesEssentialField(), DMPlexInsertBoundaryValuesEssentialBdField(), DMAddBoundary()
    739: @*/
    740: PetscErrorCode DMPlexInsertBoundaryValuesEssential(DM dm, PetscReal time, PetscInt field, PetscInt Nc, const PetscInt comps[], DMLabel label, PetscInt numids, const PetscInt ids[], PetscErrorCode (*func)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void *ctx, Vec locX)
    741: {
    742:   PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal x[], PetscInt, PetscScalar *u, void *ctx);
    743:   void            **ctxs;
    744:   PetscInt          numFields;
    745:   PetscErrorCode    ierr;

    748:   DMGetNumFields(dm, &numFields);
    749:   PetscCalloc2(numFields,&funcs,numFields,&ctxs);
    750:   funcs[field] = func;
    751:   ctxs[field]  = ctx;
    752:   DMProjectFunctionLabelLocal(dm, time, label, numids, ids, Nc, comps, funcs, ctxs, INSERT_BC_VALUES, locX);
    753:   PetscFree2(funcs,ctxs);
    754:   return(0);
    755: }

    757: /*@C
    758:   DMPlexInsertBoundaryValuesEssentialField - Insert boundary values into a local vector using a function of the coordinates and field data

    760:   Input Parameters:
    761: + dm     - The DM, with a PetscDS that matches the problem being constrained
    762: . time   - The time
    763: . locU   - A local vector with the input solution values
    764: . field  - The field to constrain
    765: . Nc     - The number of constrained field components, or 0 for all components
    766: . comps  - An array of constrained component numbers, or NULL for all components
    767: . label  - The DMLabel defining constrained points
    768: . numids - The number of DMLabel ids for constrained points
    769: . ids    - An array of ids for constrained points
    770: . func   - A pointwise function giving boundary values
    771: - ctx    - An optional user context for bcFunc

    773:   Output Parameter:
    774: . locX   - A local vector to receives the boundary values

    776:   Level: developer

    778: .seealso: DMPlexInsertBoundaryValuesEssential(), DMPlexInsertBoundaryValuesEssentialBdField(), DMAddBoundary()
    779: @*/
    780: PetscErrorCode DMPlexInsertBoundaryValuesEssentialField(DM dm, PetscReal time, Vec locU, PetscInt field, PetscInt Nc, const PetscInt comps[], DMLabel label, PetscInt numids, const PetscInt ids[],
    781:                                                         void (*func)(PetscInt, PetscInt, PetscInt,
    782:                                                                      const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[],
    783:                                                                      const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[],
    784:                                                                      PetscReal, const PetscReal[], PetscInt, const PetscScalar[],
    785:                                                                      PetscScalar[]),
    786:                                                         void *ctx, Vec locX)
    787: {
    788:   void (**funcs)(PetscInt, PetscInt, PetscInt,
    789:                  const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[],
    790:                  const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[],
    791:                  PetscReal, const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[]);
    792:   void            **ctxs;
    793:   PetscInt          numFields;
    794:   PetscErrorCode    ierr;

    797:   DMGetNumFields(dm, &numFields);
    798:   PetscCalloc2(numFields,&funcs,numFields,&ctxs);
    799:   funcs[field] = func;
    800:   ctxs[field]  = ctx;
    801:   DMProjectFieldLabelLocal(dm, time, label, numids, ids, Nc, comps, locU, funcs, INSERT_BC_VALUES, locX);
    802:   PetscFree2(funcs,ctxs);
    803:   return(0);
    804: }

    806: /*@C
    807:   DMPlexInsertBoundaryValuesEssentialBdField - Insert boundary values into a local vector using a function of the coodinates and boundary field data

    809:   Collective on dm

    811:   Input Parameters:
    812: + dm     - The DM, with a PetscDS that matches the problem being constrained
    813: . time   - The time
    814: . locU   - A local vector with the input solution values
    815: . field  - The field to constrain
    816: . Nc     - The number of constrained field components, or 0 for all components
    817: . comps  - An array of constrained component numbers, or NULL for all components
    818: . label  - The DMLabel defining constrained points
    819: . numids - The number of DMLabel ids for constrained points
    820: . ids    - An array of ids for constrained points
    821: . func   - A pointwise function giving boundary values, the calling sequence is given in DMProjectBdFieldLabelLocal()
    822: - ctx    - An optional user context for bcFunc

    824:   Output Parameter:
    825: . locX   - A local vector to receive the boundary values

    827:   Level: developer

    829: .seealso: DMProjectBdFieldLabelLocal(), DMPlexInsertBoundaryValuesEssential(), DMPlexInsertBoundaryValuesEssentialField(), DMAddBoundary()
    830: @*/
    831: PetscErrorCode DMPlexInsertBoundaryValuesEssentialBdField(DM dm, PetscReal time, Vec locU, PetscInt field, PetscInt Nc, const PetscInt comps[], DMLabel label, PetscInt numids, const PetscInt ids[],
    832:                                                           void (*func)(PetscInt, PetscInt, PetscInt,
    833:                                                                        const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[],
    834:                                                                        const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[],
    835:                                                                        PetscReal, const PetscReal[], const PetscReal[], PetscInt, const PetscScalar[],
    836:                                                                        PetscScalar[]),
    837:                                                           void *ctx, Vec locX)
    838: {
    839:   void (**funcs)(PetscInt, PetscInt, PetscInt,
    840:                  const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[],
    841:                  const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[],
    842:                  PetscReal, const PetscReal[], const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[]);
    843:   void            **ctxs;
    844:   PetscInt          numFields;
    845:   PetscErrorCode    ierr;

    848:   DMGetNumFields(dm, &numFields);
    849:   PetscCalloc2(numFields,&funcs,numFields,&ctxs);
    850:   funcs[field] = func;
    851:   ctxs[field]  = ctx;
    852:   DMProjectBdFieldLabelLocal(dm, time, label, numids, ids, Nc, comps, locU, funcs, INSERT_BC_VALUES, locX);
    853:   PetscFree2(funcs,ctxs);
    854:   return(0);
    855: }

    857: /*@C
    858:   DMPlexInsertBoundaryValuesRiemann - Insert boundary values into a local vector

    860:   Input Parameters:
    861: + dm     - The DM, with a PetscDS that matches the problem being constrained
    862: . time   - The time
    863: . faceGeometry - A vector with the FVM face geometry information
    864: . cellGeometry - A vector with the FVM cell geometry information
    865: . Grad         - A vector with the FVM cell gradient information
    866: . field  - The field to constrain
    867: . Nc     - The number of constrained field components, or 0 for all components
    868: . comps  - An array of constrained component numbers, or NULL for all components
    869: . label  - The DMLabel defining constrained points
    870: . numids - The number of DMLabel ids for constrained points
    871: . ids    - An array of ids for constrained points
    872: . func   - A pointwise function giving boundary values
    873: - ctx    - An optional user context for bcFunc

    875:   Output Parameter:
    876: . locX   - A local vector to receives the boundary values

    878:   Note: This implementation currently ignores the numcomps/comps argument from DMAddBoundary()

    880:   Level: developer

    882: .seealso: DMPlexInsertBoundaryValuesEssential(), DMPlexInsertBoundaryValuesEssentialField(), DMAddBoundary()
    883: @*/
    884: PetscErrorCode DMPlexInsertBoundaryValuesRiemann(DM dm, PetscReal time, Vec faceGeometry, Vec cellGeometry, Vec Grad, PetscInt field, PetscInt Nc, const PetscInt comps[], DMLabel label, PetscInt numids, const PetscInt ids[],
    885:                                                  PetscErrorCode (*func)(PetscReal,const PetscReal*,const PetscReal*,const PetscScalar*,PetscScalar*,void*), void *ctx, Vec locX)
    886: {
    887:   PetscDS            prob;
    888:   PetscSF            sf;
    889:   DM                 dmFace, dmCell, dmGrad;
    890:   const PetscScalar *facegeom, *cellgeom = NULL, *grad;
    891:   const PetscInt    *leaves;
    892:   PetscScalar       *x, *fx;
    893:   PetscInt           dim, nleaves, loc, fStart, fEnd, pdim, i;
    894:   PetscErrorCode     ierr, ierru = 0;

    897:   DMGetPointSF(dm, &sf);
    898:   PetscSFGetGraph(sf, NULL, &nleaves, &leaves, NULL);
    899:   nleaves = PetscMax(0, nleaves);
    900:   DMGetDimension(dm, &dim);
    901:   DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd);
    902:   DMGetDS(dm, &prob);
    903:   VecGetDM(faceGeometry, &dmFace);
    904:   VecGetArrayRead(faceGeometry, &facegeom);
    905:   if (cellGeometry) {
    906:     VecGetDM(cellGeometry, &dmCell);
    907:     VecGetArrayRead(cellGeometry, &cellgeom);
    908:   }
    909:   if (Grad) {
    910:     PetscFV fv;

    912:     PetscDSGetDiscretization(prob, field, (PetscObject *) &fv);
    913:     VecGetDM(Grad, &dmGrad);
    914:     VecGetArrayRead(Grad, &grad);
    915:     PetscFVGetNumComponents(fv, &pdim);
    916:     DMGetWorkArray(dm, pdim, MPIU_SCALAR, &fx);
    917:   }
    918:   VecGetArray(locX, &x);
    919:   for (i = 0; i < numids; ++i) {
    920:     IS              faceIS;
    921:     const PetscInt *faces;
    922:     PetscInt        numFaces, f;

    924:     DMLabelGetStratumIS(label, ids[i], &faceIS);
    925:     if (!faceIS) continue; /* No points with that id on this process */
    926:     ISGetLocalSize(faceIS, &numFaces);
    927:     ISGetIndices(faceIS, &faces);
    928:     for (f = 0; f < numFaces; ++f) {
    929:       const PetscInt         face = faces[f], *cells;
    930:       PetscFVFaceGeom        *fg;

    932:       if ((face < fStart) || (face >= fEnd)) continue; /* Refinement adds non-faces to labels */
    933:       PetscFindInt(face, nleaves, (PetscInt *) leaves, &loc);
    934:       if (loc >= 0) continue;
    935:       DMPlexPointLocalRead(dmFace, face, facegeom, &fg);
    936:       DMPlexGetSupport(dm, face, &cells);
    937:       if (Grad) {
    938:         PetscFVCellGeom       *cg;
    939:         PetscScalar           *cx, *cgrad;
    940:         PetscScalar           *xG;
    941:         PetscReal              dx[3];
    942:         PetscInt               d;

    944:         DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cg);
    945:         DMPlexPointLocalRead(dm, cells[0], x, &cx);
    946:         DMPlexPointLocalRead(dmGrad, cells[0], grad, &cgrad);
    947:         DMPlexPointLocalFieldRef(dm, cells[1], field, x, &xG);
    948:         DMPlex_WaxpyD_Internal(dim, -1, cg->centroid, fg->centroid, dx);
    949:         for (d = 0; d < pdim; ++d) fx[d] = cx[d] + DMPlex_DotD_Internal(dim, &cgrad[d*dim], dx);
    950:         ierru = (*func)(time, fg->centroid, fg->normal, fx, xG, ctx);
    951:         if (ierru) {
    952:           ISRestoreIndices(faceIS, &faces);
    953:           ISDestroy(&faceIS);
    954:           goto cleanup;
    955:         }
    956:       } else {
    957:         PetscScalar       *xI;
    958:         PetscScalar       *xG;

    960:         DMPlexPointLocalRead(dm, cells[0], x, &xI);
    961:         DMPlexPointLocalFieldRef(dm, cells[1], field, x, &xG);
    962:         ierru = (*func)(time, fg->centroid, fg->normal, xI, xG, ctx);
    963:         if (ierru) {
    964:           ISRestoreIndices(faceIS, &faces);
    965:           ISDestroy(&faceIS);
    966:           goto cleanup;
    967:         }
    968:       }
    969:     }
    970:     ISRestoreIndices(faceIS, &faces);
    971:     ISDestroy(&faceIS);
    972:   }
    973:   cleanup:
    974:   VecRestoreArray(locX, &x);
    975:   if (Grad) {
    976:     DMRestoreWorkArray(dm, pdim, MPIU_SCALAR, &fx);
    977:     VecRestoreArrayRead(Grad, &grad);
    978:   }
    979:   if (cellGeometry) {VecRestoreArrayRead(cellGeometry, &cellgeom);}
    980:   VecRestoreArrayRead(faceGeometry, &facegeom);
    981:   CHKERRQ(ierru);
    982:   return(0);
    983: }

    985: static PetscErrorCode zero(PetscInt dim, PetscReal time, const PetscReal x[], PetscInt Nc, PetscScalar *u, void *ctx)
    986: {
    987:   PetscInt c;
    988:   for (c = 0; c < Nc; ++c) u[c] = 0.0;
    989:   return 0;
    990: }

    992: PetscErrorCode DMPlexInsertBoundaryValues_Plex(DM dm, PetscBool insertEssential, Vec locX, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM)
    993: {
    994:   PetscObject    isZero;
    995:   PetscDS        prob;
    996:   PetscInt       numBd, b;

    1000:   DMGetDS(dm, &prob);
    1001:   PetscDSGetNumBoundary(prob, &numBd);
    1002:   PetscObjectQuery((PetscObject) locX, "__Vec_bc_zero__", &isZero);
    1003:   for (b = 0; b < numBd; ++b) {
    1004:     PetscWeakForm           wf;
    1005:     DMBoundaryConditionType type;
    1006:     const char             *name;
    1007:     DMLabel                 label;
    1008:     PetscInt                field, Nc;
    1009:     const PetscInt         *comps;
    1010:     PetscObject             obj;
    1011:     PetscClassId            id;
    1012:     void                  (*bvfunc)(void);
    1013:     PetscInt                numids;
    1014:     const PetscInt         *ids;
    1015:     void                   *ctx;

    1017:     PetscDSGetBoundary(prob, b, &wf, &type, &name, &label, &numids, &ids, &field, &Nc, &comps, &bvfunc, NULL, &ctx);
    1018:     if (insertEssential != (type & DM_BC_ESSENTIAL)) continue;
    1019:     DMGetField(dm, field, NULL, &obj);
    1020:     PetscObjectGetClassId(obj, &id);
    1021:     if (id == PETSCFE_CLASSID) {
    1022:       switch (type) {
    1023:         /* for FEM, there is no insertion to be done for non-essential boundary conditions */
    1024:       case DM_BC_ESSENTIAL:
    1025:         {
    1026:           PetscSimplePointFunc func = (PetscSimplePointFunc) bvfunc;

    1028:           if (isZero) func = zero;
    1029:           DMPlexLabelAddCells(dm,label);
    1030:           DMPlexInsertBoundaryValuesEssential(dm, time, field, Nc, comps, label, numids, ids, func, ctx, locX);
    1031:           DMPlexLabelClearCells(dm,label);
    1032:         }
    1033:         break;
    1034:       case DM_BC_ESSENTIAL_FIELD:
    1035:         {
    1036:           PetscPointFunc func = (PetscPointFunc) bvfunc;

    1038:           DMPlexLabelAddCells(dm,label);
    1039:           DMPlexInsertBoundaryValuesEssentialField(dm, time, locX, field, Nc, comps, label, numids, ids, func, ctx, locX);
    1040:           DMPlexLabelClearCells(dm,label);
    1041:         }
    1042:         break;
    1043:       default: break;
    1044:       }
    1045:     } else if (id == PETSCFV_CLASSID) {
    1046:       {
    1047:         PetscErrorCode (*func)(PetscReal,const PetscReal*,const PetscReal*,const PetscScalar*,PetscScalar*,void*) = (PetscErrorCode (*)(PetscReal,const PetscReal*,const PetscReal*,const PetscScalar*,PetscScalar*,void*)) bvfunc;

    1049:         if (!faceGeomFVM) continue;
    1050:         DMPlexInsertBoundaryValuesRiemann(dm, time, faceGeomFVM, cellGeomFVM, gradFVM, field, Nc, comps, label, numids, ids, func, ctx, locX);
    1051:       }
    1052:     } else SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %D", field);
    1053:   }
    1054:   return(0);
    1055: }

    1057: PetscErrorCode DMPlexInsertTimeDerivativeBoundaryValues_Plex(DM dm, PetscBool insertEssential, Vec locX, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM)
    1058: {
    1059:   PetscObject    isZero;
    1060:   PetscDS        prob;
    1061:   PetscInt       numBd, b;

    1065:   if (!locX) return(0);
    1066:   DMGetDS(dm, &prob);
    1067:   PetscDSGetNumBoundary(prob, &numBd);
    1068:   PetscObjectQuery((PetscObject) locX, "__Vec_bc_zero__", &isZero);
    1069:   for (b = 0; b < numBd; ++b) {
    1070:     PetscWeakForm           wf;
    1071:     DMBoundaryConditionType type;
    1072:     const char             *name;
    1073:     DMLabel                 label;
    1074:     PetscInt                field, Nc;
    1075:     const PetscInt         *comps;
    1076:     PetscObject             obj;
    1077:     PetscClassId            id;
    1078:     PetscInt                numids;
    1079:     const PetscInt         *ids;
    1080:     void                  (*bvfunc)(void);
    1081:     void                   *ctx;

    1083:     PetscDSGetBoundary(prob, b, &wf, &type, &name, &label, &numids, &ids, &field, &Nc, &comps, NULL, &bvfunc, &ctx);
    1084:     if (insertEssential != (type & DM_BC_ESSENTIAL)) continue;
    1085:     DMGetField(dm, field, NULL, &obj);
    1086:     PetscObjectGetClassId(obj, &id);
    1087:     if (id == PETSCFE_CLASSID) {
    1088:       switch (type) {
    1089:         /* for FEM, there is no insertion to be done for non-essential boundary conditions */
    1090:       case DM_BC_ESSENTIAL:
    1091:         {
    1092:           PetscSimplePointFunc func_t = (PetscSimplePointFunc) bvfunc;

    1094:           if (isZero) func_t = zero;
    1095:           DMPlexLabelAddCells(dm,label);
    1096:           DMPlexInsertBoundaryValuesEssential(dm, time, field, Nc, comps, label, numids, ids, func_t, ctx, locX);
    1097:           DMPlexLabelClearCells(dm,label);
    1098:         }
    1099:         break;
    1100:       case DM_BC_ESSENTIAL_FIELD:
    1101:         {
    1102:           PetscPointFunc func_t = (PetscPointFunc) bvfunc;

    1104:           DMPlexLabelAddCells(dm,label);
    1105:           DMPlexInsertBoundaryValuesEssentialField(dm, time, locX, field, Nc, comps, label, numids, ids, func_t, ctx, locX);
    1106:           DMPlexLabelClearCells(dm,label);
    1107:         }
    1108:         break;
    1109:       default: break;
    1110:       }
    1111:     } else SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %D", field);
    1112:   }
    1113:   return(0);
    1114: }

    1116: /*@
    1117:   DMPlexInsertBoundaryValues - Puts coefficients which represent boundary values into the local solution vector

    1119:   Input Parameters:
    1120: + dm - The DM
    1121: . insertEssential - Should I insert essential (e.g. Dirichlet) or inessential (e.g. Neumann) boundary conditions
    1122: . time - The time
    1123: . faceGeomFVM - Face geometry data for FV discretizations
    1124: . cellGeomFVM - Cell geometry data for FV discretizations
    1125: - gradFVM - Gradient reconstruction data for FV discretizations

    1127:   Output Parameters:
    1128: . locX - Solution updated with boundary values

    1130:   Level: developer

    1132: .seealso: DMProjectFunctionLabelLocal()
    1133: @*/
    1134: PetscErrorCode DMPlexInsertBoundaryValues(DM dm, PetscBool insertEssential, Vec locX, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM)
    1135: {

    1144:   PetscTryMethod(dm,"DMPlexInsertBoundaryValues_C",(DM,PetscBool,Vec,PetscReal,Vec,Vec,Vec),(dm,insertEssential,locX,time,faceGeomFVM,cellGeomFVM,gradFVM));
    1145:   return(0);
    1146: }

    1148: /*@
    1149:   DMPlexInsertTimeDerivativeBoundaryValues - Puts coefficients which represent boundary values of the time derivative into the local solution vector

    1151:   Input Parameters:
    1152: + dm - The DM
    1153: . insertEssential - Should I insert essential (e.g. Dirichlet) or inessential (e.g. Neumann) boundary conditions
    1154: . time - The time
    1155: . faceGeomFVM - Face geometry data for FV discretizations
    1156: . cellGeomFVM - Cell geometry data for FV discretizations
    1157: - gradFVM - Gradient reconstruction data for FV discretizations

    1159:   Output Parameters:
    1160: . locX_t - Solution updated with boundary values

    1162:   Level: developer

    1164: .seealso: DMProjectFunctionLabelLocal()
    1165: @*/
    1166: PetscErrorCode DMPlexInsertTimeDerivativeBoundaryValues(DM dm, PetscBool insertEssential, Vec locX_t, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM)
    1167: {

    1176:   PetscTryMethod(dm,"DMPlexInsertTimeDerviativeBoundaryValues_C",(DM,PetscBool,Vec,PetscReal,Vec,Vec,Vec),(dm,insertEssential,locX_t,time,faceGeomFVM,cellGeomFVM,gradFVM));
    1177:   return(0);
    1178: }

    1180: PetscErrorCode DMComputeL2Diff_Plex(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal [], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, PetscReal *diff)
    1181: {
    1182:   Vec              localX;
    1183:   PetscErrorCode   ierr;

    1186:   DMGetLocalVector(dm, &localX);
    1187:   DMPlexInsertBoundaryValues(dm, PETSC_TRUE, localX, time, NULL, NULL, NULL);
    1188:   DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX);
    1189:   DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX);
    1190:   DMPlexComputeL2DiffLocal(dm, time, funcs, ctxs, localX, diff);
    1191:   DMRestoreLocalVector(dm, &localX);
    1192:   return(0);
    1193: }

    1195: /*@C
    1196:   DMComputeL2DiffLocal - This function computes the L_2 difference between a function u and an FEM interpolant solution u_h.

    1198:   Collective on dm

    1200:   Input Parameters:
    1201: + dm     - The DM
    1202: . time   - The time
    1203: . funcs  - The functions to evaluate for each field component
    1204: . ctxs   - Optional array of contexts to pass to each function, or NULL.
    1205: - localX - The coefficient vector u_h, a local vector

    1207:   Output Parameter:
    1208: . diff - The diff ||u - u_h||_2

    1210:   Level: developer

    1212: .seealso: DMProjectFunction(), DMComputeL2FieldDiff(), DMComputeL2GradientDiff()
    1213: @*/
    1214: PetscErrorCode DMPlexComputeL2DiffLocal(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal [], PetscInt, PetscScalar *, void *), void **ctxs, Vec localX, PetscReal *diff)
    1215: {
    1216:   const PetscInt   debug = ((DM_Plex*)dm->data)->printL2;
    1217:   DM               tdm;
    1218:   Vec              tv;
    1219:   PetscSection     section;
    1220:   PetscQuadrature  quad;
    1221:   PetscFEGeom      fegeom;
    1222:   PetscScalar     *funcVal, *interpolant;
    1223:   PetscReal       *coords, *gcoords;
    1224:   PetscReal        localDiff = 0.0;
    1225:   const PetscReal *quadWeights;
    1226:   PetscInt         dim, coordDim, numFields, numComponents = 0, qNc, Nq, cellHeight, cStart, cEnd, c, field, fieldOffset;
    1227:   PetscBool        transform;
    1228:   PetscErrorCode   ierr;

    1231:   DMGetDimension(dm, &dim);
    1232:   DMGetCoordinateDim(dm, &coordDim);
    1233:   fegeom.dimEmbed = coordDim;
    1234:   DMGetLocalSection(dm, &section);
    1235:   PetscSectionGetNumFields(section, &numFields);
    1236:   DMGetBasisTransformDM_Internal(dm, &tdm);
    1237:   DMGetBasisTransformVec_Internal(dm, &tv);
    1238:   DMHasBasisTransform(dm, &transform);
    1239:   for (field = 0; field < numFields; ++field) {
    1240:     PetscObject  obj;
    1241:     PetscClassId id;
    1242:     PetscInt     Nc;

    1244:     DMGetField(dm, field, NULL, &obj);
    1245:     PetscObjectGetClassId(obj, &id);
    1246:     if (id == PETSCFE_CLASSID) {
    1247:       PetscFE fe = (PetscFE) obj;

    1249:       PetscFEGetQuadrature(fe, &quad);
    1250:       PetscFEGetNumComponents(fe, &Nc);
    1251:     } else if (id == PETSCFV_CLASSID) {
    1252:       PetscFV fv = (PetscFV) obj;

    1254:       PetscFVGetQuadrature(fv, &quad);
    1255:       PetscFVGetNumComponents(fv, &Nc);
    1256:     } else SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %D", field);
    1257:     numComponents += Nc;
    1258:   }
    1259:   PetscQuadratureGetData(quad, NULL, &qNc, &Nq, NULL, &quadWeights);
    1260:   if ((qNc != 1) && (qNc != numComponents)) SETERRQ2(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_SIZ, "Quadrature components %D != %D field components", qNc, numComponents);
    1261:   PetscMalloc6(numComponents,&funcVal,numComponents,&interpolant,coordDim*Nq,&coords,Nq,&fegeom.detJ,coordDim*coordDim*Nq,&fegeom.J,coordDim*coordDim*Nq,&fegeom.invJ);
    1262:   DMPlexGetVTKCellHeight(dm, &cellHeight);
    1263:   DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd);
    1264:   for (c = cStart; c < cEnd; ++c) {
    1265:     PetscScalar *x = NULL;
    1266:     PetscReal    elemDiff = 0.0;
    1267:     PetscInt     qc = 0;

    1269:     DMPlexComputeCellGeometryFEM(dm, c, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ);
    1270:     DMPlexVecGetClosure(dm, NULL, localX, c, NULL, &x);

    1272:     for (field = 0, fieldOffset = 0; field < numFields; ++field) {
    1273:       PetscObject  obj;
    1274:       PetscClassId id;
    1275:       void * const ctx = ctxs ? ctxs[field] : NULL;
    1276:       PetscInt     Nb, Nc, q, fc;

    1278:       DMGetField(dm, field, NULL, &obj);
    1279:       PetscObjectGetClassId(obj, &id);
    1280:       if (id == PETSCFE_CLASSID)      {PetscFEGetNumComponents((PetscFE) obj, &Nc);PetscFEGetDimension((PetscFE) obj, &Nb);}
    1281:       else if (id == PETSCFV_CLASSID) {PetscFVGetNumComponents((PetscFV) obj, &Nc);Nb = 1;}
    1282:       else SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %D", field);
    1283:       if (debug) {
    1284:         char title[1024];
    1285:         PetscSNPrintf(title, 1023, "Solution for Field %D", field);
    1286:         DMPrintCellVector(c, title, Nb, &x[fieldOffset]);
    1287:       }
    1288:       for (q = 0; q < Nq; ++q) {
    1289:         PetscFEGeom qgeom;

    1291:         qgeom.dimEmbed = fegeom.dimEmbed;
    1292:         qgeom.J        = &fegeom.J[q*coordDim*coordDim];
    1293:         qgeom.invJ     = &fegeom.invJ[q*coordDim*coordDim];
    1294:         qgeom.detJ     = &fegeom.detJ[q];
    1295:         if (fegeom.detJ[q] <= 0.0) SETERRQ3(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid determinant %g for element %D, point %D", (double)fegeom.detJ[q], c, q);
    1296:         if (transform) {
    1297:           gcoords = &coords[coordDim*Nq];
    1298:           DMPlexBasisTransformApplyReal_Internal(dm, &coords[coordDim*q], PETSC_TRUE, coordDim, &coords[coordDim*q], gcoords, dm->transformCtx);
    1299:         } else {
    1300:           gcoords = &coords[coordDim*q];
    1301:         }
    1302:         (*funcs[field])(coordDim, time, gcoords, Nc, funcVal, ctx);
    1303:         if (ierr) {
    1304:           PetscErrorCode ierr2;
    1305:           ierr2 = DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x);CHKERRQ(ierr2);
    1306:           ierr2 = DMRestoreLocalVector(dm, &localX);CHKERRQ(ierr2);
    1307:           ierr2 = PetscFree6(funcVal,interpolant,coords,fegeom.detJ,fegeom.J,fegeom.invJ);CHKERRQ(ierr2);
    1308:
    1309:         }
    1310:         if (transform) {DMPlexBasisTransformApply_Internal(dm, &coords[coordDim*q], PETSC_FALSE, Nc, funcVal, funcVal, dm->transformCtx);}
    1311:         if (id == PETSCFE_CLASSID)      {PetscFEInterpolate_Static((PetscFE) obj, &x[fieldOffset], &qgeom, q, interpolant);}
    1312:         else if (id == PETSCFV_CLASSID) {PetscFVInterpolate_Static((PetscFV) obj, &x[fieldOffset], q, interpolant);}
    1313:         else SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %D", field);
    1314:         for (fc = 0; fc < Nc; ++fc) {
    1315:           const PetscReal wt = quadWeights[q*qNc+(qNc == 1 ? 0 : qc+fc)];
    1316:           if (debug) {PetscPrintf(PETSC_COMM_SELF, "    elem %D field %D,%D point %g %g %g diff %g\n", c, field, fc, (double)(coordDim > 0 ? coords[coordDim*q] : 0.), (double)(coordDim > 1 ? coords[coordDim*q+1] : 0.),(double)(coordDim > 2 ? coords[coordDim*q+2] : 0.), (double)(PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc]))*wt*fegeom.detJ[q]));}
    1317:           elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc]))*wt*fegeom.detJ[q];
    1318:         }
    1319:       }
    1320:       fieldOffset += Nb;
    1321:       qc += Nc;
    1322:     }
    1323:     DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x);
    1324:     if (debug) {PetscPrintf(PETSC_COMM_SELF, "  elem %D diff %g\n", c, (double)elemDiff);}
    1325:     localDiff += elemDiff;
    1326:   }
    1327:   PetscFree6(funcVal,interpolant,coords,fegeom.detJ,fegeom.J,fegeom.invJ);
    1328:   MPIU_Allreduce(&localDiff, diff, 1, MPIU_REAL, MPIU_SUM, PetscObjectComm((PetscObject)dm));
    1329:   *diff = PetscSqrtReal(*diff);
    1330:   return(0);
    1331: }

    1333: PetscErrorCode DMComputeL2GradientDiff_Plex(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal [], const PetscReal [], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, const PetscReal n[], PetscReal *diff)
    1334: {
    1335:   const PetscInt   debug = ((DM_Plex*)dm->data)->printL2;
    1336:   DM               tdm;
    1337:   PetscSection     section;
    1338:   PetscQuadrature  quad;
    1339:   Vec              localX, tv;
    1340:   PetscScalar     *funcVal, *interpolant;
    1341:   const PetscReal *quadWeights;
    1342:   PetscFEGeom      fegeom;
    1343:   PetscReal       *coords, *gcoords;
    1344:   PetscReal        localDiff = 0.0;
    1345:   PetscInt         dim, coordDim, qNc = 0, Nq = 0, numFields, numComponents = 0, cStart, cEnd, c, field, fieldOffset;
    1346:   PetscBool        transform;
    1347:   PetscErrorCode   ierr;

    1350:   DMGetDimension(dm, &dim);
    1351:   DMGetCoordinateDim(dm, &coordDim);
    1352:   fegeom.dimEmbed = coordDim;
    1353:   DMGetLocalSection(dm, &section);
    1354:   PetscSectionGetNumFields(section, &numFields);
    1355:   DMGetLocalVector(dm, &localX);
    1356:   DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX);
    1357:   DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX);
    1358:   DMGetBasisTransformDM_Internal(dm, &tdm);
    1359:   DMGetBasisTransformVec_Internal(dm, &tv);
    1360:   DMHasBasisTransform(dm, &transform);
    1361:   for (field = 0; field < numFields; ++field) {
    1362:     PetscFE  fe;
    1363:     PetscInt Nc;

    1365:     DMGetField(dm, field, NULL, (PetscObject *) &fe);
    1366:     PetscFEGetQuadrature(fe, &quad);
    1367:     PetscFEGetNumComponents(fe, &Nc);
    1368:     numComponents += Nc;
    1369:   }
    1370:   PetscQuadratureGetData(quad, NULL, &qNc, &Nq, NULL, &quadWeights);
    1371:   if ((qNc != 1) && (qNc != numComponents)) SETERRQ2(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_SIZ, "Quadrature components %D != %D field components", qNc, numComponents);
    1372:   /* DMProjectFunctionLocal(dm, fe, funcs, INSERT_BC_VALUES, localX); */
    1373:   PetscMalloc6(numComponents,&funcVal,coordDim*Nq,&coords,coordDim*coordDim*Nq,&fegeom.J,coordDim*coordDim*Nq,&fegeom.invJ,numComponents*coordDim,&interpolant,Nq,&fegeom.detJ);
    1374:   DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd);
    1375:   for (c = cStart; c < cEnd; ++c) {
    1376:     PetscScalar *x = NULL;
    1377:     PetscReal    elemDiff = 0.0;
    1378:     PetscInt     qc = 0;

    1380:     DMPlexComputeCellGeometryFEM(dm, c, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ);
    1381:     DMPlexVecGetClosure(dm, NULL, localX, c, NULL, &x);

    1383:     for (field = 0, fieldOffset = 0; field < numFields; ++field) {
    1384:       PetscFE          fe;
    1385:       void * const     ctx = ctxs ? ctxs[field] : NULL;
    1386:       PetscInt         Nb, Nc, q, fc;

    1388:       DMGetField(dm, field, NULL, (PetscObject *) &fe);
    1389:       PetscFEGetDimension(fe, &Nb);
    1390:       PetscFEGetNumComponents(fe, &Nc);
    1391:       if (debug) {
    1392:         char title[1024];
    1393:         PetscSNPrintf(title, 1023, "Solution for Field %D", field);
    1394:         DMPrintCellVector(c, title, Nb, &x[fieldOffset]);
    1395:       }
    1396:       for (q = 0; q < Nq; ++q) {
    1397:         PetscFEGeom qgeom;

    1399:         qgeom.dimEmbed = fegeom.dimEmbed;
    1400:         qgeom.J        = &fegeom.J[q*coordDim*coordDim];
    1401:         qgeom.invJ     = &fegeom.invJ[q*coordDim*coordDim];
    1402:         qgeom.detJ     = &fegeom.detJ[q];
    1403:         if (fegeom.detJ[q] <= 0.0) SETERRQ3(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid determinant %g for element %D, quadrature points %D", (double)fegeom.detJ[q], c, q);
    1404:         if (transform) {
    1405:           gcoords = &coords[coordDim*Nq];
    1406:           DMPlexBasisTransformApplyReal_Internal(dm, &coords[coordDim*q], PETSC_TRUE, coordDim, &coords[coordDim*q], gcoords, dm->transformCtx);
    1407:         } else {
    1408:           gcoords = &coords[coordDim*q];
    1409:         }
    1410:         (*funcs[field])(coordDim, time, gcoords, n, Nc, funcVal, ctx);
    1411:         if (ierr) {
    1412:           PetscErrorCode ierr2;
    1413:           ierr2 = DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x);CHKERRQ(ierr2);
    1414:           ierr2 = DMRestoreLocalVector(dm, &localX);CHKERRQ(ierr2);
    1415:           ierr2 = PetscFree6(funcVal,coords,fegeom.J,fegeom.invJ,interpolant,fegeom.detJ);CHKERRQ(ierr2);
    1416:
    1417:         }
    1418:         if (transform) {DMPlexBasisTransformApply_Internal(dm, &coords[coordDim*q], PETSC_FALSE, Nc, funcVal, funcVal, dm->transformCtx);}
    1419:         PetscFEInterpolateGradient_Static(fe, 1, &x[fieldOffset], &qgeom, q, interpolant);
    1420:         /* Overwrite with the dot product if the normal is given */
    1421:         if (n) {
    1422:           for (fc = 0; fc < Nc; ++fc) {
    1423:             PetscScalar sum = 0.0;
    1424:             PetscInt    d;
    1425:             for (d = 0; d < dim; ++d) sum += interpolant[fc*dim+d]*n[d];
    1426:             interpolant[fc] = sum;
    1427:           }
    1428:         }
    1429:         for (fc = 0; fc < Nc; ++fc) {
    1430:           const PetscReal wt = quadWeights[q*qNc+(qNc == 1 ? 0 : qc+fc)];
    1431:           if (debug) {PetscPrintf(PETSC_COMM_SELF, "    elem %D fieldDer %D,%D diff %g\n", c, field, fc, (double)(PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc]))*wt*fegeom.detJ[q]));}
    1432:           elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc]))*wt*fegeom.detJ[q];
    1433:         }
    1434:       }
    1435:       fieldOffset += Nb;
    1436:       qc          += Nc;
    1437:     }
    1438:     DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x);
    1439:     if (debug) {PetscPrintf(PETSC_COMM_SELF, "  elem %D diff %g\n", c, (double)elemDiff);}
    1440:     localDiff += elemDiff;
    1441:   }
    1442:   PetscFree6(funcVal,coords,fegeom.J,fegeom.invJ,interpolant,fegeom.detJ);
    1443:   DMRestoreLocalVector(dm, &localX);
    1444:   MPIU_Allreduce(&localDiff, diff, 1, MPIU_REAL, MPIU_SUM, PetscObjectComm((PetscObject)dm));
    1445:   *diff = PetscSqrtReal(*diff);
    1446:   return(0);
    1447: }

    1449: PetscErrorCode DMComputeL2FieldDiff_Plex(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal [], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, PetscReal *diff)
    1450: {
    1451:   const PetscInt   debug = ((DM_Plex*)dm->data)->printL2;
    1452:   DM               tdm;
    1453:   DMLabel          depthLabel;
    1454:   PetscSection     section;
    1455:   Vec              localX, tv;
    1456:   PetscReal       *localDiff;
    1457:   PetscInt         dim, depth, dE, Nf, f, Nds, s;
    1458:   PetscBool        transform;
    1459:   PetscErrorCode   ierr;

    1462:   DMGetDimension(dm, &dim);
    1463:   DMGetCoordinateDim(dm, &dE);
    1464:   DMGetLocalSection(dm, &section);
    1465:   DMGetLocalVector(dm, &localX);
    1466:   DMGetBasisTransformDM_Internal(dm, &tdm);
    1467:   DMGetBasisTransformVec_Internal(dm, &tv);
    1468:   DMHasBasisTransform(dm, &transform);
    1469:   DMGetNumFields(dm, &Nf);
    1470:   DMPlexGetDepthLabel(dm, &depthLabel);
    1471:   DMLabelGetNumValues(depthLabel, &depth);

    1473:   VecSet(localX, 0.0);
    1474:   DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX);
    1475:   DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX);
    1476:   DMProjectFunctionLocal(dm, time, funcs, ctxs, INSERT_BC_VALUES, localX);
    1477:   DMGetNumDS(dm, &Nds);
    1478:   PetscCalloc1(Nf, &localDiff);
    1479:   for (s = 0; s < Nds; ++s) {
    1480:     PetscDS          ds;
    1481:     DMLabel          label;
    1482:     IS               fieldIS, pointIS;
    1483:     const PetscInt  *fields, *points = NULL;
    1484:     PetscQuadrature  quad;
    1485:     const PetscReal *quadPoints, *quadWeights;
    1486:     PetscFEGeom      fegeom;
    1487:     PetscReal       *coords, *gcoords;
    1488:     PetscScalar     *funcVal, *interpolant;
    1489:     PetscBool        isHybrid;
    1490:     PetscInt         qNc, Nq, totNc, cStart = 0, cEnd, c, dsNf;

    1492:     DMGetRegionNumDS(dm, s, &label, &fieldIS, &ds);
    1493:     ISGetIndices(fieldIS, &fields);
    1494:     PetscDSGetHybrid(ds, &isHybrid);
    1495:     PetscDSGetNumFields(ds, &dsNf);
    1496:     PetscDSGetTotalComponents(ds, &totNc);
    1497:     PetscDSGetQuadrature(ds, &quad);
    1498:     PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights);
    1499:     if ((qNc != 1) && (qNc != totNc)) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Quadrature components %D != %D field components", qNc, totNc);
    1500:     PetscCalloc6(totNc, &funcVal, totNc, &interpolant, dE*(Nq+1), &coords,Nq, &fegeom.detJ, dE*dE*Nq, &fegeom.J, dE*dE*Nq, &fegeom.invJ);
    1501:     if (!label) {
    1502:       DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd);
    1503:     } else {
    1504:       DMLabelGetStratumIS(label, 1, &pointIS);
    1505:       ISGetLocalSize(pointIS, &cEnd);
    1506:       ISGetIndices(pointIS, &points);
    1507:     }
    1508:     for (c = cStart; c < cEnd; ++c) {
    1509:       const PetscInt cell = points ? points[c] : c;
    1510:       PetscScalar   *x    = NULL;
    1511:       PetscInt       qc   = 0, fOff = 0, dep, fStart = isHybrid ? dsNf-1 : 0;

    1513:       DMLabelGetValue(depthLabel, cell, &dep);
    1514:       if (dep != depth-1) continue;
    1515:       if (isHybrid) {
    1516:         const PetscInt *cone;

    1518:         DMPlexGetCone(dm, cell, &cone);
    1519:         DMPlexComputeCellGeometryFEM(dm, cone[0], quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ);
    1520:       } else {
    1521:         DMPlexComputeCellGeometryFEM(dm, cell, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ);
    1522:       }
    1523:       DMPlexVecGetClosure(dm, NULL, localX, cell, NULL, &x);
    1524:       for (f = fStart; f < dsNf; ++f) {
    1525:         PetscObject  obj;
    1526:         PetscClassId id;
    1527:         void * const ctx = ctxs ? ctxs[fields[f]] : NULL;
    1528:         PetscInt     Nb, Nc, q, fc;
    1529:         PetscReal    elemDiff = 0.0;

    1531:         PetscDSGetDiscretization(ds, f, &obj);
    1532:         PetscObjectGetClassId(obj, &id);
    1533:         if (id == PETSCFE_CLASSID)      {PetscFEGetNumComponents((PetscFE) obj, &Nc);PetscFEGetDimension((PetscFE) obj, &Nb);}
    1534:         else if (id == PETSCFV_CLASSID) {PetscFVGetNumComponents((PetscFV) obj, &Nc);Nb = 1;}
    1535:         else SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %D", fields[f]);
    1536:         if (debug) {
    1537:           char title[1024];
    1538:           PetscSNPrintf(title, 1023, "Solution for Field %D", fields[f]);
    1539:           DMPrintCellVector(cell, title, Nb, &x[fOff]);
    1540:         }
    1541:         for (q = 0; q < Nq; ++q) {
    1542:           PetscFEGeom qgeom;

    1544:           qgeom.dimEmbed = fegeom.dimEmbed;
    1545:           qgeom.J        = &fegeom.J[q*dE*dE];
    1546:           qgeom.invJ     = &fegeom.invJ[q*dE*dE];
    1547:           qgeom.detJ     = &fegeom.detJ[q];
    1548:           if (fegeom.detJ[q] <= 0.0) SETERRQ3(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid determinant %g for cell %D, quadrature point %D", (double)fegeom.detJ[q], cell, q);
    1549:           if (transform) {
    1550:             gcoords = &coords[dE*Nq];
    1551:             DMPlexBasisTransformApplyReal_Internal(dm, &coords[dE*q], PETSC_TRUE, dE, &coords[dE*q], gcoords, dm->transformCtx);
    1552:           } else {
    1553:             gcoords = &coords[dE*q];
    1554:           }
    1555:           (*funcs[fields[f]])(dE, time, gcoords, Nc, funcVal, ctx);
    1556:           if (ierr) {
    1557:             PetscErrorCode ierr2;
    1558:             ierr2 = DMPlexVecRestoreClosure(dm, NULL, localX, cell, NULL, &x);CHKERRQ(ierr2);
    1559:             ierr2 = DMRestoreLocalVector(dm, &localX);CHKERRQ(ierr2);
    1560:             ierr2 = PetscFree6(funcVal,interpolant,coords,fegeom.detJ,fegeom.J,fegeom.invJ);CHKERRQ(ierr2);
    1561:
    1562:           }
    1563:           if (transform) {DMPlexBasisTransformApply_Internal(dm, &coords[dE*q], PETSC_FALSE, Nc, funcVal, funcVal, dm->transformCtx);}
    1564:           /* Call once for each face, except for lagrange field */
    1565:           if (id == PETSCFE_CLASSID)      {PetscFEInterpolate_Static((PetscFE) obj, &x[fOff], &qgeom, q, interpolant);}
    1566:           else if (id == PETSCFV_CLASSID) {PetscFVInterpolate_Static((PetscFV) obj, &x[fOff], q, interpolant);}
    1567:           else SETERRQ1(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %D", fields[f]);
    1568:           for (fc = 0; fc < Nc; ++fc) {
    1569:             const PetscReal wt = quadWeights[q*qNc+(qNc == 1 ? 0 : qc+fc)];
    1570:             if (debug) {PetscPrintf(PETSC_COMM_SELF, "    cell %D field %D,%D point %g %g %g diff %g\n", cell, fields[f], fc, (double)(dE > 0 ? coords[dE*q] : 0.), (double)(dE > 1 ? coords[dE*q+1] : 0.),(double)(dE > 2 ? coords[dE*q+2] : 0.), (double)(PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc]))*wt*fegeom.detJ[q]));}
    1571:             elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc]))*wt*fegeom.detJ[q];
    1572:           }
    1573:         }
    1574:         fOff += Nb;
    1575:         qc   += Nc;
    1576:         localDiff[fields[f]] += elemDiff;
    1577:         if (debug) {PetscPrintf(PETSC_COMM_SELF, "  cell %D field %D cum diff %g\n", cell, fields[f], (double)localDiff[fields[f]]);}
    1578:       }
    1579:       DMPlexVecRestoreClosure(dm, NULL, localX, cell, NULL, &x);
    1580:     }
    1581:     if (label) {
    1582:       ISRestoreIndices(pointIS, &points);
    1583:       ISDestroy(&pointIS);
    1584:     }
    1585:     ISRestoreIndices(fieldIS, &fields);
    1586:     PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ);
    1587:   }
    1588:   DMRestoreLocalVector(dm, &localX);
    1589:   MPIU_Allreduce(localDiff, diff, Nf, MPIU_REAL, MPIU_SUM, PetscObjectComm((PetscObject)dm));
    1590:   PetscFree(localDiff);
    1591:   for (f = 0; f < Nf; ++f) diff[f] = PetscSqrtReal(diff[f]);
    1592:   return(0);
    1593: }

    1595: /*@C
    1596:   DMPlexComputeL2DiffVec - This function computes the cellwise L_2 difference between a function u and an FEM interpolant solution u_h, and stores it in a Vec.

    1598:   Collective on dm

    1600:   Input Parameters:
    1601: + dm    - The DM
    1602: . time  - The time
    1603: . funcs - The functions to evaluate for each field component: NULL means that component does not contribute to error calculation
    1604: . ctxs  - Optional array of contexts to pass to each function, or NULL.
    1605: - X     - The coefficient vector u_h

    1607:   Output Parameter:
    1608: . D - A Vec which holds the difference ||u - u_h||_2 for each cell

    1610:   Level: developer

    1612: .seealso: DMProjectFunction(), DMComputeL2Diff(), DMPlexComputeL2FieldDiff(), DMComputeL2GradientDiff()
    1613: @*/
    1614: PetscErrorCode DMPlexComputeL2DiffVec(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal [], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, Vec D)
    1615: {
    1616:   PetscSection     section;
    1617:   PetscQuadrature  quad;
    1618:   Vec              localX;
    1619:   PetscFEGeom      fegeom;
    1620:   PetscScalar     *funcVal, *interpolant;
    1621:   PetscReal       *coords;
    1622:   const PetscReal *quadPoints, *quadWeights;
    1623:   PetscInt         dim, coordDim, numFields, numComponents = 0, qNc, Nq, cStart, cEnd, c, field, fieldOffset;
    1624:   PetscErrorCode   ierr;

    1627:   VecSet(D, 0.0);
    1628:   DMGetDimension(dm, &dim);
    1629:   DMGetCoordinateDim(dm, &coordDim);
    1630:   DMGetLocalSection(dm, &section);
    1631:   PetscSectionGetNumFields(section, &numFields);
    1632:   DMGetLocalVector(dm, &localX);
    1633:   DMProjectFunctionLocal(dm, time, funcs, ctxs, INSERT_BC_VALUES, localX);
    1634:   DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX);
    1635:   DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX);
    1636:   for (field = 0; field < numFields; ++field) {
    1637:     PetscObject  obj;
    1638:     PetscClassId id;
    1639:     PetscInt     Nc;

    1641:     DMGetField(dm, field, NULL, &obj);
    1642:     PetscObjectGetClassId(obj, &id);
    1643:     if (id == PETSCFE_CLASSID) {
    1644:       PetscFE fe = (PetscFE) obj;

    1646:       PetscFEGetQuadrature(fe, &quad);
    1647:       PetscFEGetNumComponents(fe, &Nc);
    1648:     } else if (id == PETSCFV_CLASSID) {
    1649:       PetscFV fv = (PetscFV) obj;

    1651:       PetscFVGetQuadrature(fv, &quad);
    1652:       PetscFVGetNumComponents(fv, &Nc);
    1653:     } else SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %D", field);
    1654:     numComponents += Nc;
    1655:   }
    1656:   PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights);
    1657:   if ((qNc != 1) && (qNc != numComponents)) SETERRQ2(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_SIZ, "Quadrature components %D != %D field components", qNc, numComponents);
    1658:   PetscMalloc6(numComponents,&funcVal,numComponents,&interpolant,coordDim*Nq,&coords,Nq,&fegeom.detJ,coordDim*coordDim*Nq,&fegeom.J,coordDim*coordDim*Nq,&fegeom.invJ);
    1659:   DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd);
    1660:   for (c = cStart; c < cEnd; ++c) {
    1661:     PetscScalar *x = NULL;
    1662:     PetscScalar  elemDiff = 0.0;
    1663:     PetscInt     qc = 0;

    1665:     DMPlexComputeCellGeometryFEM(dm, c, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ);
    1666:     DMPlexVecGetClosure(dm, NULL, localX, c, NULL, &x);

    1668:     for (field = 0, fieldOffset = 0; field < numFields; ++field) {
    1669:       PetscObject  obj;
    1670:       PetscClassId id;
    1671:       void * const ctx = ctxs ? ctxs[field] : NULL;
    1672:       PetscInt     Nb, Nc, q, fc;

    1674:       DMGetField(dm, field, NULL, &obj);
    1675:       PetscObjectGetClassId(obj, &id);
    1676:       if (id == PETSCFE_CLASSID)      {PetscFEGetNumComponents((PetscFE) obj, &Nc);PetscFEGetDimension((PetscFE) obj, &Nb);}
    1677:       else if (id == PETSCFV_CLASSID) {PetscFVGetNumComponents((PetscFV) obj, &Nc);Nb = 1;}
    1678:       else SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %D", field);
    1679:       if (funcs[field]) {
    1680:         for (q = 0; q < Nq; ++q) {
    1681:           PetscFEGeom qgeom;

    1683:           qgeom.dimEmbed = fegeom.dimEmbed;
    1684:           qgeom.J        = &fegeom.J[q*coordDim*coordDim];
    1685:           qgeom.invJ     = &fegeom.invJ[q*coordDim*coordDim];
    1686:           qgeom.detJ     = &fegeom.detJ[q];
    1687:           if (fegeom.detJ[q] <= 0.0) SETERRQ3(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid determinant %g for element %D, quadrature points %D", (double)fegeom.detJ[q], c, q);
    1688:           (*funcs[field])(coordDim, time, &coords[q*coordDim], Nc, funcVal, ctx);
    1689:           if (ierr) {
    1690:             PetscErrorCode ierr2;
    1691:             ierr2 = DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x);CHKERRQ(ierr2);
    1692:             ierr2 = PetscFree6(funcVal,interpolant,coords,fegeom.detJ,fegeom.J,fegeom.invJ);CHKERRQ(ierr2);
    1693:             ierr2 = DMRestoreLocalVector(dm, &localX);CHKERRQ(ierr2);
    1694:
    1695:           }
    1696:           if (id == PETSCFE_CLASSID)      {PetscFEInterpolate_Static((PetscFE) obj, &x[fieldOffset], &qgeom, q, interpolant);}
    1697:           else if (id == PETSCFV_CLASSID) {PetscFVInterpolate_Static((PetscFV) obj, &x[fieldOffset], q, interpolant);}
    1698:           else SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %D", field);
    1699:           for (fc = 0; fc < Nc; ++fc) {
    1700:             const PetscReal wt = quadWeights[q*qNc+(qNc == 1 ? 0 : qc+fc)];
    1701:             elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc]))*wt*fegeom.detJ[q];
    1702:           }
    1703:         }
    1704:       }
    1705:       fieldOffset += Nb;
    1706:       qc          += Nc;
    1707:     }
    1708:     DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x);
    1709:     VecSetValue(D, c - cStart, elemDiff, INSERT_VALUES);
    1710:   }
    1711:   PetscFree6(funcVal,interpolant,coords,fegeom.detJ,fegeom.J,fegeom.invJ);
    1712:   DMRestoreLocalVector(dm, &localX);
    1713:   VecSqrtAbs(D);
    1714:   return(0);
    1715: }

    1717: /*@C
    1718:   DMPlexComputeGradientClementInterpolant - This function computes the L2 projection of the cellwise gradient of a function u onto P1, and stores it in a Vec.

    1720:   Collective on dm

    1722:   Input Parameters:
    1723: + dm - The DM
    1724: - LocX  - The coefficient vector u_h

    1726:   Output Parameter:
    1727: . locC - A Vec which holds the Clement interpolant of the gradient

    1729:   Notes:
    1730:     Add citation to (Clement, 1975) and definition of the interpolant
    1731:   \nabla u_h(v_i) = \sum_{T_i \in support(v_i)} |T_i| \nabla u_h(T_i) / \sum_{T_i \in support(v_i)} |T_i| where |T_i| is the cell volume

    1733:   Level: developer

    1735: .seealso: DMProjectFunction(), DMComputeL2Diff(), DMPlexComputeL2FieldDiff(), DMComputeL2GradientDiff()
    1736: @*/
    1737: PetscErrorCode DMPlexComputeGradientClementInterpolant(DM dm, Vec locX, Vec locC)
    1738: {
    1739:   DM_Plex         *mesh  = (DM_Plex *) dm->data;
    1740:   PetscInt         debug = mesh->printFEM;
    1741:   DM               dmC;
    1742:   PetscQuadrature  quad;
    1743:   PetscScalar     *interpolant, *gradsum;
    1744:   PetscFEGeom      fegeom;
    1745:   PetscReal       *coords;
    1746:   const PetscReal *quadPoints, *quadWeights;
    1747:   PetscInt         dim, coordDim, numFields, numComponents = 0, qNc, Nq, cStart, cEnd, vStart, vEnd, v, field, fieldOffset;
    1748:   PetscErrorCode   ierr;

    1751:   VecGetDM(locC, &dmC);
    1752:   VecSet(locC, 0.0);
    1753:   DMGetDimension(dm, &dim);
    1754:   DMGetCoordinateDim(dm, &coordDim);
    1755:   fegeom.dimEmbed = coordDim;
    1756:   DMGetNumFields(dm, &numFields);
    1757:   if (numFields == 0) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fields is zero!");
    1758:   for (field = 0; field < numFields; ++field) {
    1759:     PetscObject  obj;
    1760:     PetscClassId id;
    1761:     PetscInt     Nc;

    1763:     DMGetField(dm, field, NULL, &obj);
    1764:     PetscObjectGetClassId(obj, &id);
    1765:     if (id == PETSCFE_CLASSID) {
    1766:       PetscFE fe = (PetscFE) obj;

    1768:       PetscFEGetQuadrature(fe, &quad);
    1769:       PetscFEGetNumComponents(fe, &Nc);
    1770:     } else if (id == PETSCFV_CLASSID) {
    1771:       PetscFV fv = (PetscFV) obj;

    1773:       PetscFVGetQuadrature(fv, &quad);
    1774:       PetscFVGetNumComponents(fv, &Nc);
    1775:     } else SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %D", field);
    1776:     numComponents += Nc;
    1777:   }
    1778:   PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights);
    1779:   if ((qNc != 1) && (qNc != numComponents)) SETERRQ2(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_SIZ, "Quadrature components %D != %D field components", qNc, numComponents);
    1780:   PetscMalloc6(coordDim*numComponents*2,&gradsum,coordDim*numComponents,&interpolant,coordDim*Nq,&coords,Nq,&fegeom.detJ,coordDim*coordDim*Nq,&fegeom.J,coordDim*coordDim*Nq,&fegeom.invJ);
    1781:   DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd);
    1782:   DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd);
    1783:   for (v = vStart; v < vEnd; ++v) {
    1784:     PetscScalar volsum = 0.0;
    1785:     PetscInt   *star = NULL;
    1786:     PetscInt    starSize, st, d, fc;

    1788:     PetscArrayzero(gradsum, coordDim*numComponents);
    1789:     DMPlexGetTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star);
    1790:     for (st = 0; st < starSize*2; st += 2) {
    1791:       const PetscInt cell = star[st];
    1792:       PetscScalar   *grad = &gradsum[coordDim*numComponents];
    1793:       PetscScalar   *x    = NULL;
    1794:       PetscReal      vol  = 0.0;

    1796:       if ((cell < cStart) || (cell >= cEnd)) continue;
    1797:       DMPlexComputeCellGeometryFEM(dm, cell, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ);
    1798:       DMPlexVecGetClosure(dm, NULL, locX, cell, NULL, &x);
    1799:       for (field = 0, fieldOffset = 0; field < numFields; ++field) {
    1800:         PetscObject  obj;
    1801:         PetscClassId id;
    1802:         PetscInt     Nb, Nc, q, qc = 0;

    1804:         PetscArrayzero(grad, coordDim*numComponents);
    1805:         DMGetField(dm, field, NULL, &obj);
    1806:         PetscObjectGetClassId(obj, &id);
    1807:         if (id == PETSCFE_CLASSID)      {PetscFEGetNumComponents((PetscFE) obj, &Nc);PetscFEGetDimension((PetscFE) obj, &Nb);}
    1808:         else if (id == PETSCFV_CLASSID) {PetscFVGetNumComponents((PetscFV) obj, &Nc);Nb = 1;}
    1809:         else SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %D", field);
    1810:         for (q = 0; q < Nq; ++q) {
    1811:           PetscFEGeom qgeom;

    1813:           qgeom.dimEmbed = fegeom.dimEmbed;
    1814:           qgeom.J        = &fegeom.J[q*coordDim*coordDim];
    1815:           qgeom.invJ     = &fegeom.invJ[q*coordDim*coordDim];
    1816:           qgeom.detJ     = &fegeom.detJ[q];
    1817:           if (fegeom.detJ[q] <= 0.0) SETERRQ3(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid determinant %g for element %D, quadrature points %D", (double)fegeom.detJ[q], cell, q);
    1818:           if (ierr) {
    1819:             PetscErrorCode ierr2;
    1820:             ierr2 = DMPlexVecRestoreClosure(dm, NULL, locX, cell, NULL, &x);CHKERRQ(ierr2);
    1821:             ierr2 = DMPlexRestoreTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star);CHKERRQ(ierr2);
    1822:             ierr2 = PetscFree6(gradsum,interpolant,coords,fegeom.detJ,fegeom.J,fegeom.invJ);CHKERRQ(ierr2);
    1823:
    1824:           }
    1825:           if (id == PETSCFE_CLASSID)      {PetscFEInterpolateGradient_Static((PetscFE) obj, 1, &x[fieldOffset], &qgeom, q, interpolant);}
    1826:           else SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %D", field);
    1827:           for (fc = 0; fc < Nc; ++fc) {
    1828:             const PetscReal wt = quadWeights[q*qNc+qc+fc];

    1830:             for (d = 0; d < coordDim; ++d) grad[fc*coordDim+d] += interpolant[fc*dim+d]*wt*fegeom.detJ[q];
    1831:           }
    1832:           vol += quadWeights[q*qNc]*fegeom.detJ[q];
    1833:         }
    1834:         fieldOffset += Nb;
    1835:         qc          += Nc;
    1836:       }
    1837:       DMPlexVecRestoreClosure(dm, NULL, locX, cell, NULL, &x);
    1838:       for (fc = 0; fc < numComponents; ++fc) {
    1839:         for (d = 0; d < coordDim; ++d) {
    1840:           gradsum[fc*coordDim+d] += grad[fc*coordDim+d];
    1841:         }
    1842:       }
    1843:       volsum += vol;
    1844:       if (debug) {
    1845:         PetscPrintf(PETSC_COMM_SELF, "Cell %D gradient: [", cell);
    1846:         for (fc = 0; fc < numComponents; ++fc) {
    1847:           for (d = 0; d < coordDim; ++d) {
    1848:             if (fc || d > 0) {PetscPrintf(PETSC_COMM_SELF, ", ");}
    1849:             PetscPrintf(PETSC_COMM_SELF, "%g", (double)PetscRealPart(grad[fc*coordDim+d]));
    1850:           }
    1851:         }
    1852:         PetscPrintf(PETSC_COMM_SELF, "]\n");
    1853:       }
    1854:     }
    1855:     for (fc = 0; fc < numComponents; ++fc) {
    1856:       for (d = 0; d < coordDim; ++d) gradsum[fc*coordDim+d] /= volsum;
    1857:     }
    1858:     DMPlexRestoreTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star);
    1859:     DMPlexVecSetClosure(dmC, NULL, locC, v, gradsum, INSERT_VALUES);
    1860:   }
    1861:   PetscFree6(gradsum,interpolant,coords,fegeom.detJ,fegeom.J,fegeom.invJ);
    1862:   return(0);
    1863: }

    1865: static PetscErrorCode DMPlexComputeIntegral_Internal(DM dm, Vec X, PetscInt cStart, PetscInt cEnd, PetscScalar *cintegral, void *user)
    1866: {
    1867:   DM                 dmAux = NULL;
    1868:   PetscDS            prob,    probAux = NULL;
    1869:   PetscSection       section, sectionAux;
    1870:   Vec                locX,    locA;
    1871:   PetscInt           dim, numCells = cEnd - cStart, c, f;
    1872:   PetscBool          useFVM = PETSC_FALSE;
    1873:   /* DS */
    1874:   PetscInt           Nf,    totDim,    *uOff, *uOff_x, numConstants;
    1875:   PetscInt           NfAux, totDimAux, *aOff;
    1876:   PetscScalar       *u, *a;
    1877:   const PetscScalar *constants;
    1878:   /* Geometry */
    1879:   PetscFEGeom       *cgeomFEM;
    1880:   DM                 dmGrad;
    1881:   PetscQuadrature    affineQuad = NULL;
    1882:   Vec                cellGeometryFVM = NULL, faceGeometryFVM = NULL, locGrad = NULL;
    1883:   PetscFVCellGeom   *cgeomFVM;
    1884:   const PetscScalar *lgrad;
    1885:   PetscInt           maxDegree;
    1886:   DMField            coordField;
    1887:   IS                 cellIS;
    1888:   PetscErrorCode     ierr;

    1891:   DMGetDS(dm, &prob);
    1892:   DMGetDimension(dm, &dim);
    1893:   DMGetLocalSection(dm, &section);
    1894:   DMGetNumFields(dm, &Nf);
    1895:   /* Determine which discretizations we have */
    1896:   for (f = 0; f < Nf; ++f) {
    1897:     PetscObject  obj;
    1898:     PetscClassId id;

    1900:     PetscDSGetDiscretization(prob, f, &obj);
    1901:     PetscObjectGetClassId(obj, &id);
    1902:     if (id == PETSCFV_CLASSID) useFVM = PETSC_TRUE;
    1903:   }
    1904:   /* Get local solution with boundary values */
    1905:   DMGetLocalVector(dm, &locX);
    1906:   DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locX, 0.0, NULL, NULL, NULL);
    1907:   DMGlobalToLocalBegin(dm, X, INSERT_VALUES, locX);
    1908:   DMGlobalToLocalEnd(dm, X, INSERT_VALUES, locX);
    1909:   /* Read DS information */
    1910:   PetscDSGetTotalDimension(prob, &totDim);
    1911:   PetscDSGetComponentOffsets(prob, &uOff);
    1912:   PetscDSGetComponentDerivativeOffsets(prob, &uOff_x);
    1913:   ISCreateStride(PETSC_COMM_SELF,numCells,cStart,1,&cellIS);
    1914:   PetscDSGetConstants(prob, &numConstants, &constants);
    1915:   /* Read Auxiliary DS information */
    1916:   DMGetAuxiliaryVec(dm, NULL, 0, &locA);
    1917:   if (locA) {
    1918:     VecGetDM(locA, &dmAux);
    1919:     DMGetDS(dmAux, &probAux);
    1920:     PetscDSGetNumFields(probAux, &NfAux);
    1921:     DMGetLocalSection(dmAux, &sectionAux);
    1922:     PetscDSGetTotalDimension(probAux, &totDimAux);
    1923:     PetscDSGetComponentOffsets(probAux, &aOff);
    1924:   }
    1925:   /* Allocate data  arrays */
    1926:   PetscCalloc1(numCells*totDim, &u);
    1927:   if (dmAux) {PetscMalloc1(numCells*totDimAux, &a);}
    1928:   /* Read out geometry */
    1929:   DMGetCoordinateField(dm,&coordField);
    1930:   DMFieldGetDegree(coordField,cellIS,NULL,&maxDegree);
    1931:   if (maxDegree <= 1) {
    1932:     DMFieldCreateDefaultQuadrature(coordField,cellIS,&affineQuad);
    1933:     if (affineQuad) {
    1934:       DMFieldCreateFEGeom(coordField,cellIS,affineQuad,PETSC_FALSE,&cgeomFEM);
    1935:     }
    1936:   }
    1937:   if (useFVM) {
    1938:     PetscFV   fv = NULL;
    1939:     Vec       grad;
    1940:     PetscInt  fStart, fEnd;
    1941:     PetscBool compGrad;

    1943:     for (f = 0; f < Nf; ++f) {
    1944:       PetscObject  obj;
    1945:       PetscClassId id;

    1947:       PetscDSGetDiscretization(prob, f, &obj);
    1948:       PetscObjectGetClassId(obj, &id);
    1949:       if (id == PETSCFV_CLASSID) {fv = (PetscFV) obj; break;}
    1950:     }
    1951:     PetscFVGetComputeGradients(fv, &compGrad);
    1952:     PetscFVSetComputeGradients(fv, PETSC_TRUE);
    1953:     DMPlexComputeGeometryFVM(dm, &cellGeometryFVM, &faceGeometryFVM);
    1954:     DMPlexComputeGradientFVM(dm, fv, faceGeometryFVM, cellGeometryFVM, &dmGrad);
    1955:     PetscFVSetComputeGradients(fv, compGrad);
    1956:     VecGetArrayRead(cellGeometryFVM, (const PetscScalar **) &cgeomFVM);
    1957:     /* Reconstruct and limit cell gradients */
    1958:     DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd);
    1959:     DMGetGlobalVector(dmGrad, &grad);
    1960:     DMPlexReconstructGradients_Internal(dm, fv, fStart, fEnd, faceGeometryFVM, cellGeometryFVM, locX, grad);
    1961:     /* Communicate gradient values */
    1962:     DMGetLocalVector(dmGrad, &locGrad);
    1963:     DMGlobalToLocalBegin(dmGrad, grad, INSERT_VALUES, locGrad);
    1964:     DMGlobalToLocalEnd(dmGrad, grad, INSERT_VALUES, locGrad);
    1965:     DMRestoreGlobalVector(dmGrad, &grad);
    1966:     /* Handle non-essential (e.g. outflow) boundary values */
    1967:     DMPlexInsertBoundaryValues(dm, PETSC_FALSE, locX, 0.0, faceGeometryFVM, cellGeometryFVM, locGrad);
    1968:     VecGetArrayRead(locGrad, &lgrad);
    1969:   }
    1970:   /* Read out data from inputs */
    1971:   for (c = cStart; c < cEnd; ++c) {
    1972:     PetscScalar *x = NULL;
    1973:     PetscInt     i;

    1975:     DMPlexVecGetClosure(dm, section, locX, c, NULL, &x);
    1976:     for (i = 0; i < totDim; ++i) u[c*totDim+i] = x[i];
    1977:     DMPlexVecRestoreClosure(dm, section, locX, c, NULL, &x);
    1978:     if (dmAux) {
    1979:       DMPlexVecGetClosure(dmAux, sectionAux, locA, c, NULL, &x);
    1980:       for (i = 0; i < totDimAux; ++i) a[c*totDimAux+i] = x[i];
    1981:       DMPlexVecRestoreClosure(dmAux, sectionAux, locA, c, NULL, &x);
    1982:     }
    1983:   }
    1984:   /* Do integration for each field */
    1985:   for (f = 0; f < Nf; ++f) {
    1986:     PetscObject  obj;
    1987:     PetscClassId id;
    1988:     PetscInt     numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset;

    1990:     PetscDSGetDiscretization(prob, f, &obj);
    1991:     PetscObjectGetClassId(obj, &id);
    1992:     if (id == PETSCFE_CLASSID) {
    1993:       PetscFE         fe = (PetscFE) obj;
    1994:       PetscQuadrature q;
    1995:       PetscFEGeom     *chunkGeom = NULL;
    1996:       PetscInt        Nq, Nb;

    1998:       PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches);
    1999:       PetscFEGetQuadrature(fe, &q);
    2000:       PetscQuadratureGetData(q, NULL, NULL, &Nq, NULL, NULL);
    2001:       PetscFEGetDimension(fe, &Nb);
    2002:       blockSize = Nb*Nq;
    2003:       batchSize = numBlocks * blockSize;
    2004:       PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches);
    2005:       numChunks = numCells / (numBatches*batchSize);
    2006:       Ne        = numChunks*numBatches*batchSize;
    2007:       Nr        = numCells % (numBatches*batchSize);
    2008:       offset    = numCells - Nr;
    2009:       if (!affineQuad) {
    2010:         DMFieldCreateFEGeom(coordField,cellIS,q,PETSC_FALSE,&cgeomFEM);
    2011:       }
    2012:       PetscFEGeomGetChunk(cgeomFEM,0,offset,&chunkGeom);
    2013:       PetscFEIntegrate(prob, f, Ne, chunkGeom, u, probAux, a, cintegral);
    2014:       PetscFEGeomGetChunk(cgeomFEM,offset,numCells,&chunkGeom);
    2015:       PetscFEIntegrate(prob, f, Nr, chunkGeom, &u[offset*totDim], probAux, &a[offset*totDimAux], &cintegral[offset*Nf]);
    2016:       PetscFEGeomRestoreChunk(cgeomFEM,offset,numCells,&chunkGeom);
    2017:       if (!affineQuad) {
    2018:         PetscFEGeomDestroy(&cgeomFEM);
    2019:       }
    2020:     } else if (id == PETSCFV_CLASSID) {
    2021:       PetscInt       foff;
    2022:       PetscPointFunc obj_func;
    2023:       PetscScalar    lint;

    2025:       PetscDSGetObjective(prob, f, &obj_func);
    2026:       PetscDSGetFieldOffset(prob, f, &foff);
    2027:       if (obj_func) {
    2028:         for (c = 0; c < numCells; ++c) {
    2029:           PetscScalar *u_x;

    2031:           DMPlexPointLocalRead(dmGrad, c, lgrad, &u_x);
    2032:           obj_func(dim, Nf, NfAux, uOff, uOff_x, &u[totDim*c+foff], NULL, u_x, aOff, NULL, &a[totDimAux*c], NULL, NULL, 0.0, cgeomFVM[c].centroid, numConstants, constants, &lint);
    2033:           cintegral[c*Nf+f] += PetscRealPart(lint)*cgeomFVM[c].volume;
    2034:         }
    2035:       }
    2036:     } else SETERRQ1(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %D", f);
    2037:   }
    2038:   /* Cleanup data arrays */
    2039:   if (useFVM) {
    2040:     VecRestoreArrayRead(locGrad, &lgrad);
    2041:     VecRestoreArrayRead(cellGeometryFVM, (const PetscScalar **) &cgeomFVM);
    2042:     DMRestoreLocalVector(dmGrad, &locGrad);
    2043:     VecDestroy(&faceGeometryFVM);
    2044:     VecDestroy(&cellGeometryFVM);
    2045:     DMDestroy(&dmGrad);
    2046:   }
    2047:   if (dmAux) {PetscFree(a);}
    2048:   PetscFree(u);
    2049:   /* Cleanup */
    2050:   if (affineQuad) {
    2051:     PetscFEGeomDestroy(&cgeomFEM);
    2052:   }
    2053:   PetscQuadratureDestroy(&affineQuad);
    2054:   ISDestroy(&cellIS);
    2055:   DMRestoreLocalVector(dm, &locX);
    2056:   return(0);
    2057: }

    2059: /*@
    2060:   DMPlexComputeIntegralFEM - Form the integral over the domain from the global input X using pointwise functions specified by the user

    2062:   Input Parameters:
    2063: + dm - The mesh
    2064: . X  - Global input vector
    2065: - user - The user context

    2067:   Output Parameter:
    2068: . integral - Integral for each field

    2070:   Level: developer

    2072: .seealso: DMPlexSNESComputeResidualFEM()
    2073: @*/
    2074: PetscErrorCode DMPlexComputeIntegralFEM(DM dm, Vec X, PetscScalar *integral, void *user)
    2075: {
    2076:   DM_Plex       *mesh = (DM_Plex *) dm->data;
    2077:   PetscScalar   *cintegral, *lintegral;
    2078:   PetscInt       Nf, f, cellHeight, cStart, cEnd, cell;

    2085:   PetscLogEventBegin(DMPLEX_IntegralFEM,dm,0,0,0);
    2086:   DMGetNumFields(dm, &Nf);
    2087:   DMPlexGetVTKCellHeight(dm, &cellHeight);
    2088:   DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd);
    2089:   /* TODO Introduce a loop over large chunks (right now this is a single chunk) */
    2090:   PetscCalloc2(Nf, &lintegral, (cEnd-cStart)*Nf, &cintegral);
    2091:   DMPlexComputeIntegral_Internal(dm, X, cStart, cEnd, cintegral, user);
    2092:   /* Sum up values */
    2093:   for (cell = cStart; cell < cEnd; ++cell) {
    2094:     const PetscInt c = cell - cStart;

    2096:     if (mesh->printFEM > 1) {DMPrintCellVector(cell, "Cell Integral", Nf, &cintegral[c*Nf]);}
    2097:     for (f = 0; f < Nf; ++f) lintegral[f] += cintegral[c*Nf+f];
    2098:   }
    2099:   MPIU_Allreduce(lintegral, integral, Nf, MPIU_SCALAR, MPIU_SUM, PetscObjectComm((PetscObject) dm));
    2100:   if (mesh->printFEM) {
    2101:     PetscPrintf(PetscObjectComm((PetscObject) dm), "Integral:");
    2102:     for (f = 0; f < Nf; ++f) {PetscPrintf(PetscObjectComm((PetscObject) dm), " %g", (double) PetscRealPart(integral[f]));}
    2103:     PetscPrintf(PetscObjectComm((PetscObject) dm), "\n");
    2104:   }
    2105:   PetscFree2(lintegral, cintegral);
    2106:   PetscLogEventEnd(DMPLEX_IntegralFEM,dm,0,0,0);
    2107:   return(0);
    2108: }

    2110: /*@
    2111:   DMPlexComputeCellwiseIntegralFEM - Form the vector of cellwise integrals F from the global input X using pointwise functions specified by the user

    2113:   Input Parameters:
    2114: + dm - The mesh
    2115: . X  - Global input vector
    2116: - user - The user context

    2118:   Output Parameter:
    2119: . integral - Cellwise integrals for each field

    2121:   Level: developer

    2123: .seealso: DMPlexSNESComputeResidualFEM()
    2124: @*/
    2125: PetscErrorCode DMPlexComputeCellwiseIntegralFEM(DM dm, Vec X, Vec F, void *user)
    2126: {
    2127:   DM_Plex       *mesh = (DM_Plex *) dm->data;
    2128:   DM             dmF;
    2129:   PetscSection   sectionF;
    2130:   PetscScalar   *cintegral, *af;
    2131:   PetscInt       Nf, f, cellHeight, cStart, cEnd, cell;

    2138:   PetscLogEventBegin(DMPLEX_IntegralFEM,dm,0,0,0);
    2139:   DMGetNumFields(dm, &Nf);
    2140:   DMPlexGetVTKCellHeight(dm, &cellHeight);
    2141:   DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd);
    2142:   /* TODO Introduce a loop over large chunks (right now this is a single chunk) */
    2143:   PetscCalloc1((cEnd-cStart)*Nf, &cintegral);
    2144:   DMPlexComputeIntegral_Internal(dm, X, cStart, cEnd, cintegral, user);
    2145:   /* Put values in F*/
    2146:   VecGetDM(F, &dmF);
    2147:   DMGetLocalSection(dmF, &sectionF);
    2148:   VecGetArray(F, &af);
    2149:   for (cell = cStart; cell < cEnd; ++cell) {
    2150:     const PetscInt c = cell - cStart;
    2151:     PetscInt       dof, off;

    2153:     if (mesh->printFEM > 1) {DMPrintCellVector(cell, "Cell Integral", Nf, &cintegral[c*Nf]);}
    2154:     PetscSectionGetDof(sectionF, cell, &dof);
    2155:     PetscSectionGetOffset(sectionF, cell, &off);
    2156:     if (dof != Nf) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "The number of cell dofs %D != %D", dof, Nf);
    2157:     for (f = 0; f < Nf; ++f) af[off+f] = cintegral[c*Nf+f];
    2158:   }
    2159:   VecRestoreArray(F, &af);
    2160:   PetscFree(cintegral);
    2161:   PetscLogEventEnd(DMPLEX_IntegralFEM,dm,0,0,0);
    2162:   return(0);
    2163: }

    2165: static PetscErrorCode DMPlexComputeBdIntegral_Internal(DM dm, Vec locX, IS pointIS,
    2166:                                                        void (*func)(PetscInt, PetscInt, PetscInt,
    2167:                                                                     const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[],
    2168:                                                                     const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[],
    2169:                                                                     PetscReal, const PetscReal[], const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[]),
    2170:                                                        PetscScalar *fintegral, void *user)
    2171: {
    2172:   DM                 plex = NULL, plexA = NULL;
    2173:   DMEnclosureType    encAux;
    2174:   PetscDS            prob, probAux = NULL;
    2175:   PetscSection       section, sectionAux = NULL;
    2176:   Vec                locA = NULL;
    2177:   DMField            coordField;
    2178:   PetscInt           Nf,        totDim,        *uOff, *uOff_x;
    2179:   PetscInt           NfAux = 0, totDimAux = 0, *aOff = NULL;
    2180:   PetscScalar       *u, *a = NULL;
    2181:   const PetscScalar *constants;
    2182:   PetscInt           numConstants, f;
    2183:   PetscErrorCode     ierr;

    2186:   DMGetCoordinateField(dm, &coordField);
    2187:   DMConvert(dm, DMPLEX, &plex);
    2188:   DMGetDS(dm, &prob);
    2189:   DMGetLocalSection(dm, &section);
    2190:   PetscSectionGetNumFields(section, &Nf);
    2191:   /* Determine which discretizations we have */
    2192:   for (f = 0; f < Nf; ++f) {
    2193:     PetscObject  obj;
    2194:     PetscClassId id;

    2196:     PetscDSGetDiscretization(prob, f, &obj);
    2197:     PetscObjectGetClassId(obj, &id);
    2198:     if (id == PETSCFV_CLASSID) SETERRQ1(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Not supported for FVM (field %D)", f);
    2199:   }
    2200:   /* Read DS information */
    2201:   PetscDSGetTotalDimension(prob, &totDim);
    2202:   PetscDSGetComponentOffsets(prob, &uOff);
    2203:   PetscDSGetComponentDerivativeOffsets(prob, &uOff_x);
    2204:   PetscDSGetConstants(prob, &numConstants, &constants);
    2205:   /* Read Auxiliary DS information */
    2206:   DMGetAuxiliaryVec(dm, NULL, 0, &locA);
    2207:   if (locA) {
    2208:     DM dmAux;

    2210:     VecGetDM(locA, &dmAux);
    2211:     DMGetEnclosureRelation(dmAux, dm, &encAux);
    2212:     DMConvert(dmAux, DMPLEX, &plexA);
    2213:     DMGetDS(dmAux, &probAux);
    2214:     PetscDSGetNumFields(probAux, &NfAux);
    2215:     DMGetLocalSection(dmAux, &sectionAux);
    2216:     PetscDSGetTotalDimension(probAux, &totDimAux);
    2217:     PetscDSGetComponentOffsets(probAux, &aOff);
    2218:   }
    2219:   /* Integrate over points */
    2220:   {
    2221:     PetscFEGeom    *fgeom, *chunkGeom = NULL;
    2222:     PetscInt        maxDegree;
    2223:     PetscQuadrature qGeom = NULL;
    2224:     const PetscInt *points;
    2225:     PetscInt        numFaces, face, Nq, field;
    2226:     PetscInt        numChunks, chunkSize, chunk, Nr, offset;

    2228:     ISGetLocalSize(pointIS, &numFaces);
    2229:     ISGetIndices(pointIS, &points);
    2230:     PetscCalloc2(numFaces*totDim, &u, locA ? numFaces*totDimAux : 0, &a);
    2231:     DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree);
    2232:     for (field = 0; field < Nf; ++field) {
    2233:       PetscFE fe;

    2235:       PetscDSGetDiscretization(prob, field, (PetscObject *) &fe);
    2236:       if (maxDegree <= 1) {DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom);}
    2237:       if (!qGeom) {
    2238:         PetscFEGetFaceQuadrature(fe, &qGeom);
    2239:         PetscObjectReference((PetscObject) qGeom);
    2240:       }
    2241:       PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL);
    2242:       DMPlexGetFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom);
    2243:       for (face = 0; face < numFaces; ++face) {
    2244:         const PetscInt point = points[face], *support;
    2245:         PetscScalar    *x    = NULL;
    2246:         PetscInt       i;

    2248:         DMPlexGetSupport(dm, point, &support);
    2249:         DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x);
    2250:         for (i = 0; i < totDim; ++i) u[face*totDim+i] = x[i];
    2251:         DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x);
    2252:         if (locA) {
    2253:           PetscInt subp;
    2254:           DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp);
    2255:           DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x);
    2256:           for (i = 0; i < totDimAux; ++i) a[f*totDimAux+i] = x[i];
    2257:           DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x);
    2258:         }
    2259:       }
    2260:       /* Get blocking */
    2261:       {
    2262:         PetscQuadrature q;
    2263:         PetscInt        numBatches, batchSize, numBlocks, blockSize;
    2264:         PetscInt        Nq, Nb;

    2266:         PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches);
    2267:         PetscFEGetQuadrature(fe, &q);
    2268:         PetscQuadratureGetData(q, NULL, NULL, &Nq, NULL, NULL);
    2269:         PetscFEGetDimension(fe, &Nb);
    2270:         blockSize = Nb*Nq;
    2271:         batchSize = numBlocks * blockSize;
    2272:         chunkSize = numBatches*batchSize;
    2273:         PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches);
    2274:         numChunks = numFaces / chunkSize;
    2275:         Nr        = numFaces % chunkSize;
    2276:         offset    = numFaces - Nr;
    2277:       }
    2278:       /* Do integration for each field */
    2279:       for (chunk = 0; chunk < numChunks; ++chunk) {
    2280:         PetscFEGeomGetChunk(fgeom, chunk*chunkSize, (chunk+1)*chunkSize, &chunkGeom);
    2281:         PetscFEIntegrateBd(prob, field, func, chunkSize, chunkGeom, u, probAux, a, fintegral);
    2282:         PetscFEGeomRestoreChunk(fgeom, 0, offset, &chunkGeom);
    2283:       }
    2284:       PetscFEGeomGetChunk(fgeom, offset, numFaces, &chunkGeom);
    2285:       PetscFEIntegrateBd(prob, field, func, Nr, chunkGeom, &u[offset*totDim], probAux, a ? &a[offset*totDimAux] : NULL, &fintegral[offset*Nf]);
    2286:       PetscFEGeomRestoreChunk(fgeom, offset, numFaces, &chunkGeom);
    2287:       /* Cleanup data arrays */
    2288:       DMPlexRestoreFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom);
    2289:       PetscQuadratureDestroy(&qGeom);
    2290:       PetscFree2(u, a);
    2291:       ISRestoreIndices(pointIS, &points);
    2292:     }
    2293:   }
    2294:   if (plex)  {DMDestroy(&plex);}
    2295:   if (plexA) {DMDestroy(&plexA);}
    2296:   return(0);
    2297: }

    2299: /*@
    2300:   DMPlexComputeBdIntegral - Form the integral over the specified boundary from the global input X using pointwise functions specified by the user

    2302:   Input Parameters:
    2303: + dm      - The mesh
    2304: . X       - Global input vector
    2305: . label   - The boundary DMLabel
    2306: . numVals - The number of label values to use, or PETSC_DETERMINE for all values
    2307: . vals    - The label values to use, or PETSC_NULL for all values
    2308: . func    = The function to integrate along the boundary
    2309: - user    - The user context

    2311:   Output Parameter:
    2312: . integral - Integral for each field

    2314:   Level: developer

    2316: .seealso: DMPlexComputeIntegralFEM(), DMPlexComputeBdResidualFEM()
    2317: @*/
    2318: PetscErrorCode DMPlexComputeBdIntegral(DM dm, Vec X, DMLabel label, PetscInt numVals, const PetscInt vals[],
    2319:                                        void (*func)(PetscInt, PetscInt, PetscInt,
    2320:                                                     const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[],
    2321:                                                     const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[],
    2322:                                                     PetscReal, const PetscReal[], const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[]),
    2323:                                        PetscScalar *integral, void *user)
    2324: {
    2325:   Vec            locX;
    2326:   PetscSection   section;
    2327:   DMLabel        depthLabel;
    2328:   IS             facetIS;
    2329:   PetscInt       dim, Nf, f, v;

    2338:   PetscLogEventBegin(DMPLEX_IntegralFEM,dm,0,0,0);
    2339:   DMPlexGetDepthLabel(dm, &depthLabel);
    2340:   DMGetDimension(dm, &dim);
    2341:   DMLabelGetStratumIS(depthLabel, dim-1, &facetIS);
    2342:   DMGetLocalSection(dm, &section);
    2343:   PetscSectionGetNumFields(section, &Nf);
    2344:   /* Get local solution with boundary values */
    2345:   DMGetLocalVector(dm, &locX);
    2346:   DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locX, 0.0, NULL, NULL, NULL);
    2347:   DMGlobalToLocalBegin(dm, X, INSERT_VALUES, locX);
    2348:   DMGlobalToLocalEnd(dm, X, INSERT_VALUES, locX);
    2349:   /* Loop over label values */
    2350:   PetscArrayzero(integral, Nf);
    2351:   for (v = 0; v < numVals; ++v) {
    2352:     IS           pointIS;
    2353:     PetscInt     numFaces, face;
    2354:     PetscScalar *fintegral;

    2356:     DMLabelGetStratumIS(label, vals[v], &pointIS);
    2357:     if (!pointIS) continue; /* No points with that id on this process */
    2358:     {
    2359:       IS isectIS;

    2361:       /* TODO: Special cases of ISIntersect where it is quick to check a priori if one is a superset of the other */
    2362:       ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS);
    2363:       ISDestroy(&pointIS);
    2364:       pointIS = isectIS;
    2365:     }
    2366:     ISGetLocalSize(pointIS, &numFaces);
    2367:     PetscCalloc1(numFaces*Nf, &fintegral);
    2368:     DMPlexComputeBdIntegral_Internal(dm, locX, pointIS, func, fintegral, user);
    2369:     /* Sum point contributions into integral */
    2370:     for (f = 0; f < Nf; ++f) for (face = 0; face < numFaces; ++face) integral[f] += fintegral[face*Nf+f];
    2371:     PetscFree(fintegral);
    2372:     ISDestroy(&pointIS);
    2373:   }
    2374:   DMRestoreLocalVector(dm, &locX);
    2375:   ISDestroy(&facetIS);
    2376:   PetscLogEventEnd(DMPLEX_IntegralFEM,dm,0,0,0);
    2377:   return(0);
    2378: }

    2380: /*@
    2381:   DMPlexComputeInterpolatorNested - Form the local portion of the interpolation matrix I from the coarse DM to a uniformly refined DM.

    2383:   Input Parameters:
    2384: + dmc  - The coarse mesh
    2385: . dmf  - The fine mesh
    2386: . isRefined - Flag indicating regular refinement, rather than the same topology
    2387: - user - The user context

    2389:   Output Parameter:
    2390: . In  - The interpolation matrix

    2392:   Level: developer

    2394: .seealso: DMPlexComputeInterpolatorGeneral(), DMPlexComputeJacobianFEM()
    2395: @*/
    2396: PetscErrorCode DMPlexComputeInterpolatorNested(DM dmc, DM dmf, PetscBool isRefined, Mat In, void *user)
    2397: {
    2398:   DM_Plex          *mesh  = (DM_Plex *) dmc->data;
    2399:   const char       *name  = "Interpolator";
    2400:   PetscFE          *feRef;
    2401:   PetscFV          *fvRef;
    2402:   PetscSection      fsection, fglobalSection;
    2403:   PetscSection      csection, cglobalSection;
    2404:   PetscScalar      *elemMat;
    2405:   PetscInt          dim, Nf, f, fieldI, fieldJ, offsetI, offsetJ, cStart, cEnd, c;
    2406:   PetscInt          cTotDim=0, rTotDim = 0;
    2407:   PetscErrorCode    ierr;

    2410:   PetscLogEventBegin(DMPLEX_InterpolatorFEM,dmc,dmf,0,0);
    2411:   DMGetDimension(dmf, &dim);
    2412:   DMGetLocalSection(dmf, &fsection);
    2413:   DMGetGlobalSection(dmf, &fglobalSection);
    2414:   DMGetLocalSection(dmc, &csection);
    2415:   DMGetGlobalSection(dmc, &cglobalSection);
    2416:   PetscSectionGetNumFields(fsection, &Nf);
    2417:   DMPlexGetSimplexOrBoxCells(dmc, 0, &cStart, &cEnd);
    2418:   PetscCalloc2(Nf, &feRef, Nf, &fvRef);
    2419:   for (f = 0; f < Nf; ++f) {
    2420:     PetscObject  obj, objc;
    2421:     PetscClassId id, idc;
    2422:     PetscInt     rNb = 0, Nc = 0, cNb = 0;

    2424:     DMGetField(dmf, f, NULL, &obj);
    2425:     PetscObjectGetClassId(obj, &id);
    2426:     if (id == PETSCFE_CLASSID) {
    2427:       PetscFE fe = (PetscFE) obj;

    2429:       if (isRefined) {
    2430:         PetscFERefine(fe, &feRef[f]);
    2431:       } else {
    2432:         PetscObjectReference((PetscObject) fe);
    2433:         feRef[f] = fe;
    2434:       }
    2435:       PetscFEGetDimension(feRef[f], &rNb);
    2436:       PetscFEGetNumComponents(fe, &Nc);
    2437:     } else if (id == PETSCFV_CLASSID) {
    2438:       PetscFV        fv = (PetscFV) obj;
    2439:       PetscDualSpace Q;

    2441:       if (isRefined) {
    2442:         PetscFVRefine(fv, &fvRef[f]);
    2443:       } else {
    2444:         PetscObjectReference((PetscObject) fv);
    2445:         fvRef[f] = fv;
    2446:       }
    2447:       PetscFVGetDualSpace(fvRef[f], &Q);
    2448:       PetscDualSpaceGetDimension(Q, &rNb);
    2449:       PetscFVGetDualSpace(fv, &Q);
    2450:       PetscFVGetNumComponents(fv, &Nc);
    2451:     }
    2452:     DMGetField(dmc, f, NULL, &objc);
    2453:     PetscObjectGetClassId(objc, &idc);
    2454:     if (idc == PETSCFE_CLASSID) {
    2455:       PetscFE fe = (PetscFE) objc;

    2457:       PetscFEGetDimension(fe, &cNb);
    2458:     } else if (id == PETSCFV_CLASSID) {
    2459:       PetscFV        fv = (PetscFV) obj;
    2460:       PetscDualSpace Q;

    2462:       PetscFVGetDualSpace(fv, &Q);
    2463:       PetscDualSpaceGetDimension(Q, &cNb);
    2464:     }
    2465:     rTotDim += rNb;
    2466:     cTotDim += cNb;
    2467:   }
    2468:   PetscMalloc1(rTotDim*cTotDim,&elemMat);
    2469:   PetscArrayzero(elemMat, rTotDim*cTotDim);
    2470:   for (fieldI = 0, offsetI = 0; fieldI < Nf; ++fieldI) {
    2471:     PetscDualSpace   Qref;
    2472:     PetscQuadrature  f;
    2473:     const PetscReal *qpoints, *qweights;
    2474:     PetscReal       *points;
    2475:     PetscInt         npoints = 0, Nc, Np, fpdim, i, k, p, d;

    2477:     /* Compose points from all dual basis functionals */
    2478:     if (feRef[fieldI]) {
    2479:       PetscFEGetDualSpace(feRef[fieldI], &Qref);
    2480:       PetscFEGetNumComponents(feRef[fieldI], &Nc);
    2481:     } else {
    2482:       PetscFVGetDualSpace(fvRef[fieldI], &Qref);
    2483:       PetscFVGetNumComponents(fvRef[fieldI], &Nc);
    2484:     }
    2485:     PetscDualSpaceGetDimension(Qref, &fpdim);
    2486:     for (i = 0; i < fpdim; ++i) {
    2487:       PetscDualSpaceGetFunctional(Qref, i, &f);
    2488:       PetscQuadratureGetData(f, NULL, NULL, &Np, NULL, NULL);
    2489:       npoints += Np;
    2490:     }
    2491:     PetscMalloc1(npoints*dim,&points);
    2492:     for (i = 0, k = 0; i < fpdim; ++i) {
    2493:       PetscDualSpaceGetFunctional(Qref, i, &f);
    2494:       PetscQuadratureGetData(f, NULL, NULL, &Np, &qpoints, NULL);
    2495:       for (p = 0; p < Np; ++p, ++k) for (d = 0; d < dim; ++d) points[k*dim+d] = qpoints[p*dim+d];
    2496:     }

    2498:     for (fieldJ = 0, offsetJ = 0; fieldJ < Nf; ++fieldJ) {
    2499:       PetscObject  obj;
    2500:       PetscClassId id;
    2501:       PetscInt     NcJ = 0, cpdim = 0, j, qNc;

    2503:       DMGetField(dmc, fieldJ, NULL, &obj);
    2504:       PetscObjectGetClassId(obj, &id);
    2505:       if (id == PETSCFE_CLASSID) {
    2506:         PetscFE           fe = (PetscFE) obj;
    2507:         PetscTabulation T  = NULL;

    2509:         /* Evaluate basis at points */
    2510:         PetscFEGetNumComponents(fe, &NcJ);
    2511:         PetscFEGetDimension(fe, &cpdim);
    2512:         /* For now, fields only interpolate themselves */
    2513:         if (fieldI == fieldJ) {
    2514:           if (Nc != NcJ) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of components in fine space field %D does not match coarse field %D", Nc, NcJ);
    2515:           PetscFECreateTabulation(fe, 1, npoints, points, 0, &T);
    2516:           for (i = 0, k = 0; i < fpdim; ++i) {
    2517:             PetscDualSpaceGetFunctional(Qref, i, &f);
    2518:             PetscQuadratureGetData(f, NULL, &qNc, &Np, NULL, &qweights);
    2519:             if (qNc != NcJ) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of components in quadrature %D does not match coarse field %D", qNc, NcJ);
    2520:             for (p = 0; p < Np; ++p, ++k) {
    2521:               for (j = 0; j < cpdim; ++j) {
    2522:                 /*
    2523:                    cTotDim:            Total columns in element interpolation matrix, sum of number of dual basis functionals in each field
    2524:                    offsetI, offsetJ:   Offsets into the larger element interpolation matrix for different fields
    2525:                    fpdim, i, cpdim, j: Dofs for fine and coarse grids, correspond to dual space basis functionals
    2526:                    qNC, Nc, Ncj, c:    Number of components in this field
    2527:                    Np, p:              Number of quad points in the fine grid functional i
    2528:                    k:                  i*Np + p, overall point number for the interpolation
    2529:                 */
    2530:                 for (c = 0; c < Nc; ++c) elemMat[(offsetI + i)*cTotDim + offsetJ + j] += T->T[0][k*cpdim*NcJ+j*Nc+c]*qweights[p*qNc+c];
    2531:               }
    2532:             }
    2533:           }
    2534:           PetscTabulationDestroy(&T);
    2535:         }
    2536:       } else if (id == PETSCFV_CLASSID) {
    2537:         PetscFV        fv = (PetscFV) obj;

    2539:         /* Evaluate constant function at points */
    2540:         PetscFVGetNumComponents(fv, &NcJ);
    2541:         cpdim = 1;
    2542:         /* For now, fields only interpolate themselves */
    2543:         if (fieldI == fieldJ) {
    2544:           if (Nc != NcJ) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of components in fine space field %D does not match coarse field %D", Nc, NcJ);
    2545:           for (i = 0, k = 0; i < fpdim; ++i) {
    2546:             PetscDualSpaceGetFunctional(Qref, i, &f);
    2547:             PetscQuadratureGetData(f, NULL, &qNc, &Np, NULL, &qweights);
    2548:             if (qNc != NcJ) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of components in quadrature %D does not match coarse field %D", qNc, NcJ);
    2549:             for (p = 0; p < Np; ++p, ++k) {
    2550:               for (j = 0; j < cpdim; ++j) {
    2551:                 for (c = 0; c < Nc; ++c) elemMat[(offsetI + i)*cTotDim + offsetJ + j] += 1.0*qweights[p*qNc+c];
    2552:               }
    2553:             }
    2554:           }
    2555:         }
    2556:       }
    2557:       offsetJ += cpdim;
    2558:     }
    2559:     offsetI += fpdim;
    2560:     PetscFree(points);
    2561:   }
    2562:   if (mesh->printFEM > 1) {DMPrintCellMatrix(0, name, rTotDim, cTotDim, elemMat);}
    2563:   /* Preallocate matrix */
    2564:   {
    2565:     Mat          preallocator;
    2566:     PetscScalar *vals;
    2567:     PetscInt    *cellCIndices, *cellFIndices;
    2568:     PetscInt     locRows, locCols, cell;

    2570:     MatGetLocalSize(In, &locRows, &locCols);
    2571:     MatCreate(PetscObjectComm((PetscObject) In), &preallocator);
    2572:     MatSetType(preallocator, MATPREALLOCATOR);
    2573:     MatSetSizes(preallocator, locRows, locCols, PETSC_DETERMINE, PETSC_DETERMINE);
    2574:     MatSetUp(preallocator);
    2575:     PetscCalloc3(rTotDim*cTotDim, &vals,cTotDim,&cellCIndices,rTotDim,&cellFIndices);
    2576:     for (cell = cStart; cell < cEnd; ++cell) {
    2577:       if (isRefined) {
    2578:         DMPlexMatGetClosureIndicesRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, cell, cellCIndices, cellFIndices);
    2579:         MatSetValues(preallocator, rTotDim, cellFIndices, cTotDim, cellCIndices, vals, INSERT_VALUES);
    2580:       } else {
    2581:         DMPlexMatSetClosureGeneral(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, preallocator, cell, vals, INSERT_VALUES);
    2582:       }
    2583:     }
    2584:     PetscFree3(vals,cellCIndices,cellFIndices);
    2585:     MatAssemblyBegin(preallocator, MAT_FINAL_ASSEMBLY);
    2586:     MatAssemblyEnd(preallocator, MAT_FINAL_ASSEMBLY);
    2587:     MatPreallocatorPreallocate(preallocator, PETSC_TRUE, In);
    2588:     MatDestroy(&preallocator);
    2589:   }
    2590:   /* Fill matrix */
    2591:   MatZeroEntries(In);
    2592:   for (c = cStart; c < cEnd; ++c) {
    2593:     if (isRefined) {
    2594:       DMPlexMatSetClosureRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, In, c, elemMat, INSERT_VALUES);
    2595:     } else {
    2596:       DMPlexMatSetClosureGeneral(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, In, c, elemMat, INSERT_VALUES);
    2597:     }
    2598:   }
    2599:   for (f = 0; f < Nf; ++f) {PetscFEDestroy(&feRef[f]);}
    2600:   PetscFree2(feRef,fvRef);
    2601:   PetscFree(elemMat);
    2602:   MatAssemblyBegin(In, MAT_FINAL_ASSEMBLY);
    2603:   MatAssemblyEnd(In, MAT_FINAL_ASSEMBLY);
    2604:   if (mesh->printFEM > 1) {
    2605:     PetscPrintf(PetscObjectComm((PetscObject)In), "%s:\n", name);
    2606:     MatChop(In, 1.0e-10);
    2607:     MatView(In, NULL);
    2608:   }
    2609:   PetscLogEventEnd(DMPLEX_InterpolatorFEM,dmc,dmf,0,0);
    2610:   return(0);
    2611: }

    2613: PetscErrorCode DMPlexComputeMassMatrixNested(DM dmc, DM dmf, Mat mass, void *user)
    2614: {
    2615:   SETERRQ(PetscObjectComm((PetscObject) dmc), PETSC_ERR_SUP, "Laziness");
    2616: }

    2618: /*@
    2619:   DMPlexComputeInterpolatorGeneral - Form the local portion of the interpolation matrix I from the coarse DM to a non-nested fine DM.

    2621:   Input Parameters:
    2622: + dmf  - The fine mesh
    2623: . dmc  - The coarse mesh
    2624: - user - The user context

    2626:   Output Parameter:
    2627: . In  - The interpolation matrix

    2629:   Level: developer

    2631: .seealso: DMPlexComputeInterpolatorNested(), DMPlexComputeJacobianFEM()
    2632: @*/
    2633: PetscErrorCode DMPlexComputeInterpolatorGeneral(DM dmc, DM dmf, Mat In, void *user)
    2634: {
    2635:   DM_Plex       *mesh = (DM_Plex *) dmf->data;
    2636:   const char    *name = "Interpolator";
    2637:   PetscDS        prob;
    2638:   PetscSection   fsection, csection, globalFSection, globalCSection;
    2639:   PetscHSetIJ    ht;
    2640:   PetscLayout    rLayout;
    2641:   PetscInt      *dnz, *onz;
    2642:   PetscInt       locRows, rStart, rEnd;
    2643:   PetscReal     *x, *v0, *J, *invJ, detJ;
    2644:   PetscReal     *v0c, *Jc, *invJc, detJc;
    2645:   PetscScalar   *elemMat;
    2646:   PetscInt       dim, Nf, field, totDim, cStart, cEnd, cell, ccell;

    2650:   PetscLogEventBegin(DMPLEX_InterpolatorFEM,dmc,dmf,0,0);
    2651:   DMGetCoordinateDim(dmc, &dim);
    2652:   DMGetDS(dmc, &prob);
    2653:   PetscDSGetWorkspace(prob, &x, NULL, NULL, NULL, NULL);
    2654:   PetscDSGetNumFields(prob, &Nf);
    2655:   PetscMalloc3(dim,&v0,dim*dim,&J,dim*dim,&invJ);
    2656:   PetscMalloc3(dim,&v0c,dim*dim,&Jc,dim*dim,&invJc);
    2657:   DMGetLocalSection(dmf, &fsection);
    2658:   DMGetGlobalSection(dmf, &globalFSection);
    2659:   DMGetLocalSection(dmc, &csection);
    2660:   DMGetGlobalSection(dmc, &globalCSection);
    2661:   DMPlexGetHeightStratum(dmf, 0, &cStart, &cEnd);
    2662:   PetscDSGetTotalDimension(prob, &totDim);
    2663:   PetscMalloc1(totDim, &elemMat);

    2665:   MatGetLocalSize(In, &locRows, NULL);
    2666:   PetscLayoutCreate(PetscObjectComm((PetscObject) In), &rLayout);
    2667:   PetscLayoutSetLocalSize(rLayout, locRows);
    2668:   PetscLayoutSetBlockSize(rLayout, 1);
    2669:   PetscLayoutSetUp(rLayout);
    2670:   PetscLayoutGetRange(rLayout, &rStart, &rEnd);
    2671:   PetscLayoutDestroy(&rLayout);
    2672:   PetscCalloc2(locRows,&dnz,locRows,&onz);
    2673:   PetscHSetIJCreate(&ht);
    2674:   for (field = 0; field < Nf; ++field) {
    2675:     PetscObject      obj;
    2676:     PetscClassId     id;
    2677:     PetscDualSpace   Q = NULL;
    2678:     PetscQuadrature  f;
    2679:     const PetscReal *qpoints;
    2680:     PetscInt         Nc, Np, fpdim, i, d;

    2682:     PetscDSGetDiscretization(prob, field, &obj);
    2683:     PetscObjectGetClassId(obj, &id);
    2684:     if (id == PETSCFE_CLASSID) {
    2685:       PetscFE fe = (PetscFE) obj;

    2687:       PetscFEGetDualSpace(fe, &Q);
    2688:       PetscFEGetNumComponents(fe, &Nc);
    2689:     } else if (id == PETSCFV_CLASSID) {
    2690:       PetscFV fv = (PetscFV) obj;

    2692:       PetscFVGetDualSpace(fv, &Q);
    2693:       Nc   = 1;
    2694:     }
    2695:     PetscDualSpaceGetDimension(Q, &fpdim);
    2696:     /* For each fine grid cell */
    2697:     for (cell = cStart; cell < cEnd; ++cell) {
    2698:       PetscInt *findices,   *cindices;
    2699:       PetscInt  numFIndices, numCIndices;

    2701:       DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL);
    2702:       DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ);
    2703:       if (numFIndices != fpdim) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fine indices %D != %D dual basis vecs", numFIndices, fpdim);
    2704:       for (i = 0; i < fpdim; ++i) {
    2705:         Vec             pointVec;
    2706:         PetscScalar    *pV;
    2707:         PetscSF         coarseCellSF = NULL;
    2708:         const PetscSFNode *coarseCells;
    2709:         PetscInt        numCoarseCells, q, c;

    2711:         /* Get points from the dual basis functional quadrature */
    2712:         PetscDualSpaceGetFunctional(Q, i, &f);
    2713:         PetscQuadratureGetData(f, NULL, NULL, &Np, &qpoints, NULL);
    2714:         VecCreateSeq(PETSC_COMM_SELF, Np*dim, &pointVec);
    2715:         VecSetBlockSize(pointVec, dim);
    2716:         VecGetArray(pointVec, &pV);
    2717:         for (q = 0; q < Np; ++q) {
    2718:           const PetscReal xi0[3] = {-1., -1., -1.};

    2720:           /* Transform point to real space */
    2721:           CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q*dim], x);
    2722:           for (d = 0; d < dim; ++d) pV[q*dim+d] = x[d];
    2723:         }
    2724:         VecRestoreArray(pointVec, &pV);
    2725:         /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
    2726:         /* OPT: Pack all quad points from fine cell */
    2727:         DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF);
    2728:         PetscSFViewFromOptions(coarseCellSF, NULL, "-interp_sf_view");
    2729:         /* Update preallocation info */
    2730:         PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells);
    2731:         if (numCoarseCells != Np) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_PLIB,"Not all closure points located");
    2732:         {
    2733:           PetscHashIJKey key;
    2734:           PetscBool      missing;

    2736:           key.i = findices[i];
    2737:           if (key.i >= 0) {
    2738:             /* Get indices for coarse elements */
    2739:             for (ccell = 0; ccell < numCoarseCells; ++ccell) {
    2740:               DMPlexGetClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL);
    2741:               for (c = 0; c < numCIndices; ++c) {
    2742:                 key.j = cindices[c];
    2743:                 if (key.j < 0) continue;
    2744:                 PetscHSetIJQueryAdd(ht, key, &missing);
    2745:                 if (missing) {
    2746:                   if ((key.j >= rStart) && (key.j < rEnd)) ++dnz[key.i-rStart];
    2747:                   else                                     ++onz[key.i-rStart];
    2748:                 }
    2749:               }
    2750:               DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL);
    2751:             }
    2752:           }
    2753:         }
    2754:         PetscSFDestroy(&coarseCellSF);
    2755:         VecDestroy(&pointVec);
    2756:       }
    2757:       DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL);
    2758:     }
    2759:   }
    2760:   PetscHSetIJDestroy(&ht);
    2761:   MatXAIJSetPreallocation(In, 1, dnz, onz, NULL, NULL);
    2762:   MatSetOption(In, MAT_NEW_NONZERO_ALLOCATION_ERR,PETSC_TRUE);
    2763:   PetscFree2(dnz,onz);
    2764:   for (field = 0; field < Nf; ++field) {
    2765:     PetscObject       obj;
    2766:     PetscClassId      id;
    2767:     PetscDualSpace    Q = NULL;
    2768:     PetscTabulation T = NULL;
    2769:     PetscQuadrature   f;
    2770:     const PetscReal  *qpoints, *qweights;
    2771:     PetscInt          Nc, qNc, Np, fpdim, i, d;

    2773:     PetscDSGetDiscretization(prob, field, &obj);
    2774:     PetscObjectGetClassId(obj, &id);
    2775:     if (id == PETSCFE_CLASSID) {
    2776:       PetscFE fe = (PetscFE) obj;

    2778:       PetscFEGetDualSpace(fe, &Q);
    2779:       PetscFEGetNumComponents(fe, &Nc);
    2780:       PetscFECreateTabulation(fe, 1, 1, x, 0, &T);
    2781:     } else if (id == PETSCFV_CLASSID) {
    2782:       PetscFV fv = (PetscFV) obj;

    2784:       PetscFVGetDualSpace(fv, &Q);
    2785:       Nc   = 1;
    2786:     } else SETERRQ1(PetscObjectComm((PetscObject)dmc),PETSC_ERR_ARG_WRONG,"Unknown discretization type for field %D",field);
    2787:     PetscDualSpaceGetDimension(Q, &fpdim);
    2788:     /* For each fine grid cell */
    2789:     for (cell = cStart; cell < cEnd; ++cell) {
    2790:       PetscInt *findices,   *cindices;
    2791:       PetscInt  numFIndices, numCIndices;

    2793:       DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL);
    2794:       DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ);
    2795:       if (numFIndices != fpdim) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fine indices %D != %D dual basis vecs", numFIndices, fpdim);
    2796:       for (i = 0; i < fpdim; ++i) {
    2797:         Vec             pointVec;
    2798:         PetscScalar    *pV;
    2799:         PetscSF         coarseCellSF = NULL;
    2800:         const PetscSFNode *coarseCells;
    2801:         PetscInt        numCoarseCells, cpdim, q, c, j;

    2803:         /* Get points from the dual basis functional quadrature */
    2804:         PetscDualSpaceGetFunctional(Q, i, &f);
    2805:         PetscQuadratureGetData(f, NULL, &qNc, &Np, &qpoints, &qweights);
    2806:         if (qNc != Nc) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of components in quadrature %D does not match coarse field %D", qNc, Nc);
    2807:         VecCreateSeq(PETSC_COMM_SELF, Np*dim, &pointVec);
    2808:         VecSetBlockSize(pointVec, dim);
    2809:         VecGetArray(pointVec, &pV);
    2810:         for (q = 0; q < Np; ++q) {
    2811:           const PetscReal xi0[3] = {-1., -1., -1.};

    2813:           /* Transform point to real space */
    2814:           CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q*dim], x);
    2815:           for (d = 0; d < dim; ++d) pV[q*dim+d] = x[d];
    2816:         }
    2817:         VecRestoreArray(pointVec, &pV);
    2818:         /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
    2819:         /* OPT: Read this out from preallocation information */
    2820:         DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF);
    2821:         /* Update preallocation info */
    2822:         PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells);
    2823:         if (numCoarseCells != Np) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_PLIB,"Not all closure points located");
    2824:         VecGetArray(pointVec, &pV);
    2825:         for (ccell = 0; ccell < numCoarseCells; ++ccell) {
    2826:           PetscReal pVReal[3];
    2827:           const PetscReal xi0[3] = {-1., -1., -1.};

    2829:           DMPlexGetClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL);
    2830:           /* Transform points from real space to coarse reference space */
    2831:           DMPlexComputeCellGeometryFEM(dmc, coarseCells[ccell].index, NULL, v0c, Jc, invJc, &detJc);
    2832:           for (d = 0; d < dim; ++d) pVReal[d] = PetscRealPart(pV[ccell*dim+d]);
    2833:           CoordinatesRealToRef(dim, dim, xi0, v0c, invJc, pVReal, x);

    2835:           if (id == PETSCFE_CLASSID) {
    2836:             PetscFE fe = (PetscFE) obj;

    2838:             /* Evaluate coarse basis on contained point */
    2839:             PetscFEGetDimension(fe, &cpdim);
    2840:             PetscFEComputeTabulation(fe, 1, x, 0, T);
    2841:             PetscArrayzero(elemMat, cpdim);
    2842:             /* Get elemMat entries by multiplying by weight */
    2843:             for (j = 0; j < cpdim; ++j) {
    2844:               for (c = 0; c < Nc; ++c) elemMat[j] += T->T[0][j*Nc + c]*qweights[ccell*qNc + c];
    2845:             }
    2846:           } else {
    2847:             cpdim = 1;
    2848:             for (j = 0; j < cpdim; ++j) {
    2849:               for (c = 0; c < Nc; ++c) elemMat[j] += 1.0*qweights[ccell*qNc + c];
    2850:             }
    2851:           }
    2852:           /* Update interpolator */
    2853:           if (mesh->printFEM > 1) {DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat);}
    2854:           if (numCIndices != cpdim) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %D != %D", numCIndices, cpdim);
    2855:           MatSetValues(In, 1, &findices[i], numCIndices, cindices, elemMat, INSERT_VALUES);
    2856:           DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL);
    2857:         }
    2858:         VecRestoreArray(pointVec, &pV);
    2859:         PetscSFDestroy(&coarseCellSF);
    2860:         VecDestroy(&pointVec);
    2861:       }
    2862:       DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL);
    2863:     }
    2864:     if (id == PETSCFE_CLASSID) {PetscTabulationDestroy(&T);}
    2865:   }
    2866:   PetscFree3(v0,J,invJ);
    2867:   PetscFree3(v0c,Jc,invJc);
    2868:   PetscFree(elemMat);
    2869:   MatAssemblyBegin(In, MAT_FINAL_ASSEMBLY);
    2870:   MatAssemblyEnd(In, MAT_FINAL_ASSEMBLY);
    2871:   PetscLogEventEnd(DMPLEX_InterpolatorFEM,dmc,dmf,0,0);
    2872:   return(0);
    2873: }

    2875: /*@
    2876:   DMPlexComputeMassMatrixGeneral - Form the local portion of the mass matrix M from the coarse DM to a non-nested fine DM.

    2878:   Input Parameters:
    2879: + dmf  - The fine mesh
    2880: . dmc  - The coarse mesh
    2881: - user - The user context

    2883:   Output Parameter:
    2884: . mass  - The mass matrix

    2886:   Level: developer

    2888: .seealso: DMPlexComputeMassMatrixNested(), DMPlexComputeInterpolatorNested(), DMPlexComputeInterpolatorGeneral(), DMPlexComputeJacobianFEM()
    2889: @*/
    2890: PetscErrorCode DMPlexComputeMassMatrixGeneral(DM dmc, DM dmf, Mat mass, void *user)
    2891: {
    2892:   DM_Plex       *mesh = (DM_Plex *) dmf->data;
    2893:   const char    *name = "Mass Matrix";
    2894:   PetscDS        prob;
    2895:   PetscSection   fsection, csection, globalFSection, globalCSection;
    2896:   PetscHSetIJ    ht;
    2897:   PetscLayout    rLayout;
    2898:   PetscInt      *dnz, *onz;
    2899:   PetscInt       locRows, rStart, rEnd;
    2900:   PetscReal     *x, *v0, *J, *invJ, detJ;
    2901:   PetscReal     *v0c, *Jc, *invJc, detJc;
    2902:   PetscScalar   *elemMat;
    2903:   PetscInt       dim, Nf, field, totDim, cStart, cEnd, cell, ccell;

    2907:   DMGetCoordinateDim(dmc, &dim);
    2908:   DMGetDS(dmc, &prob);
    2909:   PetscDSGetWorkspace(prob, &x, NULL, NULL, NULL, NULL);
    2910:   PetscDSGetNumFields(prob, &Nf);
    2911:   PetscMalloc3(dim,&v0,dim*dim,&J,dim*dim,&invJ);
    2912:   PetscMalloc3(dim,&v0c,dim*dim,&Jc,dim*dim,&invJc);
    2913:   DMGetLocalSection(dmf, &fsection);
    2914:   DMGetGlobalSection(dmf, &globalFSection);
    2915:   DMGetLocalSection(dmc, &csection);
    2916:   DMGetGlobalSection(dmc, &globalCSection);
    2917:   DMPlexGetHeightStratum(dmf, 0, &cStart, &cEnd);
    2918:   PetscDSGetTotalDimension(prob, &totDim);
    2919:   PetscMalloc1(totDim, &elemMat);

    2921:   MatGetLocalSize(mass, &locRows, NULL);
    2922:   PetscLayoutCreate(PetscObjectComm((PetscObject) mass), &rLayout);
    2923:   PetscLayoutSetLocalSize(rLayout, locRows);
    2924:   PetscLayoutSetBlockSize(rLayout, 1);
    2925:   PetscLayoutSetUp(rLayout);
    2926:   PetscLayoutGetRange(rLayout, &rStart, &rEnd);
    2927:   PetscLayoutDestroy(&rLayout);
    2928:   PetscCalloc2(locRows,&dnz,locRows,&onz);
    2929:   PetscHSetIJCreate(&ht);
    2930:   for (field = 0; field < Nf; ++field) {
    2931:     PetscObject      obj;
    2932:     PetscClassId     id;
    2933:     PetscQuadrature  quad;
    2934:     const PetscReal *qpoints;
    2935:     PetscInt         Nq, Nc, i, d;

    2937:     PetscDSGetDiscretization(prob, field, &obj);
    2938:     PetscObjectGetClassId(obj, &id);
    2939:     if (id == PETSCFE_CLASSID) {PetscFEGetQuadrature((PetscFE) obj, &quad);}
    2940:     else                       {PetscFVGetQuadrature((PetscFV) obj, &quad);}
    2941:     PetscQuadratureGetData(quad, NULL, &Nc, &Nq, &qpoints, NULL);
    2942:     /* For each fine grid cell */
    2943:     for (cell = cStart; cell < cEnd; ++cell) {
    2944:       Vec                pointVec;
    2945:       PetscScalar       *pV;
    2946:       PetscSF            coarseCellSF = NULL;
    2947:       const PetscSFNode *coarseCells;
    2948:       PetscInt           numCoarseCells, q, c;
    2949:       PetscInt          *findices,   *cindices;
    2950:       PetscInt           numFIndices, numCIndices;

    2952:       DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL);
    2953:       DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ);
    2954:       /* Get points from the quadrature */
    2955:       VecCreateSeq(PETSC_COMM_SELF, Nq*dim, &pointVec);
    2956:       VecSetBlockSize(pointVec, dim);
    2957:       VecGetArray(pointVec, &pV);
    2958:       for (q = 0; q < Nq; ++q) {
    2959:         const PetscReal xi0[3] = {-1., -1., -1.};

    2961:         /* Transform point to real space */
    2962:         CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q*dim], x);
    2963:         for (d = 0; d < dim; ++d) pV[q*dim+d] = x[d];
    2964:       }
    2965:       VecRestoreArray(pointVec, &pV);
    2966:       /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
    2967:       DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF);
    2968:       PetscSFViewFromOptions(coarseCellSF, NULL, "-interp_sf_view");
    2969:       /* Update preallocation info */
    2970:       PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells);
    2971:       if (numCoarseCells != Nq) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_PLIB,"Not all closure points located");
    2972:       {
    2973:         PetscHashIJKey key;
    2974:         PetscBool      missing;

    2976:         for (i = 0; i < numFIndices; ++i) {
    2977:           key.i = findices[i];
    2978:           if (key.i >= 0) {
    2979:             /* Get indices for coarse elements */
    2980:             for (ccell = 0; ccell < numCoarseCells; ++ccell) {
    2981:               DMPlexGetClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL);
    2982:               for (c = 0; c < numCIndices; ++c) {
    2983:                 key.j = cindices[c];
    2984:                 if (key.j < 0) continue;
    2985:                 PetscHSetIJQueryAdd(ht, key, &missing);
    2986:                 if (missing) {
    2987:                   if ((key.j >= rStart) && (key.j < rEnd)) ++dnz[key.i-rStart];
    2988:                   else                                     ++onz[key.i-rStart];
    2989:                 }
    2990:               }
    2991:               DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL);
    2992:             }
    2993:           }
    2994:         }
    2995:       }
    2996:       PetscSFDestroy(&coarseCellSF);
    2997:       VecDestroy(&pointVec);
    2998:       DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL);
    2999:     }
    3000:   }
    3001:   PetscHSetIJDestroy(&ht);
    3002:   MatXAIJSetPreallocation(mass, 1, dnz, onz, NULL, NULL);
    3003:   MatSetOption(mass, MAT_NEW_NONZERO_ALLOCATION_ERR,PETSC_TRUE);
    3004:   PetscFree2(dnz,onz);
    3005:   for (field = 0; field < Nf; ++field) {
    3006:     PetscObject       obj;
    3007:     PetscClassId      id;
    3008:     PetscTabulation T, Tfine;
    3009:     PetscQuadrature   quad;
    3010:     const PetscReal  *qpoints, *qweights;
    3011:     PetscInt          Nq, Nc, i, d;

    3013:     PetscDSGetDiscretization(prob, field, &obj);
    3014:     PetscObjectGetClassId(obj, &id);
    3015:     if (id == PETSCFE_CLASSID) {
    3016:       PetscFEGetQuadrature((PetscFE) obj, &quad);
    3017:       PetscFEGetCellTabulation((PetscFE) obj, 1, &Tfine);
    3018:       PetscFECreateTabulation((PetscFE) obj, 1, 1, x, 0, &T);
    3019:     } else {
    3020:       PetscFVGetQuadrature((PetscFV) obj, &quad);
    3021:     }
    3022:     PetscQuadratureGetData(quad, NULL, &Nc, &Nq, &qpoints, &qweights);
    3023:     /* For each fine grid cell */
    3024:     for (cell = cStart; cell < cEnd; ++cell) {
    3025:       Vec                pointVec;
    3026:       PetscScalar       *pV;
    3027:       PetscSF            coarseCellSF = NULL;
    3028:       const PetscSFNode *coarseCells;
    3029:       PetscInt           numCoarseCells, cpdim, q, c, j;
    3030:       PetscInt          *findices,   *cindices;
    3031:       PetscInt           numFIndices, numCIndices;

    3033:       DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL);
    3034:       DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ);
    3035:       /* Get points from the quadrature */
    3036:       VecCreateSeq(PETSC_COMM_SELF, Nq*dim, &pointVec);
    3037:       VecSetBlockSize(pointVec, dim);
    3038:       VecGetArray(pointVec, &pV);
    3039:       for (q = 0; q < Nq; ++q) {
    3040:         const PetscReal xi0[3] = {-1., -1., -1.};

    3042:         /* Transform point to real space */
    3043:         CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q*dim], x);
    3044:         for (d = 0; d < dim; ++d) pV[q*dim+d] = x[d];
    3045:       }
    3046:       VecRestoreArray(pointVec, &pV);
    3047:       /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
    3048:       DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF);
    3049:       /* Update matrix */
    3050:       PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells);
    3051:       if (numCoarseCells != Nq) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_PLIB,"Not all closure points located");
    3052:       VecGetArray(pointVec, &pV);
    3053:       for (ccell = 0; ccell < numCoarseCells; ++ccell) {
    3054:         PetscReal pVReal[3];
    3055:         const PetscReal xi0[3] = {-1., -1., -1.};

    3057:         DMPlexGetClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL);
    3058:         /* Transform points from real space to coarse reference space */
    3059:         DMPlexComputeCellGeometryFEM(dmc, coarseCells[ccell].index, NULL, v0c, Jc, invJc, &detJc);
    3060:         for (d = 0; d < dim; ++d) pVReal[d] = PetscRealPart(pV[ccell*dim+d]);
    3061:         CoordinatesRealToRef(dim, dim, xi0, v0c, invJc, pVReal, x);

    3063:         if (id == PETSCFE_CLASSID) {
    3064:           PetscFE fe = (PetscFE) obj;

    3066:           /* Evaluate coarse basis on contained point */
    3067:           PetscFEGetDimension(fe, &cpdim);
    3068:           PetscFEComputeTabulation(fe, 1, x, 0, T);
    3069:           /* Get elemMat entries by multiplying by weight */
    3070:           for (i = 0; i < numFIndices; ++i) {
    3071:             PetscArrayzero(elemMat, cpdim);
    3072:             for (j = 0; j < cpdim; ++j) {
    3073:               for (c = 0; c < Nc; ++c) elemMat[j] += T->T[0][j*Nc + c]*Tfine->T[0][(ccell*numFIndices + i)*Nc + c]*qweights[ccell*Nc + c]*detJ;
    3074:             }
    3075:             /* Update interpolator */
    3076:             if (mesh->printFEM > 1) {DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat);}
    3077:             if (numCIndices != cpdim) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %D != %D", numCIndices, cpdim);
    3078:             MatSetValues(mass, 1, &findices[i], numCIndices, cindices, elemMat, ADD_VALUES);
    3079:           }
    3080:         } else {
    3081:           cpdim = 1;
    3082:           for (i = 0; i < numFIndices; ++i) {
    3083:             PetscArrayzero(elemMat, cpdim);
    3084:             for (j = 0; j < cpdim; ++j) {
    3085:               for (c = 0; c < Nc; ++c) elemMat[j] += 1.0*1.0*qweights[ccell*Nc + c]*detJ;
    3086:             }
    3087:             /* Update interpolator */
    3088:             if (mesh->printFEM > 1) {DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat);}
    3089:             PetscPrintf(PETSC_COMM_SELF, "Nq: %D %D Nf: %D %D Nc: %D %D\n", ccell, Nq, i, numFIndices, j, numCIndices);
    3090:             if (numCIndices != cpdim) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %D != %D", numCIndices, cpdim);
    3091:             MatSetValues(mass, 1, &findices[i], numCIndices, cindices, elemMat, ADD_VALUES);
    3092:           }
    3093:         }
    3094:         DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL);
    3095:       }
    3096:       VecRestoreArray(pointVec, &pV);
    3097:       PetscSFDestroy(&coarseCellSF);
    3098:       VecDestroy(&pointVec);
    3099:       DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL);
    3100:     }
    3101:     if (id == PETSCFE_CLASSID) {PetscTabulationDestroy(&T);}
    3102:   }
    3103:   PetscFree3(v0,J,invJ);
    3104:   PetscFree3(v0c,Jc,invJc);
    3105:   PetscFree(elemMat);
    3106:   MatAssemblyBegin(mass, MAT_FINAL_ASSEMBLY);
    3107:   MatAssemblyEnd(mass, MAT_FINAL_ASSEMBLY);
    3108:   return(0);
    3109: }

    3111: /*@
    3112:   DMPlexComputeInjectorFEM - Compute a mapping from coarse unknowns to fine unknowns

    3114:   Input Parameters:
    3115: + dmc  - The coarse mesh
    3116: - dmf  - The fine mesh
    3117: - user - The user context

    3119:   Output Parameter:
    3120: . sc   - The mapping

    3122:   Level: developer

    3124: .seealso: DMPlexComputeInterpolatorNested(), DMPlexComputeJacobianFEM()
    3125: @*/
    3126: PetscErrorCode DMPlexComputeInjectorFEM(DM dmc, DM dmf, VecScatter *sc, void *user)
    3127: {
    3128:   PetscDS        prob;
    3129:   PetscFE       *feRef;
    3130:   PetscFV       *fvRef;
    3131:   Vec            fv, cv;
    3132:   IS             fis, cis;
    3133:   PetscSection   fsection, fglobalSection, csection, cglobalSection;
    3134:   PetscInt      *cmap, *cellCIndices, *cellFIndices, *cindices, *findices;
    3135:   PetscInt       cTotDim, fTotDim = 0, Nf, f, field, cStart, cEnd, c, dim, d, startC, endC, offsetC, offsetF, m;
    3136:   PetscBool     *needAvg;

    3140:   PetscLogEventBegin(DMPLEX_InjectorFEM,dmc,dmf,0,0);
    3141:   DMGetDimension(dmf, &dim);
    3142:   DMGetLocalSection(dmf, &fsection);
    3143:   DMGetGlobalSection(dmf, &fglobalSection);
    3144:   DMGetLocalSection(dmc, &csection);
    3145:   DMGetGlobalSection(dmc, &cglobalSection);
    3146:   PetscSectionGetNumFields(fsection, &Nf);
    3147:   DMPlexGetSimplexOrBoxCells(dmc, 0, &cStart, &cEnd);
    3148:   DMGetDS(dmc, &prob);
    3149:   PetscCalloc3(Nf,&feRef,Nf,&fvRef,Nf,&needAvg);
    3150:   for (f = 0; f < Nf; ++f) {
    3151:     PetscObject  obj;
    3152:     PetscClassId id;
    3153:     PetscInt     fNb = 0, Nc = 0;

    3155:     PetscDSGetDiscretization(prob, f, &obj);
    3156:     PetscObjectGetClassId(obj, &id);
    3157:     if (id == PETSCFE_CLASSID) {
    3158:       PetscFE    fe = (PetscFE) obj;
    3159:       PetscSpace sp;
    3160:       PetscInt   maxDegree;

    3162:       PetscFERefine(fe, &feRef[f]);
    3163:       PetscFEGetDimension(feRef[f], &fNb);
    3164:       PetscFEGetNumComponents(fe, &Nc);
    3165:       PetscFEGetBasisSpace(fe, &sp);
    3166:       PetscSpaceGetDegree(sp, NULL, &maxDegree);
    3167:       if (!maxDegree) needAvg[f] = PETSC_TRUE;
    3168:     } else if (id == PETSCFV_CLASSID) {
    3169:       PetscFV        fv = (PetscFV) obj;
    3170:       PetscDualSpace Q;

    3172:       PetscFVRefine(fv, &fvRef[f]);
    3173:       PetscFVGetDualSpace(fvRef[f], &Q);
    3174:       PetscDualSpaceGetDimension(Q, &fNb);
    3175:       PetscFVGetNumComponents(fv, &Nc);
    3176:       needAvg[f] = PETSC_TRUE;
    3177:     }
    3178:     fTotDim += fNb;
    3179:   }
    3180:   PetscDSGetTotalDimension(prob, &cTotDim);
    3181:   PetscMalloc1(cTotDim,&cmap);
    3182:   for (field = 0, offsetC = 0, offsetF = 0; field < Nf; ++field) {
    3183:     PetscFE        feC;
    3184:     PetscFV        fvC;
    3185:     PetscDualSpace QF, QC;
    3186:     PetscInt       order = -1, NcF, NcC, fpdim, cpdim;

    3188:     if (feRef[field]) {
    3189:       PetscDSGetDiscretization(prob, field, (PetscObject *) &feC);
    3190:       PetscFEGetNumComponents(feC, &NcC);
    3191:       PetscFEGetNumComponents(feRef[field], &NcF);
    3192:       PetscFEGetDualSpace(feRef[field], &QF);
    3193:       PetscDualSpaceGetOrder(QF, &order);
    3194:       PetscDualSpaceGetDimension(QF, &fpdim);
    3195:       PetscFEGetDualSpace(feC, &QC);
    3196:       PetscDualSpaceGetDimension(QC, &cpdim);
    3197:     } else {
    3198:       PetscDSGetDiscretization(prob, field, (PetscObject *) &fvC);
    3199:       PetscFVGetNumComponents(fvC, &NcC);
    3200:       PetscFVGetNumComponents(fvRef[field], &NcF);
    3201:       PetscFVGetDualSpace(fvRef[field], &QF);
    3202:       PetscDualSpaceGetDimension(QF, &fpdim);
    3203:       PetscFVGetDualSpace(fvC, &QC);
    3204:       PetscDualSpaceGetDimension(QC, &cpdim);
    3205:     }
    3206:     if (NcF != NcC) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of components in fine space field %D does not match coarse field %D", NcF, NcC);
    3207:     for (c = 0; c < cpdim; ++c) {
    3208:       PetscQuadrature  cfunc;
    3209:       const PetscReal *cqpoints, *cqweights;
    3210:       PetscInt         NqcC, NpC;
    3211:       PetscBool        found = PETSC_FALSE;

    3213:       PetscDualSpaceGetFunctional(QC, c, &cfunc);
    3214:       PetscQuadratureGetData(cfunc, NULL, &NqcC, &NpC, &cqpoints, &cqweights);
    3215:       if (NqcC != NcC) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of quadrature components %D must match number of field components %D", NqcC, NcC);
    3216:       if (NpC != 1 && feRef[field]) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Do not know how to do injection for moments");
    3217:       for (f = 0; f < fpdim; ++f) {
    3218:         PetscQuadrature  ffunc;
    3219:         const PetscReal *fqpoints, *fqweights;
    3220:         PetscReal        sum = 0.0;
    3221:         PetscInt         NqcF, NpF;

    3223:         PetscDualSpaceGetFunctional(QF, f, &ffunc);
    3224:         PetscQuadratureGetData(ffunc, NULL, &NqcF, &NpF, &fqpoints, &fqweights);
    3225:         if (NqcF != NcF) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of quadrature components %D must match number of field components %D", NqcF, NcF);
    3226:         if (NpC != NpF) continue;
    3227:         for (d = 0; d < dim; ++d) sum += PetscAbsReal(cqpoints[d] - fqpoints[d]);
    3228:         if (sum > 1.0e-9) continue;
    3229:         for (d = 0; d < NcC; ++d) sum += PetscAbsReal(cqweights[d]*fqweights[d]);
    3230:         if (sum < 1.0e-9) continue;
    3231:         cmap[offsetC+c] = offsetF+f;
    3232:         found = PETSC_TRUE;
    3233:         break;
    3234:       }
    3235:       if (!found) {
    3236:         /* TODO We really want the average here, but some asshole put VecScatter in the interface */
    3237:         if (fvRef[field] || (feRef[field] && order == 0)) {
    3238:           cmap[offsetC+c] = offsetF+0;
    3239:         } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Could not locate matching functional for injection");
    3240:       }
    3241:     }
    3242:     offsetC += cpdim;
    3243:     offsetF += fpdim;
    3244:   }
    3245:   for (f = 0; f < Nf; ++f) {PetscFEDestroy(&feRef[f]);PetscFVDestroy(&fvRef[f]);}
    3246:   PetscFree3(feRef,fvRef,needAvg);

    3248:   DMGetGlobalVector(dmf, &fv);
    3249:   DMGetGlobalVector(dmc, &cv);
    3250:   VecGetOwnershipRange(cv, &startC, &endC);
    3251:   PetscSectionGetConstrainedStorageSize(cglobalSection, &m);
    3252:   PetscMalloc2(cTotDim,&cellCIndices,fTotDim,&cellFIndices);
    3253:   PetscMalloc1(m,&cindices);
    3254:   PetscMalloc1(m,&findices);
    3255:   for (d = 0; d < m; ++d) cindices[d] = findices[d] = -1;
    3256:   for (c = cStart; c < cEnd; ++c) {
    3257:     DMPlexMatGetClosureIndicesRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, c, cellCIndices, cellFIndices);
    3258:     for (d = 0; d < cTotDim; ++d) {
    3259:       if ((cellCIndices[d] < startC) || (cellCIndices[d] >= endC)) continue;
    3260:       if ((findices[cellCIndices[d]-startC] >= 0) && (findices[cellCIndices[d]-startC] != cellFIndices[cmap[d]])) SETERRQ3(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Coarse dof %D maps to both %D and %D", cindices[cellCIndices[d]-startC], findices[cellCIndices[d]-startC], cellFIndices[cmap[d]]);
    3261:       cindices[cellCIndices[d]-startC] = cellCIndices[d];
    3262:       findices[cellCIndices[d]-startC] = cellFIndices[cmap[d]];
    3263:     }
    3264:   }
    3265:   PetscFree(cmap);
    3266:   PetscFree2(cellCIndices,cellFIndices);

    3268:   ISCreateGeneral(PETSC_COMM_SELF, m, cindices, PETSC_OWN_POINTER, &cis);
    3269:   ISCreateGeneral(PETSC_COMM_SELF, m, findices, PETSC_OWN_POINTER, &fis);
    3270:   VecScatterCreate(cv, cis, fv, fis, sc);
    3271:   ISDestroy(&cis);
    3272:   ISDestroy(&fis);
    3273:   DMRestoreGlobalVector(dmf, &fv);
    3274:   DMRestoreGlobalVector(dmc, &cv);
    3275:   PetscLogEventEnd(DMPLEX_InjectorFEM,dmc,dmf,0,0);
    3276:   return(0);
    3277: }

    3279: /*@C
    3280:   DMPlexGetCellFields - Retrieve the field values values for a chunk of cells

    3282:   Input Parameters:
    3283: + dm     - The DM
    3284: . cellIS - The cells to include
    3285: . locX   - A local vector with the solution fields
    3286: . locX_t - A local vector with solution field time derivatives, or NULL
    3287: - locA   - A local vector with auxiliary fields, or NULL

    3289:   Output Parameters:
    3290: + u   - The field coefficients
    3291: . u_t - The fields derivative coefficients
    3292: - a   - The auxiliary field coefficients

    3294:   Level: developer

    3296: .seealso: DMPlexGetFaceFields()
    3297: @*/
    3298: PetscErrorCode DMPlexGetCellFields(DM dm, IS cellIS, Vec locX, Vec locX_t, Vec locA, PetscScalar **u, PetscScalar **u_t, PetscScalar **a)
    3299: {
    3300:   DM              plex, plexA = NULL;
    3301:   DMEnclosureType encAux;
    3302:   PetscSection    section, sectionAux;
    3303:   PetscDS         prob;
    3304:   const PetscInt *cells;
    3305:   PetscInt        cStart, cEnd, numCells, totDim, totDimAux, c;
    3306:   PetscErrorCode  ierr;

    3316:   DMPlexConvertPlex(dm, &plex, PETSC_FALSE);
    3317:   ISGetPointRange(cellIS, &cStart, &cEnd, &cells);
    3318:   DMGetLocalSection(dm, &section);
    3319:   DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob);
    3320:   PetscDSGetTotalDimension(prob, &totDim);
    3321:   if (locA) {
    3322:     DM      dmAux;
    3323:     PetscDS probAux;

    3325:     VecGetDM(locA, &dmAux);
    3326:     DMGetEnclosureRelation(dmAux, dm, &encAux);
    3327:     DMPlexConvertPlex(dmAux, &plexA, PETSC_FALSE);
    3328:     DMGetLocalSection(dmAux, &sectionAux);
    3329:     DMGetDS(dmAux, &probAux);
    3330:     PetscDSGetTotalDimension(probAux, &totDimAux);
    3331:   }
    3332:   numCells = cEnd - cStart;
    3333:   DMGetWorkArray(dm, numCells*totDim, MPIU_SCALAR, u);
    3334:   if (locX_t) {DMGetWorkArray(dm, numCells*totDim, MPIU_SCALAR, u_t);} else {*u_t = NULL;}
    3335:   if (locA)   {DMGetWorkArray(dm, numCells*totDimAux, MPIU_SCALAR, a);} else {*a = NULL;}
    3336:   for (c = cStart; c < cEnd; ++c) {
    3337:     const PetscInt cell = cells ? cells[c] : c;
    3338:     const PetscInt cind = c - cStart;
    3339:     PetscScalar   *x = NULL, *x_t = NULL, *ul = *u, *ul_t = *u_t, *al = *a;
    3340:     PetscInt       i;

    3342:     DMPlexVecGetClosure(plex, section, locX, cell, NULL, &x);
    3343:     for (i = 0; i < totDim; ++i) ul[cind*totDim+i] = x[i];
    3344:     DMPlexVecRestoreClosure(plex, section, locX, cell, NULL, &x);
    3345:     if (locX_t) {
    3346:       DMPlexVecGetClosure(plex, section, locX_t, cell, NULL, &x_t);
    3347:       for (i = 0; i < totDim; ++i) ul_t[cind*totDim+i] = x_t[i];
    3348:       DMPlexVecRestoreClosure(plex, section, locX_t, cell, NULL, &x_t);
    3349:     }
    3350:     if (locA) {
    3351:       PetscInt subcell;
    3352:       DMGetEnclosurePoint(plexA, dm, encAux, cell, &subcell);
    3353:       DMPlexVecGetClosure(plexA, sectionAux, locA, subcell, NULL, &x);
    3354:       for (i = 0; i < totDimAux; ++i) al[cind*totDimAux+i] = x[i];
    3355:       DMPlexVecRestoreClosure(plexA, sectionAux, locA, subcell, NULL, &x);
    3356:     }
    3357:   }
    3358:   DMDestroy(&plex);
    3359:   if (locA) {DMDestroy(&plexA);}
    3360:   ISRestorePointRange(cellIS, &cStart, &cEnd, &cells);
    3361:   return(0);
    3362: }

    3364: /*@C
    3365:   DMPlexRestoreCellFields - Restore the field values values for a chunk of cells

    3367:   Input Parameters:
    3368: + dm     - The DM
    3369: . cellIS - The cells to include
    3370: . locX   - A local vector with the solution fields
    3371: . locX_t - A local vector with solution field time derivatives, or NULL
    3372: - locA   - A local vector with auxiliary fields, or NULL

    3374:   Output Parameters:
    3375: + u   - The field coefficients
    3376: . u_t - The fields derivative coefficients
    3377: - a   - The auxiliary field coefficients

    3379:   Level: developer

    3381: .seealso: DMPlexGetFaceFields()
    3382: @*/
    3383: PetscErrorCode DMPlexRestoreCellFields(DM dm, IS cellIS, Vec locX, Vec locX_t, Vec locA, PetscScalar **u, PetscScalar **u_t, PetscScalar **a)
    3384: {

    3388:   DMRestoreWorkArray(dm, 0, MPIU_SCALAR, u);
    3389:   if (locX_t) {DMRestoreWorkArray(dm, 0, MPIU_SCALAR, u_t);}
    3390:   if (locA)   {DMRestoreWorkArray(dm, 0, MPIU_SCALAR, a);}
    3391:   return(0);
    3392: }

    3394: /*
    3395:   Get the auxiliary field vectors for the negative side (s = 0) and positive side (s = 1) of the interfaace
    3396: */
    3397: static PetscErrorCode DMPlexGetHybridAuxFields(DM dm, DM dmAux[], PetscDS dsAux[], IS cellIS, Vec locA[], PetscScalar *a[])
    3398: {
    3399:   DM              plexA[2];
    3400:   DMEnclosureType encAux[2];
    3401:   PetscSection    sectionAux[2];
    3402:   const PetscInt *cells;
    3403:   PetscInt        cStart, cEnd, numCells, c, s, totDimAux[2];
    3404:   PetscErrorCode  ierr;

    3408:   if (!locA[0] || !locA[1]) return(0);
    3412:   ISGetPointRange(cellIS, &cStart, &cEnd, &cells);
    3413:   numCells = cEnd - cStart;
    3414:   for (s = 0; s < 2; ++s) {
    3418:     DMPlexConvertPlex(dmAux[s], &plexA[s], PETSC_FALSE);
    3419:     DMGetEnclosureRelation(dmAux[s], dm, &encAux[s]);
    3420:     DMGetLocalSection(dmAux[s], &sectionAux[s]);
    3421:     PetscDSGetTotalDimension(dsAux[s], &totDimAux[s]);
    3422:     DMGetWorkArray(dmAux[s], numCells*totDimAux[s], MPIU_SCALAR, &a[s]);
    3423:   }
    3424:   for (c = cStart; c < cEnd; ++c) {
    3425:     const PetscInt  cell = cells ? cells[c] : c;
    3426:     const PetscInt  cind = c - cStart;
    3427:     const PetscInt *cone, *ornt;

    3429:     DMPlexGetCone(dm, cell, &cone);
    3430:     DMPlexGetConeOrientation(dm, cell, &ornt);
    3431:     if (ornt[0]) SETERRQ3(PETSC_COMM_SELF, PETSC_ERR_SUP, "Face %D in hybrid cell %D has orientation %D != 0", cone[0], cell, ornt[0]);
    3432:     for (s = 0; s < 2; ++s) {
    3433:       PetscScalar   *x = NULL, *al = a[s];
    3434:       const PetscInt tdA = totDimAux[s];
    3435:       PetscInt       subface, Na, i;

    3437:       DMGetEnclosurePoint(plexA[s], dm, encAux[s], cone[s], &subface);
    3438:       DMPlexVecGetClosure(plexA[s], sectionAux[s], locA[s], subface, &Na, &x);
    3439:       for (i = 0; i < Na; ++i) al[cind*tdA+i] = x[i];
    3440:       DMPlexVecRestoreClosure(plexA[s], sectionAux[s], locA[s], subface, &Na, &x);
    3441:     }
    3442:   }
    3443:   for (s = 0; s < 2; ++s) {DMDestroy(&plexA[s]);}
    3444:   ISRestorePointRange(cellIS, &cStart, &cEnd, &cells);
    3445:   return(0);
    3446: }

    3448: static PetscErrorCode DMPlexRestoreHybridAuxFields(DM dmAux[], PetscDS dsAux[], IS cellIS, Vec locA[], PetscScalar *a[])
    3449: {

    3453:   if (!locA[0] || !locA[1]) return(0);
    3454:   DMRestoreWorkArray(dmAux[0], 0, MPIU_SCALAR, &a[0]);
    3455:   DMRestoreWorkArray(dmAux[1], 0, MPIU_SCALAR, &a[1]);
    3456:   return(0);
    3457: }

    3459: /*@C
    3460:   DMPlexGetFaceFields - Retrieve the field values values for a chunk of faces

    3462:   Input Parameters:
    3463: + dm     - The DM
    3464: . fStart - The first face to include
    3465: . fEnd   - The first face to exclude
    3466: . locX   - A local vector with the solution fields
    3467: . locX_t - A local vector with solution field time derivatives, or NULL
    3468: . faceGeometry - A local vector with face geometry
    3469: . cellGeometry - A local vector with cell geometry
    3470: - locaGrad - A local vector with field gradients, or NULL

    3472:   Output Parameters:
    3473: + Nface - The number of faces with field values
    3474: . uL - The field values at the left side of the face
    3475: - uR - The field values at the right side of the face

    3477:   Level: developer

    3479: .seealso: DMPlexGetCellFields()
    3480: @*/
    3481: PetscErrorCode DMPlexGetFaceFields(DM dm, PetscInt fStart, PetscInt fEnd, Vec locX, Vec locX_t, Vec faceGeometry, Vec cellGeometry, Vec locGrad, PetscInt *Nface, PetscScalar **uL, PetscScalar **uR)
    3482: {
    3483:   DM                 dmFace, dmCell, dmGrad = NULL;
    3484:   PetscSection       section;
    3485:   PetscDS            prob;
    3486:   DMLabel            ghostLabel;
    3487:   const PetscScalar *facegeom, *cellgeom, *x, *lgrad;
    3488:   PetscBool         *isFE;
    3489:   PetscInt           dim, Nf, f, Nc, numFaces = fEnd - fStart, iface, face;
    3490:   PetscErrorCode     ierr;

    3501:   DMGetDimension(dm, &dim);
    3502:   DMGetDS(dm, &prob);
    3503:   DMGetLocalSection(dm, &section);
    3504:   PetscDSGetNumFields(prob, &Nf);
    3505:   PetscDSGetTotalComponents(prob, &Nc);
    3506:   PetscMalloc1(Nf, &isFE);
    3507:   for (f = 0; f < Nf; ++f) {
    3508:     PetscObject  obj;
    3509:     PetscClassId id;

    3511:     PetscDSGetDiscretization(prob, f, &obj);
    3512:     PetscObjectGetClassId(obj, &id);
    3513:     if (id == PETSCFE_CLASSID)      {isFE[f] = PETSC_TRUE;}
    3514:     else if (id == PETSCFV_CLASSID) {isFE[f] = PETSC_FALSE;}
    3515:     else                            {isFE[f] = PETSC_FALSE;}
    3516:   }
    3517:   DMGetLabel(dm, "ghost", &ghostLabel);
    3518:   VecGetArrayRead(locX, &x);
    3519:   VecGetDM(faceGeometry, &dmFace);
    3520:   VecGetArrayRead(faceGeometry, &facegeom);
    3521:   VecGetDM(cellGeometry, &dmCell);
    3522:   VecGetArrayRead(cellGeometry, &cellgeom);
    3523:   if (locGrad) {
    3524:     VecGetDM(locGrad, &dmGrad);
    3525:     VecGetArrayRead(locGrad, &lgrad);
    3526:   }
    3527:   DMGetWorkArray(dm, numFaces*Nc, MPIU_SCALAR, uL);
    3528:   DMGetWorkArray(dm, numFaces*Nc, MPIU_SCALAR, uR);
    3529:   /* Right now just eat the extra work for FE (could make a cell loop) */
    3530:   for (face = fStart, iface = 0; face < fEnd; ++face) {
    3531:     const PetscInt        *cells;
    3532:     PetscFVFaceGeom       *fg;
    3533:     PetscFVCellGeom       *cgL, *cgR;
    3534:     PetscScalar           *xL, *xR, *gL, *gR;
    3535:     PetscScalar           *uLl = *uL, *uRl = *uR;
    3536:     PetscInt               ghost, nsupp, nchild;

    3538:     DMLabelGetValue(ghostLabel, face, &ghost);
    3539:     DMPlexGetSupportSize(dm, face, &nsupp);
    3540:     DMPlexGetTreeChildren(dm, face, &nchild, NULL);
    3541:     if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
    3542:     DMPlexPointLocalRead(dmFace, face, facegeom, &fg);
    3543:     DMPlexGetSupport(dm, face, &cells);
    3544:     DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cgL);
    3545:     DMPlexPointLocalRead(dmCell, cells[1], cellgeom, &cgR);
    3546:     for (f = 0; f < Nf; ++f) {
    3547:       PetscInt off;

    3549:       PetscDSGetComponentOffset(prob, f, &off);
    3550:       if (isFE[f]) {
    3551:         const PetscInt *cone;
    3552:         PetscInt        comp, coneSizeL, coneSizeR, faceLocL, faceLocR, ldof, rdof, d;

    3554:         xL = xR = NULL;
    3555:         PetscSectionGetFieldComponents(section, f, &comp);
    3556:         DMPlexVecGetClosure(dm, section, locX, cells[0], &ldof, (PetscScalar **) &xL);
    3557:         DMPlexVecGetClosure(dm, section, locX, cells[1], &rdof, (PetscScalar **) &xR);
    3558:         DMPlexGetCone(dm, cells[0], &cone);
    3559:         DMPlexGetConeSize(dm, cells[0], &coneSizeL);
    3560:         for (faceLocL = 0; faceLocL < coneSizeL; ++faceLocL) if (cone[faceLocL] == face) break;
    3561:         DMPlexGetCone(dm, cells[1], &cone);
    3562:         DMPlexGetConeSize(dm, cells[1], &coneSizeR);
    3563:         for (faceLocR = 0; faceLocR < coneSizeR; ++faceLocR) if (cone[faceLocR] == face) break;
    3564:         if (faceLocL == coneSizeL && faceLocR == coneSizeR) SETERRQ3(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find face %D in cone of cell %D or cell %D", face, cells[0], cells[1]);
    3565:         /* Check that FEM field has values in the right cell (sometimes its an FV ghost cell) */
    3566:         /* TODO: this is a hack that might not be right for nonconforming */
    3567:         if (faceLocL < coneSizeL) {
    3568:           PetscFEEvaluateFaceFields_Internal(prob, f, faceLocL, xL, &uLl[iface*Nc+off]);
    3569:           if (rdof == ldof && faceLocR < coneSizeR) {PetscFEEvaluateFaceFields_Internal(prob, f, faceLocR, xR, &uRl[iface*Nc+off]);}
    3570:           else              {for (d = 0; d < comp; ++d) uRl[iface*Nc+off+d] = uLl[iface*Nc+off+d];}
    3571:         }
    3572:         else {
    3573:           PetscFEEvaluateFaceFields_Internal(prob, f, faceLocR, xR, &uRl[iface*Nc+off]);
    3574:           PetscSectionGetFieldComponents(section, f, &comp);
    3575:           for (d = 0; d < comp; ++d) uLl[iface*Nc+off+d] = uRl[iface*Nc+off+d];
    3576:         }
    3577:         DMPlexVecRestoreClosure(dm, section, locX, cells[0], &ldof, (PetscScalar **) &xL);
    3578:         DMPlexVecRestoreClosure(dm, section, locX, cells[1], &rdof, (PetscScalar **) &xR);
    3579:       } else {
    3580:         PetscFV  fv;
    3581:         PetscInt numComp, c;

    3583:         PetscDSGetDiscretization(prob, f, (PetscObject *) &fv);
    3584:         PetscFVGetNumComponents(fv, &numComp);
    3585:         DMPlexPointLocalFieldRead(dm, cells[0], f, x, &xL);
    3586:         DMPlexPointLocalFieldRead(dm, cells[1], f, x, &xR);
    3587:         if (dmGrad) {
    3588:           PetscReal dxL[3], dxR[3];

    3590:           DMPlexPointLocalRead(dmGrad, cells[0], lgrad, &gL);
    3591:           DMPlexPointLocalRead(dmGrad, cells[1], lgrad, &gR);
    3592:           DMPlex_WaxpyD_Internal(dim, -1, cgL->centroid, fg->centroid, dxL);
    3593:           DMPlex_WaxpyD_Internal(dim, -1, cgR->centroid, fg->centroid, dxR);
    3594:           for (c = 0; c < numComp; ++c) {
    3595:             uLl[iface*Nc+off+c] = xL[c] + DMPlex_DotD_Internal(dim, &gL[c*dim], dxL);
    3596:             uRl[iface*Nc+off+c] = xR[c] + DMPlex_DotD_Internal(dim, &gR[c*dim], dxR);
    3597:           }
    3598:         } else {
    3599:           for (c = 0; c < numComp; ++c) {
    3600:             uLl[iface*Nc+off+c] = xL[c];
    3601:             uRl[iface*Nc+off+c] = xR[c];
    3602:           }
    3603:         }
    3604:       }
    3605:     }
    3606:     ++iface;
    3607:   }
    3608:   *Nface = iface;
    3609:   VecRestoreArrayRead(locX, &x);
    3610:   VecRestoreArrayRead(faceGeometry, &facegeom);
    3611:   VecRestoreArrayRead(cellGeometry, &cellgeom);
    3612:   if (locGrad) {
    3613:     VecRestoreArrayRead(locGrad, &lgrad);
    3614:   }
    3615:   PetscFree(isFE);
    3616:   return(0);
    3617: }

    3619: /*@C
    3620:   DMPlexRestoreFaceFields - Restore the field values values for a chunk of faces

    3622:   Input Parameters:
    3623: + dm     - The DM
    3624: . fStart - The first face to include
    3625: . fEnd   - The first face to exclude
    3626: . locX   - A local vector with the solution fields
    3627: . locX_t - A local vector with solution field time derivatives, or NULL
    3628: . faceGeometry - A local vector with face geometry
    3629: . cellGeometry - A local vector with cell geometry
    3630: - locaGrad - A local vector with field gradients, or NULL

    3632:   Output Parameters:
    3633: + Nface - The number of faces with field values
    3634: . uL - The field values at the left side of the face
    3635: - uR - The field values at the right side of the face

    3637:   Level: developer

    3639: .seealso: DMPlexGetFaceFields()
    3640: @*/
    3641: PetscErrorCode DMPlexRestoreFaceFields(DM dm, PetscInt fStart, PetscInt fEnd, Vec locX, Vec locX_t, Vec faceGeometry, Vec cellGeometry, Vec locGrad, PetscInt *Nface, PetscScalar **uL, PetscScalar **uR)
    3642: {

    3646:   DMRestoreWorkArray(dm, 0, MPIU_SCALAR, uL);
    3647:   DMRestoreWorkArray(dm, 0, MPIU_SCALAR, uR);
    3648:   return(0);
    3649: }

    3651: /*@C
    3652:   DMPlexGetFaceGeometry - Retrieve the geometric values for a chunk of faces

    3654:   Input Parameters:
    3655: + dm     - The DM
    3656: . fStart - The first face to include
    3657: . fEnd   - The first face to exclude
    3658: . faceGeometry - A local vector with face geometry
    3659: - cellGeometry - A local vector with cell geometry

    3661:   Output Parameters:
    3662: + Nface - The number of faces with field values
    3663: . fgeom - The extract the face centroid and normal
    3664: - vol   - The cell volume

    3666:   Level: developer

    3668: .seealso: DMPlexGetCellFields()
    3669: @*/
    3670: PetscErrorCode DMPlexGetFaceGeometry(DM dm, PetscInt fStart, PetscInt fEnd, Vec faceGeometry, Vec cellGeometry, PetscInt *Nface, PetscFVFaceGeom **fgeom, PetscReal **vol)
    3671: {
    3672:   DM                 dmFace, dmCell;
    3673:   DMLabel            ghostLabel;
    3674:   const PetscScalar *facegeom, *cellgeom;
    3675:   PetscInt           dim, numFaces = fEnd - fStart, iface, face;
    3676:   PetscErrorCode     ierr;

    3684:   DMGetDimension(dm, &dim);
    3685:   DMGetLabel(dm, "ghost", &ghostLabel);
    3686:   VecGetDM(faceGeometry, &dmFace);
    3687:   VecGetArrayRead(faceGeometry, &facegeom);
    3688:   VecGetDM(cellGeometry, &dmCell);
    3689:   VecGetArrayRead(cellGeometry, &cellgeom);
    3690:   PetscMalloc1(numFaces, fgeom);
    3691:   DMGetWorkArray(dm, numFaces*2, MPIU_SCALAR, vol);
    3692:   for (face = fStart, iface = 0; face < fEnd; ++face) {
    3693:     const PetscInt        *cells;
    3694:     PetscFVFaceGeom       *fg;
    3695:     PetscFVCellGeom       *cgL, *cgR;
    3696:     PetscFVFaceGeom       *fgeoml = *fgeom;
    3697:     PetscReal             *voll   = *vol;
    3698:     PetscInt               ghost, d, nchild, nsupp;

    3700:     DMLabelGetValue(ghostLabel, face, &ghost);
    3701:     DMPlexGetSupportSize(dm, face, &nsupp);
    3702:     DMPlexGetTreeChildren(dm, face, &nchild, NULL);
    3703:     if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
    3704:     DMPlexPointLocalRead(dmFace, face, facegeom, &fg);
    3705:     DMPlexGetSupport(dm, face, &cells);
    3706:     DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cgL);
    3707:     DMPlexPointLocalRead(dmCell, cells[1], cellgeom, &cgR);
    3708:     for (d = 0; d < dim; ++d) {
    3709:       fgeoml[iface].centroid[d] = fg->centroid[d];
    3710:       fgeoml[iface].normal[d]   = fg->normal[d];
    3711:     }
    3712:     voll[iface*2+0] = cgL->volume;
    3713:     voll[iface*2+1] = cgR->volume;
    3714:     ++iface;
    3715:   }
    3716:   *Nface = iface;
    3717:   VecRestoreArrayRead(faceGeometry, &facegeom);
    3718:   VecRestoreArrayRead(cellGeometry, &cellgeom);
    3719:   return(0);
    3720: }

    3722: /*@C
    3723:   DMPlexRestoreFaceGeometry - Restore the field values values for a chunk of faces

    3725:   Input Parameters:
    3726: + dm     - The DM
    3727: . fStart - The first face to include
    3728: . fEnd   - The first face to exclude
    3729: . faceGeometry - A local vector with face geometry
    3730: - cellGeometry - A local vector with cell geometry

    3732:   Output Parameters:
    3733: + Nface - The number of faces with field values
    3734: . fgeom - The extract the face centroid and normal
    3735: - vol   - The cell volume

    3737:   Level: developer

    3739: .seealso: DMPlexGetFaceFields()
    3740: @*/
    3741: PetscErrorCode DMPlexRestoreFaceGeometry(DM dm, PetscInt fStart, PetscInt fEnd, Vec faceGeometry, Vec cellGeometry, PetscInt *Nface, PetscFVFaceGeom **fgeom, PetscReal **vol)
    3742: {

    3746:   PetscFree(*fgeom);
    3747:   DMRestoreWorkArray(dm, 0, MPIU_REAL, vol);
    3748:   return(0);
    3749: }

    3751: PetscErrorCode DMSNESGetFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom)
    3752: {
    3753:   char            composeStr[33] = {0};
    3754:   PetscObjectId   id;
    3755:   PetscContainer  container;
    3756:   PetscErrorCode  ierr;

    3759:   PetscObjectGetId((PetscObject)quad,&id);
    3760:   PetscSNPrintf(composeStr, 32, "DMSNESGetFEGeom_%x\n", id);
    3761:   PetscObjectQuery((PetscObject) pointIS, composeStr, (PetscObject *) &container);
    3762:   if (container) {
    3763:     PetscContainerGetPointer(container, (void **) geom);
    3764:   } else {
    3765:     DMFieldCreateFEGeom(coordField, pointIS, quad, faceData, geom);
    3766:     PetscContainerCreate(PETSC_COMM_SELF,&container);
    3767:     PetscContainerSetPointer(container, (void *) *geom);
    3768:     PetscContainerSetUserDestroy(container, PetscContainerUserDestroy_PetscFEGeom);
    3769:     PetscObjectCompose((PetscObject) pointIS, composeStr, (PetscObject) container);
    3770:     PetscContainerDestroy(&container);
    3771:   }
    3772:   return(0);
    3773: }

    3775: PetscErrorCode DMSNESRestoreFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom)
    3776: {
    3778:   *geom = NULL;
    3779:   return(0);
    3780: }

    3782: PetscErrorCode DMPlexComputeResidual_Patch_Internal(DM dm, PetscSection section, IS cellIS, PetscReal t, Vec locX, Vec locX_t, Vec locF, void *user)
    3783: {
    3784:   DM_Plex         *mesh       = (DM_Plex *) dm->data;
    3785:   const char      *name       = "Residual";
    3786:   DM               dmAux      = NULL;
    3787:   DMLabel          ghostLabel = NULL;
    3788:   PetscDS          prob       = NULL;
    3789:   PetscDS          probAux    = NULL;
    3790:   PetscBool        useFEM     = PETSC_FALSE;
    3791:   PetscBool        isImplicit = (locX_t || t == PETSC_MIN_REAL) ? PETSC_TRUE : PETSC_FALSE;
    3792:   DMField          coordField = NULL;
    3793:   Vec              locA;
    3794:   PetscScalar     *u = NULL, *u_t, *a, *uL = NULL, *uR = NULL;
    3795:   IS               chunkIS;
    3796:   const PetscInt  *cells;
    3797:   PetscInt         cStart, cEnd, numCells;
    3798:   PetscInt         Nf, f, totDim, totDimAux, numChunks, cellChunkSize, chunk, fStart, fEnd;
    3799:   PetscInt         maxDegree = PETSC_MAX_INT;
    3800:   PetscFormKey key;
    3801:   PetscQuadrature  affineQuad = NULL, *quads = NULL;
    3802:   PetscFEGeom     *affineGeom = NULL, **geoms = NULL;
    3803:   PetscErrorCode   ierr;

    3806:   PetscLogEventBegin(DMPLEX_ResidualFEM,dm,0,0,0);
    3807:   /* FEM+FVM */
    3808:   /* 1: Get sizes from dm and dmAux */
    3809:   DMGetLabel(dm, "ghost", &ghostLabel);
    3810:   DMGetDS(dm, &prob);
    3811:   PetscDSGetNumFields(prob, &Nf);
    3812:   PetscDSGetTotalDimension(prob, &totDim);
    3813:   DMGetAuxiliaryVec(dm, NULL, 0, &locA);
    3814:   if (locA) {
    3815:     VecGetDM(locA, &dmAux);
    3816:     DMGetDS(dmAux, &probAux);
    3817:     PetscDSGetTotalDimension(probAux, &totDimAux);
    3818:   }
    3819:   /* 2: Get geometric data */
    3820:   for (f = 0; f < Nf; ++f) {
    3821:     PetscObject  obj;
    3822:     PetscClassId id;
    3823:     PetscBool    fimp;

    3825:     PetscDSGetImplicit(prob, f, &fimp);
    3826:     if (isImplicit != fimp) continue;
    3827:     PetscDSGetDiscretization(prob, f, &obj);
    3828:     PetscObjectGetClassId(obj, &id);
    3829:     if (id == PETSCFE_CLASSID) {useFEM = PETSC_TRUE;}
    3830:     if (id == PETSCFV_CLASSID) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_WRONG,"Use of FVM with PCPATCH not yet implemented");
    3831:   }
    3832:   if (useFEM) {
    3833:     DMGetCoordinateField(dm, &coordField);
    3834:     DMFieldGetDegree(coordField,cellIS,NULL,&maxDegree);
    3835:     if (maxDegree <= 1) {
    3836:       DMFieldCreateDefaultQuadrature(coordField,cellIS,&affineQuad);
    3837:       if (affineQuad) {
    3838:         DMSNESGetFEGeom(coordField,cellIS,affineQuad,PETSC_FALSE,&affineGeom);
    3839:       }
    3840:     } else {
    3841:       PetscCalloc2(Nf,&quads,Nf,&geoms);
    3842:       for (f = 0; f < Nf; ++f) {
    3843:         PetscObject  obj;
    3844:         PetscClassId id;
    3845:         PetscBool    fimp;

    3847:         PetscDSGetImplicit(prob, f, &fimp);
    3848:         if (isImplicit != fimp) continue;
    3849:         PetscDSGetDiscretization(prob, f, &obj);
    3850:         PetscObjectGetClassId(obj, &id);
    3851:         if (id == PETSCFE_CLASSID) {
    3852:           PetscFE fe = (PetscFE) obj;

    3854:           PetscFEGetQuadrature(fe, &quads[f]);
    3855:           PetscObjectReference((PetscObject)quads[f]);
    3856:           DMSNESGetFEGeom(coordField,cellIS,quads[f],PETSC_FALSE,&geoms[f]);
    3857:         }
    3858:       }
    3859:     }
    3860:   }
    3861:   /* Loop over chunks */
    3862:   ISGetPointRange(cellIS, &cStart, &cEnd, &cells);
    3863:   DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd);
    3864:   if (useFEM) {ISCreate(PETSC_COMM_SELF, &chunkIS);}
    3865:   numCells      = cEnd - cStart;
    3866:   numChunks     = 1;
    3867:   cellChunkSize = numCells/numChunks;
    3868:   numChunks     = PetscMin(1,numCells);
    3869:   key.label     = NULL;
    3870:   key.value     = 0;
    3871:   key.part      = 0;
    3872:   for (chunk = 0; chunk < numChunks; ++chunk) {
    3873:     PetscScalar     *elemVec, *fluxL = NULL, *fluxR = NULL;
    3874:     PetscReal       *vol = NULL;
    3875:     PetscFVFaceGeom *fgeom = NULL;
    3876:     PetscInt         cS = cStart+chunk*cellChunkSize, cE = PetscMin(cS+cellChunkSize, cEnd), numCells = cE - cS, c;
    3877:     PetscInt         numFaces = 0;

    3879:     /* Extract field coefficients */
    3880:     if (useFEM) {
    3881:       ISGetPointSubrange(chunkIS, cS, cE, cells);
    3882:       DMPlexGetCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a);
    3883:       DMGetWorkArray(dm, numCells*totDim, MPIU_SCALAR, &elemVec);
    3884:       PetscArrayzero(elemVec, numCells*totDim);
    3885:     }
    3886:     /* TODO We will interlace both our field coefficients (u, u_t, uL, uR, etc.) and our output (elemVec, fL, fR). I think this works */
    3887:     /* Loop over fields */
    3888:     for (f = 0; f < Nf; ++f) {
    3889:       PetscObject  obj;
    3890:       PetscClassId id;
    3891:       PetscBool    fimp;
    3892:       PetscInt     numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset;

    3894:       key.field = f;
    3895:       PetscDSGetImplicit(prob, f, &fimp);
    3896:       if (isImplicit != fimp) continue;
    3897:       PetscDSGetDiscretization(prob, f, &obj);
    3898:       PetscObjectGetClassId(obj, &id);
    3899:       if (id == PETSCFE_CLASSID) {
    3900:         PetscFE         fe = (PetscFE) obj;
    3901:         PetscFEGeom    *geom = affineGeom ? affineGeom : geoms[f];
    3902:         PetscFEGeom    *chunkGeom = NULL;
    3903:         PetscQuadrature quad = affineQuad ? affineQuad : quads[f];
    3904:         PetscInt        Nq, Nb;

    3906:         PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches);
    3907:         PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL);
    3908:         PetscFEGetDimension(fe, &Nb);
    3909:         blockSize = Nb;
    3910:         batchSize = numBlocks * blockSize;
    3911:         PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches);
    3912:         numChunks = numCells / (numBatches*batchSize);
    3913:         Ne        = numChunks*numBatches*batchSize;
    3914:         Nr        = numCells % (numBatches*batchSize);
    3915:         offset    = numCells - Nr;
    3916:         /* Integrate FE residual to get elemVec (need fields at quadrature points) */
    3917:         /*   For FV, I think we use a P0 basis and the cell coefficients (for subdivided cells, we can tweak the basis tabulation to be the indicator function) */
    3918:         PetscFEGeomGetChunk(geom,0,offset,&chunkGeom);
    3919:         PetscFEIntegrateResidual(prob, key, Ne, chunkGeom, u, u_t, probAux, a, t, elemVec);
    3920:         PetscFEGeomGetChunk(geom,offset,numCells,&chunkGeom);
    3921:         PetscFEIntegrateResidual(prob, key, Nr, chunkGeom, &u[offset*totDim], u_t ? &u_t[offset*totDim] : NULL, probAux, &a[offset*totDimAux], t, &elemVec[offset*totDim]);
    3922:         PetscFEGeomRestoreChunk(geom,offset,numCells,&chunkGeom);
    3923:       } else if (id == PETSCFV_CLASSID) {
    3924:         PetscFV fv = (PetscFV) obj;

    3926:         Ne = numFaces;
    3927:         /* Riemann solve over faces (need fields at face centroids) */
    3928:         /*   We need to evaluate FE fields at those coordinates */
    3929:         PetscFVIntegrateRHSFunction(fv, prob, f, Ne, fgeom, vol, uL, uR, fluxL, fluxR);
    3930:       } else SETERRQ1(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %D", f);
    3931:     }
    3932:     /* Loop over domain */
    3933:     if (useFEM) {
    3934:       /* Add elemVec to locX */
    3935:       for (c = cS; c < cE; ++c) {
    3936:         const PetscInt cell = cells ? cells[c] : c;
    3937:         const PetscInt cind = c - cStart;

    3939:         if (mesh->printFEM > 1) {DMPrintCellVector(cell, name, totDim, &elemVec[cind*totDim]);}
    3940:         if (ghostLabel) {
    3941:           PetscInt ghostVal;

    3943:           DMLabelGetValue(ghostLabel,cell,&ghostVal);
    3944:           if (ghostVal > 0) continue;
    3945:         }
    3946:         DMPlexVecSetClosure(dm, section, locF, cell, &elemVec[cind*totDim], ADD_ALL_VALUES);
    3947:       }
    3948:     }
    3949:     /* Handle time derivative */
    3950:     if (locX_t) {
    3951:       PetscScalar *x_t, *fa;

    3953:       VecGetArray(locF, &fa);
    3954:       VecGetArray(locX_t, &x_t);
    3955:       for (f = 0; f < Nf; ++f) {
    3956:         PetscFV      fv;
    3957:         PetscObject  obj;
    3958:         PetscClassId id;
    3959:         PetscInt     pdim, d;

    3961:         PetscDSGetDiscretization(prob, f, &obj);
    3962:         PetscObjectGetClassId(obj, &id);
    3963:         if (id != PETSCFV_CLASSID) continue;
    3964:         fv   = (PetscFV) obj;
    3965:         PetscFVGetNumComponents(fv, &pdim);
    3966:         for (c = cS; c < cE; ++c) {
    3967:           const PetscInt cell = cells ? cells[c] : c;
    3968:           PetscScalar   *u_t, *r;

    3970:           if (ghostLabel) {
    3971:             PetscInt ghostVal;

    3973:             DMLabelGetValue(ghostLabel, cell, &ghostVal);
    3974:             if (ghostVal > 0) continue;
    3975:           }
    3976:           DMPlexPointLocalFieldRead(dm, cell, f, x_t, &u_t);
    3977:           DMPlexPointLocalFieldRef(dm, cell, f, fa, &r);
    3978:           for (d = 0; d < pdim; ++d) r[d] += u_t[d];
    3979:         }
    3980:       }
    3981:       VecRestoreArray(locX_t, &x_t);
    3982:       VecRestoreArray(locF, &fa);
    3983:     }
    3984:     if (useFEM) {
    3985:       DMPlexRestoreCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a);
    3986:       DMRestoreWorkArray(dm, numCells*totDim, MPIU_SCALAR, &elemVec);
    3987:     }
    3988:   }
    3989:   if (useFEM) {ISDestroy(&chunkIS);}
    3990:   ISRestorePointRange(cellIS, &cStart, &cEnd, &cells);
    3991:   /* TODO Could include boundary residual here (see DMPlexComputeResidual_Internal) */
    3992:   if (useFEM) {
    3993:     if (maxDegree <= 1) {
    3994:       DMSNESRestoreFEGeom(coordField,cellIS,affineQuad,PETSC_FALSE,&affineGeom);
    3995:       PetscQuadratureDestroy(&affineQuad);
    3996:     } else {
    3997:       for (f = 0; f < Nf; ++f) {
    3998:         DMSNESRestoreFEGeom(coordField,cellIS,quads[f],PETSC_FALSE,&geoms[f]);
    3999:         PetscQuadratureDestroy(&quads[f]);
    4000:       }
    4001:       PetscFree2(quads,geoms);
    4002:     }
    4003:   }
    4004:   PetscLogEventEnd(DMPLEX_ResidualFEM,dm,0,0,0);
    4005:   return(0);
    4006: }

    4008: /*
    4009:   We always assemble JacP, and if the matrix is different from Jac and two different sets of point functions are provided, we also assemble Jac

    4011:   X   - The local solution vector
    4012:   X_t - The local solution time derivative vector, or NULL
    4013: */
    4014: PetscErrorCode DMPlexComputeJacobian_Patch_Internal(DM dm, PetscSection section, PetscSection globalSection, IS cellIS,
    4015:                                                     PetscReal t, PetscReal X_tShift, Vec X, Vec X_t, Mat Jac, Mat JacP, void *ctx)
    4016: {
    4017:   DM_Plex         *mesh  = (DM_Plex *) dm->data;
    4018:   const char      *name = "Jacobian", *nameP = "JacobianPre";
    4019:   DM               dmAux = NULL;
    4020:   PetscDS          prob,   probAux = NULL;
    4021:   PetscSection     sectionAux = NULL;
    4022:   Vec              A;
    4023:   DMField          coordField;
    4024:   PetscFEGeom     *cgeomFEM;
    4025:   PetscQuadrature  qGeom = NULL;
    4026:   Mat              J = Jac, JP = JacP;
    4027:   PetscScalar     *work, *u = NULL, *u_t = NULL, *a = NULL, *elemMat = NULL, *elemMatP = NULL, *elemMatD = NULL;
    4028:   PetscBool        hasJac, hasPrec, hasDyn, assembleJac, isMatIS, isMatISP, *isFE, hasFV = PETSC_FALSE;
    4029:   const PetscInt  *cells;
    4030:   PetscFormKey key;
    4031:   PetscInt         Nf, fieldI, fieldJ, maxDegree, numCells, cStart, cEnd, numChunks, chunkSize, chunk, totDim, totDimAux = 0, sz, wsz, off = 0, offCell = 0;
    4032:   PetscErrorCode   ierr;

    4035:   ISGetLocalSize(cellIS, &numCells);
    4036:   ISGetPointRange(cellIS, &cStart, &cEnd, &cells);
    4037:   PetscLogEventBegin(DMPLEX_JacobianFEM,dm,0,0,0);
    4038:   DMGetDS(dm, &prob);
    4039:   DMGetAuxiliaryVec(dm, NULL, 0, &A);
    4040:   if (A) {
    4041:     VecGetDM(A, &dmAux);
    4042:     DMGetLocalSection(dmAux, &sectionAux);
    4043:     DMGetDS(dmAux, &probAux);
    4044:   }
    4045:   /* Get flags */
    4046:   PetscDSGetNumFields(prob, &Nf);
    4047:   DMGetWorkArray(dm, Nf, MPIU_BOOL, &isFE);
    4048:   for (fieldI = 0; fieldI < Nf; ++fieldI) {
    4049:     PetscObject  disc;
    4050:     PetscClassId id;
    4051:     PetscDSGetDiscretization(prob, fieldI, &disc);
    4052:     PetscObjectGetClassId(disc, &id);
    4053:     if (id == PETSCFE_CLASSID)      {isFE[fieldI] = PETSC_TRUE;}
    4054:     else if (id == PETSCFV_CLASSID) {hasFV = PETSC_TRUE; isFE[fieldI] = PETSC_FALSE;}
    4055:   }
    4056:   PetscDSHasJacobian(prob, &hasJac);
    4057:   PetscDSHasJacobianPreconditioner(prob, &hasPrec);
    4058:   PetscDSHasDynamicJacobian(prob, &hasDyn);
    4059:   assembleJac = hasJac && hasPrec && (Jac != JacP) ? PETSC_TRUE : PETSC_FALSE;
    4060:   hasDyn      = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
    4061:   PetscObjectTypeCompare((PetscObject) Jac,  MATIS, &isMatIS);
    4062:   PetscObjectTypeCompare((PetscObject) JacP, MATIS, &isMatISP);
    4063:   /* Setup input data and temp arrays (should be DMGetWorkArray) */
    4064:   if (isMatISP || isMatISP) {DMPlexGetSubdomainSection(dm, &globalSection);}
    4065:   if (isMatIS)  {MatISGetLocalMat(Jac,  &J);}
    4066:   if (isMatISP) {MatISGetLocalMat(JacP, &JP);}
    4067:   if (hasFV)    {MatSetOption(JP, MAT_IGNORE_ZERO_ENTRIES, PETSC_TRUE);} /* No allocated space for FV stuff, so ignore the zero entries */
    4068:   PetscDSGetTotalDimension(prob, &totDim);
    4069:   if (probAux) {PetscDSGetTotalDimension(probAux, &totDimAux);}
    4070:   /* Compute batch sizes */
    4071:   if (isFE[0]) {
    4072:     PetscFE         fe;
    4073:     PetscQuadrature q;
    4074:     PetscInt        numQuadPoints, numBatches, batchSize, numBlocks, blockSize, Nb;

    4076:     PetscDSGetDiscretization(prob, 0, (PetscObject *) &fe);
    4077:     PetscFEGetQuadrature(fe, &q);
    4078:     PetscQuadratureGetData(q, NULL, NULL, &numQuadPoints, NULL, NULL);
    4079:     PetscFEGetDimension(fe, &Nb);
    4080:     PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches);
    4081:     blockSize = Nb*numQuadPoints;
    4082:     batchSize = numBlocks  * blockSize;
    4083:     chunkSize = numBatches * batchSize;
    4084:     numChunks = numCells / chunkSize + numCells % chunkSize;
    4085:     PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches);
    4086:   } else {
    4087:     chunkSize = numCells;
    4088:     numChunks = 1;
    4089:   }
    4090:   /* Get work space */
    4091:   wsz  = (((X?1:0) + (X_t?1:0) + (dmAux?1:0))*totDim + ((hasJac?1:0) + (hasPrec?1:0) + (hasDyn?1:0))*totDim*totDim)*chunkSize;
    4092:   DMGetWorkArray(dm, wsz, MPIU_SCALAR, &work);
    4093:   PetscArrayzero(work, wsz);
    4094:   off      = 0;
    4095:   u        = X       ? (sz = chunkSize*totDim,        off += sz, work+off-sz) : NULL;
    4096:   u_t      = X_t     ? (sz = chunkSize*totDim,        off += sz, work+off-sz) : NULL;
    4097:   a        = dmAux   ? (sz = chunkSize*totDimAux,     off += sz, work+off-sz) : NULL;
    4098:   elemMat  = hasJac  ? (sz = chunkSize*totDim*totDim, off += sz, work+off-sz) : NULL;
    4099:   elemMatP = hasPrec ? (sz = chunkSize*totDim*totDim, off += sz, work+off-sz) : NULL;
    4100:   elemMatD = hasDyn  ? (sz = chunkSize*totDim*totDim, off += sz, work+off-sz) : NULL;
    4101:   if (off != wsz) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Error is workspace size %D should be %D", off, wsz);
    4102:   /* Setup geometry */
    4103:   DMGetCoordinateField(dm, &coordField);
    4104:   DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree);
    4105:   if (maxDegree <= 1) {DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom);}
    4106:   if (!qGeom) {
    4107:     PetscFE fe;

    4109:     PetscDSGetDiscretization(prob, 0, (PetscObject *) &fe);
    4110:     PetscFEGetQuadrature(fe, &qGeom);
    4111:     PetscObjectReference((PetscObject) qGeom);
    4112:   }
    4113:   DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM);
    4114:   /* Compute volume integrals */
    4115:   if (assembleJac) {MatZeroEntries(J);}
    4116:   MatZeroEntries(JP);
    4117:   key.label = NULL;
    4118:   key.value = 0;
    4119:   key.part  = 0;
    4120:   for (chunk = 0; chunk < numChunks; ++chunk, offCell += chunkSize) {
    4121:     const PetscInt   Ncell = PetscMin(chunkSize, numCells - offCell);
    4122:     PetscInt         c;

    4124:     /* Extract values */
    4125:     for (c = 0; c < Ncell; ++c) {
    4126:       const PetscInt cell = cells ? cells[c+offCell] : c+offCell;
    4127:       PetscScalar   *x = NULL,  *x_t = NULL;
    4128:       PetscInt       i;

    4130:       if (X) {
    4131:         DMPlexVecGetClosure(dm, section, X, cell, NULL, &x);
    4132:         for (i = 0; i < totDim; ++i) u[c*totDim+i] = x[i];
    4133:         DMPlexVecRestoreClosure(dm, section, X, cell, NULL, &x);
    4134:       }
    4135:       if (X_t) {
    4136:         DMPlexVecGetClosure(dm, section, X_t, cell, NULL, &x_t);
    4137:         for (i = 0; i < totDim; ++i) u_t[c*totDim+i] = x_t[i];
    4138:         DMPlexVecRestoreClosure(dm, section, X_t, cell, NULL, &x_t);
    4139:       }
    4140:       if (dmAux) {
    4141:         DMPlexVecGetClosure(dmAux, sectionAux, A, cell, NULL, &x);
    4142:         for (i = 0; i < totDimAux; ++i) a[c*totDimAux+i] = x[i];
    4143:         DMPlexVecRestoreClosure(dmAux, sectionAux, A, cell, NULL, &x);
    4144:       }
    4145:     }
    4146:     for (fieldI = 0; fieldI < Nf; ++fieldI) {
    4147:       PetscFE fe;
    4148:       PetscDSGetDiscretization(prob, fieldI, (PetscObject *) &fe);
    4149:       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
    4150:         key.field = fieldI*Nf + fieldJ;
    4151:         if (hasJac)  {PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN,     key, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMat);}
    4152:         if (hasPrec) {PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_PRE, key, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMatP);}
    4153:         if (hasDyn)  {PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMatD);}
    4154:       }
    4155:       /* For finite volume, add the identity */
    4156:       if (!isFE[fieldI]) {
    4157:         PetscFV  fv;
    4158:         PetscInt eOffset = 0, Nc, fc, foff;

    4160:         PetscDSGetFieldOffset(prob, fieldI, &foff);
    4161:         PetscDSGetDiscretization(prob, fieldI, (PetscObject *) &fv);
    4162:         PetscFVGetNumComponents(fv, &Nc);
    4163:         for (c = 0; c < chunkSize; ++c, eOffset += totDim*totDim) {
    4164:           for (fc = 0; fc < Nc; ++fc) {
    4165:             const PetscInt i = foff + fc;
    4166:             if (hasJac)  {elemMat [eOffset+i*totDim+i] = 1.0;}
    4167:             if (hasPrec) {elemMatP[eOffset+i*totDim+i] = 1.0;}
    4168:           }
    4169:         }
    4170:       }
    4171:     }
    4172:     /*   Add contribution from X_t */
    4173:     if (hasDyn) {for (c = 0; c < chunkSize*totDim*totDim; ++c) elemMat[c] += X_tShift*elemMatD[c];}
    4174:     /* Insert values into matrix */
    4175:     for (c = 0; c < Ncell; ++c) {
    4176:       const PetscInt cell = cells ? cells[c+offCell] : c+offCell;
    4177:       if (mesh->printFEM > 1) {
    4178:         if (hasJac)  {DMPrintCellMatrix(cell, name,  totDim, totDim, &elemMat[(c-cStart)*totDim*totDim]);}
    4179:         if (hasPrec) {DMPrintCellMatrix(cell, nameP, totDim, totDim, &elemMatP[(c-cStart)*totDim*totDim]);}
    4180:       }
    4181:       if (assembleJac) {DMPlexMatSetClosure(dm, section, globalSection, Jac, cell, &elemMat[(c-cStart)*totDim*totDim], ADD_VALUES);}
    4182:       DMPlexMatSetClosure(dm, section, globalSection, JP, cell, &elemMat[(c-cStart)*totDim*totDim], ADD_VALUES);
    4183:     }
    4184:   }
    4185:   /* Cleanup */
    4186:   DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM);
    4187:   PetscQuadratureDestroy(&qGeom);
    4188:   if (hasFV) {MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_FALSE);}
    4189:   DMRestoreWorkArray(dm, Nf, MPIU_BOOL, &isFE);
    4190:   DMRestoreWorkArray(dm, ((1 + (X_t?1:0) + (dmAux?1:0))*totDim + ((hasJac?1:0) + (hasPrec?1:0) + (hasDyn?1:0))*totDim*totDim)*chunkSize, MPIU_SCALAR, &work);
    4191:   /* Compute boundary integrals */
    4192:   /* DMPlexComputeBdJacobian_Internal(dm, X, X_t, t, X_tShift, Jac, JacP, ctx); */
    4193:   /* Assemble matrix */
    4194:   if (assembleJac) {MatAssemblyBegin(Jac, MAT_FINAL_ASSEMBLY);MatAssemblyEnd(Jac, MAT_FINAL_ASSEMBLY);}
    4195:   MatAssemblyBegin(JacP, MAT_FINAL_ASSEMBLY);MatAssemblyEnd(JacP, MAT_FINAL_ASSEMBLY);
    4196:   PetscLogEventEnd(DMPLEX_JacobianFEM,dm,0,0,0);
    4197:   return(0);
    4198: }

    4200: /******** FEM Assembly Function ********/

    4202: static PetscErrorCode DMConvertPlex_Internal(DM dm, DM *plex, PetscBool copy)
    4203: {
    4204:   PetscBool      isPlex;

    4208:   PetscObjectTypeCompare((PetscObject) dm, DMPLEX, &isPlex);
    4209:   if (isPlex) {
    4210:     *plex = dm;
    4211:     PetscObjectReference((PetscObject) dm);
    4212:   } else {
    4213:     PetscObjectQuery((PetscObject) dm, "dm_plex", (PetscObject *) plex);
    4214:     if (!*plex) {
    4215:       DMConvert(dm,DMPLEX,plex);
    4216:       PetscObjectCompose((PetscObject) dm, "dm_plex", (PetscObject) *plex);
    4217:       if (copy) {
    4218:         DMCopyAuxiliaryVec(dm, *plex);
    4219:       }
    4220:     } else {
    4221:       PetscObjectReference((PetscObject) *plex);
    4222:     }
    4223:   }
    4224:   return(0);
    4225: }

    4227: /*@
    4228:   DMPlexGetGeometryFVM - Return precomputed geometric data

    4230:   Collective on DM

    4232:   Input Parameter:
    4233: . dm - The DM

    4235:   Output Parameters:
    4236: + facegeom - The values precomputed from face geometry
    4237: . cellgeom - The values precomputed from cell geometry
    4238: - minRadius - The minimum radius over the mesh of an inscribed sphere in a cell

    4240:   Level: developer

    4242: .seealso: DMTSSetRHSFunctionLocal()
    4243: @*/
    4244: PetscErrorCode DMPlexGetGeometryFVM(DM dm, Vec *facegeom, Vec *cellgeom, PetscReal *minRadius)
    4245: {
    4246:   DM             plex;

    4251:   DMConvertPlex_Internal(dm,&plex,PETSC_TRUE);
    4252:   DMPlexGetDataFVM(plex, NULL, cellgeom, facegeom, NULL);
    4253:   if (minRadius) {DMPlexGetMinRadius(plex, minRadius);}
    4254:   DMDestroy(&plex);
    4255:   return(0);
    4256: }

    4258: /*@
    4259:   DMPlexGetGradientDM - Return gradient data layout

    4261:   Collective on DM

    4263:   Input Parameters:
    4264: + dm - The DM
    4265: - fv - The PetscFV

    4267:   Output Parameter:
    4268: . dmGrad - The layout for gradient values

    4270:   Level: developer

    4272: .seealso: DMPlexGetGeometryFVM()
    4273: @*/
    4274: PetscErrorCode DMPlexGetGradientDM(DM dm, PetscFV fv, DM *dmGrad)
    4275: {
    4276:   DM             plex;
    4277:   PetscBool      computeGradients;

    4284:   PetscFVGetComputeGradients(fv, &computeGradients);
    4285:   if (!computeGradients) {*dmGrad = NULL; return(0);}
    4286:   DMConvertPlex_Internal(dm,&plex,PETSC_TRUE);
    4287:   DMPlexGetDataFVM(plex, fv, NULL, NULL, dmGrad);
    4288:   DMDestroy(&plex);
    4289:   return(0);
    4290: }

    4292: static PetscErrorCode DMPlexComputeBdResidual_Single_Internal(DM dm, PetscReal t, PetscWeakForm wf, PetscFormKey key, Vec locX, Vec locX_t, Vec locF, DMField coordField, IS facetIS)
    4293: {
    4294:   DM_Plex         *mesh = (DM_Plex *) dm->data;
    4295:   DM               plex = NULL, plexA = NULL;
    4296:   DMEnclosureType  encAux;
    4297:   PetscDS          prob, probAux = NULL;
    4298:   PetscSection     section, sectionAux = NULL;
    4299:   Vec              locA = NULL;
    4300:   PetscScalar     *u = NULL, *u_t = NULL, *a = NULL, *elemVec = NULL;
    4301:   PetscInt         totDim, totDimAux = 0;
    4302:   PetscErrorCode   ierr;

    4305:   DMConvert(dm, DMPLEX, &plex);
    4306:   DMGetLocalSection(dm, &section);
    4307:   DMGetDS(dm, &prob);
    4308:   PetscDSGetTotalDimension(prob, &totDim);
    4309:   DMGetAuxiliaryVec(dm, key.label, key.value, &locA);
    4310:   if (locA) {
    4311:     DM dmAux;

    4313:     VecGetDM(locA, &dmAux);
    4314:     DMGetEnclosureRelation(dmAux, dm, &encAux);
    4315:     DMConvert(dmAux, DMPLEX, &plexA);
    4316:     DMGetDS(plexA, &probAux);
    4317:     PetscDSGetTotalDimension(probAux, &totDimAux);
    4318:     DMGetLocalSection(plexA, &sectionAux);
    4319:   }
    4320:   {
    4321:     PetscFEGeom     *fgeom;
    4322:     PetscInt         maxDegree;
    4323:     PetscQuadrature  qGeom = NULL;
    4324:     IS               pointIS;
    4325:     const PetscInt  *points;
    4326:     PetscInt         numFaces, face, Nq;

    4328:     DMLabelGetStratumIS(key.label, key.value, &pointIS);
    4329:     if (!pointIS) goto end; /* No points with that id on this process */
    4330:     {
    4331:       IS isectIS;

    4333:       /* TODO: Special cases of ISIntersect where it is quick to check a priori if one is a superset of the other */
    4334:       ISIntersect_Caching_Internal(facetIS,pointIS,&isectIS);
    4335:       ISDestroy(&pointIS);
    4336:       pointIS = isectIS;
    4337:     }
    4338:     ISGetLocalSize(pointIS,&numFaces);
    4339:     ISGetIndices(pointIS,&points);
    4340:     PetscMalloc4(numFaces*totDim, &u, locX_t ? numFaces*totDim : 0, &u_t, numFaces*totDim, &elemVec, locA ? numFaces*totDimAux : 0, &a);
    4341:     DMFieldGetDegree(coordField,pointIS,NULL,&maxDegree);
    4342:     if (maxDegree <= 1) {
    4343:       DMFieldCreateDefaultQuadrature(coordField,pointIS,&qGeom);
    4344:     }
    4345:     if (!qGeom) {
    4346:       PetscFE fe;

    4348:       PetscDSGetDiscretization(prob, key.field, (PetscObject *) &fe);
    4349:       PetscFEGetFaceQuadrature(fe, &qGeom);
    4350:       PetscObjectReference((PetscObject)qGeom);
    4351:     }
    4352:     PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL);
    4353:     DMSNESGetFEGeom(coordField,pointIS,qGeom,PETSC_TRUE,&fgeom);
    4354:     for (face = 0; face < numFaces; ++face) {
    4355:       const PetscInt point = points[face], *support;
    4356:       PetscScalar   *x     = NULL;
    4357:       PetscInt       i;

    4359:       DMPlexGetSupport(dm, point, &support);
    4360:       DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x);
    4361:       for (i = 0; i < totDim; ++i) u[face*totDim+i] = x[i];
    4362:       DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x);
    4363:       if (locX_t) {
    4364:         DMPlexVecGetClosure(plex, section, locX_t, support[0], NULL, &x);
    4365:         for (i = 0; i < totDim; ++i) u_t[face*totDim+i] = x[i];
    4366:         DMPlexVecRestoreClosure(plex, section, locX_t, support[0], NULL, &x);
    4367:       }
    4368:       if (locA) {
    4369:         PetscInt subp;

    4371:         DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp);
    4372:         DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x);
    4373:         for (i = 0; i < totDimAux; ++i) a[face*totDimAux+i] = x[i];
    4374:         DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x);
    4375:       }
    4376:     }
    4377:     PetscArrayzero(elemVec, numFaces*totDim);
    4378:     {
    4379:       PetscFE         fe;
    4380:       PetscInt        Nb;
    4381:       PetscFEGeom     *chunkGeom = NULL;
    4382:       /* Conforming batches */
    4383:       PetscInt        numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
    4384:       /* Remainder */
    4385:       PetscInt        Nr, offset;

    4387:       PetscDSGetDiscretization(prob, key.field, (PetscObject *) &fe);
    4388:       PetscFEGetDimension(fe, &Nb);
    4389:       PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches);
    4390:       /* TODO: documentation is unclear about what is going on with these numbers: how should Nb / Nq factor in ? */
    4391:       blockSize = Nb;
    4392:       batchSize = numBlocks * blockSize;
    4393:        PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches);
    4394:       numChunks = numFaces / (numBatches*batchSize);
    4395:       Ne        = numChunks*numBatches*batchSize;
    4396:       Nr        = numFaces % (numBatches*batchSize);
    4397:       offset    = numFaces - Nr;
    4398:       PetscFEGeomGetChunk(fgeom,0,offset,&chunkGeom);
    4399:       PetscFEIntegrateBdResidual(prob, wf, key, Ne, chunkGeom, u, u_t, probAux, a, t, elemVec);
    4400:       PetscFEGeomRestoreChunk(fgeom, 0, offset, &chunkGeom);
    4401:       PetscFEGeomGetChunk(fgeom,offset,numFaces,&chunkGeom);
    4402:       PetscFEIntegrateBdResidual(prob, wf, key, Nr, chunkGeom, &u[offset*totDim], u_t ? &u_t[offset*totDim] : NULL, probAux, a ? &a[offset*totDimAux] : NULL, t, &elemVec[offset*totDim]);
    4403:       PetscFEGeomRestoreChunk(fgeom,offset,numFaces,&chunkGeom);
    4404:     }
    4405:     for (face = 0; face < numFaces; ++face) {
    4406:       const PetscInt point = points[face], *support;

    4408:       if (mesh->printFEM > 1) {DMPrintCellVector(point, "BdResidual", totDim, &elemVec[face*totDim]);}
    4409:       DMPlexGetSupport(plex, point, &support);
    4410:       DMPlexVecSetClosure(plex, NULL, locF, support[0], &elemVec[face*totDim], ADD_ALL_VALUES);
    4411:     }
    4412:     DMSNESRestoreFEGeom(coordField,pointIS,qGeom,PETSC_TRUE,&fgeom);
    4413:     PetscQuadratureDestroy(&qGeom);
    4414:     ISRestoreIndices(pointIS, &points);
    4415:     ISDestroy(&pointIS);
    4416:     PetscFree4(u, u_t, elemVec, a);
    4417:   }
    4418:   end:
    4419:   DMDestroy(&plex);
    4420:   DMDestroy(&plexA);
    4421:   return(0);
    4422: }

    4424: PetscErrorCode DMPlexComputeBdResidualSingle(DM dm, PetscReal t, PetscWeakForm wf, PetscFormKey key, Vec locX, Vec locX_t, Vec locF)
    4425: {
    4426:   DMField        coordField;
    4427:   DMLabel        depthLabel;
    4428:   IS             facetIS;
    4429:   PetscInt       dim;

    4433:   DMGetDimension(dm, &dim);
    4434:   DMPlexGetDepthLabel(dm, &depthLabel);
    4435:   DMLabelGetStratumIS(depthLabel, dim-1, &facetIS);
    4436:   DMGetCoordinateField(dm, &coordField);
    4437:   DMPlexComputeBdResidual_Single_Internal(dm, t, wf, key, locX, locX_t, locF, coordField, facetIS);
    4438:   ISDestroy(&facetIS);
    4439:   return(0);
    4440: }

    4442: PetscErrorCode DMPlexComputeBdResidual_Internal(DM dm, Vec locX, Vec locX_t, PetscReal t, Vec locF, void *user)
    4443: {
    4444:   PetscDS        prob;
    4445:   PetscInt       numBd, bd;
    4446:   DMField        coordField = NULL;
    4447:   IS             facetIS    = NULL;
    4448:   DMLabel        depthLabel;
    4449:   PetscInt       dim;

    4453:   DMGetDS(dm, &prob);
    4454:   DMPlexGetDepthLabel(dm, &depthLabel);
    4455:   DMGetDimension(dm, &dim);
    4456:   DMLabelGetStratumIS(depthLabel,dim - 1,&facetIS);
    4457:   PetscDSGetNumBoundary(prob, &numBd);
    4458:   for (bd = 0; bd < numBd; ++bd) {
    4459:     PetscWeakForm           wf;
    4460:     DMBoundaryConditionType type;
    4461:     DMLabel                 label;
    4462:     const PetscInt         *values;
    4463:     PetscInt                field, numValues, v;
    4464:     PetscObject             obj;
    4465:     PetscClassId            id;
    4466:     PetscFormKey            key;

    4468:     PetscDSGetBoundary(prob, bd, &wf, &type, NULL, &label, &numValues, &values, &field, NULL, NULL, NULL, NULL, NULL);
    4469:     PetscDSGetDiscretization(prob, field, &obj);
    4470:     PetscObjectGetClassId(obj, &id);
    4471:     if ((id != PETSCFE_CLASSID) || (type & DM_BC_ESSENTIAL)) continue;
    4472:     if (!facetIS) {
    4473:       DMLabel  depthLabel;
    4474:       PetscInt dim;

    4476:       DMPlexGetDepthLabel(dm, &depthLabel);
    4477:       DMGetDimension(dm, &dim);
    4478:       DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS);
    4479:     }
    4480:     DMGetCoordinateField(dm, &coordField);
    4481:     for (v = 0; v < numValues; ++v) {
    4482:       key.label = label;
    4483:       key.value = values[v];
    4484:       key.field = field;
    4485:       key.part  = 0;
    4486:       DMPlexComputeBdResidual_Single_Internal(dm, t, wf, key, locX, locX_t, locF, coordField, facetIS);
    4487:     }
    4488:   }
    4489:   ISDestroy(&facetIS);
    4490:   return(0);
    4491: }

    4493: PetscErrorCode DMPlexComputeResidual_Internal(DM dm, PetscFormKey key, IS cellIS, PetscReal time, Vec locX, Vec locX_t, PetscReal t, Vec locF, void *user)
    4494: {
    4495:   DM_Plex         *mesh       = (DM_Plex *) dm->data;
    4496:   const char      *name       = "Residual";
    4497:   DM               dmAux      = NULL;
    4498:   DM               dmGrad     = NULL;
    4499:   DMLabel          ghostLabel = NULL;
    4500:   PetscDS          ds         = NULL;
    4501:   PetscDS          dsAux      = NULL;
    4502:   PetscSection     section    = NULL;
    4503:   PetscBool        useFEM     = PETSC_FALSE;
    4504:   PetscBool        useFVM     = PETSC_FALSE;
    4505:   PetscBool        isImplicit = (locX_t || time == PETSC_MIN_REAL) ? PETSC_TRUE : PETSC_FALSE;
    4506:   PetscFV          fvm        = NULL;
    4507:   PetscFVCellGeom *cgeomFVM   = NULL;
    4508:   PetscFVFaceGeom *fgeomFVM   = NULL;
    4509:   DMField          coordField = NULL;
    4510:   Vec              locA, cellGeometryFVM = NULL, faceGeometryFVM = NULL, grad, locGrad = NULL;
    4511:   PetscScalar     *u = NULL, *u_t, *a, *uL, *uR;
    4512:   IS               chunkIS;
    4513:   const PetscInt  *cells;
    4514:   PetscInt         cStart, cEnd, numCells;
    4515:   PetscInt         Nf, f, totDim, totDimAux, numChunks, cellChunkSize, faceChunkSize, chunk, fStart, fEnd;
    4516:   PetscInt         maxDegree = PETSC_MAX_INT;
    4517:   PetscQuadrature  affineQuad = NULL, *quads = NULL;
    4518:   PetscFEGeom     *affineGeom = NULL, **geoms = NULL;
    4519:   PetscErrorCode   ierr;

    4522:   PetscLogEventBegin(DMPLEX_ResidualFEM,dm,0,0,0);
    4523:   /* TODO The places where we have to use isFE are probably the member functions for the PetscDisc class */
    4524:   /* TODO The FVM geometry is over-manipulated. Make the precalc functions return exactly what we need */
    4525:   /* FEM+FVM */
    4526:   ISGetPointRange(cellIS, &cStart, &cEnd, &cells);
    4527:   DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd);
    4528:   /* 1: Get sizes from dm and dmAux */
    4529:   DMGetLocalSection(dm, &section);
    4530:   DMGetLabel(dm, "ghost", &ghostLabel);
    4531:   DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds);
    4532:   PetscDSGetNumFields(ds, &Nf);
    4533:   PetscDSGetTotalDimension(ds, &totDim);
    4534:   DMGetAuxiliaryVec(dm, key.label, key.value, &locA);
    4535:   if (locA) {
    4536:     PetscInt subcell;
    4537:     VecGetDM(locA, &dmAux);
    4538:     DMGetEnclosurePoint(dmAux, dm, DM_ENC_UNKNOWN, cStart, &subcell);
    4539:     DMGetCellDS(dmAux, subcell, &dsAux);
    4540:     PetscDSGetTotalDimension(dsAux, &totDimAux);
    4541:   }
    4542:   /* 2: Get geometric data */
    4543:   for (f = 0; f < Nf; ++f) {
    4544:     PetscObject  obj;
    4545:     PetscClassId id;
    4546:     PetscBool    fimp;

    4548:     PetscDSGetImplicit(ds, f, &fimp);
    4549:     if (isImplicit != fimp) continue;
    4550:     PetscDSGetDiscretization(ds, f, &obj);
    4551:     PetscObjectGetClassId(obj, &id);
    4552:     if (id == PETSCFE_CLASSID) {useFEM = PETSC_TRUE;}
    4553:     if (id == PETSCFV_CLASSID) {useFVM = PETSC_TRUE; fvm = (PetscFV) obj;}
    4554:   }
    4555:   if (useFEM) {
    4556:     DMGetCoordinateField(dm, &coordField);
    4557:     DMFieldGetDegree(coordField,cellIS,NULL,&maxDegree);
    4558:     if (maxDegree <= 1) {
    4559:       DMFieldCreateDefaultQuadrature(coordField,cellIS,&affineQuad);
    4560:       if (affineQuad) {
    4561:         DMSNESGetFEGeom(coordField,cellIS,affineQuad,PETSC_FALSE,&affineGeom);
    4562:       }
    4563:     } else {
    4564:       PetscCalloc2(Nf,&quads,Nf,&geoms);
    4565:       for (f = 0; f < Nf; ++f) {
    4566:         PetscObject  obj;
    4567:         PetscClassId id;
    4568:         PetscBool    fimp;

    4570:         PetscDSGetImplicit(ds, f, &fimp);
    4571:         if (isImplicit != fimp) continue;
    4572:         PetscDSGetDiscretization(ds, f, &obj);
    4573:         PetscObjectGetClassId(obj, &id);
    4574:         if (id == PETSCFE_CLASSID) {
    4575:           PetscFE fe = (PetscFE) obj;

    4577:           PetscFEGetQuadrature(fe, &quads[f]);
    4578:           PetscObjectReference((PetscObject)quads[f]);
    4579:           DMSNESGetFEGeom(coordField,cellIS,quads[f],PETSC_FALSE,&geoms[f]);
    4580:         }
    4581:       }
    4582:     }
    4583:   }
    4584:   if (useFVM) {
    4585:     DMPlexGetGeometryFVM(dm, &faceGeometryFVM, &cellGeometryFVM, NULL);
    4586:     VecGetArrayRead(faceGeometryFVM, (const PetscScalar **) &fgeomFVM);
    4587:     VecGetArrayRead(cellGeometryFVM, (const PetscScalar **) &cgeomFVM);
    4588:     /* Reconstruct and limit cell gradients */
    4589:     DMPlexGetGradientDM(dm, fvm, &dmGrad);
    4590:     if (dmGrad) {
    4591:       DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd);
    4592:       DMGetGlobalVector(dmGrad, &grad);
    4593:       DMPlexReconstructGradients_Internal(dm, fvm, fStart, fEnd, faceGeometryFVM, cellGeometryFVM, locX, grad);
    4594:       /* Communicate gradient values */
    4595:       DMGetLocalVector(dmGrad, &locGrad);
    4596:       DMGlobalToLocalBegin(dmGrad, grad, INSERT_VALUES, locGrad);
    4597:       DMGlobalToLocalEnd(dmGrad, grad, INSERT_VALUES, locGrad);
    4598:       DMRestoreGlobalVector(dmGrad, &grad);
    4599:     }
    4600:     /* Handle non-essential (e.g. outflow) boundary values */
    4601:     DMPlexInsertBoundaryValues(dm, PETSC_FALSE, locX, time, faceGeometryFVM, cellGeometryFVM, locGrad);
    4602:   }
    4603:   /* Loop over chunks */
    4604:   if (useFEM) {ISCreate(PETSC_COMM_SELF, &chunkIS);}
    4605:   numCells      = cEnd - cStart;
    4606:   numChunks     = 1;
    4607:   cellChunkSize = numCells/numChunks;
    4608:   faceChunkSize = (fEnd - fStart)/numChunks;
    4609:   numChunks     = PetscMin(1,numCells);
    4610:   for (chunk = 0; chunk < numChunks; ++chunk) {
    4611:     PetscScalar     *elemVec, *fluxL, *fluxR;
    4612:     PetscReal       *vol;
    4613:     PetscFVFaceGeom *fgeom;
    4614:     PetscInt         cS = cStart+chunk*cellChunkSize, cE = PetscMin(cS+cellChunkSize, cEnd), numCells = cE - cS, c;
    4615:     PetscInt         fS = fStart+chunk*faceChunkSize, fE = PetscMin(fS+faceChunkSize, fEnd), numFaces = 0, face;

    4617:     /* Extract field coefficients */
    4618:     if (useFEM) {
    4619:       ISGetPointSubrange(chunkIS, cS, cE, cells);
    4620:       DMPlexGetCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a);
    4621:       DMGetWorkArray(dm, numCells*totDim, MPIU_SCALAR, &elemVec);
    4622:       PetscArrayzero(elemVec, numCells*totDim);
    4623:     }
    4624:     if (useFVM) {
    4625:       DMPlexGetFaceFields(dm, fS, fE, locX, locX_t, faceGeometryFVM, cellGeometryFVM, locGrad, &numFaces, &uL, &uR);
    4626:       DMPlexGetFaceGeometry(dm, fS, fE, faceGeometryFVM, cellGeometryFVM, &numFaces, &fgeom, &vol);
    4627:       DMGetWorkArray(dm, numFaces*totDim, MPIU_SCALAR, &fluxL);
    4628:       DMGetWorkArray(dm, numFaces*totDim, MPIU_SCALAR, &fluxR);
    4629:       PetscArrayzero(fluxL, numFaces*totDim);
    4630:       PetscArrayzero(fluxR, numFaces*totDim);
    4631:     }
    4632:     /* TODO We will interlace both our field coefficients (u, u_t, uL, uR, etc.) and our output (elemVec, fL, fR). I think this works */
    4633:     /* Loop over fields */
    4634:     for (f = 0; f < Nf; ++f) {
    4635:       PetscObject  obj;
    4636:       PetscClassId id;
    4637:       PetscBool    fimp;
    4638:       PetscInt     numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset;

    4640:       key.field = f;
    4641:       PetscDSGetImplicit(ds, f, &fimp);
    4642:       if (isImplicit != fimp) continue;
    4643:       PetscDSGetDiscretization(ds, f, &obj);
    4644:       PetscObjectGetClassId(obj, &id);
    4645:       if (id == PETSCFE_CLASSID) {
    4646:         PetscFE         fe = (PetscFE) obj;
    4647:         PetscFEGeom    *geom = affineGeom ? affineGeom : geoms[f];
    4648:         PetscFEGeom    *chunkGeom = NULL;
    4649:         PetscQuadrature quad = affineQuad ? affineQuad : quads[f];
    4650:         PetscInt        Nq, Nb;

    4652:         PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches);
    4653:         PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL);
    4654:         PetscFEGetDimension(fe, &Nb);
    4655:         blockSize = Nb;
    4656:         batchSize = numBlocks * blockSize;
    4657:         PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches);
    4658:         numChunks = numCells / (numBatches*batchSize);
    4659:         Ne        = numChunks*numBatches*batchSize;
    4660:         Nr        = numCells % (numBatches*batchSize);
    4661:         offset    = numCells - Nr;
    4662:         /* Integrate FE residual to get elemVec (need fields at quadrature points) */
    4663:         /*   For FV, I think we use a P0 basis and the cell coefficients (for subdivided cells, we can tweak the basis tabulation to be the indicator function) */
    4664:         PetscFEGeomGetChunk(geom,0,offset,&chunkGeom);
    4665:         PetscFEIntegrateResidual(ds, key, Ne, chunkGeom, u, u_t, dsAux, a, t, elemVec);
    4666:         PetscFEGeomGetChunk(geom,offset,numCells,&chunkGeom);
    4667:         PetscFEIntegrateResidual(ds, key, Nr, chunkGeom, &u[offset*totDim], u_t ? &u_t[offset*totDim] : NULL, dsAux, &a[offset*totDimAux], t, &elemVec[offset*totDim]);
    4668:         PetscFEGeomRestoreChunk(geom,offset,numCells,&chunkGeom);
    4669:       } else if (id == PETSCFV_CLASSID) {
    4670:         PetscFV fv = (PetscFV) obj;

    4672:         Ne = numFaces;
    4673:         /* Riemann solve over faces (need fields at face centroids) */
    4674:         /*   We need to evaluate FE fields at those coordinates */
    4675:         PetscFVIntegrateRHSFunction(fv, ds, f, Ne, fgeom, vol, uL, uR, fluxL, fluxR);
    4676:       } else SETERRQ1(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %D", f);
    4677:     }
    4678:     /* Loop over domain */
    4679:     if (useFEM) {
    4680:       /* Add elemVec to locX */
    4681:       for (c = cS; c < cE; ++c) {
    4682:         const PetscInt cell = cells ? cells[c] : c;
    4683:         const PetscInt cind = c - cStart;

    4685:         if (mesh->printFEM > 1) {DMPrintCellVector(cell, name, totDim, &elemVec[cind*totDim]);}
    4686:         if (ghostLabel) {
    4687:           PetscInt ghostVal;

    4689:           DMLabelGetValue(ghostLabel,cell,&ghostVal);
    4690:           if (ghostVal > 0) continue;
    4691:         }
    4692:         DMPlexVecSetClosure(dm, section, locF, cell, &elemVec[cind*totDim], ADD_ALL_VALUES);
    4693:       }
    4694:     }
    4695:     if (useFVM) {
    4696:       PetscScalar *fa;
    4697:       PetscInt     iface;

    4699:       VecGetArray(locF, &fa);
    4700:       for (f = 0; f < Nf; ++f) {
    4701:         PetscFV      fv;
    4702:         PetscObject  obj;
    4703:         PetscClassId id;
    4704:         PetscInt     foff, pdim;

    4706:         PetscDSGetDiscretization(ds, f, &obj);
    4707:         PetscDSGetFieldOffset(ds, f, &foff);
    4708:         PetscObjectGetClassId(obj, &id);
    4709:         if (id != PETSCFV_CLASSID) continue;
    4710:         fv   = (PetscFV) obj;
    4711:         PetscFVGetNumComponents(fv, &pdim);
    4712:         /* Accumulate fluxes to cells */
    4713:         for (face = fS, iface = 0; face < fE; ++face) {
    4714:           const PetscInt *scells;
    4715:           PetscScalar    *fL = NULL, *fR = NULL;
    4716:           PetscInt        ghost, d, nsupp, nchild;

    4718:           DMLabelGetValue(ghostLabel, face, &ghost);
    4719:           DMPlexGetSupportSize(dm, face, &nsupp);
    4720:           DMPlexGetTreeChildren(dm, face, &nchild, NULL);
    4721:           if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
    4722:           DMPlexGetSupport(dm, face, &scells);
    4723:           DMLabelGetValue(ghostLabel,scells[0],&ghost);
    4724:           if (ghost <= 0) {DMPlexPointLocalFieldRef(dm, scells[0], f, fa, &fL);}
    4725:           DMLabelGetValue(ghostLabel,scells[1],&ghost);
    4726:           if (ghost <= 0) {DMPlexPointLocalFieldRef(dm, scells[1], f, fa, &fR);}
    4727:           for (d = 0; d < pdim; ++d) {
    4728:             if (fL) fL[d] -= fluxL[iface*totDim+foff+d];
    4729:             if (fR) fR[d] += fluxR[iface*totDim+foff+d];
    4730:           }
    4731:           ++iface;
    4732:         }
    4733:       }
    4734:       VecRestoreArray(locF, &fa);
    4735:     }
    4736:     /* Handle time derivative */
    4737:     if (locX_t) {
    4738:       PetscScalar *x_t, *fa;

    4740:       VecGetArray(locF, &fa);
    4741:       VecGetArray(locX_t, &x_t);
    4742:       for (f = 0; f < Nf; ++f) {
    4743:         PetscFV      fv;
    4744:         PetscObject  obj;
    4745:         PetscClassId id;
    4746:         PetscInt     pdim, d;

    4748:         PetscDSGetDiscretization(ds, f, &obj);
    4749:         PetscObjectGetClassId(obj, &id);
    4750:         if (id != PETSCFV_CLASSID) continue;
    4751:         fv   = (PetscFV) obj;
    4752:         PetscFVGetNumComponents(fv, &pdim);
    4753:         for (c = cS; c < cE; ++c) {
    4754:           const PetscInt cell = cells ? cells[c] : c;
    4755:           PetscScalar   *u_t, *r;

    4757:           if (ghostLabel) {
    4758:             PetscInt ghostVal;

    4760:             DMLabelGetValue(ghostLabel, cell, &ghostVal);
    4761:             if (ghostVal > 0) continue;
    4762:           }
    4763:           DMPlexPointLocalFieldRead(dm, cell, f, x_t, &u_t);
    4764:           DMPlexPointLocalFieldRef(dm, cell, f, fa, &r);
    4765:           for (d = 0; d < pdim; ++d) r[d] += u_t[d];
    4766:         }
    4767:       }
    4768:       VecRestoreArray(locX_t, &x_t);
    4769:       VecRestoreArray(locF, &fa);
    4770:     }
    4771:     if (useFEM) {
    4772:       DMPlexRestoreCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a);
    4773:       DMRestoreWorkArray(dm, numCells*totDim, MPIU_SCALAR, &elemVec);
    4774:     }
    4775:     if (useFVM) {
    4776:       DMPlexRestoreFaceFields(dm, fS, fE, locX, locX_t, faceGeometryFVM, cellGeometryFVM, locGrad, &numFaces, &uL, &uR);
    4777:       DMPlexRestoreFaceGeometry(dm, fS, fE, faceGeometryFVM, cellGeometryFVM, &numFaces, &fgeom, &vol);
    4778:       DMRestoreWorkArray(dm, numFaces*totDim, MPIU_SCALAR, &fluxL);
    4779:       DMRestoreWorkArray(dm, numFaces*totDim, MPIU_SCALAR, &fluxR);
    4780:       if (dmGrad) {DMRestoreLocalVector(dmGrad, &locGrad);}
    4781:     }
    4782:   }
    4783:   if (useFEM) {ISDestroy(&chunkIS);}
    4784:   ISRestorePointRange(cellIS, &cStart, &cEnd, &cells);

    4786:   if (useFEM) {
    4787:     DMPlexComputeBdResidual_Internal(dm, locX, locX_t, t, locF, user);

    4789:     if (maxDegree <= 1) {
    4790:       DMSNESRestoreFEGeom(coordField,cellIS,affineQuad,PETSC_FALSE,&affineGeom);
    4791:       PetscQuadratureDestroy(&affineQuad);
    4792:     } else {
    4793:       for (f = 0; f < Nf; ++f) {
    4794:         DMSNESRestoreFEGeom(coordField,cellIS,quads[f],PETSC_FALSE,&geoms[f]);
    4795:         PetscQuadratureDestroy(&quads[f]);
    4796:       }
    4797:       PetscFree2(quads,geoms);
    4798:     }
    4799:   }

    4801:   /* FEM */
    4802:   /* 1: Get sizes from dm and dmAux */
    4803:   /* 2: Get geometric data */
    4804:   /* 3: Handle boundary values */
    4805:   /* 4: Loop over domain */
    4806:   /*   Extract coefficients */
    4807:   /* Loop over fields */
    4808:   /*   Set tiling for FE*/
    4809:   /*   Integrate FE residual to get elemVec */
    4810:   /*     Loop over subdomain */
    4811:   /*       Loop over quad points */
    4812:   /*         Transform coords to real space */
    4813:   /*         Evaluate field and aux fields at point */
    4814:   /*         Evaluate residual at point */
    4815:   /*         Transform residual to real space */
    4816:   /*       Add residual to elemVec */
    4817:   /* Loop over domain */
    4818:   /*   Add elemVec to locX */

    4820:   /* FVM */
    4821:   /* Get geometric data */
    4822:   /* If using gradients */
    4823:   /*   Compute gradient data */
    4824:   /*   Loop over domain faces */
    4825:   /*     Count computational faces */
    4826:   /*     Reconstruct cell gradient */
    4827:   /*   Loop over domain cells */
    4828:   /*     Limit cell gradients */
    4829:   /* Handle boundary values */
    4830:   /* Loop over domain faces */
    4831:   /*   Read out field, centroid, normal, volume for each side of face */
    4832:   /* Riemann solve over faces */
    4833:   /* Loop over domain faces */
    4834:   /*   Accumulate fluxes to cells */
    4835:   /* TODO Change printFEM to printDisc here */
    4836:   if (mesh->printFEM) {
    4837:     Vec         locFbc;
    4838:     PetscInt    pStart, pEnd, p, maxDof;
    4839:     PetscScalar *zeroes;

    4841:     VecDuplicate(locF,&locFbc);
    4842:     VecCopy(locF,locFbc);
    4843:     PetscSectionGetChart(section,&pStart,&pEnd);
    4844:     PetscSectionGetMaxDof(section,&maxDof);
    4845:     PetscCalloc1(maxDof,&zeroes);
    4846:     for (p = pStart; p < pEnd; p++) {
    4847:       VecSetValuesSection(locFbc,section,p,zeroes,INSERT_BC_VALUES);
    4848:     }
    4849:     PetscFree(zeroes);
    4850:     DMPrintLocalVec(dm, name, mesh->printTol, locFbc);
    4851:     VecDestroy(&locFbc);
    4852:   }
    4853:   PetscLogEventEnd(DMPLEX_ResidualFEM,dm,0,0,0);
    4854:   return(0);
    4855: }

    4857: /*
    4858:   1) Allow multiple kernels for BdResidual for hybrid DS

    4860:   DONE 2) Get out dsAux for either side at the same time as cohesive cell dsAux

    4862:   DONE 3) Change DMGetCellFields() to get different aux data a[] for each side
    4863:      - I think I just need to replace a[] with the closure from each face

    4865:   4) Run both kernels for each non-hybrid field with correct dsAux, and then hybrid field as before
    4866: */
    4867: PetscErrorCode DMPlexComputeResidual_Hybrid_Internal(DM dm, PetscFormKey key[], IS cellIS, PetscReal time, Vec locX, Vec locX_t, PetscReal t, Vec locF, void *user)
    4868: {
    4869:   DM_Plex         *mesh       = (DM_Plex *) dm->data;
    4870:   const char      *name       = "Hybrid Residual";
    4871:   DM               dmAux[3]   = {NULL, NULL, NULL};
    4872:   DMLabel          ghostLabel = NULL;
    4873:   PetscDS          ds         = NULL;
    4874:   PetscDS          dsAux[3]   = {NULL, NULL, NULL};
    4875:   Vec              locA[3]    = {NULL, NULL, NULL};
    4876:   PetscSection     section    = NULL;
    4877:   DMField          coordField = NULL;
    4878:   PetscScalar     *u = NULL, *u_t, *a[3];
    4879:   PetscScalar     *elemVec;
    4880:   IS               chunkIS;
    4881:   const PetscInt  *cells;
    4882:   PetscInt        *faces;
    4883:   PetscInt         cStart, cEnd, numCells;
    4884:   PetscInt         Nf, f, totDim, totDimAux[3], numChunks, cellChunkSize, chunk;
    4885:   PetscInt         maxDegree = PETSC_MAX_INT;
    4886:   PetscQuadrature  affineQuad = NULL, *quads = NULL;
    4887:   PetscFEGeom     *affineGeom = NULL, **geoms = NULL;
    4888:   PetscErrorCode   ierr;

    4891:   PetscLogEventBegin(DMPLEX_ResidualFEM,dm,0,0,0);
    4892:   /* TODO The places where we have to use isFE are probably the member functions for the PetscDisc class */
    4893:   /* FEM */
    4894:   ISGetLocalSize(cellIS, &numCells);
    4895:   ISGetPointRange(cellIS, &cStart, &cEnd, &cells);
    4896:   /* 1: Get sizes from dm and dmAux */
    4897:   DMGetSection(dm, &section);
    4898:   DMGetLabel(dm, "ghost", &ghostLabel);
    4899:   DMGetCellDS(dm, cStart, &ds);
    4900:   PetscDSGetNumFields(ds, &Nf);
    4901:   PetscDSGetTotalDimension(ds, &totDim);
    4902:   DMGetAuxiliaryVec(dm, key[2].label, key[2].value, &locA[2]);
    4903:   if (locA[2]) {
    4904:     VecGetDM(locA[2], &dmAux[2]);
    4905:     DMGetCellDS(dmAux[2], cStart, &dsAux[2]);
    4906:     PetscDSGetTotalDimension(dsAux[2], &totDimAux[2]);
    4907:     {
    4908:       const PetscInt *cone;
    4909:       PetscInt        c;

    4911:       DMPlexGetCone(dm, cStart, &cone);
    4912:       for (c = 0; c < 2; ++c) {
    4913:         const PetscInt *support;
    4914:         PetscInt ssize, s;

    4916:         DMPlexGetSupport(dm, cone[c], &support);
    4917:         DMPlexGetSupportSize(dm, cone[c], &ssize);
    4918:         if (ssize != 2) SETERRQ3(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %D from cell %D has support size %D != 2", cone[c], cStart, ssize);
    4919:         if      (support[0] == cStart) s = 1;
    4920:         else if (support[1] == cStart) s = 0;
    4921:         else SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %D does not have cell %D in its support", cone[c], cStart);
    4922:         DMGetAuxiliaryVec(dm, key[c].label, key[c].value, &locA[c]);
    4923:         if (locA[c]) {VecGetDM(locA[c], &dmAux[c]);}
    4924:         else         {dmAux[c] = dmAux[2];}
    4925:         DMGetCellDS(dmAux[c], support[s], &dsAux[c]);
    4926:         PetscDSGetTotalDimension(dsAux[c], &totDimAux[c]);
    4927:       }
    4928:     }
    4929:   }
    4930:   /* 2: Setup geometric data */
    4931:   DMGetCoordinateField(dm, &coordField);
    4932:   DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree);
    4933:   if (maxDegree > 1) {
    4934:     PetscCalloc2(Nf, &quads, Nf, &geoms);
    4935:     for (f = 0; f < Nf; ++f) {
    4936:       PetscFE fe;

    4938:       PetscDSGetDiscretization(ds, f, (PetscObject *) &fe);
    4939:       if (fe) {
    4940:         PetscFEGetQuadrature(fe, &quads[f]);
    4941:         PetscObjectReference((PetscObject) quads[f]);
    4942:       }
    4943:     }
    4944:   }
    4945:   /* Loop over chunks */
    4946:   cellChunkSize = numCells;
    4947:   numChunks     = !numCells ? 0 : PetscCeilReal(((PetscReal) numCells)/cellChunkSize);
    4948:   PetscCalloc1(2*cellChunkSize, &faces);
    4949:   ISCreateGeneral(PETSC_COMM_SELF, cellChunkSize, faces, PETSC_USE_POINTER, &chunkIS);
    4950:   /* Extract field coefficients */
    4951:   /* NOTE This needs the end cap faces to have identical orientations */
    4952:   DMPlexGetCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]);
    4953:   DMPlexGetHybridAuxFields(dm, dmAux, dsAux, cellIS, locA, a);
    4954:   DMGetWorkArray(dm, cellChunkSize*totDim, MPIU_SCALAR, &elemVec);
    4955:   for (chunk = 0; chunk < numChunks; ++chunk) {
    4956:     PetscInt cS = cStart+chunk*cellChunkSize, cE = PetscMin(cS+cellChunkSize, cEnd), numCells = cE - cS, c;

    4958:     PetscMemzero(elemVec, cellChunkSize*totDim * sizeof(PetscScalar));
    4959:     /* Get faces */
    4960:     for (c = cS; c < cE; ++c) {
    4961:       const PetscInt  cell = cells ? cells[c] : c;
    4962:       const PetscInt *cone;
    4963:       DMPlexGetCone(dm, cell, &cone);
    4964:       faces[(c-cS)*2+0] = cone[0];
    4965:       faces[(c-cS)*2+1] = cone[1];
    4966:     }
    4967:     ISGeneralSetIndices(chunkIS, cellChunkSize, faces, PETSC_USE_POINTER);
    4968:     /* Get geometric data */
    4969:     if (maxDegree <= 1) {
    4970:       if (!affineQuad) {DMFieldCreateDefaultQuadrature(coordField, chunkIS, &affineQuad);}
    4971:       if (affineQuad)  {DMSNESGetFEGeom(coordField, chunkIS, affineQuad, PETSC_TRUE, &affineGeom);}
    4972:     } else {
    4973:       for (f = 0; f < Nf; ++f) {
    4974:         if (quads[f]) {DMSNESGetFEGeom(coordField, chunkIS, quads[f], PETSC_TRUE, &geoms[f]);}
    4975:       }
    4976:     }
    4977:     /* Loop over fields */
    4978:     for (f = 0; f < Nf; ++f) {
    4979:       PetscFE         fe;
    4980:       PetscFEGeom    *geom = affineGeom ? affineGeom : geoms[f];
    4981:       PetscFEGeom    *chunkGeom = NULL, *remGeom = NULL;
    4982:       PetscQuadrature quad = affineQuad ? affineQuad : quads[f];
    4983:       PetscInt        numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset, Nq, Nb;

    4985:       PetscDSGetDiscretization(ds, f, (PetscObject *) &fe);
    4986:       if (!fe) continue;
    4987:       PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches);
    4988:       PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL);
    4989:       PetscFEGetDimension(fe, &Nb);
    4990:       blockSize = Nb;
    4991:       batchSize = numBlocks * blockSize;
    4992:       PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches);
    4993:       numChunks = numCells / (numBatches*batchSize);
    4994:       Ne        = numChunks*numBatches*batchSize;
    4995:       Nr        = numCells % (numBatches*batchSize);
    4996:       offset    = numCells - Nr;
    4997:       PetscFEGeomGetChunk(geom,0,offset,&chunkGeom);
    4998:       PetscFEGeomGetChunk(geom,offset,numCells,&remGeom);
    4999:       chunkGeom->isHybrid = remGeom->isHybrid = PETSC_TRUE;
    5000:       if (f == Nf-1) {
    5001:         key[2].field = f;
    5002:         PetscFEIntegrateHybridResidual(ds, key[2], Ne, chunkGeom, u, u_t, dsAux[2], a[2], t, elemVec);
    5003:         PetscFEIntegrateHybridResidual(ds, key[2], Nr, remGeom,  &u[offset*totDim], u_t ? &u_t[offset*totDim] : NULL, dsAux[2], &a[2][offset*totDimAux[2]], t, &elemVec[offset*totDim]);
    5004:       } else {
    5005:         key[0].field = f;
    5006:         key[1].field = f;
    5007:         PetscFEIntegrateHybridResidual(ds, key[0], Ne, chunkGeom, u, u_t, dsAux[0], a[0], t, elemVec);
    5008:         PetscFEIntegrateHybridResidual(ds, key[1], Ne, chunkGeom, u, u_t, dsAux[1], a[1], t, elemVec);
    5009:         PetscFEIntegrateHybridResidual(ds, key[0], Nr, remGeom,  &u[offset*totDim], u_t ? &u_t[offset*totDim] : NULL, dsAux[0], &a[0][offset*totDimAux[0]], t, &elemVec[offset*totDim]);
    5010:         PetscFEIntegrateHybridResidual(ds, key[1], Nr, remGeom,  &u[offset*totDim], u_t ? &u_t[offset*totDim] : NULL, dsAux[1], &a[1][offset*totDimAux[1]], t, &elemVec[offset*totDim]);
    5011:       }
    5012:       PetscFEGeomRestoreChunk(geom,offset,numCells,&remGeom);
    5013:       PetscFEGeomRestoreChunk(geom,0,offset,&chunkGeom);
    5014:     }
    5015:     /* Add elemVec to locX */
    5016:     for (c = cS; c < cE; ++c) {
    5017:       const PetscInt cell = cells ? cells[c] : c;
    5018:       const PetscInt cind = c - cStart;

    5020:       if (mesh->printFEM > 1) {DMPrintCellVector(cell, name, totDim, &elemVec[cind*totDim]);}
    5021:       if (ghostLabel) {
    5022:         PetscInt ghostVal;

    5024:         DMLabelGetValue(ghostLabel,cell,&ghostVal);
    5025:         if (ghostVal > 0) continue;
    5026:       }
    5027:       DMPlexVecSetClosure(dm, section, locF, cell, &elemVec[cind*totDim], ADD_ALL_VALUES);
    5028:     }
    5029:   }
    5030:   DMPlexRestoreCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]);
    5031:   DMPlexRestoreHybridAuxFields(dmAux, dsAux, cellIS, locA, a);
    5032:   DMRestoreWorkArray(dm, numCells*totDim, MPIU_SCALAR, &elemVec);
    5033:   PetscFree(faces);
    5034:   ISDestroy(&chunkIS);
    5035:   ISRestorePointRange(cellIS, &cStart, &cEnd, &cells);
    5036:   if (maxDegree <= 1) {
    5037:     DMSNESRestoreFEGeom(coordField,cellIS,affineQuad,PETSC_FALSE,&affineGeom);
    5038:     PetscQuadratureDestroy(&affineQuad);
    5039:   } else {
    5040:     for (f = 0; f < Nf; ++f) {
    5041:       if (geoms) {DMSNESRestoreFEGeom(coordField,cellIS,quads[f],PETSC_FALSE,&geoms[f]);}
    5042:       if (quads) {PetscQuadratureDestroy(&quads[f]);}
    5043:     }
    5044:     PetscFree2(quads,geoms);
    5045:   }
    5046:   PetscLogEventEnd(DMPLEX_ResidualFEM,dm,0,0,0);
    5047:   return(0);
    5048: }

    5050: PetscErrorCode DMPlexComputeBdJacobian_Single_Internal(DM dm, PetscReal t, PetscWeakForm wf, DMLabel label, PetscInt numValues, const PetscInt values[], PetscInt fieldI, Vec locX, Vec locX_t, PetscReal X_tShift, Mat Jac, Mat JacP, DMField coordField, IS facetIS)
    5051: {
    5052:   DM_Plex        *mesh = (DM_Plex *) dm->data;
    5053:   DM              plex = NULL, plexA = NULL, tdm;
    5054:   DMEnclosureType encAux;
    5055:   PetscDS         prob, probAux = NULL;
    5056:   PetscSection    section, sectionAux = NULL;
    5057:   PetscSection    globalSection, subSection = NULL;
    5058:   Vec             locA = NULL, tv;
    5059:   PetscScalar    *u = NULL, *u_t = NULL, *a = NULL, *elemMat = NULL;
    5060:   PetscInt        v;
    5061:   PetscInt        Nf, totDim, totDimAux = 0;
    5062:   PetscBool       isMatISP, transform;
    5063:   PetscErrorCode  ierr;

    5066:   DMConvert(dm, DMPLEX, &plex);
    5067:   DMHasBasisTransform(dm, &transform);
    5068:   DMGetBasisTransformDM_Internal(dm, &tdm);
    5069:   DMGetBasisTransformVec_Internal(dm, &tv);
    5070:   DMGetLocalSection(dm, &section);
    5071:   DMGetDS(dm, &prob);
    5072:   PetscDSGetNumFields(prob, &Nf);
    5073:   PetscDSGetTotalDimension(prob, &totDim);
    5074:   DMGetAuxiliaryVec(dm, label, values[0], &locA);
    5075:   if (locA) {
    5076:     DM dmAux;

    5078:     VecGetDM(locA, &dmAux);
    5079:     DMGetEnclosureRelation(dmAux, dm, &encAux);
    5080:     DMConvert(dmAux, DMPLEX, &plexA);
    5081:     DMGetDS(plexA, &probAux);
    5082:     PetscDSGetTotalDimension(probAux, &totDimAux);
    5083:     DMGetLocalSection(plexA, &sectionAux);
    5084:   }

    5086:   PetscObjectTypeCompare((PetscObject) JacP, MATIS, &isMatISP);
    5087:   DMGetGlobalSection(dm, &globalSection);
    5088:   if (isMatISP) {DMPlexGetSubdomainSection(dm, &subSection);}
    5089:   for (v = 0; v < numValues; ++v) {
    5090:     PetscFEGeom     *fgeom;
    5091:     PetscInt         maxDegree;
    5092:     PetscQuadrature  qGeom = NULL;
    5093:     IS               pointIS;
    5094:     const PetscInt  *points;
    5095:     PetscFormKey key;
    5096:     PetscInt         numFaces, face, Nq;

    5098:     key.label = label;
    5099:     key.value = values[v];
    5100:     key.part  = 0;
    5101:     DMLabelGetStratumIS(label, values[v], &pointIS);
    5102:     if (!pointIS) continue; /* No points with that id on this process */
    5103:     {
    5104:       IS isectIS;

    5106:       /* TODO: Special cases of ISIntersect where it is quick to check a prior if one is a superset of the other */
    5107:       ISIntersect_Caching_Internal(facetIS,pointIS,&isectIS);
    5108:       ISDestroy(&pointIS);
    5109:       pointIS = isectIS;
    5110:     }
    5111:     ISGetLocalSize(pointIS, &numFaces);
    5112:     ISGetIndices(pointIS, &points);
    5113:     PetscMalloc4(numFaces*totDim, &u, locX_t ? numFaces*totDim : 0, &u_t, numFaces*totDim*totDim, &elemMat, locA ? numFaces*totDimAux : 0, &a);
    5114:     DMFieldGetDegree(coordField,pointIS,NULL,&maxDegree);
    5115:     if (maxDegree <= 1) {
    5116:       DMFieldCreateDefaultQuadrature(coordField,pointIS,&qGeom);
    5117:     }
    5118:     if (!qGeom) {
    5119:       PetscFE fe;

    5121:       PetscDSGetDiscretization(prob, fieldI, (PetscObject *) &fe);
    5122:       PetscFEGetFaceQuadrature(fe, &qGeom);
    5123:       PetscObjectReference((PetscObject)qGeom);
    5124:     }
    5125:     PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL);
    5126:     DMSNESGetFEGeom(coordField,pointIS,qGeom,PETSC_TRUE,&fgeom);
    5127:     for (face = 0; face < numFaces; ++face) {
    5128:       const PetscInt point = points[face], *support;
    5129:       PetscScalar   *x     = NULL;
    5130:       PetscInt       i;

    5132:       DMPlexGetSupport(dm, point, &support);
    5133:       DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x);
    5134:       for (i = 0; i < totDim; ++i) u[face*totDim+i] = x[i];
    5135:       DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x);
    5136:       if (locX_t) {
    5137:         DMPlexVecGetClosure(plex, section, locX_t, support[0], NULL, &x);
    5138:         for (i = 0; i < totDim; ++i) u_t[face*totDim+i] = x[i];
    5139:         DMPlexVecRestoreClosure(plex, section, locX_t, support[0], NULL, &x);
    5140:       }
    5141:       if (locA) {
    5142:         PetscInt subp;
    5143:         DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp);
    5144:         DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x);
    5145:         for (i = 0; i < totDimAux; ++i) a[face*totDimAux+i] = x[i];
    5146:         DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x);
    5147:       }
    5148:     }
    5149:     PetscArrayzero(elemMat, numFaces*totDim*totDim);
    5150:     {
    5151:       PetscFE         fe;
    5152:       PetscInt        Nb;
    5153:       /* Conforming batches */
    5154:       PetscInt        numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
    5155:       /* Remainder */
    5156:       PetscFEGeom    *chunkGeom = NULL;
    5157:       PetscInt        fieldJ, Nr, offset;

    5159:       PetscDSGetDiscretization(prob, fieldI, (PetscObject *) &fe);
    5160:       PetscFEGetDimension(fe, &Nb);
    5161:       PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches);
    5162:       blockSize = Nb;
    5163:       batchSize = numBlocks * blockSize;
    5164:       PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches);
    5165:       numChunks = numFaces / (numBatches*batchSize);
    5166:       Ne        = numChunks*numBatches*batchSize;
    5167:       Nr        = numFaces % (numBatches*batchSize);
    5168:       offset    = numFaces - Nr;
    5169:       PetscFEGeomGetChunk(fgeom,0,offset,&chunkGeom);
    5170:       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
    5171:         key.field = fieldI*Nf+fieldJ;
    5172:         PetscFEIntegrateBdJacobian(prob, wf, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMat);
    5173:       }
    5174:       PetscFEGeomGetChunk(fgeom,offset,numFaces,&chunkGeom);
    5175:       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
    5176:         key.field = fieldI*Nf+fieldJ;
    5177:         PetscFEIntegrateBdJacobian(prob, wf, key, Nr, chunkGeom, &u[offset*totDim], u_t ? &u_t[offset*totDim] : NULL, probAux, a ? &a[offset*totDimAux] : NULL, t, X_tShift, &elemMat[offset*totDim*totDim]);
    5178:       }
    5179:       PetscFEGeomRestoreChunk(fgeom,offset,numFaces,&chunkGeom);
    5180:     }
    5181:     for (face = 0; face < numFaces; ++face) {
    5182:       const PetscInt point = points[face], *support;

    5184:       /* Transform to global basis before insertion in Jacobian */
    5185:       DMPlexGetSupport(plex, point, &support);
    5186:       if (transform) {DMPlexBasisTransformPointTensor_Internal(dm, tdm, tv, support[0], PETSC_TRUE, totDim, &elemMat[face*totDim*totDim]);}
    5187:       if (mesh->printFEM > 1) {DMPrintCellMatrix(point, "BdJacobian", totDim, totDim, &elemMat[face*totDim*totDim]);}
    5188:       if (!isMatISP) {
    5189:         DMPlexMatSetClosure(plex, section, globalSection, JacP, support[0], &elemMat[face*totDim*totDim], ADD_VALUES);
    5190:       } else {
    5191:         Mat lJ;

    5193:         MatISGetLocalMat(JacP, &lJ);
    5194:         DMPlexMatSetClosure(plex, section, subSection, lJ, support[0], &elemMat[face*totDim*totDim], ADD_VALUES);
    5195:       }
    5196:     }
    5197:     DMSNESRestoreFEGeom(coordField,pointIS,qGeom,PETSC_TRUE,&fgeom);
    5198:     PetscQuadratureDestroy(&qGeom);
    5199:     ISRestoreIndices(pointIS, &points);
    5200:     ISDestroy(&pointIS);
    5201:     PetscFree4(u, u_t, elemMat, a);
    5202:   }
    5203:   if (plex)  {DMDestroy(&plex);}
    5204:   if (plexA) {DMDestroy(&plexA);}
    5205:   return(0);
    5206: }

    5208: PetscErrorCode DMPlexComputeBdJacobianSingle(DM dm, PetscReal t, PetscWeakForm wf, DMLabel label, PetscInt numValues, const PetscInt values[], PetscInt field, Vec locX, Vec locX_t, PetscReal X_tShift, Mat Jac, Mat JacP)
    5209: {
    5210:   DMField        coordField;
    5211:   DMLabel        depthLabel;
    5212:   IS             facetIS;
    5213:   PetscInt       dim;

    5217:   DMGetDimension(dm, &dim);
    5218:   DMPlexGetDepthLabel(dm, &depthLabel);
    5219:   DMLabelGetStratumIS(depthLabel, dim-1, &facetIS);
    5220:   DMGetCoordinateField(dm, &coordField);
    5221:   DMPlexComputeBdJacobian_Single_Internal(dm, t, wf, label, numValues, values, field, locX, locX_t, X_tShift, Jac, JacP, coordField, facetIS);
    5222:   ISDestroy(&facetIS);
    5223:   return(0);
    5224: }

    5226: PetscErrorCode DMPlexComputeBdJacobian_Internal(DM dm, Vec locX, Vec locX_t, PetscReal t, PetscReal X_tShift, Mat Jac, Mat JacP, void *user)
    5227: {
    5228:   PetscDS          prob;
    5229:   PetscInt         dim, numBd, bd;
    5230:   DMLabel          depthLabel;
    5231:   DMField          coordField = NULL;
    5232:   IS               facetIS;
    5233:   PetscErrorCode   ierr;

    5236:   DMGetDS(dm, &prob);
    5237:   DMPlexGetDepthLabel(dm, &depthLabel);
    5238:   DMGetDimension(dm, &dim);
    5239:   DMLabelGetStratumIS(depthLabel, dim-1, &facetIS);
    5240:   PetscDSGetNumBoundary(prob, &numBd);
    5241:   DMGetCoordinateField(dm, &coordField);
    5242:   for (bd = 0; bd < numBd; ++bd) {
    5243:     PetscWeakForm           wf;
    5244:     DMBoundaryConditionType type;
    5245:     DMLabel                 label;
    5246:     const PetscInt         *values;
    5247:     PetscInt                fieldI, numValues;
    5248:     PetscObject             obj;
    5249:     PetscClassId            id;

    5251:     PetscDSGetBoundary(prob, bd, &wf, &type, NULL, &label, &numValues, &values, &fieldI, NULL, NULL, NULL, NULL, NULL);
    5252:     PetscDSGetDiscretization(prob, fieldI, &obj);
    5253:     PetscObjectGetClassId(obj, &id);
    5254:     if ((id != PETSCFE_CLASSID) || (type & DM_BC_ESSENTIAL)) continue;
    5255:     DMPlexComputeBdJacobian_Single_Internal(dm, t, wf, label, numValues, values, fieldI, locX, locX_t, X_tShift, Jac, JacP, coordField, facetIS);
    5256:   }
    5257:   ISDestroy(&facetIS);
    5258:   return(0);
    5259: }

    5261: PetscErrorCode DMPlexComputeJacobian_Internal(DM dm, PetscFormKey key, IS cellIS, PetscReal t, PetscReal X_tShift, Vec X, Vec X_t, Mat Jac, Mat JacP,void *user)
    5262: {
    5263:   DM_Plex        *mesh  = (DM_Plex *) dm->data;
    5264:   const char     *name  = "Jacobian";
    5265:   DM              dmAux = NULL, plex, tdm;
    5266:   DMEnclosureType encAux;
    5267:   Vec             A, tv;
    5268:   DMField         coordField;
    5269:   PetscDS         prob, probAux = NULL;
    5270:   PetscSection    section, globalSection, subSection, sectionAux;
    5271:   PetscScalar    *elemMat, *elemMatP, *elemMatD, *u, *u_t, *a = NULL;
    5272:   const PetscInt *cells;
    5273:   PetscInt        Nf, fieldI, fieldJ;
    5274:   PetscInt        totDim, totDimAux, cStart, cEnd, numCells, c;
    5275:   PetscBool       isMatIS, isMatISP, hasJac, hasPrec, hasDyn, hasFV = PETSC_FALSE, transform;
    5276:   PetscErrorCode  ierr;

    5279:   PetscLogEventBegin(DMPLEX_JacobianFEM,dm,0,0,0);
    5280:   ISGetLocalSize(cellIS, &numCells);
    5281:   ISGetPointRange(cellIS, &cStart, &cEnd, &cells);
    5282:   DMHasBasisTransform(dm, &transform);
    5283:   DMGetBasisTransformDM_Internal(dm, &tdm);
    5284:   DMGetBasisTransformVec_Internal(dm, &tv);
    5285:   DMGetLocalSection(dm, &section);
    5286:   PetscObjectTypeCompare((PetscObject) JacP, MATIS, &isMatISP);
    5287:   DMGetGlobalSection(dm, &globalSection);
    5288:   if (isMatISP) {DMPlexGetSubdomainSection(dm, &subSection);}
    5289:   DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob);
    5290:   PetscDSGetNumFields(prob, &Nf);
    5291:   PetscDSGetTotalDimension(prob, &totDim);
    5292:   PetscDSHasJacobian(prob, &hasJac);
    5293:   PetscDSHasJacobianPreconditioner(prob, &hasPrec);
    5294:   /* user passed in the same matrix, avoid double contributions and
    5295:      only assemble the Jacobian */
    5296:   if (hasJac && Jac == JacP) hasPrec = PETSC_FALSE;
    5297:   PetscDSHasDynamicJacobian(prob, &hasDyn);
    5298:   hasDyn = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
    5299:   DMGetAuxiliaryVec(dm, key.label, key.value, &A);
    5300:   if (A) {
    5301:     VecGetDM(A, &dmAux);
    5302:     DMGetEnclosureRelation(dmAux, dm, &encAux);
    5303:     DMConvert(dmAux, DMPLEX, &plex);
    5304:     DMGetLocalSection(plex, &sectionAux);
    5305:     DMGetDS(dmAux, &probAux);
    5306:     PetscDSGetTotalDimension(probAux, &totDimAux);
    5307:   }
    5308:   PetscMalloc5(numCells*totDim,&u,X_t ? numCells*totDim : 0,&u_t,hasJac ? numCells*totDim*totDim : 0,&elemMat,hasPrec ? numCells*totDim*totDim : 0, &elemMatP,hasDyn ? numCells*totDim*totDim : 0, &elemMatD);
    5309:   if (dmAux) {PetscMalloc1(numCells*totDimAux, &a);}
    5310:   DMGetCoordinateField(dm, &coordField);
    5311:   for (c = cStart; c < cEnd; ++c) {
    5312:     const PetscInt cell = cells ? cells[c] : c;
    5313:     const PetscInt cind = c - cStart;
    5314:     PetscScalar   *x = NULL,  *x_t = NULL;
    5315:     PetscInt       i;

    5317:     DMPlexVecGetClosure(dm, section, X, cell, NULL, &x);
    5318:     for (i = 0; i < totDim; ++i) u[cind*totDim+i] = x[i];
    5319:     DMPlexVecRestoreClosure(dm, section, X, cell, NULL, &x);
    5320:     if (X_t) {
    5321:       DMPlexVecGetClosure(dm, section, X_t, cell, NULL, &x_t);
    5322:       for (i = 0; i < totDim; ++i) u_t[cind*totDim+i] = x_t[i];
    5323:       DMPlexVecRestoreClosure(dm, section, X_t, cell, NULL, &x_t);
    5324:     }
    5325:     if (dmAux) {
    5326:       PetscInt subcell;
    5327:       DMGetEnclosurePoint(dmAux, dm, encAux, cell, &subcell);
    5328:       DMPlexVecGetClosure(plex, sectionAux, A, subcell, NULL, &x);
    5329:       for (i = 0; i < totDimAux; ++i) a[cind*totDimAux+i] = x[i];
    5330:       DMPlexVecRestoreClosure(plex, sectionAux, A, subcell, NULL, &x);
    5331:     }
    5332:   }
    5333:   if (hasJac)  {PetscArrayzero(elemMat,  numCells*totDim*totDim);}
    5334:   if (hasPrec) {PetscArrayzero(elemMatP, numCells*totDim*totDim);}
    5335:   if (hasDyn)  {PetscArrayzero(elemMatD, numCells*totDim*totDim);}
    5336:   for (fieldI = 0; fieldI < Nf; ++fieldI) {
    5337:     PetscClassId    id;
    5338:     PetscFE         fe;
    5339:     PetscQuadrature qGeom = NULL;
    5340:     PetscInt        Nb;
    5341:     /* Conforming batches */
    5342:     PetscInt        numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
    5343:     /* Remainder */
    5344:     PetscInt        Nr, offset, Nq;
    5345:     PetscInt        maxDegree;
    5346:     PetscFEGeom     *cgeomFEM, *chunkGeom = NULL, *remGeom = NULL;

    5348:     PetscDSGetDiscretization(prob, fieldI, (PetscObject *) &fe);
    5349:     PetscObjectGetClassId((PetscObject) fe, &id);
    5350:     if (id == PETSCFV_CLASSID) {hasFV = PETSC_TRUE; continue;}
    5351:     PetscFEGetDimension(fe, &Nb);
    5352:     PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches);
    5353:     DMFieldGetDegree(coordField,cellIS,NULL,&maxDegree);
    5354:     if (maxDegree <= 1) {
    5355:       DMFieldCreateDefaultQuadrature(coordField,cellIS,&qGeom);
    5356:     }
    5357:     if (!qGeom) {
    5358:       PetscFEGetQuadrature(fe,&qGeom);
    5359:       PetscObjectReference((PetscObject)qGeom);
    5360:     }
    5361:     PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL);
    5362:     DMSNESGetFEGeom(coordField,cellIS,qGeom,PETSC_FALSE,&cgeomFEM);
    5363:     blockSize = Nb;
    5364:     batchSize = numBlocks * blockSize;
    5365:     PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches);
    5366:     numChunks = numCells / (numBatches*batchSize);
    5367:     Ne        = numChunks*numBatches*batchSize;
    5368:     Nr        = numCells % (numBatches*batchSize);
    5369:     offset    = numCells - Nr;
    5370:     PetscFEGeomGetChunk(cgeomFEM,0,offset,&chunkGeom);
    5371:     PetscFEGeomGetChunk(cgeomFEM,offset,numCells,&remGeom);
    5372:     for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
    5373:       key.field = fieldI*Nf+fieldJ;
    5374:       if (hasJac) {
    5375:         PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMat);
    5376:         PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Nr, remGeom, &u[offset*totDim], u_t ? &u_t[offset*totDim] : NULL, probAux, &a[offset*totDimAux], t, X_tShift, &elemMat[offset*totDim*totDim]);
    5377:       }
    5378:       if (hasPrec) {
    5379:         PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_PRE, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatP);
    5380:         PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_PRE, key, Nr, remGeom, &u[offset*totDim], u_t ? &u_t[offset*totDim] : NULL, probAux, &a[offset*totDimAux], t, X_tShift, &elemMatP[offset*totDim*totDim]);
    5381:       }
    5382:       if (hasDyn) {
    5383:         PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatD);
    5384:         PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Nr, remGeom, &u[offset*totDim], u_t ? &u_t[offset*totDim] : NULL, probAux, &a[offset*totDimAux], t, X_tShift, &elemMatD[offset*totDim*totDim]);
    5385:       }
    5386:     }
    5387:     PetscFEGeomRestoreChunk(cgeomFEM,offset,numCells,&remGeom);
    5388:     PetscFEGeomRestoreChunk(cgeomFEM,0,offset,&chunkGeom);
    5389:     DMSNESRestoreFEGeom(coordField,cellIS,qGeom,PETSC_FALSE,&cgeomFEM);
    5390:     PetscQuadratureDestroy(&qGeom);
    5391:   }
    5392:   /*   Add contribution from X_t */
    5393:   if (hasDyn) {for (c = 0; c < numCells*totDim*totDim; ++c) elemMat[c] += X_tShift*elemMatD[c];}
    5394:   if (hasFV) {
    5395:     PetscClassId id;
    5396:     PetscFV      fv;
    5397:     PetscInt     offsetI, NcI, NbI = 1, fc, f;

    5399:     for (fieldI = 0; fieldI < Nf; ++fieldI) {
    5400:       PetscDSGetDiscretization(prob, fieldI, (PetscObject *) &fv);
    5401:       PetscDSGetFieldOffset(prob, fieldI, &offsetI);
    5402:       PetscObjectGetClassId((PetscObject) fv, &id);
    5403:       if (id != PETSCFV_CLASSID) continue;
    5404:       /* Put in the identity */
    5405:       PetscFVGetNumComponents(fv, &NcI);
    5406:       for (c = cStart; c < cEnd; ++c) {
    5407:         const PetscInt cind    = c - cStart;
    5408:         const PetscInt eOffset = cind*totDim*totDim;
    5409:         for (fc = 0; fc < NcI; ++fc) {
    5410:           for (f = 0; f < NbI; ++f) {
    5411:             const PetscInt i = offsetI + f*NcI+fc;
    5412:             if (hasPrec) {
    5413:               if (hasJac) {elemMat[eOffset+i*totDim+i] = 1.0;}
    5414:               elemMatP[eOffset+i*totDim+i] = 1.0;
    5415:             } else {elemMat[eOffset+i*totDim+i] = 1.0;}
    5416:           }
    5417:         }
    5418:       }
    5419:     }
    5420:     /* No allocated space for FV stuff, so ignore the zero entries */
    5421:     MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_TRUE);
    5422:   }
    5423:   /* Insert values into matrix */
    5424:   isMatIS = PETSC_FALSE;
    5425:   if (hasPrec && hasJac) {
    5426:     PetscObjectTypeCompare((PetscObject) JacP, MATIS, &isMatIS);
    5427:   }
    5428:   if (isMatIS && !subSection) {
    5429:     DMPlexGetSubdomainSection(dm, &subSection);
    5430:   }
    5431:   for (c = cStart; c < cEnd; ++c) {
    5432:     const PetscInt cell = cells ? cells[c] : c;
    5433:     const PetscInt cind = c - cStart;

    5435:     /* Transform to global basis before insertion in Jacobian */
    5436:     if (transform) {DMPlexBasisTransformPointTensor_Internal(dm, tdm, tv, cell, PETSC_TRUE, totDim, &elemMat[cind*totDim*totDim]);}
    5437:     if (hasPrec) {
    5438:       if (hasJac) {
    5439:         if (mesh->printFEM > 1) {DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[cind*totDim*totDim]);}
    5440:         if (!isMatIS) {
    5441:           DMPlexMatSetClosure(dm, section, globalSection, Jac, cell, &elemMat[cind*totDim*totDim], ADD_VALUES);
    5442:         } else {
    5443:           Mat lJ;

    5445:           MatISGetLocalMat(Jac,&lJ);
    5446:           DMPlexMatSetClosure(dm, section, subSection, lJ, cell, &elemMat[cind*totDim*totDim], ADD_VALUES);
    5447:         }
    5448:       }
    5449:       if (mesh->printFEM > 1) {DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatP[cind*totDim*totDim]);}
    5450:       if (!isMatISP) {
    5451:         DMPlexMatSetClosure(dm, section, globalSection, JacP, cell, &elemMatP[cind*totDim*totDim], ADD_VALUES);
    5452:       } else {
    5453:         Mat lJ;

    5455:         MatISGetLocalMat(JacP,&lJ);
    5456:         DMPlexMatSetClosure(dm, section, subSection, lJ, cell, &elemMatP[cind*totDim*totDim], ADD_VALUES);
    5457:       }
    5458:     } else {
    5459:       if (hasJac) {
    5460:         if (mesh->printFEM > 1) {DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[cind*totDim*totDim]);}
    5461:         if (!isMatISP) {
    5462:           DMPlexMatSetClosure(dm, section, globalSection, JacP, cell, &elemMat[cind*totDim*totDim], ADD_VALUES);
    5463:         } else {
    5464:           Mat lJ;

    5466:           MatISGetLocalMat(JacP,&lJ);
    5467:           DMPlexMatSetClosure(dm, section, subSection, lJ, cell, &elemMat[cind*totDim*totDim], ADD_VALUES);
    5468:         }
    5469:       }
    5470:     }
    5471:   }
    5472:   ISRestorePointRange(cellIS, &cStart, &cEnd, &cells);
    5473:   if (hasFV) {MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_FALSE);}
    5474:   PetscFree5(u,u_t,elemMat,elemMatP,elemMatD);
    5475:   if (dmAux) {
    5476:     PetscFree(a);
    5477:     DMDestroy(&plex);
    5478:   }
    5479:   /* Compute boundary integrals */
    5480:   DMPlexComputeBdJacobian_Internal(dm, X, X_t, t, X_tShift, Jac, JacP, user);
    5481:   /* Assemble matrix */
    5482:   if (hasJac && hasPrec) {
    5483:     MatAssemblyBegin(Jac, MAT_FINAL_ASSEMBLY);
    5484:     MatAssemblyEnd(Jac, MAT_FINAL_ASSEMBLY);
    5485:   }
    5486:   MatAssemblyBegin(JacP, MAT_FINAL_ASSEMBLY);
    5487:   MatAssemblyEnd(JacP, MAT_FINAL_ASSEMBLY);
    5488:   PetscLogEventEnd(DMPLEX_JacobianFEM,dm,0,0,0);
    5489:   return(0);
    5490: }

    5492: PetscErrorCode DMPlexComputeJacobian_Hybrid_Internal(DM dm, PetscFormKey key[], IS cellIS, PetscReal t, PetscReal X_tShift, Vec locX, Vec locX_t, Mat Jac, Mat JacP, void *user)
    5493: {
    5494:   DM_Plex         *mesh          = (DM_Plex *) dm->data;
    5495:   const char      *name          = "Hybrid Jacobian";
    5496:   DM               dmAux[3]      = {NULL, NULL, NULL};
    5497:   DMLabel          ghostLabel    = NULL;
    5498:   DM               plex          = NULL;
    5499:   DM               plexA         = NULL;
    5500:   PetscDS          ds            = NULL;
    5501:   PetscDS          dsAux[3]      = {NULL, NULL, NULL};
    5502:   Vec              locA[3]       = {NULL, NULL, NULL};
    5503:   PetscSection     section       = NULL;
    5504:   PetscSection     sectionAux[3] = {NULL, NULL, NULL};
    5505:   DMField          coordField    = NULL;
    5506:   PetscScalar     *u = NULL, *u_t, *a[3];
    5507:   PetscScalar     *elemMat, *elemMatP;
    5508:   PetscSection     globalSection, subSection;
    5509:   IS               chunkIS;
    5510:   const PetscInt  *cells;
    5511:   PetscInt        *faces;
    5512:   PetscInt         cStart, cEnd, numCells;
    5513:   PetscInt         Nf, fieldI, fieldJ, totDim, totDimAux[3], numChunks, cellChunkSize, chunk;
    5514:   PetscInt         maxDegree = PETSC_MAX_INT;
    5515:   PetscQuadrature  affineQuad = NULL, *quads = NULL;
    5516:   PetscFEGeom     *affineGeom = NULL, **geoms = NULL;
    5517:   PetscBool        repeatKey = PETSC_FALSE, isMatIS = PETSC_FALSE, isMatISP = PETSC_FALSE, hasBdJac, hasBdPrec;
    5518:   PetscErrorCode   ierr;

    5521:   /* If keys are the same, both kernel will be run using the first key */
    5522:   repeatKey = ((key[0].label == key[1].label) && (key[0].value == key[1].value)) ? PETSC_TRUE : PETSC_FALSE;
    5523:   PetscLogEventBegin(DMPLEX_JacobianFEM,dm,0,0,0);
    5524:   ISGetLocalSize(cellIS, &numCells);
    5525:   ISGetPointRange(cellIS, &cStart, &cEnd, &cells);
    5526:   DMConvert(dm, DMPLEX, &plex);
    5527:   DMGetSection(dm, &section);
    5528:   DMGetGlobalSection(dm, &globalSection);
    5529:   DMGetLabel(dm, "ghost", &ghostLabel);
    5530:   DMGetCellDS(dm, cStart, &ds);
    5531:   PetscDSGetNumFields(ds, &Nf);
    5532:   PetscDSGetTotalDimension(ds, &totDim);
    5533:   PetscDSHasBdJacobian(ds, &hasBdJac);
    5534:   PetscDSHasBdJacobianPreconditioner(ds, &hasBdPrec);
    5535:   PetscObjectTypeCompare((PetscObject) JacP, MATIS, &isMatISP);
    5536:   if (isMatISP)               {DMPlexGetSubdomainSection(plex, &subSection);}
    5537:   if (hasBdPrec && hasBdJac)  {PetscObjectTypeCompare((PetscObject) JacP, MATIS, &isMatIS);}
    5538:   if (isMatIS && !subSection) {DMPlexGetSubdomainSection(plex, &subSection);}
    5539:   DMGetAuxiliaryVec(dm, key[2].label, key[2].value, &locA[2]);
    5540:   if (locA[2]) {
    5541:     VecGetDM(locA[2], &dmAux[2]);
    5542:     DMConvert(dmAux[2], DMPLEX, &plexA);
    5543:     DMGetSection(dmAux[2], &sectionAux[2]);
    5544:     DMGetCellDS(dmAux[2], cStart, &dsAux[2]);
    5545:     PetscDSGetTotalDimension(dsAux[2], &totDimAux[2]);
    5546:     {
    5547:       const PetscInt *cone;
    5548:       PetscInt        c;

    5550:       DMPlexGetCone(dm, cStart, &cone);
    5551:       for (c = 0; c < 2; ++c) {
    5552:         const PetscInt *support;
    5553:         PetscInt ssize, s;

    5555:         DMPlexGetSupport(dm, cone[c], &support);
    5556:         DMPlexGetSupportSize(dm, cone[c], &ssize);
    5557:         if (ssize != 2) SETERRQ3(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %D from cell %D has support size %D != 2", cone[c], cStart, ssize);
    5558:         if      (support[0] == cStart) s = 1;
    5559:         else if (support[1] == cStart) s = 0;
    5560:         else SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %D does not have cell %D in its support", cone[c], cStart);
    5561:         DMGetAuxiliaryVec(dm, key[c].label, key[c].value, &locA[c]);
    5562:         if (locA[c]) {VecGetDM(locA[c], &dmAux[c]);}
    5563:         else         {dmAux[c] = dmAux[2];}
    5564:         DMGetCellDS(dmAux[c], support[s], &dsAux[c]);
    5565:         PetscDSGetTotalDimension(dsAux[c], &totDimAux[c]);
    5566:       }
    5567:     }
    5568:   }
    5569:   DMGetCoordinateField(dm, &coordField);
    5570:   DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree);
    5571:   if (maxDegree > 1) {
    5572:     PetscInt f;
    5573:     PetscCalloc2(Nf, &quads, Nf, &geoms);
    5574:     for (f = 0; f < Nf; ++f) {
    5575:       PetscFE fe;

    5577:       PetscDSGetDiscretization(ds, f, (PetscObject *) &fe);
    5578:       if (fe) {
    5579:         PetscFEGetQuadrature(fe, &quads[f]);
    5580:         PetscObjectReference((PetscObject) quads[f]);
    5581:       }
    5582:     }
    5583:   }
    5584:   cellChunkSize = numCells;
    5585:   numChunks     = !numCells ? 0 : PetscCeilReal(((PetscReal) numCells)/cellChunkSize);
    5586:   PetscCalloc1(2*cellChunkSize, &faces);
    5587:   ISCreateGeneral(PETSC_COMM_SELF, cellChunkSize, faces, PETSC_USE_POINTER, &chunkIS);
    5588:   DMPlexGetCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]);
    5589:   DMPlexGetHybridAuxFields(dm, dmAux, dsAux, cellIS, locA, a);
    5590:   DMGetWorkArray(dm, hasBdJac  ? cellChunkSize*totDim*totDim : 0, MPIU_SCALAR, &elemMat);
    5591:   DMGetWorkArray(dm, hasBdPrec ? cellChunkSize*totDim*totDim : 0, MPIU_SCALAR, &elemMatP);
    5592:   for (chunk = 0; chunk < numChunks; ++chunk) {
    5593:     PetscInt cS = cStart+chunk*cellChunkSize, cE = PetscMin(cS+cellChunkSize, cEnd), numCells = cE - cS, c;

    5595:     if (hasBdJac)  {PetscMemzero(elemMat,  numCells*totDim*totDim * sizeof(PetscScalar));}
    5596:     if (hasBdPrec) {PetscMemzero(elemMatP, numCells*totDim*totDim * sizeof(PetscScalar));}
    5597:     /* Get faces */
    5598:     for (c = cS; c < cE; ++c) {
    5599:       const PetscInt  cell = cells ? cells[c] : c;
    5600:       const PetscInt *cone;
    5601:       DMPlexGetCone(plex, cell, &cone);
    5602:       faces[(c-cS)*2+0] = cone[0];
    5603:       faces[(c-cS)*2+1] = cone[1];
    5604:     }
    5605:     ISGeneralSetIndices(chunkIS, cellChunkSize, faces, PETSC_USE_POINTER);
    5606:     if (maxDegree <= 1) {
    5607:       if (!affineQuad) {DMFieldCreateDefaultQuadrature(coordField, chunkIS, &affineQuad);}
    5608:       if (affineQuad)  {DMSNESGetFEGeom(coordField, chunkIS, affineQuad, PETSC_TRUE, &affineGeom);}
    5609:     } else {
    5610:       PetscInt f;
    5611:       for (f = 0; f < Nf; ++f) {
    5612:         if (quads[f]) {DMSNESGetFEGeom(coordField, chunkIS, quads[f], PETSC_TRUE, &geoms[f]);}
    5613:       }
    5614:     }

    5616:     for (fieldI = 0; fieldI < Nf; ++fieldI) {
    5617:       PetscFE         feI;
    5618:       PetscFEGeom    *geom = affineGeom ? affineGeom : geoms[fieldI];
    5619:       PetscFEGeom    *chunkGeom = NULL, *remGeom = NULL;
    5620:       PetscQuadrature quad = affineQuad ? affineQuad : quads[fieldI];
    5621:       PetscInt        numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset, Nq, Nb;

    5623:       PetscDSGetDiscretization(ds, fieldI, (PetscObject *) &feI);
    5624:       if (!feI) continue;
    5625:       PetscFEGetTileSizes(feI, NULL, &numBlocks, NULL, &numBatches);
    5626:       PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL);
    5627:       PetscFEGetDimension(feI, &Nb);
    5628:       blockSize = Nb;
    5629:       batchSize = numBlocks * blockSize;
    5630:       PetscFESetTileSizes(feI, blockSize, numBlocks, batchSize, numBatches);
    5631:       numChunks = numCells / (numBatches*batchSize);
    5632:       Ne        = numChunks*numBatches*batchSize;
    5633:       Nr        = numCells % (numBatches*batchSize);
    5634:       offset    = numCells - Nr;
    5635:       PetscFEGeomGetChunk(geom,0,offset,&chunkGeom);
    5636:       PetscFEGeomGetChunk(geom,offset,numCells,&remGeom);
    5637:       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
    5638:         PetscFE feJ;

    5640:         PetscDSGetDiscretization(ds, fieldJ, (PetscObject *) &feJ);
    5641:         if (!feJ) continue;
    5642:         if (fieldI == Nf-1) {
    5643:           key[2].field = fieldI*Nf+fieldJ;
    5644:           if (hasBdJac) {
    5645:             PetscFEIntegrateHybridJacobian(ds, PETSCFE_JACOBIAN, key[2], Ne, chunkGeom, u, u_t, dsAux[2], a[2], t, X_tShift, elemMat);
    5646:             PetscFEIntegrateHybridJacobian(ds, PETSCFE_JACOBIAN, key[2], Nr, remGeom, &u[offset*totDim], u_t ? &u_t[offset*totDim] : NULL, dsAux[2], &a[2][offset*totDimAux[2]], t, X_tShift, &elemMat[offset*totDim*totDim]);
    5647:           }
    5648:           if (hasBdPrec) {
    5649:             PetscFEIntegrateHybridJacobian(ds, PETSCFE_JACOBIAN_PRE, key[2], Ne, chunkGeom, u, u_t, dsAux[2], a[2], t, X_tShift, elemMatP);
    5650:             PetscFEIntegrateHybridJacobian(ds, PETSCFE_JACOBIAN_PRE, key[2], Nr, remGeom, &u[offset*totDim], u_t ? &u_t[offset*totDim] : NULL, dsAux[2], &a[2][offset*totDimAux[2]], t, X_tShift, &elemMatP[offset*totDim*totDim]);
    5651:           }
    5652:         } else {
    5653:           key[0].field = fieldI*Nf+fieldJ;
    5654:           key[1].field = fieldI*Nf+fieldJ;
    5655:           if (hasBdJac) {
    5656:             PetscFEIntegrateHybridJacobian(ds, PETSCFE_JACOBIAN, key[0], Ne, chunkGeom, u, u_t, dsAux[0], a[0], t, X_tShift, elemMat);
    5657:             PetscFEIntegrateHybridJacobian(ds, PETSCFE_JACOBIAN, key[0], Nr, remGeom, &u[offset*totDim], u_t ? &u_t[offset*totDim] : NULL, dsAux[0], &a[0][offset*totDimAux[0]], t, X_tShift, &elemMat[offset*totDim*totDim]);
    5658:             if (!repeatKey) {
    5659:               PetscFEIntegrateHybridJacobian(ds, PETSCFE_JACOBIAN, key[1], Ne, chunkGeom, u, u_t, dsAux[1], a[1], t, X_tShift, elemMat);
    5660:               PetscFEIntegrateHybridJacobian(ds, PETSCFE_JACOBIAN, key[1], Nr, remGeom, &u[offset*totDim], u_t ? &u_t[offset*totDim] : NULL, dsAux[1], &a[1][offset*totDimAux[1]], t, X_tShift, &elemMat[offset*totDim*totDim]);
    5661:             }
    5662:           }
    5663:           if (hasBdPrec) {
    5664:             PetscFEIntegrateHybridJacobian(ds, PETSCFE_JACOBIAN_PRE, key[0], Ne, chunkGeom, u, u_t, dsAux[0], a[0], t, X_tShift, elemMatP);
    5665:             PetscFEIntegrateHybridJacobian(ds, PETSCFE_JACOBIAN_PRE, key[0], Nr, remGeom, &u[offset*totDim], u_t ? &u_t[offset*totDim] : NULL, dsAux[0], &a[0][offset*totDimAux[0]], t, X_tShift, &elemMatP[offset*totDim*totDim]);
    5666:             if (!repeatKey) {
    5667:               PetscFEIntegrateHybridJacobian(ds, PETSCFE_JACOBIAN_PRE, key[1], Ne, chunkGeom, u, u_t, dsAux[1], a[1], t, X_tShift, elemMatP);
    5668:               PetscFEIntegrateHybridJacobian(ds, PETSCFE_JACOBIAN_PRE, key[1], Nr, remGeom, &u[offset*totDim], u_t ? &u_t[offset*totDim] : NULL, dsAux[1], &a[1][offset*totDimAux[1]], t, X_tShift, &elemMatP[offset*totDim*totDim]);
    5669:             }
    5670:           }
    5671:         }
    5672:       }
    5673:       PetscFEGeomRestoreChunk(geom,offset,numCells,&remGeom);
    5674:       PetscFEGeomRestoreChunk(geom,0,offset,&chunkGeom);
    5675:     }
    5676:     /* Insert values into matrix */
    5677:     for (c = cS; c < cE; ++c) {
    5678:       const PetscInt cell = cells ? cells[c] : c;
    5679:       const PetscInt cind = c - cS;

    5681:       if (hasBdPrec) {
    5682:         if (hasBdJac) {
    5683:           if (mesh->printFEM > 1) {DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[cind*totDim*totDim]);}
    5684:           if (!isMatIS) {
    5685:             DMPlexMatSetClosure(plex, section, globalSection, Jac, cell, &elemMat[cind*totDim*totDim], ADD_VALUES);
    5686:           } else {
    5687:             Mat lJ;

    5689:             MatISGetLocalMat(Jac,&lJ);
    5690:             DMPlexMatSetClosure(plex, section, subSection, lJ, cell, &elemMat[cind*totDim*totDim], ADD_VALUES);
    5691:           }
    5692:         }
    5693:         if (mesh->printFEM > 1) {DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatP[cind*totDim*totDim]);}
    5694:         if (!isMatISP) {
    5695:           DMPlexMatSetClosure(plex, section, globalSection, JacP, cell, &elemMatP[cind*totDim*totDim], ADD_VALUES);
    5696:         } else {
    5697:           Mat lJ;

    5699:           MatISGetLocalMat(JacP,&lJ);
    5700:           DMPlexMatSetClosure(plex, section, subSection, lJ, cell, &elemMatP[cind*totDim*totDim], ADD_VALUES);
    5701:         }
    5702:       } else if (hasBdJac) {
    5703:         if (mesh->printFEM > 1) {DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[cind*totDim*totDim]);}
    5704:         if (!isMatISP) {
    5705:           DMPlexMatSetClosure(plex, section, globalSection, JacP, cell, &elemMat[cind*totDim*totDim], ADD_VALUES);
    5706:         } else {
    5707:           Mat lJ;

    5709:           MatISGetLocalMat(JacP,&lJ);
    5710:           DMPlexMatSetClosure(plex, section, subSection, lJ, cell, &elemMat[cind*totDim*totDim], ADD_VALUES);
    5711:         }
    5712:       }
    5713:     }
    5714:   }
    5715:   DMPlexRestoreCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]);
    5716:   DMPlexRestoreHybridAuxFields(dmAux, dsAux, cellIS, locA, a);
    5717:   DMRestoreWorkArray(dm, hasBdJac  ? cellChunkSize*totDim*totDim : 0, MPIU_SCALAR, &elemMat);
    5718:   DMRestoreWorkArray(dm, hasBdPrec ? cellChunkSize*totDim*totDim : 0, MPIU_SCALAR, &elemMatP);
    5719:   PetscFree(faces);
    5720:   ISDestroy(&chunkIS);
    5721:   ISRestorePointRange(cellIS, &cStart, &cEnd, &cells);
    5722:   if (maxDegree <= 1) {
    5723:     DMSNESRestoreFEGeom(coordField,cellIS,affineQuad,PETSC_FALSE,&affineGeom);
    5724:     PetscQuadratureDestroy(&affineQuad);
    5725:   } else {
    5726:     PetscInt f;
    5727:     for (f = 0; f < Nf; ++f) {
    5728:       if (geoms) {DMSNESRestoreFEGeom(coordField,cellIS,quads[f],PETSC_FALSE, &geoms[f]);}
    5729:       if (quads) {PetscQuadratureDestroy(&quads[f]);}
    5730:     }
    5731:     PetscFree2(quads,geoms);
    5732:   }
    5733:   if (dmAux[2]) {DMDestroy(&plexA);}
    5734:   DMDestroy(&plex);
    5735:   /* Assemble matrix */
    5736:   if (hasBdJac && hasBdPrec) {
    5737:     MatAssemblyBegin(Jac, MAT_FINAL_ASSEMBLY);
    5738:     MatAssemblyEnd(Jac, MAT_FINAL_ASSEMBLY);
    5739:   }
    5740:   MatAssemblyBegin(JacP, MAT_FINAL_ASSEMBLY);
    5741:   MatAssemblyEnd(JacP, MAT_FINAL_ASSEMBLY);
    5742:   PetscLogEventEnd(DMPLEX_JacobianFEM,dm,0,0,0);
    5743:   return(0);
    5744: }

    5746: /*
    5747:   DMPlexComputeJacobian_Action_Internal - Form the local portion of the Jacobian action Z = J(X) Y at the local solution X using pointwise functions specified by the user.

    5749:   Input Parameters:
    5750: + dm     - The mesh
    5751: . key    - The PetscWeakFormKey indcating where integration should happen
    5752: . cellIS - The cells to integrate over
    5753: . t      - The time
    5754: . X_tShift - The multiplier for the Jacobian with repsect to X_t
    5755: . X      - Local solution vector
    5756: . X_t    - Time-derivative of the local solution vector
    5757: . Y      - Local input vector
    5758: - user   - the user context

    5760:   Output Parameter:
    5761: . Z - Local output vector

    5763:   Note:
    5764:   We form the residual one batch of elements at a time. This allows us to offload work onto an accelerator,
    5765:   like a GPU, or vectorize on a multicore machine.
    5766: */
    5767: PetscErrorCode DMPlexComputeJacobian_Action_Internal(DM dm, PetscFormKey key, IS cellIS, PetscReal t, PetscReal X_tShift, Vec X, Vec X_t, Vec Y, Vec Z, void *user)
    5768: {
    5769:   DM_Plex        *mesh  = (DM_Plex *) dm->data;
    5770:   const char     *name  = "Jacobian";
    5771:   DM              dmAux = NULL, plex, plexAux = NULL;
    5772:   DMEnclosureType encAux;
    5773:   Vec             A;
    5774:   DMField         coordField;
    5775:   PetscDS         prob, probAux = NULL;
    5776:   PetscQuadrature quad;
    5777:   PetscSection    section, globalSection, sectionAux;
    5778:   PetscScalar    *elemMat, *elemMatD, *u, *u_t, *a = NULL, *y, *z;
    5779:   const PetscInt *cells;
    5780:   PetscInt        Nf, fieldI, fieldJ;
    5781:   PetscInt        totDim, totDimAux = 0, cStart, cEnd, numCells, c;
    5782:   PetscBool       hasDyn;
    5783:   PetscErrorCode  ierr;

    5786:   PetscLogEventBegin(DMPLEX_JacobianFEM,dm,0,0,0);
    5787:   DMConvert(dm, DMPLEX, &plex);
    5788:   if (!cellIS) {
    5789:     PetscInt depth;

    5791:     DMPlexGetDepth(plex, &depth);
    5792:     DMGetStratumIS(plex, "dim", depth, &cellIS);
    5793:     if (!cellIS) {DMGetStratumIS(plex, "depth", depth, &cellIS);}
    5794:   } else {
    5795:     PetscObjectReference((PetscObject) cellIS);
    5796:   }
    5797:   ISGetLocalSize(cellIS, &numCells);
    5798:   ISGetPointRange(cellIS, &cStart, &cEnd, &cells);
    5799:   DMGetLocalSection(dm, &section);
    5800:   DMGetGlobalSection(dm, &globalSection);
    5801:   DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob);
    5802:   PetscDSGetNumFields(prob, &Nf);
    5803:   PetscDSGetTotalDimension(prob, &totDim);
    5804:   PetscDSHasDynamicJacobian(prob, &hasDyn);
    5805:   hasDyn = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
    5806:   DMGetAuxiliaryVec(dm, key.label, key.value, &A);
    5807:   if (A) {
    5808:     VecGetDM(A, &dmAux);
    5809:     DMGetEnclosureRelation(dmAux, dm, &encAux);
    5810:     DMConvert(dmAux, DMPLEX, &plexAux);
    5811:     DMGetLocalSection(plexAux, &sectionAux);
    5812:     DMGetDS(dmAux, &probAux);
    5813:     PetscDSGetTotalDimension(probAux, &totDimAux);
    5814:   }
    5815:   VecSet(Z, 0.0);
    5816:   PetscMalloc6(numCells*totDim,&u,X_t ? numCells*totDim : 0,&u_t,numCells*totDim*totDim,&elemMat,hasDyn ? numCells*totDim*totDim : 0, &elemMatD,numCells*totDim,&y,totDim,&z);
    5817:   if (dmAux) {PetscMalloc1(numCells*totDimAux, &a);}
    5818:   DMGetCoordinateField(dm, &coordField);
    5819:   for (c = cStart; c < cEnd; ++c) {
    5820:     const PetscInt cell = cells ? cells[c] : c;
    5821:     const PetscInt cind = c - cStart;
    5822:     PetscScalar   *x = NULL,  *x_t = NULL;
    5823:     PetscInt       i;

    5825:     DMPlexVecGetClosure(plex, section, X, cell, NULL, &x);
    5826:     for (i = 0; i < totDim; ++i) u[cind*totDim+i] = x[i];
    5827:     DMPlexVecRestoreClosure(plex, section, X, cell, NULL, &x);
    5828:     if (X_t) {
    5829:       DMPlexVecGetClosure(plex, section, X_t, cell, NULL, &x_t);
    5830:       for (i = 0; i < totDim; ++i) u_t[cind*totDim+i] = x_t[i];
    5831:       DMPlexVecRestoreClosure(plex, section, X_t, cell, NULL, &x_t);
    5832:     }
    5833:     if (dmAux) {
    5834:       PetscInt subcell;
    5835:       DMGetEnclosurePoint(dmAux, dm, encAux, cell, &subcell);
    5836:       DMPlexVecGetClosure(plexAux, sectionAux, A, subcell, NULL, &x);
    5837:       for (i = 0; i < totDimAux; ++i) a[cind*totDimAux+i] = x[i];
    5838:       DMPlexVecRestoreClosure(plexAux, sectionAux, A, subcell, NULL, &x);
    5839:     }
    5840:     DMPlexVecGetClosure(plex, section, Y, cell, NULL, &x);
    5841:     for (i = 0; i < totDim; ++i) y[cind*totDim+i] = x[i];
    5842:     DMPlexVecRestoreClosure(plex, section, Y, cell, NULL, &x);
    5843:   }
    5844:   PetscArrayzero(elemMat, numCells*totDim*totDim);
    5845:   if (hasDyn)  {PetscArrayzero(elemMatD, numCells*totDim*totDim);}
    5846:   for (fieldI = 0; fieldI < Nf; ++fieldI) {
    5847:     PetscFE  fe;
    5848:     PetscInt Nb;
    5849:     /* Conforming batches */
    5850:     PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
    5851:     /* Remainder */
    5852:     PetscInt Nr, offset, Nq;
    5853:     PetscQuadrature qGeom = NULL;
    5854:     PetscInt    maxDegree;
    5855:     PetscFEGeom *cgeomFEM, *chunkGeom = NULL, *remGeom = NULL;

    5857:     PetscDSGetDiscretization(prob, fieldI, (PetscObject *) &fe);
    5858:     PetscFEGetQuadrature(fe, &quad);
    5859:     PetscFEGetDimension(fe, &Nb);
    5860:     PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches);
    5861:     DMFieldGetDegree(coordField,cellIS,NULL,&maxDegree);
    5862:     if (maxDegree <= 1) {DMFieldCreateDefaultQuadrature(coordField,cellIS,&qGeom);}
    5863:     if (!qGeom) {
    5864:       PetscFEGetQuadrature(fe,&qGeom);
    5865:       PetscObjectReference((PetscObject)qGeom);
    5866:     }
    5867:     PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL);
    5868:     DMSNESGetFEGeom(coordField,cellIS,qGeom,PETSC_FALSE,&cgeomFEM);
    5869:     blockSize = Nb;
    5870:     batchSize = numBlocks * blockSize;
    5871:     PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches);
    5872:     numChunks = numCells / (numBatches*batchSize);
    5873:     Ne        = numChunks*numBatches*batchSize;
    5874:     Nr        = numCells % (numBatches*batchSize);
    5875:     offset    = numCells - Nr;
    5876:     PetscFEGeomGetChunk(cgeomFEM,0,offset,&chunkGeom);
    5877:     PetscFEGeomGetChunk(cgeomFEM,offset,numCells,&remGeom);
    5878:     for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
    5879:       key.field = fieldI*Nf + fieldJ;
    5880:       PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMat);
    5881:       PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Nr, remGeom, &u[offset*totDim], u_t ? &u_t[offset*totDim] : NULL, probAux, &a[offset*totDimAux], t, X_tShift, &elemMat[offset*totDim*totDim]);
    5882:       if (hasDyn) {
    5883:         PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatD);
    5884:         PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Nr, remGeom, &u[offset*totDim], u_t ? &u_t[offset*totDim] : NULL, probAux, &a[offset*totDimAux], t, X_tShift, &elemMatD[offset*totDim*totDim]);
    5885:       }
    5886:     }
    5887:     PetscFEGeomRestoreChunk(cgeomFEM,offset,numCells,&remGeom);
    5888:     PetscFEGeomRestoreChunk(cgeomFEM,0,offset,&chunkGeom);
    5889:     DMSNESRestoreFEGeom(coordField,cellIS,qGeom,PETSC_FALSE,&cgeomFEM);
    5890:     PetscQuadratureDestroy(&qGeom);
    5891:   }
    5892:   if (hasDyn) {
    5893:     for (c = 0; c < numCells*totDim*totDim; ++c) elemMat[c] += X_tShift*elemMatD[c];
    5894:   }
    5895:   for (c = cStart; c < cEnd; ++c) {
    5896:     const PetscInt     cell = cells ? cells[c] : c;
    5897:     const PetscInt     cind = c - cStart;
    5898:     const PetscBLASInt M = totDim, one = 1;
    5899:     const PetscScalar  a = 1.0, b = 0.0;

    5901:     PetscStackCallBLAS("BLASgemv", BLASgemv_("N", &M, &M, &a, &elemMat[cind*totDim*totDim], &M, &y[cind*totDim], &one, &b, z, &one));
    5902:     if (mesh->printFEM > 1) {
    5903:       DMPrintCellMatrix(c, name, totDim, totDim, &elemMat[cind*totDim*totDim]);
    5904:       DMPrintCellVector(c, "Y",  totDim, &y[cind*totDim]);
    5905:       DMPrintCellVector(c, "Z",  totDim, z);
    5906:     }
    5907:     DMPlexVecSetClosure(dm, section, Z, cell, z, ADD_VALUES);
    5908:   }
    5909:   PetscFree6(u,u_t,elemMat,elemMatD,y,z);
    5910:   if (mesh->printFEM) {
    5911:     PetscPrintf(PetscObjectComm((PetscObject)Z), "Z:\n");
    5912:     VecView(Z, NULL);
    5913:   }
    5914:   PetscFree(a);
    5915:   ISDestroy(&cellIS);
    5916:   DMDestroy(&plexAux);
    5917:   DMDestroy(&plex);
    5918:   PetscLogEventEnd(DMPLEX_JacobianFEM,dm,0,0,0);
    5919:   return(0);
    5920: }